dcowden / cadquery

CadQuery-- a parametric cad script framework
Other
432 stars 56 forks source link

Feature Request: Polar Array #299

Closed bweissinger closed 5 years ago

bweissinger commented 5 years ago

I would like to propose the addition of a polar array operation.

I have done some testing and it seems like this would work well and has similar syntax to the rectangular array function.

    def polarArray(self, radius, angle, startAngle, count, fill=True):
        """
        Creates an polar array of points and pushes them onto the stack.
        The 0 degree reference angle is located along the local X-axis.

        :param radius: Radius of the array. ( > 0)
        :param angle: The angle (degrees) to fill with elements. A positive
            value will fill in the counter-clockwise direction, negative
            value fills clockwise. If fill is false, it is the angle
            between elements. (!= 0)
        :param startAngle: Starting angle (degrees) of array. 0 degrees is
            situated along local X-axis. (-360 to 360)
        :param count: Number of elements in array. ( > 1 )
        """

        if radius <= 0 or count <= 0:
            raise ValueError("Radius and count must be > 0 ")

        if count <= 1:
            raise ValueError("Must have more than 1 element in array")

        if angle == 0:
            raise ValueError("Angle cannot be 0")

        if startAngle < -360 or startAngle > 360:
            raise ValueError("Start angle must be in range: -360 to 360")

        # First element at start angle, convert to cartesian coords
        x = radius * math.cos(math.radians(startAngle))
        y = radius * math.sin(math.radians(startAngle))
        points = [(x, y)]

        """
        Calculate angle between elements, not needed if fill == False. Due to
        the first element being located at 0, the actual angle between
        elements must be angle / (count - 1). Example, a fill angle of 90 with
        4 elements will set elements at 0, 30, 60, and 90 as expected. Angle /
        count would place elements at 0, 22.5, 45, and 67.5. If fill angle
        is a full circle, the correction is not needed.
        """
        if fill is True:
            if angle == 360 or angle == -360:
                angle = angle / count
            else:
                angle = angle / (count - 1)

        # Add additional elements
        for i in range(1, count):
            phi = math.radians(startAngle + (angle * i))
            x = radius * math.cos(phi)
            y = radius * math.sin(phi)
            points.append((x, y))

        return self.pushPoints(points)

It allows for users to create arrays starting at a specific angle, i.e. if you know a bolt hole pattern is set at 45deg. It also allows for either filling (say 6 even spaced elements in 180deg), or stepping by a specific amount (such as every 15deg). Additionally, stepping can be done in either direction.

jmwright commented 5 years ago

+1 on this. Polar arrays are something I've used quite a bit over the years.

jmwright commented 5 years ago

And I like your implementation. I'll be happy to review a PR if you want to create one.

Just FYI - We do request that a test(s) be included with any new functionality.