Example #1
0
class PolarArray(APlm.AttachableFeature):
    """The Lattice Polar Array of placements"""
    def derivedInit(self, selfobj):
        super(PolarArray, self).derivedInit(selfobj)
        selfobj.addProperty(
            'App::PropertyLength', 'Radius', "Polar Array",
            "Radius of the array (set to zero for just rotation).")
        selfobj.Radius = 3
        selfobj.addProperty(
            'App::PropertyEnumeration', 'UseArcRange', "Polar Array",
            "If attachment mode is concentric, supporting arc's range can be used as array's Span or Step."
        )
        selfobj.UseArcRange = ['ignore', 'as Span', 'as Step']
        selfobj.addProperty(
            'App::PropertyBool', 'UseArcRadius', "Polar Array",
            "If True, and attachment mode is concentric, supporting arc's radius is used as array radius."
        )
        selfobj.addProperty(
            'App::PropertyEnumeration', 'OrientMode', "Polar Array",
            "Orientation of placements. Zero - aligns with origin. Static - aligns with self placement."
        )
        selfobj.OrientMode = [
            'Zero', 'Static', 'Radial', 'Vortex', 'Centrifuge', 'Launchpad',
            'Dominoes'
        ]
        selfobj.OrientMode = 'Radial'
        selfobj.addProperty('App::PropertyBool', 'Reverse', "Polar Array",
                            "Reverses array direction.")
        selfobj.addProperty('App::PropertyBool', 'FlipX', "Polar Array",
                            "Reverses x axis of every placement.")
        selfobj.addProperty('App::PropertyBool', 'FlipZ', "Polar Array",
                            "Reverses z axis of every placement.")

        self.assureGenerator(selfobj)

        selfobj.ValuesSource = 'Generator'
        selfobj.SpanStart = 0
        selfobj.SpanEnd = 360
        selfobj.EndInclusive = False
        selfobj.Step = 55
        selfobj.Count = 7

    def assureGenerator(self, selfobj):
        '''Adds an instance of value series generator, if one doesn't exist yet.'''
        if hasattr(self, 'generator'):
            return
        self.generator = ValueSeriesGenerator(selfobj)
        self.generator.addProperties(groupname="Polar Array",
                                     groupname_gen="Lattice Series Generator",
                                     valuesdoc="List of angles, in degrees.",
                                     valuestype='App::PropertyFloat')
        self.updateReadonlyness(selfobj)

    def updateReadonlyness(self, selfobj):
        self.generator.updateReadonlyness()

        arc = self.fetchArc(selfobj) if self.isOnArc(selfobj) else None
        selfobj.setEditorMode('Radius',
                              1 if arc and selfobj.UseArcRadius else 0)
        self.generator.setPropertyWritable(
            'SpanEnd',
            False if arc and selfobj.UseArcRange == 'as Span' else True)
        self.generator.setPropertyWritable(
            'SpanStart',
            False if arc and selfobj.UseArcRange == 'as Span' else True)
        self.generator.setPropertyWritable(
            'Step',
            False if arc and selfobj.UseArcRange == 'as Step' else True)

    def fetchArc(self, selfobj):
        """returns None, or tuple (arc_span, arc_radius)"""
        if selfobj.Support:
            lnkobj, sub = selfobj.Support[0]
            sub = sub[0]
            #resolve the link
            return fetchArc(lnkobj, sub)

    def derivedExecute(self, selfobj):
        self.assureGenerator(selfobj)
        self.updateReadonlyness(selfobj)

        selfobj.positionBySupport()

        # Apply links
        if (selfobj.UseArcRange != 'ignore'
                or selfobj.UseArcRadius) and self.isOnArc(selfobj):
            range, radius = self.fetchArc(selfobj)
            if selfobj.UseArcRange == 'as Span':
                selfobj.SpanStart = 0.0
                selfobj.SpanEnd = range / turn * 360
            elif selfobj.UseArcRange == 'as Step':
                selfobj.Step = range / turn * 360
            if selfobj.UseArcRadius:
                selfobj.Radius = radius
        self.generator.execute()

        # cache properties into variables
        radius = float(selfobj.Radius)
        values = [float(strv) for strv in selfobj.Values]

        irot = selfobj.Placement.inverse().Rotation

        # compute internam placement, one behind OrientMode property
        baseplm = App.Placement()
        is_zero = selfobj.OrientMode == 'Zero'
        is_static = selfobj.OrientMode == 'Static'
        if is_zero or is_static:
            pass
        elif selfobj.OrientMode == 'Radial':
            baseplm = App.Placement()
        elif selfobj.OrientMode == 'Vortex':
            baseplm = App.Placement(V(),
                                    App.Rotation(V(0, 1, 0), V(), V(0, 0, 1)))
        elif selfobj.OrientMode == 'Centrifuge':
            baseplm = App.Placement(V(),
                                    App.Rotation(V(0, 1, 0), V(), V(-1, 0, 0)))
        elif selfobj.OrientMode == 'Launchpad':
            baseplm = App.Placement(V(),
                                    App.Rotation(V(0, 0, 1), V(), V(1, 0, 0)))
        elif selfobj.OrientMode == 'Dominoes':
            baseplm = App.Placement(V(),
                                    App.Rotation(V(0, 0, 1), V(), V(0, -1, 0)))
        else:
            raise NotImplementedError()

        flipX = selfobj.FlipX
        flipZ = selfobj.FlipZ
        flipY = flipX ^ flipZ
        flipplm = App.Placement(
            V(),
            App.Rotation(V(-1 if flipX else 1, 0, 0),
                         V(0, -1 if flipY else 1, 0),
                         V(0, 0, -1 if flipZ else 1)))

        baseplm = baseplm.multiply(flipplm)

        # Make the array
        on_arc = self.isOnArc(selfobj)
        angleplus = -90.0 if on_arc else 0.0
        mm = -1.0 if selfobj.Reverse else +1.0
        output = []  # list of placements
        for ang in values:
            localrot = App.Rotation(App.Vector(0, 0, 1), ang * mm + angleplus)
            localtransl = localrot.multVec(App.Vector(radius, 0, 0))
            localplm = App.Placement(localtransl, localrot)
            resultplm = localplm.multiply(baseplm)
            if is_zero:
                resultplm.Rotation = irot
                resultplm = resultplm.multiply(flipplm)
            elif is_static:
                resultplm.Rotation = App.Rotation()
                resultplm = resultplm.multiply(flipplm)
            output.append(resultplm)

        return output

    def isOnArc(self, selfobj):
        return selfobj.MapMode == 'Concentric' and len(
            linkSubList_convertToOldStyle(selfobj.Support)) == 1

    def onChanged(self, selfobj, propname):
        super(PolarArray, self).onChanged(selfobj, propname)
        if 'Restore' in selfobj.State: return
        if propname == 'Reverse' and self.isOnArc(selfobj):
            if selfobj.Reverse == True and abs(selfobj.MapPathParameter -
                                               0.0) < ParaConfusion:
                selfobj.MapPathParameter = 1.0
            elif selfobj.Reverse == False and abs(selfobj.MapPathParameter -
                                                  1.0) < ParaConfusion:
                selfobj.MapPathParameter = 0.0