Example #1
0
 def derivedExecute(self,obj):
     #validity check
     nonLattices = []
     for iArr in range(0, len(obj.Links)):
         link = obj.Links[iArr]
         if not latticeBaseFeature.isObjectLattice(link):
             nonLattices.append(link.Label)
     if len(nonLattices) > 0:
         latticeExecuter.warning(obj, "Only lattice objects are expected to be linked as arrays in JoinArrays. There are "
                                 +len(nonLattices)+" objects which are not lattice objects. Results may me unexpected.")
     
     #extract placements
     listlistPlms = []
     lengths = []
     for link in obj.Links:
         leaves = LCE.AllLeaves(link.Shape)
         listlistPlms.append([child.Placement for child in leaves])
         lengths.append(len(leaves))
     
     #processing
     output = [] #list of placements
     if obj.Interleave:
         for l in lengths[1:]:
             if l != lengths[0]:
                 latticeExecuter.warning(obj,"Array lengths are unequal: "+repr(lengths)+". Interleaving will be inconsistent.")
                 break
         
         for iItem in range(0,max(lengths)):
             for list in listlistPlms:
                 if iItem < len(list):
                     output.append(list[iItem])
     else:
         for list in listlistPlms:
             output.extend(list)
     return output
Example #2
0
    def Activated(self):
        if len(FreeCADGui.Selection.getSelection()) == 1 :
            FreeCAD.ActiveDocument.openTransaction("Explode Array")
            latticeExecuter.globalIsCreatingLatticeFeature = True
            try:
                obj = FreeCADGui.Selection.getSelection()[0]
                if not latticeBaseFeature.isObjectLattice(obj):
                    latticeExecuter.warning(None,"ExplodeArray expects a lattice object; a generic shape was provided instead. Results may be unexpected.")
                sh = obj.Shape
                n_elem = len(LCE.AllLeaves(sh))
                latticeExecuter.globalIsCreatingLatticeFeature = False
                for i in range(0, n_elem):
                    af = makeArrayFilter(name = 'Element')
                    af.Label = u'Element' + unicode(i)
                    af.Base = obj
                    af.FilterType = 'specific items'
                    af.items = str(i)
                    af.SingleByDesign = True
                    af.ViewObject.DontUnhideOnDelete = True
                FreeCAD.ActiveDocument.recompute()
                obj.ViewObject.hide()
            except Exception:
                FreeCAD.ActiveDocument.abortTransaction()
                raise
            finally:
                latticeExecuter.globalIsCreatingLatticeFeature = False
            FreeCAD.ActiveDocument.commitTransaction()

        else:
            mb = QtGui.QMessageBox()
            mb.setIcon(mb.Icon.Warning)
            mb.setText(translate("Lattice_ArrayFilter", "Select a lattice object, first!", None))
            mb.setWindowTitle(translate("Lattice_ArrayFilter","Bad selection", None))
            mb.exec_()
Example #3
0
    def derivedExecute(self,obj):
        #validity check
        if not latticeBaseFeature.isObjectLattice(obj.Base):
            latticeExecuter.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.")

        output = [] #variable to receive the final list of placements
        leaves = LCE.AllLeaves(obj.Base.Shape)
        input = [leaf.Placement for leaf in leaves]
        if obj.FilterType == 'bypass':
            output = input
        elif obj.FilterType == 'specific items':
            flags = [False] * len(input)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    output.append(input[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    ifrom = None   if len(r_v[0].strip()) == 0 else   int(r_v[0])                    
                    ito = None     if len(r_v[1].strip()) == 0 else   int(r_v[1])
                    istep = None   if len(r_v[2].strip()) == 0 else   int(r_v[2])
                    output=output+input[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:'+r)
            if obj.Invert :
                output = []
                for i in xrange(0,len(input)):
                    if not flags[i]:
                        output.append(input[i])
        elif obj.FilterType == 'collision-pass':
            stencil = obj.Stencil.Shape
            for plm in input:
                pnt = Part.Vertex(plm.Base)
                d = pnt.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    output.append(plm)
        elif obj.FilterType == 'window-distance':
            vals = [0.0] * len(input)
            for i in xrange(0,len(input)):
                if obj.FilterType == 'window-distance':
                    pnt = Part.Vertex(input[i].Base)
                    vals[i] = pnt.distToShape(obj.Stencil.Shape)[0]
            
            valFrom = obj.WindowFrom
            valTo = obj.WindowTo
            
            for i in xrange(0,len(input)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    output.append(input[i])
        else:
            raise ValueError('Filter mode not implemented:'+obj.FilterType)
                            
        return output
Example #4
0
    def derivedExecute(self, obj):
        # cache stuff
        base = obj.Base.Shape

        tool = obj.Tool.Shape
        if tool.ShapeType != 'Compound':
            tool = Part.makeCompound([tool])
        if obj.FlattenToolHierarchy:
            toolChildren = LCE.AllLeaves(tool)
        else:
            toolChildren = tool.childShapes()

        # validity logic
        if not latticeBaseFeature.isObjectLattice(obj.Tool):
            latticeExecuter.warning(
                obj,
                'Tool is not a lattice object. Results may be unexpected.\n')
        outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base)

        plmMatcher = App.Placement(
        )  #extra placement, that makes first item to preserve its original placement
        if obj.KeepBaseFirstItemPos:
            plmMatcher = toolChildren[0].Placement.inverse()

        # Pre-collect base placement list, if base is a lattice. For speed.
        if outputIsLattice:
            baseLeaves = LCE.AllLeaves(base)
            basePlms = []
            for leaf in baseLeaves:
                basePlms.append(plmMatcher.multiply(leaf.Placement))
            baseLeaves = None  #free memory

        # initialize output containers and loop variables
        outputShapes = []  #output list of shapes
        outputPlms = []  #list of placements

        # the essence
        for toolChild in toolChildren:
            #cache some stuff
            toolPlm = toolChild.Placement

            if outputIsLattice:
                for basePlm in basePlms:
                    outputPlms.append(toolPlm.multiply(basePlm))
            else:
                outputShape = base.copy()
                outputShape.Placement = toolPlm.multiply(
                    plmMatcher.multiply(outputShape.Placement))
                outputShapes.append(outputShape)

        if outputIsLattice:
            return outputPlms
        else:
            obj.Shape = Part.makeCompound(outputShapes)
            return None
Example #5
0
    def derivedExecute(self,obj):
        # cache stuff
        base = obj.Base.Shape
        
        tool = obj.Tool.Shape
        if tool.ShapeType != 'Compound':
            tool = Part.makeCompound([tool])
        if obj.FlattenToolHierarchy:
            toolChildren = LCE.AllLeaves(tool)
        else:
            toolChildren = tool.childShapes()
        
        # validity logic
        if not latticeBaseFeature.isObjectLattice(obj.Tool):
            latticeExecuter.warning(obj, 'Tool is not a lattice object. Results may be unexpected.\n')
        outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base)
        
        plmMatcher = App.Placement() #extra placement, that makes first item to preserve its original placement
        if obj.KeepBaseFirstItemPos:
            plmMatcher = toolChildren[0].Placement.inverse()
        
        # Pre-collect base placement list, if base is a lattice. For speed.
        if outputIsLattice:
            baseLeaves = LCE.AllLeaves(base)
            basePlms = []
            for leaf in baseLeaves:
                basePlms.append(plmMatcher.multiply(leaf.Placement))
            baseLeaves = None #free memory
        
        # initialize output containers and loop variables
        outputShapes = [] #output list of shapes
        outputPlms = [] #list of placements
        
        # the essence
        for toolChild in toolChildren:
            #cache some stuff
            toolPlm = toolChild.Placement

            if outputIsLattice:
                for basePlm in basePlms:
                    outputPlms.append(toolPlm.multiply(basePlm))
            else:
                outputShape = base.copy()
                outputShape.Placement = toolPlm.multiply(plmMatcher.multiply(outputShape.Placement))
                outputShapes.append(outputShape)
            
        if outputIsLattice:
            return outputPlms
        else:
            obj.Shape = Part.makeCompound(outputShapes)
            return None
Example #6
0
    def execute(self, obj):
        # please, don't override. Override derivedExecute instead.

        plms = self.derivedExecute(obj)

        if plms is not None:
            obj.NumElements = len(plms)
            shapes = []
            markerSize = obj.MarkerSize
            if markerSize < DistConfusion:
                markerSize = getMarkerSizeEstimate(plms)
            marker = latticeMarkers.getPlacementMarker(
                scale=markerSize, markerID=obj.MarkerShape)
            #FIXME: make hierarchy-aware
            if obj.SingleByDesign:
                if len(plms) != 1:
                    latticeExecuter.warning(
                        obj,
                        "Multiple placements are being fed, but object is single by design. Only fisrt placement will be used..."
                    )
                obj.Shape = marker.copy()
                obj.Placement = plms[0]
            else:
                for plm in plms:
                    sh = marker.copy()
                    sh.Placement = plm
                    shapes.append(sh)

                if len(shapes) == 0:
                    obj.Shape = latticeMarkers.getNullShapeShape(markerSize)
                    raise ValueError(
                        'Lattice object is null'
                    )  #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

                sh = Part.makeCompound(shapes)
                obj.Shape = sh

            if obj.isLattice == 'Auto-Off':
                obj.isLattice = 'Auto-On'

        else:
            # DerivedExecute didn't return anything. Thus we assume it
            # has assigned the shape, and thus we don't do anything.
            # Moreover, we assume that it is no longer a lattice object, so:
            if obj.isLattice == 'Auto-On':
                obj.isLattice = 'Auto-Off'
            obj.NumElements = len(obj.Shape.childShapes(False, False))

        return
    def execute(self,obj):
        # please, don't override. Override derivedExecute instead.

        plms = self.derivedExecute(obj)

        if plms is not None:
            obj.NumElements = len(plms)
            shapes = []
            markerSize = obj.MarkerSize
            if markerSize < DistConfusion:
                markerSize = getMarkerSizeEstimate(plms)
            marker = latticeMarkers.getPlacementMarker(scale= markerSize, markerID= obj.MarkerShape)
            #FIXME: make hierarchy-aware
            if obj.SingleByDesign:
                if len(plms) != 1:
                    latticeExecuter.warning(obj,"Multiple placements are being fed, but object is single by design. Only fisrt placement will be used...")
                obj.Shape = marker.copy()
                obj.Placement = plms[0]
            else:
                for plm in plms:
                    sh = marker.copy()
                    sh.Placement = plm
                    shapes.append(sh)
                    
                if len(shapes) == 0:
                    obj.Shape = latticeMarkers.getNullShapeShape(markerSize)
                    raise ValueError('Lattice object is null') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
                
                sh = Part.makeCompound(shapes)
                obj.Shape = sh

            if obj.isLattice == 'Auto-Off':
                obj.isLattice = 'Auto-On'
            
        else:
            # DerivedExecute didn't return anything. Thus we assume it 
            # has assigned the shape, and thus we don't do anything.
            # Moreover, we assume that it is no longer a lattice object, so:
            if obj.isLattice == 'Auto-On':
                obj.isLattice = 'Auto-Off'
            obj.NumElements = len(obj.Shape.childShapes(False,False))
        
        return
Example #8
0
 def derivedExecute(self,obj):
     # cache stuff
     base = obj.Base.Shape
     if not latticeBaseFeature.isObjectLattice(obj.Base):
         latticeExecuter.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n")
     baseChildren = LCE.AllLeaves(base)
                     
     #cache mode comparisons, for speed
     posIsInvert = obj.TranslateMode == 'invert'
     posIsKeep = obj.TranslateMode == 'keep'
     posIsReset = obj.TranslateMode == 'reset'
     
     oriIsInvert = obj.OrientMode == 'invert'
     oriIsKeep = obj.OrientMode == 'keep'
     oriIsReset = obj.OrientMode == 'reset'
     
     # initialize output containers and loop variables
     outputPlms = [] #list of placements
     
     # the essence
     for child in baseChildren:
         pos = App.Vector()
         ori = App.Rotation()
         inverted = child.Placement.inverse()
         if posIsInvert:
             pos = inverted.Base
         elif posIsKeep:
             pos = child.Placement.Base
         elif posIsReset:
             pass
         
         if oriIsInvert:
             ori = inverted.Rotation
         elif oriIsKeep:
             ori = child.Placement.Rotation
         elif oriIsReset:
             pass
             
         plm = App.Placement(pos, ori)
         outputPlms.append(plm)
     return outputPlms
    def derivedExecute(self,obj):
        #validity check
        if not latticeBaseFeature.isObjectLattice(obj.Base):
            latticeExecuter.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.")
        
        toolShape = obj.Tool.Shape
        if latticeBaseFeature.isObjectLattice(obj.Tool):
            latticeExecuter.warning(obj,"A lattice object was provided as Tool. It will be converted into points; orientations will be ignored.")
            leaves = LCE.AllLeaves(toolShape)
            points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves]
            toolShape = Part.makeCompound(points)

        leaves = LCE.AllLeaves(obj.Base.Shape)
        input = [leaf.Placement for leaf in leaves]

        output = [] #variable to receive the final list of placements
        
        #cache settings
        elev = float(obj.PosElevation)
        posIsKeep = obj.PositionMode == 'keep'
        posIsProjected = obj.PositionMode == 'projected'
        posIsMixed = obj.PositionMode == 'mixed'
        mixF = float(obj.PosMixFraction)
        oriIsKeep = obj.OrientMode == 'keep'
        oriIsAlongGap = obj.OrientMode == 'along gap'
        oriIsTangentPlane = obj.OrientMode == 'tangent plane'
        oriIsAlongU = obj.OrientMode == 'along u'
        oriIsAlongV = obj.OrientMode == 'along v'
        
        isMultiSol = obj.Multisolution == 'use all'
        
        for plm in input:
            v = Part.Vertex(plm.Base)
            projection = v.distToShape(toolShape)
            (dist, gaps, infos) = projection
            for iSol in range(0,len(gaps)):
                (posKeep, posPrj) = gaps[iSol]
                (dummy, dummy, dummy, el_topo, el_index, el_params) = infos[iSol]

                # Fetch all possible parameters (some may not be required, depending on modes)
                normal = posKeep - posPrj
                if normal.Length < DistConfusion:
                    normal = None
                
                tangU = None
                tangV = None
                if el_topo == 'Face':
                    face = toolShape.Faces[el_index]
                    if normal is None:
                        normal = face.normalAt(*el_params)
                    (tangU, tangV) = face.tangentAt(*el_params)
                elif el_topo == "Edge":
                    edge = toolShape.Edges[el_index]
                    tangU = edge.tangentAt(el_params)
                
                if normal is not None:
                    normal.normalize()
                
                #mode logic - compute new placement
                if posIsKeep:
                    pos = plm.Base
                elif posIsProjected:
                    pos = posPrj
                elif posIsMixed:
                    pos = posKeep*mixF + posPrj*(1-mixF)
                else:
                    raise ValueError("Positioning mode not implemented: " + obj.PositionMode )
                
                if abs(elev) > DistConfusion:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for nonzero position elevation.")
                    pos += normal * elev
                    
                    
                if oriIsKeep:
                    ori = plm.Rotation
                elif oriIsAlongGap:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("X",XAx= normal*(-1.0))
                elif oriIsTangentPlane:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("Z",ZAx= normal)
                elif oriIsAlongU:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    if tangU is None:
                        raise ValueError("TangentU vector not available for point on " + el_topo +". TangentU vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",ZAx= normal, XAx= tangU)
                elif oriIsAlongV:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    if tangV is None:
                        raise ValueError("TangentV vector not available for point on " + el_topo +". TangentV vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",ZAx= normal, XAx= tangV)
                else:
                    raise ValueError("Orientation mode not implemented: " + obj.OrientMode )
                
                output.append(App.Placement(pos,ori))
                
                if not isMultiSol:
                    break
        
        return output
Example #10
0
    def execute(self,obj):
        #validity check
        if isObjectLattice(obj.Base):
            import latticeExecuter
            latticeExecuter.warning(obj,"A generic shape is expected, but a lattice object was supplied. It will be treated as a generic shape.")

        rst = [] #variable to receive the final list of shapes
        shps = obj.Base.Shape.childShapes()
        if obj.FilterType == 'bypass':
            rst = shps
        elif obj.FilterType == 'specific items':
            rst = []
            flags = [False] * len(shps)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    rst.append(shps[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    ifrom = None   if len(r_v[0].strip()) == 0 else   int(r_v[0])                    
                    ito = None     if len(r_v[1].strip()) == 0 else   int(r_v[1])
                    istep = None   if len(r_v[2].strip()) == 0 else   int(r_v[2])
                    rst=rst+shps[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:'+r)
            if obj.Invert :
                rst = []
                for i in xrange(0,len(shps)):
                    if not flags[i]:
                        rst.append(shps[i])
        elif obj.FilterType == 'collision-pass':
            stencil = obj.Stencil.Shape
            for s in shps:
                d = s.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    rst.append(s)
        elif obj.FilterType == 'window-volume' or obj.FilterType == 'window-area' or obj.FilterType == 'window-length' or obj.FilterType == 'window-distance':
            vals = [0.0] * len(shps)
            for i in xrange(0,len(shps)):
                if obj.FilterType == 'window-volume':
                    vals[i] = shps[i].Volume
                elif obj.FilterType == 'window-area':
                    vals[i] = shps[i].Area
                elif obj.FilterType == 'window-length':
                    vals[i] = shps[i].Length
                elif obj.FilterType == 'window-distance':
                    vals[i] = shps[i].distToShape(obj.Stencil.Shape)[0]
            
            maxval = max(vals)
            if obj.Stencil:
                if obj.FilterType == 'window-volume':
                    vals[i] = obj.Stencil.Shape.Volume
                elif obj.FilterType == 'window-area':
                    vals[i] = obj.Stencil.Shape.Area
                elif obj.FilterType == 'window-length':
                    vals[i] = obj.Stencil.Shape.Length
            if obj.OverrideMaxVal:
                maxval = obj.OverrideMaxVal
            
            valFrom = obj.WindowFrom / 100.0 * maxval
            valTo = obj.WindowTo / 100.0 * maxval
            
            for i in xrange(0,len(shps)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    rst.append(shps[i])
        else:
            raise ValueError('Filter mode not implemented:'+obj.FilterType)
        
        if len(rst) == 0:
            scale = 1.0
            if not obj.Base.Shape.isNull():
                scale = obj.Base.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            print scale
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('Nothing passes through the filter') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
        
        if len(rst) > 1:
            obj.Shape = Part.makeCompound(rst)
        else: # don't make compound of one shape, output it directly
            sh = rst[0]
            sh.transformShape(sh.Placement.toMatrix(),True) #True = make copy
            sh.Placement = FreeCAD.Placement()
            obj.Shape = sh
            
        return
Example #11
0
    def derivedExecute(self, obj):
        #validity check
        if not latticeBaseFeature.isObjectLattice(obj.Base):
            latticeExecuter.warning(
                obj,
                "A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected."
            )

        toolShape = obj.Tool.Shape
        if latticeBaseFeature.isObjectLattice(obj.Tool):
            latticeExecuter.warning(
                obj,
                "A lattice object was provided as Tool. It will be converted into points; orientations will be ignored."
            )
            leaves = LCE.AllLeaves(toolShape)
            points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves]
            toolShape = Part.makeCompound(points)

        leaves = LCE.AllLeaves(obj.Base.Shape)
        input = [leaf.Placement for leaf in leaves]

        output = []  #variable to receive the final list of placements

        #cache settings
        elev = float(obj.PosElevation)
        posIsKeep = obj.PositionMode == 'keep'
        posIsProjected = obj.PositionMode == 'projected'
        posIsMixed = obj.PositionMode == 'mixed'
        mixF = float(obj.PosMixFraction)
        oriIsKeep = obj.OrientMode == 'keep'
        oriIsAlongGap = obj.OrientMode == 'along gap'
        oriIsTangentPlane = obj.OrientMode == 'tangent plane'
        oriIsAlongU = obj.OrientMode == 'along u'
        oriIsAlongV = obj.OrientMode == 'along v'

        isMultiSol = obj.Multisolution == 'use all'

        for plm in input:
            v = Part.Vertex(plm.Base)
            projection = v.distToShape(toolShape)
            (dist, gaps, infos) = projection
            for iSol in range(0, len(gaps)):
                (posKeep, posPrj) = gaps[iSol]
                (dummy, dummy, dummy, el_topo, el_index,
                 el_params) = infos[iSol]

                # Fetch all possible parameters (some may not be required, depending on modes)
                normal = posKeep - posPrj
                if normal.Length < DistConfusion:
                    normal = None

                tangU = None
                tangV = None
                if el_topo == 'Face':
                    face = toolShape.Faces[el_index]
                    if normal is None:
                        normal = face.normalAt(*el_params)
                    (tangU, tangV) = face.tangentAt(*el_params)
                elif el_topo == "Edge":
                    edge = toolShape.Edges[el_index]
                    tangU = edge.tangentAt(el_params)

                if normal is not None:
                    normal.normalize()

                #mode logic - compute new placement
                if posIsKeep:
                    pos = plm.Base
                elif posIsProjected:
                    pos = posPrj
                elif posIsMixed:
                    pos = posKeep * mixF + posPrj * (1 - mixF)
                else:
                    raise ValueError("Positioning mode not implemented: " +
                                     obj.PositionMode)

                if abs(elev) > DistConfusion:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for nonzero position elevation."
                        )
                    pos += normal * elev

                if oriIsKeep:
                    ori = plm.Rotation
                elif oriIsAlongGap:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("X",
                                                                XAx=normal *
                                                                (-1.0))
                elif oriIsTangentPlane:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("Z",
                                                                ZAx=normal)
                elif oriIsAlongU:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    if tangU is None:
                        raise ValueError(
                            "TangentU vector not available for point on " +
                            el_topo +
                            ". TangentU vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",
                                                                ZAx=normal,
                                                                XAx=tangU)
                elif oriIsAlongV:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    if tangV is None:
                        raise ValueError(
                            "TangentV vector not available for point on " +
                            el_topo +
                            ". TangentV vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",
                                                                ZAx=normal,
                                                                XAx=tangV)
                else:
                    raise ValueError("Orientation mode not implemented: " +
                                     obj.OrientMode)

                output.append(App.Placement(pos, ori))

                if not isMultiSol:
                    break

        return output
Example #12
0
    def derivedExecute(self, obj):
        # cache stuff
        base = obj.Base.Shape
        if not latticeBaseFeature.isObjectLattice(obj.Base):
            latticeExecuter.warning(
                obj,
                "Base is not a lattice, but lattice is expected. Results may be unexpected.\n"
            )
        input = [leaf.Placement for leaf in LCE.AllLeaves(base)]

        if len(input) < 2:
            raise ValueError(
                "At least 2 placements ar needed to interpolate; there are just "
                + str(len(input)) + " in base array.")
        if obj.NumberSamples < 2:
            raise ValueError("Can output no less than 2 samples; " +
                             str(obj.NumberSamples) + " was requested.")

        #cache mode comparisons, for speed
        posIsInterpolate = obj.TranslateMode == 'interpolate'
        posIsReset = obj.TranslateMode == 'reset'

        oriIsInterpolate = obj.OrientMode == 'interpolate'
        oriIsReset = obj.OrientMode == 'reset'

        # construct interpolation functions
        #  prepare lists of input samples
        IArray = [float(i) for i in range(0, len(input))]
        XArray = [plm.Base.x for plm in input]
        YArray = [plm.Base.y for plm in input]
        ZArray = [plm.Base.z for plm in input]
        QArrays = [[], [], [], []]
        prevQ = [0.0] * 4
        for plm in input:
            Q = plm.Rotation.Q
            #test if quaernion has changed sign compared to previous one.
            # Quaternions of opposite sign are equivalent in terms of rotation,
            # but sign changes confuse interpolation, so we are detecting sign
            # changes and discarding them
            if dotProduct(Q, prevQ) < -ParaConfusion:
                Q = [-v for v in Q]
            for iQ in [0, 1, 2, 3]:
                QArrays[iQ].append(Q[iQ])
            prevQ = Q

        #  constuct function objects
        if posIsInterpolate:
            FX = LIU.InterpolateF(IArray, XArray)
            FY = LIU.InterpolateF(IArray, YArray)
            FZ = LIU.InterpolateF(IArray, ZArray)
        if oriIsInterpolate:
            FQs = []
            for iQ in [0, 1, 2, 3]:
                FQs.append(LIU.InterpolateF(IArray, QArrays[iQ]))

        # initialize output containers and loop variables
        outputPlms = []  #list of placements

        for i_output in range(0,
                              math.trunc(obj.NumberSamples + ParaConfusion)):
            i_input = float(i_output) / (obj.NumberSamples - 1) * (len(input) -
                                                                   1)
            pos = App.Vector()
            ori = App.Rotation()
            if posIsInterpolate:
                pos = App.Vector(FX.value(i_input), FY.value(i_input),
                                 FZ.value(i_input))

            if oriIsInterpolate:
                ori = App.Rotation(FQs[0].value(i_input),
                                   FQs[1].value(i_input),
                                   FQs[2].value(i_input),
                                   FQs[3].value(i_input))
            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #13
0
    def derivedExecute(self,obj):
        # cache stuff
        base = obj.Base.Shape
        if base.ShapeType != 'Compound':
            base = Part.makeCompound([base])
        if obj.FlattenBaseHierarchy:
            baseChildren = LCE.AllLeaves(base)
        else:
            baseChildren = base.childShapes()
        
        tool = obj.Tool.Shape
        if tool.ShapeType != 'Compound':
            tool = Part.makeCompound([tool])
        if obj.FlattenToolHierarchy:
            toolChildren = LCE.AllLeaves(tool)
        else:
            toolChildren = tool.childShapes()
        
        iBase = 0
        isMult = obj.Operation == 'MultiplyPlacements' # cache mode comparisons to speed them up
        isAvg = obj.Operation == 'AveragePlacements'
        isIgnore = obj.Operation == 'IgnoreBasePlacements'
        isOverride = obj.Operation == 'OverrideBasePlacements'

        #mode validity logic
        if not latticeBaseFeature.isObjectLattice(obj.Tool):
            latticeExecuter.warning(obj, 'Tool is not a lattice object. Results may be unexpected.\n')
        outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base)
        if isOverride and outputIsLattice:
            latticeExecuter.warning(obj, 'Base is a lattice object. OverrideBasePlacements operation requires a generic compound as Base. So, the lattice is being treated as a generic compound.\n')
            outputIsLattice = False
        
        # initialize output containers and loop variables
        outputShapes = [] #output list of shapes
        outputPlms = [] #list of placements
        bFirst = True
        plmMatcher = App.Placement() #extra placement, that aligns first tool member and first base member
        
        
        # the essence
        for toolChild in toolChildren:
            
            # early test for termination
            if iBase > len(baseChildren)-1:
                if obj.LoopSequence:
                    iBase = 0
                else:
                    FreeCAD.Console.PrintWarning(obj.Name+': There are '+str(len(toolChildren)-len(baseChildren))+
                                                 ' more placements in Tool than children in Base. Those placements'+
                                                 ' were dropped.\n')
                    break

            #cache some stuff
            basePlm = baseChildren[iBase].Placement
            toolPlm = toolChild.Placement

            if not outputIsLattice:
                outputShape = baseChildren[iBase].copy()
            
            #prepare alignment placement
            if bFirst:
                bFirst = False
                if obj.KeepBaseFirstItemPos:
                    plmMatcher = toolPlm.inverse()

            #mode logic
            if isMult:
                outPlm = toolPlm.multiply(plmMatcher.multiply(basePlm))
            elif isAvg:
                plm1 = toolPlm
                plm2 = plmMatcher.multiply(basePlm)
                transl = plm1.Base*0.5 + plm2.Base*0.5
                a1,b1,c1,d1 = plm1.Rotation.Q
                a2,b2,c2,d2 = plm2.Rotation.Q
                rot = App.Rotation(a1+a2,b1+b2,c1+c2,d1+d2) #no divide-by-two, because FreeCAD will normalize the quaternion automatically
                outPlm = App.Placement(transl,rot)
            elif isIgnore:
                outPlm = toolPlm
            elif isOverride:
                assert(not outputIsLattice)
                outputShape.transformShape(toolPlm.inverse.multiply(plmMatcher.multiply(basePlm)))
                outPlm = toolPlm
            
            if outputIsLattice:
               outputPlms.append(outPlm)
            else:
                outputShape.Placement = outPlm
                outputShapes.append(outputShape)
            
            iBase += 1
            
        if outputIsLattice:
            return outputPlms
        else:
            obj.Shape = Part.makeCompound(outputShapes)
    def derivedExecute(self,obj):
        # Fill in (update read-only) properties that are driven by the mode.
        self.updateReadOnlyness(obj)
        if obj.Mode == 'SpanN':
            n = obj.NumberPolar
            if obj.EndInclusive:
                n -= 1
            if n == 0:
                n = 1
            obj.AngleStep = (obj.AngleSpanEnd - obj.AngleSpanStart)/n
        elif obj.Mode == 'StepN':
            n = obj.NumberPolar
            if obj.EndInclusive:
                n -= 1
            obj.AngleSpanEnd = obj.AngleSpanStart + obj.AngleStep*n
        elif obj.Mode == 'SpanStep':
            nfloat = float((obj.AngleSpanEnd - obj.AngleSpanStart) / obj.AngleStep)
            n = math.trunc(nfloat - ParaConfusion) + 1
            if obj.EndInclusive and abs(nfloat-round(nfloat)) <= ParaConfusion:
                n = n + 1
            obj.NumberPolar = n
            
        # Apply links
        if obj.AxisLink:
            if latticeBaseFeature.isObjectLattice(obj.AxisLink):
                latticeExecuter.warning(obj,"For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape.")
                
            #resolve the link        
            if len(obj.AxisLinkSubelement) > 0:
                linkedShape = obj.AxisLink.Shape.getElement(obj.AxisLinkSubelement)
            else:
                linkedShape = obj.AxisLink.Shape

            #Type check
            if linkedShape.ShapeType != 'Edge':
                raise ValueError('Axis link must be an edge; it is '+linkedShape.ShapeType+' instead.')
            
            #prepare
            dir = App.Vector()
            point = App.Vector()
            if isinstance(linkedShape.Curve, Part.Line):
                dir = linkedShape.Curve.EndPoint - linkedShape.Curve.StartPoint
                point = linkedShape.Curve.StartPoint
            elif isinstance(linkedShape.Curve, Part.Circle):
                dir = linkedShape.Curve.Axis
                point = linkedShape.Curve.Center
            else:
                raise ValueError("Edge " + repr(linkedShape) + " can't be used to derive an axis. It must be either a line or a circle/arc.")
            
            #apply
            if obj.AxisDirIsDriven:
                obj.AxisDir = dir
            if obj.AxisPointIsDriven:
                obj.AxisPoint = point
        
        # Generate the actual array. We can use Step and N directly to 
        # completely avoid mode logic, since we had updated them
        
        # cache properties into variables
        step = float(obj.AngleStep)
        startAng = float(obj.AngleSpanStart) + step*float(obj.Offset)
        n = int(obj.NumberPolar)
        radius = float(obj.Radius)
        
        # compute initial vector. It is to be perpendicular to Axis
        rot_ini = latticeGeomUtils.makeOrientationFromLocalAxes(ZAx= obj.AxisDir)
        overallPlacement = App.Placement(obj.AxisPoint, rot_ini)
        
        # Make the array
        output = [] # list of placements
        if obj.Mode != "Spreadsheet":
            for i in range(0, n):
                ang = startAng + step*i
                p = Part.Vertex()
                localrot = App.Rotation(App.Vector(0,0,1), ang)
                localtransl = localrot.multVec(App.Vector(radius,0,0))
                localplm = App.Placement(localtransl, localrot)
                resultplm = overallPlacement.multiply(localplm)
                if obj.OrientMode == 'None':
                    resultplm.Rotation = App.Rotation()
                output.append(resultplm)
        else:
            #parse address
            addr = obj.CellStart
            #assuming only two letter column
            if addr[1].isalpha():
                col = addr[0:2]
                row = addr[2:]
            else:
                col = addr[0:1]
                row = addr[1:]
            row = int(row)
            
            #loop until the value an't be read out
            while True:
                try:
                    ang = obj.SpreadSheet.get(col+str(row))
                except ValueError:
                    break
                ang = float(ang)
                p = Part.Vertex()
                localrot = App.Rotation(App.Vector(0,0,1), ang)
                localtransl = localrot.multVec(App.Vector(radius,0,0))
                localplm = App.Placement(localtransl, localrot)
                output.append( overallPlacement.multiply(localplm) )
                row += 1
            
        return output
    def execute(self,obj):
        nOfStrings = len(obj.Strings)
        lattice = obj.ArrayLink
        if lattice is None:
            plms = [App.Placement() for i in range(0,nOfStrings)]
        else:
            if not latticeBaseFeature.isObjectLattice(lattice):
                latticeExecuter.warning(obj,"ShapeString's link to array must point to a lattice. It points to a generic shape. Results may be unexpected.")
            leaves = LCE.AllLeaves(lattice.Shape)
            plms = [leaf.Placement for leaf in leaves]
        
        #update foolObj's properties
        for (proptype, propname, group, hint) in self.foolObj.properties:
            if propname != "String": #ignore "String", that will be taken care of in the following loop
                setattr(self.foolObj, propname, getattr(obj, propname))
        self.foolObj.FontFile = findFont(obj.FontFile)
        obj.FullPathToFont = self.foolObj.FontFile
        
        shapes = []
        for i in range(  0 ,  min(len(plms),len(obj.Strings))  ):
            if len(obj.Strings[i]) > 0:
                #generate shapestring using Draft
                self.foolObj.String = obj.Strings[i]
                self.foolObj.Shape = None
                self.draft_shape_string.execute(self.foolObj)
                shape = self.foolObj.Shape
                
                #calculate alignment point
                if obj.XAlign == 'None' and obj.YAlign == 'None':
                    pass #need not calculate boundbox
                else:
                    if obj.AlignPrecisionBoundBox:
                        bb = getPrecisionBoundBox(shape)
                    else:
                        bb = shape.BoundBox

                alignPnt = App.Vector()
                
                if obj.XAlign == 'Left':
                    alignPnt.x = bb.XMin
                elif obj.XAlign == 'Right':
                    alignPnt.x = bb.XMax
                elif obj.XAlign == 'Middle':
                    alignPnt.x = bb.Center.x

                if obj.YAlign == 'Bottom':
                    alignPnt.y = bb.YMin
                elif obj.YAlign == 'Top':
                    alignPnt.y = bb.YMax
                elif obj.YAlign == 'Middle':
                    alignPnt.y = bb.Center.y
                
                #Apply alignment
                shape.Placement = App.Placement(alignPnt*(-1.0), App.Rotation()).multiply(shape.Placement)
                
                #Apply placement from array
                shape.Placement = plms[i].multiply(shape.Placement)
                
                shapes.append(shape.copy())
        
        if len(shapes) == 0:
            scale = 1.0
            if lattice is not None:
                scale = lattice.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

        obj.Shape = Part.makeCompound(shapes)
Example #16
0
    def derivedExecute(self,obj):
        # cache stuff
        if latticeBaseFeature.isObjectLattice(obj.Base):
            latticeExecuter.warning(obj,"Base is a lattice object. Since a non-lattice object is required by arrayFromShape tool, the results may be unexpected.")

        base = obj.Base.Shape
        if obj.WholeObject:
            baseChildren = [base]
            #if obj.FlattenBaseHierarchy:
            #    latticeExecuter.warning(obj, "FlattenBaseHierarchy is ignored because WholeObject is set to True")
        else:
            if base.ShapeType != 'Compound':
                base = Part.makeCompound([base])
            if obj.FlattenBaseHierarchy:
                baseChildren = LCE.AllLeaves(base)
            else:
                baseChildren = base.childShapes()
        
                        
        #cache mode comparisons, for speed
        posIsNone = obj.TranslateMode == '(none)'
        posIsBase = obj.TranslateMode == 'base'
        posIsChild = obj.TranslateMode == 'child'
        posIsCenterM = obj.TranslateMode == 'child.CenterOfMass'
        posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox'
        posIsVertex = obj.TranslateMode == 'child.Vertex'
        
        oriIsNone = obj.OrientMode == '(none)'
        oriIsBase = obj.OrientMode == 'base'
        oriIsChild = obj.OrientMode == 'child'
        oriIsInertial = obj.OrientMode == 'child.InertiaAxes'
        oriIsEdge = obj.OrientMode == 'child.Edge'
        oriIsFace = obj.OrientMode == 'child.FaceAxis'
        
        # initialize output containers and loop variables
        outputPlms = [] #list of placements
        
        # the essence
        for child in baseChildren:
            pos = App.Vector()
            ori = App.Rotation()
            if posIsNone:
                pass
            elif posIsBase:
                pos = base.Placement.Base
            elif posIsChild:
                pos = child.Placement.Base
            elif posIsCenterM:
                leaves = LCE.AllLeaves(child)
                totalW = 0
                weightAttrib = {"Vertex":"",
                             "Edge":"Length",
                             "Wire":"Length",
                             "Face":"Area",
                             "Shell":"Area",
                             "Solid":"Volume",
                             "CompSolid":""}[leaves[0].ShapeType]
                #Center of mass of a compound is a weghted average of centers
                # of mass of individual objects.
                for leaf in leaves:
                    w = 1.0 if not weightAttrib else (getattr(leaf, weightAttrib))
                    if leaf.ShapeType == 'Vertex':
                        leafCM = leaf.Point
                    #elif child.ShapeType == 'CompSolid':
                        #todo
                    else: 
                        leafCM = leaf.CenterOfMass
                    pos += leafCM * w
                    totalW += w
                pos = pos * (1.0/totalW)
            elif posIsCenterBB:
                import latticeBoundBox
                bb = latticeBoundBox.getPrecisionBoundBox(child)
                pos = bb.Center
            elif posIsVertex:
                v = child.Vertexes[obj.TranslateElementIndex - 1]
                pos = v.Point
            else:
                raise ValueError("latticePolarArrayFromShape: translation mode not implemented: "+obj.TranslateMode)
            
            if oriIsNone:
                pass
            elif oriIsBase:
                ori = base.Placement.Rotation
            elif oriIsChild:
                ori = child.Placement.Rotation
            elif oriIsInertial:
                leaves = LCE.AllLeaves(child)
                if len(leaves)>1:
                    raise ValueError("latticePolarArrayFromShape: calculation of principal axes of compounds is not supported yet")
                props = leaves[0].PrincipalProperties
                XAx = props['FirstAxisOfInertia']
                ZAx = props['ThirdAxisOfInertia']
                ori = Utils.makeOrientationFromLocalAxes(ZAx, XAx)
            elif oriIsEdge:
                edge = child.Edges[obj.OrientElementIndex - 1]
                XAx = edge.Curve.tangent(edge.Curve.FirstParameter)[0]
                ori1 = Utils.makeOrientationFromLocalAxes(ZAx= XAx)
                ori2 = Utils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0),XAx= App.Vector(0,0,1))
                ori = ori1.multiply(ori2)
            elif oriIsFace:
                face = child.Faces[obj.OrientElementIndex - 1]
                ZAx = face.Surface.Axis
            else:
                raise ValueError("latticePolarArrayFromShape: rientation mode not implemented: "+obj.OrientMode)

            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #17
0
    def execute(self, obj):
        #validity check
        if isObjectLattice(obj.Base):
            import latticeExecuter
            latticeExecuter.warning(
                obj,
                "A generic shape is expected, but a lattice object was supplied. It will be treated as a generic shape."
            )

        rst = []  #variable to receive the final list of shapes
        shps = obj.Base.Shape.childShapes()
        if obj.FilterType == 'bypass':
            rst = shps
        elif obj.FilterType == 'specific items':
            rst = []
            flags = [False] * len(shps)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    rst.append(shps[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    ifrom = None if len(r_v[0].strip()) == 0 else int(r_v[0])
                    ito = None if len(r_v[1].strip()) == 0 else int(r_v[1])
                    istep = None if len(r_v[2].strip()) == 0 else int(r_v[2])
                    rst = rst + shps[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:' + r)
            if obj.Invert:
                rst = []
                for i in xrange(0, len(shps)):
                    if not flags[i]:
                        rst.append(shps[i])
        elif obj.FilterType == 'collision-pass':
            stencil = obj.Stencil.Shape
            for s in shps:
                d = s.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    rst.append(s)
        elif obj.FilterType == 'window-volume' or obj.FilterType == 'window-area' or obj.FilterType == 'window-length' or obj.FilterType == 'window-distance':
            vals = [0.0] * len(shps)
            for i in xrange(0, len(shps)):
                if obj.FilterType == 'window-volume':
                    vals[i] = shps[i].Volume
                elif obj.FilterType == 'window-area':
                    vals[i] = shps[i].Area
                elif obj.FilterType == 'window-length':
                    vals[i] = shps[i].Length
                elif obj.FilterType == 'window-distance':
                    vals[i] = shps[i].distToShape(obj.Stencil.Shape)[0]

            maxval = max(vals)
            if obj.Stencil:
                if obj.FilterType == 'window-volume':
                    maxval = obj.Stencil.Shape.Volume
                elif obj.FilterType == 'window-area':
                    maxval = obj.Stencil.Shape.Area
                elif obj.FilterType == 'window-length':
                    maxval = obj.Stencil.Shape.Length
            if obj.OverrideMaxVal:
                maxval = obj.OverrideMaxVal

            valFrom = obj.WindowFrom / 100.0 * maxval
            valTo = obj.WindowTo / 100.0 * maxval

            for i in xrange(0, len(shps)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    rst.append(shps[i])
        else:
            raise ValueError('Filter mode not implemented:' + obj.FilterType)

        if len(rst) == 0:
            scale = 1.0
            if not obj.Base.Shape.isNull():
                scale = obj.Base.Shape.BoundBox.DiagonalLength / math.sqrt(
                    3) / math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            print scale
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError(
                'Nothing passes through the filter'
            )  #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

        if len(rst) > 1:
            obj.Shape = Part.makeCompound(rst)
        else:  # don't make compound of one shape, output it directly
            sh = rst[0]
            sh.transformShape(sh.Placement.toMatrix(), True)  #True = make copy
            sh.Placement = FreeCAD.Placement()
            obj.Shape = sh

        return
    def derivedExecute(self,obj):
        self.updateReadOnlyness(obj)

        # Apply links
        if obj.Link:
            if latticeBaseFeature.isObjectLattice(obj.Link):
                latticeExecuter.warning(obj,"For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape.")
            
            #resolve the link
            if len(obj.LinkSubelement) > 0:
                linkedShape = obj.Link.Shape.getElement(obj.LinkSubelement)
            else:
                linkedShape = obj.Link.Shape
            
            #Type check
            if linkedShape.ShapeType != 'Edge':
                raise ValueError('Axis link must be an edge; it is '+linkedShape.ShapeType+' instead.')
            if type(linkedShape.Curve) is not Part.Line:
                raise ValueError('Axis link must be a line; it is '+type(linkedShape.Curve)+' instead.')
            
            #obtain
            dir = linkedShape.Curve.EndPoint - linkedShape.Curve.StartPoint
            point = linkedShape.Curve.StartPoint if not obj.Reverse else linkedShape.Curve.EndPoint
            
            if obj.DirIsDriven:
                obj.Dir = dir
            if obj.PointIsDriven:
                obj.Point = point
            if obj.DrivenProperty != 'None':
                if obj.DrivenProperty == 'Span':
                    obj.SpanEnd = obj.SpanStart + App.Units.Quantity('mm')*dir.Length
                else:
                    setattr(obj, obj.DrivenProperty, dir.Length)

        # Fill in (update read-only) properties that are driven by the mode.
        if obj.Mode == 'SpanN':
            n = obj.NumberLinear
            if obj.EndInclusive:
                n -= 1
            if n == 0:
                n = 1
            obj.Step = (obj.SpanEnd - obj.SpanStart)/n
            if obj.DrivenProperty == 'Step' and obj.Link:
                latticeExecuter.warning(obj,"Step property is being driven by both the link and the selected mode. Mode has priority.")
        elif obj.Mode == 'StepN':
            n = obj.NumberLinear
            if obj.EndInclusive:
                n -= 1
            obj.SpanEnd = obj.SpanStart + obj.Step*n
            if 'Span' in obj.DrivenProperty and obj.Link:
                latticeExecuter.warning(obj,"SpanEnd property is being driven by both the link and the selected mode. Mode has priority.")
        elif obj.Mode == 'SpanStep':
            nfloat = float((obj.SpanEnd - obj.SpanStart) / obj.Step)
            n = math.trunc(nfloat - ParaConfusion) + 1
            if obj.EndInclusive and abs(nfloat-round(nfloat)) <= ParaConfusion:
                n = n + 1
            obj.NumberLinear = n
            
        
        # Generate the actual array. We can use Step and N directly to 
        # completely avoid mode logic, since we had updated them
        
        # cache properties into variables
        step = float(obj.Step)
        start = float(obj.SpanStart) + step*float(obj.Offset)
        n = int(obj.NumberLinear)
        
        #Apply reversal
        if obj.Reverse:
            obj.Dir = obj.Dir*(-1.0)
            if not(obj.DirIsDriven and obj.Link):
                obj.Reverse = False

        # precompute orientation
        if obj.OrientMode == 'Along axis':
            ori = latticeGeomUtils.makeOrientationFromLocalAxes(ZAx= obj.Dir).multiply(
                    latticeGeomUtils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0), XAx= App.Vector(0,0,1)) )
        else:
            ori = App.Rotation()
        
        dir = obj.Dir
        dir.normalize()
        
        # Make the array
        output = [] # list of placements
        if obj.Mode != "Spreadsheet":
            for i in range(0, n):
                position = start + step*i
                output.append( App.Placement(obj.Point + obj.Dir*position, ori) )
        else:
            #parse address
            addr = obj.CellStart
            #assuming only two letter column
            if addr[1].isalpha():
                col = addr[0:2]
                row = addr[2:]
            else:
                col = addr[0:1]
                row = addr[1:]
            row = int(row)
            
            #loop until the value can't be read out
            while True:
                try:
                    position = obj.SpreadSheet.get(col+str(row))
                except ValueError:
                    break
                position = float(position)
                output.append( App.Placement(obj.Point + obj.Dir*position, ori) )
                row += 1
            
        return output
Example #19
0
    def derivedExecute(self, obj):
        # cache stuff
        base = obj.Base.Shape
        if base.ShapeType != 'Compound':
            base = Part.makeCompound([base])
        if obj.FlattenBaseHierarchy:
            baseChildren = LCE.AllLeaves(base)
        else:
            baseChildren = base.childShapes()

        tool = obj.Tool.Shape
        if tool.ShapeType != 'Compound':
            tool = Part.makeCompound([tool])
        if obj.FlattenToolHierarchy:
            toolChildren = LCE.AllLeaves(tool)
        else:
            toolChildren = tool.childShapes()

        iBase = 0
        isMult = obj.Operation == 'MultiplyPlacements'  # cache mode comparisons to speed them up
        isAvg = obj.Operation == 'AveragePlacements'
        isIgnore = obj.Operation == 'IgnoreBasePlacements'
        isOverride = obj.Operation == 'OverrideBasePlacements'

        #mode validity logic
        if not latticeBaseFeature.isObjectLattice(obj.Tool):
            latticeExecuter.warning(
                obj,
                'Tool is not a lattice object. Results may be unexpected.\n')
        outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base)
        if isOverride and outputIsLattice:
            latticeExecuter.warning(
                obj,
                'Base is a lattice object. OverrideBasePlacements operation requires a generic compound as Base. So, the lattice is being treated as a generic compound.\n'
            )
            outputIsLattice = False

        # initialize output containers and loop variables
        outputShapes = []  #output list of shapes
        outputPlms = []  #list of placements
        bFirst = True
        plmMatcher = App.Placement(
        )  #extra placement, that aligns first tool member and first base member

        # the essence
        for toolChild in toolChildren:

            # early test for termination
            if iBase > len(baseChildren) - 1:
                if obj.LoopSequence:
                    iBase = 0
                else:
                    FreeCAD.Console.PrintWarning(
                        obj.Name + ': There are ' +
                        str(len(toolChildren) - len(baseChildren)) +
                        ' more placements in Tool than children in Base. Those placements'
                        + ' were dropped.\n')
                    break

            #cache some stuff
            basePlm = baseChildren[iBase].Placement
            toolPlm = toolChild.Placement

            if not outputIsLattice:
                outputShape = baseChildren[iBase].copy()

            #prepare alignment placement
            if bFirst:
                bFirst = False
                if obj.KeepBaseFirstItemPos:
                    plmMatcher = toolPlm.inverse()

            #mode logic
            if isMult:
                outPlm = toolPlm.multiply(plmMatcher.multiply(basePlm))
            elif isAvg:
                plm1 = toolPlm
                plm2 = plmMatcher.multiply(basePlm)
                transl = plm1.Base * 0.5 + plm2.Base * 0.5
                a1, b1, c1, d1 = plm1.Rotation.Q
                a2, b2, c2, d2 = plm2.Rotation.Q
                rot = App.Rotation(
                    a1 + a2, b1 + b2, c1 + c2, d1 + d2
                )  #no divide-by-two, because FreeCAD will normalize the quaternion automatically
                outPlm = App.Placement(transl, rot)
            elif isIgnore:
                outPlm = toolPlm
            elif isOverride:
                assert (not outputIsLattice)
                outputShape.transformShape(
                    toolPlm.inverse.multiply(plmMatcher.multiply(basePlm)))
                outPlm = toolPlm

            if outputIsLattice:
                outputPlms.append(outPlm)
            else:
                outputShape.Placement = outPlm
                outputShapes.append(outputShape)

            iBase += 1

        if outputIsLattice:
            return outputPlms
        else:
            obj.Shape = Part.makeCompound(outputShapes)
Example #20
0
    def derivedExecute(self,obj):
        # cache stuff
        base = obj.Base.Shape
        if not latticeBaseFeature.isObjectLattice(obj.Base):
            latticeExecuter.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n")
        input = [leaf.Placement for leaf in LCE.AllLeaves(base)]
        
        if len(input) < 2:
            raise ValueError("At least 2 placements ar needed to interpolate; there are just "+str(len(input))+" in base array.")
        if obj.NumberSamples < 2:
            raise ValueError("Can output no less than 2 samples; "+str(obj.NumberSamples)+" was requested.")
                        
        #cache mode comparisons, for speed
        posIsInterpolate = obj.TranslateMode == 'interpolate'
        posIsReset = obj.TranslateMode == 'reset'
        
        oriIsInterpolate = obj.OrientMode == 'interpolate'
        oriIsReset = obj.OrientMode == 'reset'        
        
        # construct interpolation functions
        #  prepare lists of input samples
        IArray = [float(i) for i in range(0,len(input))]
        XArray = [plm.Base.x for plm in input]
        YArray = [plm.Base.y for plm in input]
        ZArray = [plm.Base.z for plm in input]
        QArrays = [[],[],[],[]]
        prevQ = [0.0]*4
        for plm in input:
            Q = plm.Rotation.Q
            #test if quaernion has changed sign compared to previous one. 
            # Quaternions of opposite sign are equivalent in terms of rotation, 
            # but sign changes confuse interpolation, so we are detecting sign 
            # changes and discarding them
            if dotProduct(Q,prevQ) < -ParaConfusion: 
                Q = [-v for v in Q] 
            for iQ in [0,1,2,3]:
                QArrays[iQ].append( Q[iQ] ) 
            prevQ = Q
        
        #  constuct function objects
        if posIsInterpolate:
            FX = LIU.InterpolateF(IArray,XArray)
            FY = LIU.InterpolateF(IArray,YArray)
            FZ = LIU.InterpolateF(IArray,ZArray)
        if oriIsInterpolate:
            FQs = []
            for iQ in [0,1,2,3]:
                FQs.append(LIU.InterpolateF(IArray,QArrays[iQ]))
                
        # initialize output containers and loop variables
        outputPlms = [] #list of placements

        for i_output in range(0,math.trunc(obj.NumberSamples+ParaConfusion)):
            i_input = float(i_output) / (obj.NumberSamples-1) * (len(input)-1)
            pos = App.Vector()
            ori = App.Rotation()
            if posIsInterpolate:
                pos = App.Vector(FX.value(i_input), FY.value(i_input), FZ.value(i_input))
            
            if oriIsInterpolate:
                ori = App.Rotation(FQs[0].value(i_input),
                                   FQs[1].value(i_input),
                                   FQs[2].value(i_input),
                                   FQs[3].value(i_input))                
            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #21
0
    def execute(self,obj):
        nOfStrings = len(obj.Strings)
        lattice = obj.ArrayLink
        if lattice is None:
            plms = [App.Placement() for i in range(0,nOfStrings)]
        else:
            if not latticeBaseFeature.isObjectLattice(lattice):
                latticeExecuter.warning(obj,"ShapeString's link to array must point to a lattice. It points to a generic shape. Results may be unexpected.")
            leaves = LCE.AllLeaves(lattice.Shape)
            plms = [leaf.Placement for leaf in leaves]
        
        #update foolObj's properties
        self.makeFoolObj(obj) #make sure we have one - fixes defunct Lattice ShapeString after save-load
        for (proptype, propname, group, hint) in self.foolObj.properties:
            if propname != "String": #ignore "String", that will be taken care of in the following loop
                setattr(self.foolObj, propname, getattr(obj, propname))
        self.foolObj.FontFile = findFont(obj.FontFile)
        obj.FullPathToFont = self.foolObj.FontFile
        
        shapes = []
        for i in range(  0 ,  min(len(plms),len(obj.Strings))  ):
            if len(obj.Strings[i]) > 0:
                #generate shapestring using Draft
                self.foolObj.String = obj.Strings[i]
                self.foolObj.Shape = None
                self.draft_shape_string.execute(self.foolObj)
                shape = self.foolObj.Shape
                
                #calculate alignment point
                if obj.XAlign == 'None' and obj.YAlign == 'None':
                    pass #need not calculate boundbox
                else:
                    if obj.AlignPrecisionBoundBox:
                        bb = getPrecisionBoundBox(shape)
                    else:
                        bb = shape.BoundBox

                alignPnt = App.Vector()
                
                if obj.XAlign == 'Left':
                    alignPnt.x = bb.XMin
                elif obj.XAlign == 'Right':
                    alignPnt.x = bb.XMax
                elif obj.XAlign == 'Middle':
                    alignPnt.x = bb.Center.x

                if obj.YAlign == 'Bottom':
                    alignPnt.y = bb.YMin
                elif obj.YAlign == 'Top':
                    alignPnt.y = bb.YMax
                elif obj.YAlign == 'Middle':
                    alignPnt.y = bb.Center.y
                
                #Apply alignment
                shape.Placement = App.Placement(alignPnt*(-1.0), App.Rotation()).multiply(shape.Placement)
                
                #Apply placement from array
                shape.Placement = plms[i].multiply(shape.Placement)
                
                shapes.append(shape.copy())
        
        if len(shapes) == 0:
            scale = 1.0
            if lattice is not None:
                scale = lattice.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

        obj.Shape = Part.makeCompound(shapes)