def CreateLatticePopulateChildren(name, label, shapeObj, latticeObjFrom, latticeObjTo, refmode): '''utility function; sharing common code for all populate-Children commands''' FreeCADGui.addModule("lattice2PopulateChildren") FreeCADGui.addModule("lattice2Executer") #fill in properties FreeCADGui.doCommand("f = lattice2PopulateChildren.makeLatticePopulateChildren(name='"+name+"')") FreeCADGui.doCommand("f.Object = App.ActiveDocument."+shapeObj.Name) FreeCADGui.doCommand("f.PlacementsTo = App.ActiveDocument."+latticeObjTo.Name) if latticeObjFrom is not None: FreeCADGui.doCommand("f.PlacementsFrom = App.ActiveDocument."+latticeObjFrom.Name) FreeCADGui.doCommand("f.Referencing = "+repr(refmode)) FreeCADGui.doCommand("f.Label = " + repr(label)) #execute FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") #hide something if (refmode != "Origin" and refmode != "Use PlacementsFrom") or lattice2BaseFeature.isObjectLattice(shapeObj): FreeCADGui.doCommand("f.Object.ViewObject.hide()") if lattice2BaseFeature.isObjectLattice(shapeObj): FreeCADGui.doCommand("f.PlacementsTo.ViewObject.hide()") if latticeObjFrom is not None: FreeCADGui.doCommand("f.PlacementsFrom.ViewObject.hide()") #finalize FreeCADGui.doCommand("Gui.Selection.addSelection(f)") FreeCADGui.doCommand("f = None")
def CreateLatticePopulateCopies(name, label, shapeObj, latticeObjFrom, latticeObjTo, refmode): '''utility function; sharing common code for all populate-copies commands''' FreeCADGui.addModule("lattice2PopulateCopies") FreeCADGui.addModule("lattice2Executer") #fill in properties FreeCADGui.doCommand("f = lattice2PopulateCopies.makeLatticePopulateCopies(name='"+name+"')") FreeCADGui.doCommand("f.Object = App.ActiveDocument."+shapeObj.Name) FreeCADGui.doCommand("f.PlacementsTo = App.ActiveDocument."+latticeObjTo.Name) if latticeObjFrom is not None: FreeCADGui.doCommand("f.PlacementsFrom = App.ActiveDocument."+latticeObjFrom.Name) FreeCADGui.doCommand("f.Referencing = "+repr(refmode)) FreeCADGui.doCommand("f.Label = " + repr(label)) #execute FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") #hide something if (refmode != "Origin" and refmode != "Use PlacementsFrom") or lattice2BaseFeature.isObjectLattice(shapeObj): FreeCADGui.doCommand("f.Object.ViewObject.hide()") if lattice2BaseFeature.isObjectLattice(shapeObj): FreeCADGui.doCommand("f.PlacementsTo.ViewObject.hide()") if latticeObjFrom is not None: FreeCADGui.doCommand("f.PlacementsFrom.ViewObject.hide()") #finalize FreeCADGui.doCommand("Gui.Selection.addSelection(f)") FreeCADGui.doCommand("f = None")
def getIcon(self): obj = self.Object base_is_lattice = LBF.isObjectLattice(obj.Object) pivot_is_lattice = LBF.isObjectLattice(obj.Pivot[0]) if obj.Pivot else True whole = obj.ObjectTraversal == 'Use whole' key = 'Plm' if base_is_lattice else 'Sh' key += 's' if not whole and pivot_is_lattice else '' key += 'Plms' if pivot_is_lattice else 'Sh' return getIconPath("Lattice2_Mirror_{key}.svg".format(key= key))
def execute(self, obj): if self.clone(obj): return if not obj.LatticePlacement: return if lattice2BF.isObjectLattice(obj.LatticePlacement) is True: pls = lattice2BF.getPlacementsList(obj.LatticePlacement) obj.RebarPlacements = pls self.build_shape(obj) obj.Amount = len(obj.RebarPlacements) obj.TotalLength = obj.Amount * obj.BaseRebar.Length # set Visibility of BaseRebar # this should be done in the Gui Command, # but this dos not yet exist TODO # set view of base rebar to off # if reinforcement shape is not a null shape # TODO may be use another color for base rebar if FreeCAD.GuiUp: if obj.Shape.isNull() is not True: obj.BaseRebar.ViewObject.Visibility = False else: FreeCAD.Console.PrintError( "The object provided: {} is not a Lattice2 object\n".format( obj.Name))
def derivedExecute(self,obj): self.assureGenerator(obj) self.updateReadonlyness(obj) # Apply links if obj.AxisLink: if lattice2BaseFeature.isObjectLattice(obj.AxisLink): lattice2Executer.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 self.generator.execute() # cache properties into variables radius = float(obj.Radius) values = [float(strv) for strv in obj.Values] # compute initial vector. It is to be perpendicular to Axis rot_ini = lattice2GeomUtils.makeOrientationFromLocalAxes(ZAx= obj.AxisDir) overallPlacement = App.Placement(obj.AxisPoint, rot_ini) # Make the array output = [] # list of placements for ang in values: 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) return output
def CreateLatticeArrayFilter(name, mode): sel = FreeCADGui.Selection.getSelectionEx() # selection order independence logic (lattice object and generic shape stencil can be told apart) iLtc = 0 #index of lattice object in selection iStc = 1 #index of stencil object in selection for i in range(0, len(sel)): if lattice2BaseFeature.isObjectLattice(sel[i]): iLtc = i iStc = i - 1 #this may give negative index, but python accepts negative indexes break FreeCAD.ActiveDocument.openTransaction("Create ArrayFilter") FreeCADGui.addModule("lattice2ArrayFilter") FreeCADGui.addModule("lattice2Executer") FreeCADGui.doCommand("sel = Gui.Selection.getSelectionEx()") FreeCADGui.doCommand("f = lattice2ArrayFilter.makeArrayFilter(name = '" + name + "')") FreeCADGui.doCommand("f.Base = App.ActiveDocument." + sel[iLtc].ObjectName) FreeCADGui.doCommand("f.FilterType = '" + mode + "'") if mode == 'specific items': FreeCADGui.doCommand( "f.items = lattice2ArrayFilter.makeItemListFromSelection(sel[" + str(iLtc) + "])") if len(sel[0].SubElementNames) == 1: FreeCADGui.doCommand("f.ExposePlacement = True") else: FreeCADGui.doCommand("f.Stencil = App.ActiveDocument." + sel[iStc].ObjectName) FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n" + " child.ViewObject.hide()") FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") FreeCADGui.doCommand("f = None") FreeCAD.ActiveDocument.commitTransaction()
def Activated(self): try: if len(FreeCADGui.Selection.getSelection()) == 0: infoMessage( "Make compound", "Make compound command. Combines several shapes into one. The shapes are kept as-is. They are not fused together, and can be extracted unchanged.\n\n" + "Compounds can contain combination of shapes of any topology: one can compound some edges with some solids. Compound is effectively another kind of group. But unlike normal FreeCAD group, compound only accepts OCC geometry. Compound cannot include meshes, dimensions, labels, or other objects that provide no Shape property.\n\n" + "Note that compounds that have objects that touch or intersect are considered invalid by Part CheckGeometry. Such invalid compounds cannot be used for Part Cut/Common/Fuse." ) return oldVal = lattice2Executer.globalIsCreatingLatticeFeature lattice2Executer.globalIsCreatingLatticeFeature = True sel = FreeCADGui.Selection.getSelectionEx() for s in sel: if isObjectLattice(s.Object): lattice2Executer.warning( None, "For making a compound, generic shapes are expected, but some of the selected objects are placements/arrays of placements. These will be treated as generic shapes; results may be unexpected." ) break FreeCADGui.runCommand("Part_Compound") except Exception as err: msgError(err) finally: lattice2Executer.globalIsCreatingLatticeFeature = oldVal
def CreateLatticeProjectArray(name): sel = FreeCADGui.Selection.getSelectionEx() # selection order independence logic (lattice object and generic shape stencil can be told apart) iLtc = 0 #index of lattice object in selection iStc = 1 #index of stencil object in selection for i in range(0, len(sel)): if lattice2BaseFeature.isObjectLattice(sel[i]): iLtc = i iStc = i - 1 #this may give negative index, but python accepts negative indexes break FreeCAD.ActiveDocument.openTransaction("Create ProjectArray") FreeCADGui.addModule("lattice2ProjectArray") FreeCADGui.addModule("lattice2Executer") FreeCADGui.doCommand("sel = Gui.Selection.getSelectionEx()") FreeCADGui.doCommand("f = lattice2ProjectArray.makeProjectArray(name = '" + name + "')") FreeCADGui.doCommand("f.Base = App.ActiveDocument." + sel[iLtc].ObjectName) FreeCADGui.doCommand("f.Tool = App.ActiveDocument." + sel[iStc].ObjectName) FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n" + " child.ViewObject.hide()") FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") FreeCADGui.doCommand("f = None") FreeCAD.ActiveDocument.commitTransaction()
def CreateLatticeArrayFilter(name,mode): sel = FreeCADGui.Selection.getSelectionEx() # selection order independece logic (lattice object and generic shape stencil can be told apart) iLtc = 0 #index of lattice object in selection iStc = 1 #index of stencil object in selection for i in range(0,len(sel)): if lattice2BaseFeature.isObjectLattice(sel[i]): iLtc = i iStc = i-1 #this may give negative index, but python accepts negative indexes break FreeCAD.ActiveDocument.openTransaction("Create ArrayFilter") FreeCADGui.addModule("lattice2ArrayFilter") FreeCADGui.addModule("lattice2Executer") FreeCADGui.doCommand("sel = Gui.Selection.getSelectionEx()") FreeCADGui.doCommand("f = lattice2ArrayFilter.makeArrayFilter(name = '"+name+"')") FreeCADGui.doCommand("f.Base = App.ActiveDocument."+sel[iLtc].ObjectName) FreeCADGui.doCommand("f.FilterType = '"+mode+"'") if mode == 'specific items': FreeCADGui.doCommand("f.items = lattice2ArrayFilter.makeItemListFromSelection(sel["+str(iLtc)+"])") if len(sel[0].SubElementNames) == 1: FreeCADGui.doCommand("f.ExposePlacement = True") else: FreeCADGui.doCommand("f.Stencil = App.ActiveDocument."+sel[iStc].ObjectName) FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n"+ " child.ViewObject.hide()") FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") FreeCADGui.doCommand("f = None") FreeCAD.ActiveDocument.commitTransaction()
def derivedExecute(self,obj): #validity check if not lattice2BaseFeature.isObjectLattice(obj.Base): lattice2Executer.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: if len(r_v) == 2: r_v.append("") # fix issue #1: instead of checking length here and there, simply add the missing field =) 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
def derivedExecute(self,obj): self.assureProperties(obj) # cache stuff objectShape = obj.Object.Shape placements = lattice2BaseFeature.getPlacementsList(obj.PlacementsTo, obj) outputIsLattice = lattice2BaseFeature.isObjectLattice(obj.Object) # Pre-collect base placement list, if base is a lattice. For speed. if outputIsLattice: objectPlms = lattice2BaseFeature.getPlacementsList(obj.Object,obj) placements = DereferenceArray(obj, placements, obj.PlacementsFrom, obj.Referencing) # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying) # the essence for plm in placements: if outputIsLattice: for objectPlm in objectPlms: outputPlms.append(plm.multiply(objectPlm)) else: outputShape = ShapeCopy.copyShape(objectShape, copy_method_index, plm) #outputShape.Placement = plm.multiply(outputShape.Placement) # now handled by copyShape outputShapes.append(outputShape) if outputIsLattice: return outputPlms else: # Output shape or compound (complex logic involving OutputCompounding property) #first, autosettle the OutputCompounding. if obj.OutputCompounding == "(autosettle)": if hasattr(obj.PlacementsTo,"ExposePlacement") and obj.PlacementsTo.ExposePlacement == False: obj.OutputCompounding = "always" else: obj.OutputCompounding = "only if many" #now, set the result shape if len(outputShapes) == 1 and obj.OutputCompounding == "only if many": sh = outputShapes[0] sh = ShapeCopy.transformCopy(sh) obj.Shape = sh else: obj.Shape = Part.makeCompound(outputShapes) return None
def derivedExecute(self,obj): self.assureProperties(obj) # cache stuff objectShape = screen(obj.Object).Shape placements = lattice2BaseFeature.getPlacementsList(screen(obj.PlacementsTo), obj) outputIsLattice = lattice2BaseFeature.isObjectLattice(screen(obj.Object)) # Pre-collect base placement list, if base is a lattice. For speed. if outputIsLattice: objectPlms = lattice2BaseFeature.getPlacementsList(screen(obj.Object),obj) placements = DereferenceArray(obj, placements, screen(obj.PlacementsFrom), obj.Referencing) # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying) # the essence for plm in placements: if outputIsLattice: for objectPlm in objectPlms: outputPlms.append(plm.multiply(objectPlm)) else: outputShape = ShapeCopy.copyShape(objectShape, copy_method_index, plm) #outputShape.Placement = plm.multiply(outputShape.Placement) # now handled by copyShape outputShapes.append(outputShape) if outputIsLattice: return outputPlms else: # Output shape or compound (complex logic involving OutputCompounding property) #first, autosettle the OutputCompounding. if obj.OutputCompounding == "(autosettle)": if hasattr(screen(obj.PlacementsTo),"ExposePlacement") and screen(obj.PlacementsTo).ExposePlacement == False: obj.OutputCompounding = "always" else: obj.OutputCompounding = "only if many" #now, set the result shape if len(outputShapes) == 1 and obj.OutputCompounding == "only if many": sh = outputShapes[0] sh = ShapeCopy.transformCopy(sh) obj.Shape = sh else: obj.Shape = Part.makeCompound(outputShapes) return None
def getIcon(self): if lattice2BaseFeature.isObjectLattice(self.Object): return getIconPath( {"Origin":"Lattice2_PopulateCopies_Plms_Normal.svg", "First item":"Lattice2_PopulateCopies_Plms_Array.svg", "Last item":"Lattice2_PopulateCopies_Plms_Array.svg", "Use PlacementsFrom":"Lattice2_PopulateCopies_Plms_Move.svg", }[self.Object.Referencing] ) else: return getIconPath( {"Origin":"Lattice2_PopulateCopies_Normal.svg", "First item":"Lattice2_PopulateCopies_Array.svg", "Last item":"Lattice2_PopulateCopies_Array.svg", "Use PlacementsFrom":"Lattice2_PopulateCopies_Move.svg", }[self.Object.Referencing] )
def getIcon(self): if lattice2BaseFeature.isObjectLattice(screen(self.Object)): return getIconPath( {"Origin":"Lattice2_PopulateChildren_Plms_Normal.svg", "First item":"Lattice2_PopulateChildren_Plms_Array.svg", "Last item":"Lattice2_PopulateChildren_Plms_Array.svg", "Use PlacementsFrom":"Lattice2_PopulateChildren_Plms_Move.svg", }[self.Object.Referencing] ) else: return getIconPath( {"Origin":"Lattice2_PopulateChildren_Normal.svg", "First item":"Lattice2_PopulateChildren_Array.svg", "Last item":"Lattice2_PopulateChildren_Array.svg", "Use PlacementsFrom":"Lattice2_PopulateChildren_Move.svg", }[self.Object.Referencing] )
def Activated(self): try: sel = getSelectionAsListOfLinkSub() if len(sel) == 1 : #TODO: pop-up with options CreateLatticeMirror(name= "Mirror", extra_code= "f.FlipX = True" ) elif len(sel) == 2 : #TODO: pop-up with options instead of guessing lnk = sel[1] if LBF.isObjectLattice(lnk[0]): extra_code = ( "f.FlipY = True" ) else: sh = resolveSingleSublink(lnk) if sh.ShapeType == 'Face': extra_code = ( "f.FlipZ = True" ) elif sh.ShapeType == 'Edge': extra_code = ( "f.FlipX = True\n" "f.FlipY = True" ) elif sh.ShapeType == 'Vertex': extra_code = ( "f.FlipX = True\n" "f.FlipY = True\n" "f.FlipZ = True" ) CreateLatticeMirror(name = "Mirror", extra_code= extra_code) else: infoMessage("Lattice Mirror","Lattice Mirror feature. Mirrors shapes and placements. Please select object to be mirrored, first," " and then the mirror object (optional). Then invoke this tool.\n\n" "Object to be mirrored: any shape, or compound of shapes, or a placement, or an array of placements." " Note that when a placement is mirrored, its Y axis is switched, for the coordinate system to remain right-handed.\n\n" "Mirror object: either a placement, an array of placements, a vertex, a line, or a plane face. If an array of" " placements is used, the object is reflected using each placement as mirror, and the result is packed into a compound.\n\n" "You can adjust the mirroring direction in property editor by editing FlipX, FlipY, FlipZ properties." " The mirror object is used to establish the coordinate system to work in. If the mirror object is not" " specified, global coordinate system is used (and a custom one can be set up by editing PivotPlacement).") except Exception as err: msgError(err)
def execute(self,selfobj): #validity check if isObjectLattice(selfobj.Object): import lattice2Executer lattice2Executer.warning(selfobj,"A generic shape is expected, but a placement/array was supplied. It will be treated as a generic shape.") rst = [] #variable to receive the final list of shapes lnkobj = selfobj.Object for subname in selfobj.SubNames: subname = subname.strip() if len(subname)==0: raise ValueError("Empty subname! Not allowed.") if 'Face' in subname: index = int(subname.replace('Face',''))-1 rst.append(lnkobj.Shape.Faces[index]) elif 'Edge' in subname: index = int(subname.replace('Edge',''))-1 rst.append(lnkobj.Shape.Edges[index]) elif 'Vertex' in subname: index = int(subname.replace('Vertex',''))-1 rst.append(lnkobj.Shape.Vertexes[index]) else: lattice2Executer.warning(selfobj,"Unexpected subelement name: "+subname+". Trying to extract it with .Shape.getElement()...") rst.append(linkobj.Shape.getElement(subname)) if len(rst) == 0: scale = 1.0 try: if selfobj.Object: scale = selfobj.Object[0].Shape.BoundBox.DiagonalLength/math.sqrt(3) except Exception as err: App.Console.PrintError(selfobj.Name+": Failed to estimate size of marker shape") if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError('Nothing is linked, apparently!') #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: selfobj.Shape = Part.makeCompound(rst) else: # don't make compound of one shape, output it directly sh = rst[0] # absorb placement of original shape sh = ShapeCopy.transformCopy(sh) # apply Placement that is filled into feature's Placement property (not necessary) sh.Placement = selfobj.Placement selfobj.Shape = sh
def CreateLatticeMirror(name, extra_code = ''): sel = FreeCADGui.Selection.getSelectionEx() if not LBF.isObjectLattice(sel[0].Object) and activeBody(): raise SelectionError("PartDesign Mode", "You can only mirror placements while in body. Please deactivate the body to mirror shapes. PartDesign Feature mirroring is not supported yet.") FreeCAD.ActiveDocument.openTransaction("Create LatticeMirror") FreeCADGui.addModule("lattice2Mirror") FreeCADGui.addModule("lattice2Executer") FreeCADGui.addModule("lattice2Utils") FreeCADGui.doCommand("sel = lattice2Utils.getSelectionAsListOfLinkSub()") FreeCADGui.doCommand("f = lattice2Mirror.makeLatticeMirror(name = '"+name+"')") FreeCADGui.doCommand("f.Object = sel[0][0]") FreeCADGui.doCommand("if len(sel) == 2:\n" " f.Pivot = sel[1]") FreeCADGui.doCommand("f.Label = '{name} of {olabel}'.format(name= f.Name, olabel= f.Object.Label)") if extra_code: FreeCADGui.doCommand(extra_code) FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") FreeCAD.ActiveDocument.commitTransaction()
def derivedExecute(self, obj): # cache stuff base = screen(obj.Base).Shape if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)): lattice2Executer.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): # cache stuff base = obj.Base.Shape if not lattice2BaseFeature.isObjectLattice(obj.Base): lattice2Executer.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 nonLattices = [] for iArr in range(0, len(obj.Links)): link = obj.Links[iArr] if not lattice2BaseFeature.isObjectLattice(link): nonLattices.append(link.Label) if len(nonLattices) > 0: lattice2Executer.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]: lattice2Executer.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
def derivedExecute(self, obj): # validity check nonLattices = [] for iArr in range(0, len(obj.Links)): link = obj.Links[iArr] if not lattice2BaseFeature.isObjectLattice(link): nonLattices.append(link.Label) if len(nonLattices) > 0: lattice2Executer.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]: lattice2Executer.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
def Activated(self): try: if len(FreeCADGui.Selection.getSelection())==0: infoMessage("Make compound", "Make compound command. Combines several shapes into one. The shapes are kept as-is. They are not fused together, and can be extracted unchanged.\n\n"+ "Compounds can contain combination of shapes of any topology: one can compound some edges with some solids. Compound is effectively another kind of group. But unlike normal FreeCAD group, compound only accepts OCC geometry. Compound cannot include meshes, dimensions, labels, or other objects that provide no Shape property.\n\n"+ "Note that compounds that have objects that touch or intersect are considered invalid by Part CheckGeometry. Such invalid compounds cannot be used for Part Cut/Common/Fuse.") return oldVal = lattice2Executer.globalIsCreatingLatticeFeature lattice2Executer.globalIsCreatingLatticeFeature = True sel = FreeCADGui.Selection.getSelectionEx() for s in sel: if isObjectLattice(s.Object): lattice2Executer.warning(None,"For making a compound, generic shapes are expected, but some of the selected objects are placements/arrays of placements. These will be treated as generic shapes; results may be unexpected.") break FreeCADGui.runCommand("Part_Compound") except Exception as err: msgError(err) finally: lattice2Executer.globalIsCreatingLatticeFeature = oldVal
def make_reinforcement_lattice(base_rebar, latice_obj, base_placement=FreeCAD.Placement(), name="ReinforcementLattice"): """ make_reinforcement_lattice( base_rebar, placements, [base_placement], [name] ) Adds a lattice reinforcement object. """ if lattice2BF.isObjectLattice(latice_obj) is not True: FreeCAD.Console.PrintError( "The object provided: {} is not a Lattice2 object\n".format( latice_obj.Name)) return None if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ReinforcementLattice") obj.Label = translate("Arch", name) ReinforcementLattice(obj) if FreeCAD.GuiUp: view_lattice.ViewProviderReinforcementLattice(obj.ViewObject) obj.BaseRebar = base_rebar obj.LatticePlacement = latice_obj obj.BasePlacement = base_placement # mark base_rebar obj to make it collect its new child base_rebar.touch() return obj
def CreateLatticeProjectArray(name): sel = FreeCADGui.Selection.getSelectionEx() # selection order independece logic (lattice object and generic shape stencil can be told apart) iLtc = 0 #index of lattice object in selection iStc = 1 #index of stencil object in selection for i in range(0,len(sel)): if lattice2BaseFeature.isObjectLattice(sel[i]): iLtc = i iStc = i-1 #this may give negative index, but python accepts negative indexes break FreeCAD.ActiveDocument.openTransaction("Create ProjectArray") FreeCADGui.addModule("lattice2ProjectArray") FreeCADGui.addModule("lattice2Executer") FreeCADGui.doCommand("sel = Gui.Selection.getSelectionEx()") FreeCADGui.doCommand("f = lattice2ProjectArray.makeProjectArray(name = '"+name+"')") FreeCADGui.doCommand("f.Base = App.ActiveDocument."+sel[iLtc].ObjectName) FreeCADGui.doCommand("f.Tool = App.ActiveDocument."+sel[iStc].ObjectName) FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n"+ " child.ViewObject.hide()") FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") FreeCADGui.doCommand("f = None") FreeCAD.ActiveDocument.commitTransaction()
def execute(self,obj): #validity check if isObjectLattice(obj.Base): import lattice2Executer lattice2Executer.warning(obj,"A generic shape is expected, but an array of placements 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: if len(r_v) == 2: r_v.append("") # fix issue #1: instead of checking length here and there, simply add the missing field =) 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 = ShapeCopy.transformCopy(sh) sh.Placement = obj.Placement obj.Shape = sh return
def derivedExecute(self, obj): base_is_lattice = LBF.isObjectLattice(obj.Object) pivot_is_lattice = LBF.isObjectLattice( obj.Pivot[0]) if obj.Pivot else True flipX = obj.FlipX flipY = obj.FlipY flipZ = obj.FlipZ # collect mirror pivot placements pivots = None em = 0 #editormode of PivotPlacement property. 0 = editable, 1 = read-only, 2 = hidden if obj.Pivot: em = 1 #read-only if pivot_is_lattice: pivots = LBF.getPlacementsList(obj.Pivot[0]) else: pivot_shape = resolveSingleSublink(obj.Pivot) if pivot_shape.ShapeType == 'Edge' and type( pivot_shape.Curve) is Part.Line: dir = pivot_shape.Curve.Direction base = pivot_shape.CenterOfMass if flipX != flipY: raise ValueError( "Unsupported combination of flips for mirroring against line. FlipX and FlipY must either be both on or both off." ) rot = makeOrientationFromLocalAxes(dir) pivots = [App.Placement(base, rot)] elif pivot_shape.ShapeType == 'Face' and type( pivot_shape.Surface) is Part.Plane: dir = pivot_shape.Surface.Axis base = pivot_shape.CenterOfMass if flipX != flipY: raise ValueError( "Unsupported combination of flips for mirroring against line. FlipX and FlipY must either be both on or both off." ) rot = makeOrientationFromLocalAxes(dir) pivots = [App.Placement(base, rot)] elif pivot_shape.ShapeType == 'Vertex': base = pivot_shape.Point pivots = [App.Placement(base, obj.PivotPlacement.Rotation)] em = 0 #editable else: raise TypeError("Unsupported geometry for use as mirror") if len(pivots) == 1: obj.PivotPlacement = pivots[0] else: em = 2 #hidden else: pivots = [obj.PivotPlacement] em = 0 obj.setEditorMode('PivotPlacement', em) # collect objects to be mirrored loop = False whole = obj.ObjectTraversal == 'Use whole' children = [] if base_is_lattice: children = LBF.getPlacementsList(obj.Object) else: if obj.ObjectTraversal == 'Use whole': children = [obj.Object.Shape] loop = True elif obj.ObjectTraversal == 'Direct children only': children = obj.Object.Shape.childShapes() elif obj.ObjectTraversal == 'Use whole': children = LCE.AllLeaves(obj.Object.Shape) else: raise ValueError( "Traversal mode not implemented: {mode}".format( mode=obj.ObjectTraversal)) if len(pivots) != len(children) and not loop and not whole: lattice2Executer.warning( obj, "{label}: Number of children ({nch}) doesn't match the number of pivot placements ({npiv})" .format(label=obj.Label, nch=len(children), npiv=len(pivots))) n = min(len(pivots), len(children)) else: n = len(pivots) # actual mirroring! result = [] for i in range(n): piv = pivots[i] ichild = i % len(children) if base_is_lattice: if whole: for plm in children: result.append( mirrorPlacement(plm, piv, flipX, flipY, flipZ)) else: result.append( mirrorPlacement(children[ichild], piv, flipX, flipY, flipZ)) else: result.append( mirrorShape(children[ichild], piv, flipX, flipY, flipZ)) # write out the result if base_is_lattice: return result else: if n == 1: result = ShapeCopy.transformCopy(result[0]) else: result = Part.Compound(result) obj.Shape = result return None
def execute(self,obj): nOfStrings = len(obj.Strings) lattice = screen(obj.ArrayLink) if lattice is None: plms = [App.Placement() for i in range(0,nOfStrings)] else: if not lattice2BaseFeature.isObjectLattice(lattice): lattice2Executer.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. result = Part.makeCompound(shapes) result.Placement = obj.Placement obj.Shape = result
def derivedExecute(self, obj): #validity check if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)): lattice2Executer.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 = screen(obj.Tool).Shape if lattice2BaseFeature.isObjectLattice(screen(obj.Tool)): lattice2Executer.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(screen(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.TranslateMode == 'keep' posIsProjected = obj.TranslateMode == 'projected' posIsMixed = obj.TranslateMode == '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.TranslateMode) 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
def derivedExecute(self, obj): self.assureGenerator(obj) self.assureProperties(obj) self.updateReadonlyness(obj) # Apply links if screen(obj.Link): if lattice2BaseFeature.isObjectLattice(screen(obj.Link)): lattice2Executer.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 = screen(obj.Link).Shape.getElement( obj.LinkSubelement) else: linkedShape = screen(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 start_point = linkedShape.valueAt(linkedShape.FirstParameter) end_point = linkedShape.valueAt(linkedShape.LastParameter) dir = end_point - start_point point = start_point if not obj.Reverse else end_point if obj.DirIsDriven: obj.Dir = dir if obj.PointIsDriven: obj.Point = point if obj.DrivenProperty != 'None': if obj.DrivenProperty == 'Span': propname = "SpanEnd" obj.SpanEnd = obj.SpanStart + App.Units.Quantity( 'mm') * dir.Length else: propname = obj.DrivenProperty setattr(obj, propname, dir.Length) if self.generator.isPropertyControlledByGenerator(propname): lattice2Executer.warning( obj, "Property " + propname + " is driven by both generator and link. Generator has priority." ) # Generate series of values self.generator.execute() values = [float(strv) for strv in obj.Values] #Apply reversal if obj.Reverse: obj.Dir = obj.Dir * (-1.0) if not (obj.DirIsDriven and screen(obj.Link)): obj.Reverse = False # precompute orientation if obj.OrientMode == 'Along axis': ori = lattice2GeomUtils.makeOrientationFromLocalAxes( ZAx=obj.Dir).multiply( lattice2GeomUtils.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 for v in values: output.append(App.Placement(obj.Point + obj.Dir * v, ori)) return output
def derivedExecute(self,obj): self.initNewProperties(obj) outputIsLattice = lattice2BaseFeature.isObjectLattice(obj.Object) if not lattice2BaseFeature.isObjectLattice(obj.Object): if obj.ObjectTraversal == "Direct children only": objectShapes = obj.Object.Shape.childShapes() if obj.Object.Shape.ShapeType != "Compound": lattice2Executer.warning(obj,"shape supplied as object is not a compound. It is going to be downgraded one level down (e.g, if it is a wire, the edges are going to be enumerated as children).") elif obj.ObjectTraversal == "Recursive": objectShapes = LCE.AllLeaves(obj.Object.Shape) else: raise ValueError("Traversal mode not implemented: "+obj.ObjectTraversal) else: objectPlms = lattice2BaseFeature.getPlacementsList(obj.Object, obj) placements = lattice2BaseFeature.getPlacementsList(obj.PlacementsTo, obj) # Precompute referencing placements = DereferenceArray(obj, placements, obj.PlacementsFrom, obj.Referencing) # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements iChild = 0 numChildren = len(objectPlms) if outputIsLattice else len(objectShapes) copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying) # the essence for iPlm in range(len(placements)): if iChild == numChildren: if obj.LoopObjectSequence: iChild = 0 else: break plm = placements[iPlm] if outputIsLattice: objectPlm = objectPlms[iChild] outputPlms.append(plm.multiply(objectPlm)) else: outputShape = ShapeCopy.copyShape(objectShapes[iChild], copy_method_index, plm) # outputShape.Placement = plm.multiply(outputShape.Placement) #now done by shape copy routine outputShapes.append(outputShape) iChild += 1 if len(placements) > numChildren and not obj.LoopObjectSequence: lattice2Executer.warning(obj,"There are fewer children to populate, than placements to be populated (%1, %2). Extra placements will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements)))) if len(placements) < numChildren: lattice2Executer.warning(obj,"There are more children to populate, than placements to be populated (%1, %2). Extra children will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements)))) if outputIsLattice: return outputPlms else: obj.Shape = Part.makeCompound(outputShapes) return None
def Activated(self): sel = FreeCADGui.Selection.getSelectionEx()[0] isLattice = lattice2BaseFeature.isObjectLattice(sel.Object) strStructure = [] if not hasattr(sel.Object,"Shape"): strStructure = ["<object has no shape!>"] else: if sel.Object.Shape.isNull(): strStructure.append(unicode("<NULL SHAPE!>")) else: for (child, msg, it) in LCE.CompoundExplorer(sel.Object.Shape): #child is a shape. #msg is int equal to one of three constants: # CompoundExplorer.MSG_LEAF - child is a leaf (non-compound) # CompoundExplorer.MSG_DIVEDOWN - child is a compound that is about to be traversed # CompoundExplorer.MSG_BUBBLEUP - child is a compound that was just finished traversing #it is reference to iterator class (can be useful to extract current depth, or index stack) if msg == LCE.CompoundExplorer.MSG_LEAF or msg == LCE.CompoundExplorer.MSG_DIVEDOWN: try: strMsg = ' ' * it.curDepth() + shapeInfoString(child) if msg == LCE.CompoundExplorer.MSG_DIVEDOWN: strMsg += ":" except Exception as err: strMsg = "ERROR: " + err.message strStructure.append(unicode(strMsg)) strSubInfo = [] if sel.HasSubObjects: subNames = sel.SubElementNames subObjects = sel.SubObjects for i in range(0,len(subNames)): strMsg = subNames[i] + ": " child = subObjects[i] try: strMsg += shapeInfoString(child) except Exception as err: strMsg += "ERROR: " + err.message strSubInfo.append(unicode(strMsg)) allText = u'' if sel.HasSubObjects: allText += u"Selected " + str(len(sel.SubElementNames)) + u" subelements:\n" allText += u'\n'.join(strSubInfo) + u'\n\n' allText += u'Selected document object:\n' allText += u' Name = ' + unicode(sel.Object.Name) + u'\n' allText += u' Label = ' + sel.Object.Label + u'\n' allText += u' Is placement/array = ' + unicode(repr(isLattice)) + u'\n' allText += u'Structure: \n' allText += u'\n'.join(strStructure) mb = QtGui.QMessageBox() mb.setIcon(mb.Icon.Information) lines = allText.split(u"\n") if len(lines)>30: lines = lines[0:30] lines.append(u"...") mb.setText(u"\n".join(lines)) mb.setWindowTitle(translate("Lattice2_Inspect","Selection info", None)) btnClose = mb.addButton(QtGui.QMessageBox.StandardButton.Close) btnCopy = mb.addButton("Copy to clipboard",QtGui.QMessageBox.ButtonRole.ActionRole) mb.setDefaultButton(btnClose) mb.exec_() if mb.clickedButton() is btnCopy: cb = QtGui.QClipboard() cb.setText(allText)
def derivedExecute(self, selfobj): # values generator should be functional even if recomputing is disabled, so do it first self.assureGenerator(selfobj) self.generator.updateReadonlyness() self.generator.execute() if selfobj.Recomputing == "Disabled": raise ValueError( selfobj.Name + ": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it." ) try: #test parameter references and read out their current values refstr = selfobj.ParameterRef #dict(selfobj.ExpressionEngine)["ParameterRef"] refstrs = refstr.replace(";", "\t").split("\t") defvalues = [] for refstr in refstrs: refstr = refstr.strip() val = None try: val = getParameter(selfobj.Document, refstr) except Exception as err: App.Console.PrintError( "{obj}: failed to read out parameter '{param}': {err}\n" .format(obj=selfobj.Name, param=refstr, err=str(err))) defvalues.append(val) N_params = len(defvalues) if N_params == 0: raise ValueError(selfobj.Name + ": ParameterRef is not set. It is required.") #parse values values = [] for strrow in selfobj.Values: if len(strrow) == 0: break row = strrow.split(";") row = [ (strv.strip() if len(strv.strip()) > 0 else None) for strv in row ] # clean out spaces and replace empty strings with None if len(row) < N_params: row += [None] * (N_params - len(row)) values.append(row) # convert values to type, filling in defaults where values are missing for row in values: for icol in range(N_params): strv = row[icol] val = None if strv is None: val = defvalues[icol] elif selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int': val = float(strv.replace(",", ".")) if selfobj.ParameterType == 'int': val = int(round(val)) elif selfobj.ParameterType == 'string': val = strv.strip() else: raise ValueError( selfobj.Name + ": ParameterType option not implemented: " + selfobj.ParameterType) row[icol] = val if len(values) == 0: scale = 1.0 try: if not screen(selfobj.Object).Shape.isNull(): scale = screen( selfobj.Object ).Shape.BoundBox.DiagonalLength / math.sqrt(3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError(selfobj.Name + ": list of values is empty.") bGui = False #bool(App.GuiUp) #disabled temporarily, because it causes a crash if property edits are approved by hitting Enter if bGui: import PySide progress = PySide.QtGui.QProgressDialog( u"Recomputing " + selfobj.Label, u"Abort", 0, len(values) + 1) progress.setModal(True) progress.show() doc1 = selfobj.Document doc2 = App.newDocument( ) #create temporary doc to do the computations # assign doc's filename before copying objects, otherwise we get errors with xlinks try: doc2.FileName = doc1.FileName except Exception as err: pass #in old FreeCADs, FileName property is read-only, we can safely ignore that object_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error try: doc2.copyObject(screen(selfobj.Object), True) #if there are nested paraseries in the dependencies, make sure to enable them for objd2 in doc2.Objects: if hasattr(objd2, "Recomputing"): try: objd2.Recomputing = "Enabled" objd2.purgeTouched() except exception: lattice2Executer.warning( selfobj, "Failed to enable recomputing of " + objd2.Name) object_in_doc2 = doc2.getObject(screen(selfobj.Object).Name) if bGui: progress.setValue(1) output_shapes = [] for row in values: for icol in range(len(row)): setParameter(doc2, refstrs[icol].strip(), row[icol]) #recompute doc2.recompute() #get shape shape = None for obj in doc2.Objects: if 'Invalid' in obj.State: lattice2Executer.error( obj, "Recomputing shape for parameter value of " + repr(row) + " failed.") scale = 1.0 try: if not screen(selfobj.Object).Shape.isNull(): scale = screen( selfobj.Object ).Shape.BoundBox.DiagonalLength / math.sqrt( 3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 shape = markers.getNullShapeShape(scale) if shape is None: shape = object_in_doc2.Shape.copy() output_shapes.append(shape) #update progress if bGui: progress.setValue(progress.value() + 1) if progress.wasCanceled(): raise lattice2Executer.CancelError() finally: #delete all references, before destroying the document. Probably not required, but to be sure... del (object_in_doc2) doc2_name = doc2.Name del (doc2) App.closeDocument(doc2_name) if bGui: progress.setValue(len(values) + 1) selfobj.Shape = Part.makeCompound(output_shapes) output_is_lattice = lattice2BaseFeature.isObjectLattice( screen(selfobj.Object)) if 'Auto' in selfobj.isLattice: new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' if selfobj.isLattice != new_isLattice: #check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) selfobj.isLattice = new_isLattice finally: if selfobj.Recomputing == "Recompute Once": selfobj.Recomputing = "Disabled" return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
def derivedExecute(self,selfobj): # values generator should be functional even if recomputing is disabled, so do it first self.assureGenerator(selfobj) self.generator.updateReadonlyness() self.generator.execute() if selfobj.Recomputing == "Disabled": raise ValueError(selfobj.Name+": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it.") try: #test parameter references and read out their current values refstr = selfobj.ParameterRef #dict(selfobj.ExpressionEngine)["ParameterRef"] refstrs = refstr.replace(";","\t").split("\t") defvalues = [] for refstr in refstrs: refstr = refstr.strip(); val = None; try: val = getParameter(selfobj.Document,refstr) except Exception as err: App.Console.PrintError("{obj}: failed to read out parameter '{param}': {err}\n" .format(obj= selfobj.Name, param= refstr, err= err.message)) defvalues.append(val) N_params = len(defvalues) if N_params == 0: raise ValueError(selfobj.Name+": ParameterRef is not set. It is required.") #parse values values = [] for strrow in selfobj.Values: if len(strrow) == 0: break; row = strrow.split(";") row = [(strv.strip() if len(strv.strip())>0 else None) for strv in row] # clean out spaces and replace empty strings with None if len(row) < N_params: row += [None]*(N_params - len(row)) values.append(row) # convert values to type, filling in defaults where values are missing for row in values: for icol in range(N_params): strv = row[icol] val = None if strv is None: val = defvalues[icol] elif selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int': val = float(strv.replace(",",".")) if selfobj.ParameterType == 'int': val = int(round(val)) elif selfobj.ParameterType == 'string': val = strv.strip() else: raise ValueError(selfobj.Name + ": ParameterType option not implemented: "+selfobj.ParameterType) row[icol] = val if len(values) == 0: scale = 1.0 try: if not selfobj.Object.Shape.isNull(): scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError(selfobj.Name + ": list of values is empty.") bGui = False #bool(App.GuiUp) #disabled temporarily, because it causes a crash if property edits are approved by hitting Enter if bGui: import PySide progress = PySide.QtGui.QProgressDialog(u"Recomputing "+selfobj.Label, u"Abort", 0, len(values)+1) progress.setModal(True) progress.show() doc1 = selfobj.Document doc2 = App.newDocument() object_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error try: doc2.copyObject(selfobj.Object, True) #if there are nested paraseries in the dependencies, make sure to enable them for objd2 in doc2.Objects: if hasattr(objd2,"Recomputing"): try: objd2.Recomputing = "Enabled" objd2.purgeTouched() except exception: lattice2Executer.warning(selfobj,"Failed to enable recomputing of "+objd2.Name) object_in_doc2 = doc2.getObject(selfobj.Object.Name) if bGui: progress.setValue(1) output_shapes = [] for row in values: for icol in range(len(row)): setParameter(doc2, refstrs[icol].strip(), row[icol]) #recompute doc2.recompute() #get shape shape = None for obj in doc2.Objects: if 'Invalid' in obj.State: lattice2Executer.error(obj,"Recomputing shape for parameter value of "+repr(row)+" failed.") scale = 1.0 try: if not selfobj.Object.Shape.isNull(): scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 shape = markers.getNullShapeShape(scale) if shape is None: shape = object_in_doc2.Shape.copy() output_shapes.append(shape) #update progress if bGui: progress.setValue(progress.value()+1) if progress.wasCanceled(): raise lattice2Executer.CancelError() finally: #delete all references, before destroying the document. Probably not required, but to be sure... del(object_in_doc2) doc2_name = doc2.Name del(doc2) App.closeDocument(doc2_name) if bGui: progress.setValue(len(values)+1) selfobj.Shape = Part.makeCompound(output_shapes) output_is_lattice = lattice2BaseFeature.isObjectLattice(selfobj.Object) if 'Auto' in selfobj.isLattice: new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' if selfobj.isLattice != new_isLattice:#check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) selfobj.isLattice = new_isLattice finally: if selfobj.Recomputing == "Recompute Once": selfobj.Recomputing = "Disabled" return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
def derivedExecute(self, obj): self.assureGenerator(obj) self.assureProperties(obj) self.updateReadonlyness(obj) # Apply links if screen(obj.AxisLink): if lattice2BaseFeature.isObjectLattice(screen(obj.AxisLink)): lattice2Executer.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 = screen(obj.AxisLink).Shape.getElement( obj.AxisLinkSubelement) else: linkedShape = screen(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): start_point = linkedShape.valueAt(linkedShape.FirstParameter) end_point = linkedShape.valueAt(linkedShape.LastParameter) dir = end_point - start_point point = start_point 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 self.generator.execute() # cache properties into variables radius = float(obj.Radius) values = [float(strv) for strv in obj.Values] # compute initial vector. It is to be perpendicular to Axis rot_ini = lattice2GeomUtils.makeOrientationFromLocalAxes( ZAx=obj.AxisDir) overallPlacement = App.Placement(obj.AxisPoint, rot_ini) # Make the array output = [] # list of placements for ang in values: 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) return output
def derivedExecute(self,obj): # cache stuff base = obj.Base.Shape if not lattice2BaseFeature.isObjectLattice(obj.Base): lattice2Executer.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
def execute(self, selfobj): self.updatedProperties = set() try: if LBF.isObjectLattice(selfobj.Object): plms = LBF.getPlacementsList(selfobj.Object) self.assignProp(selfobj, "App::PropertyInteger", "NumberOfPlacements", len(plms)) for i in range(min(len(plms), 10)): self.assignProp(selfobj, "App::PropertyPlacement", "Placement" + str(i), plms[i]) else: sh = selfobj.Object.Shape self.assignProp(selfobj, "App::PropertyString", "ShapeType", sh.ShapeType) if ( sh.ShapeType == "Compound" or sh.ShapeType == "CompSolid" or sh.ShapeType == "Shell" or sh.ShapeType == "Wire" ): self.assignProp( selfobj, "App::PropertyInteger", sh.ShapeType + "NumChildren", len(sh.childShapes(False, False)) ) if sh.ShapeType == "Compound": max_depth = 0 num_leaves = 0 last_leaf = None for (child, msg, it) in LCE.CompoundExplorer(sh): if it.curDepth() > max_depth: max_depth = it.curDepth() if msg == LCE.CompoundExplorer.MSG_LEAF: last_leaf = child num_leaves += 1 self.assignProp(selfobj, "App::PropertyInteger", "CompoundNestingDepth", max_depth) self.assignProp(selfobj, "App::PropertyInteger", "CompoundNumLeaves", num_leaves) if num_leaves == 1: self.assignProp( selfobj, "App::PropertyString", "ShapeType", sh.ShapeType + "(" + last_leaf.ShapeType + ")" ) sh = last_leaf self.transplant_all_attributes( selfobj, sh, "Shape", withdraw_set=set(["ShapeType", "Content", "Module", "TypeId"]) ) if sh.ShapeType == "Face": typelist = [ "BSplineSurface", "BezierSurface", "Cone", "Cylinder", "OffsetSurface", "Plane", "PlateSurface", "RectangularTrimmedSurface", "Sphere", "SurfaceOfExtrusion", "SurfaceOfRevolution", "Toroid", ] surf = sh.Surface for typename in typelist: if type(surf) is getattr(Part, typename): break typename = None self.assignProp(selfobj, "App::PropertyString", "FaceType", typename) self.transplant_all_attributes(selfobj, surf, "Face") elif sh.ShapeType == "Edge": typelist = [ "Arc", "ArcOfCircle", "ArcOfEllipse", "ArcOfHyperbola", "ArcOfParabola", "BSplineCurve", "BezierCurve", "Circle", "Ellipse", "Hyperbola", "Line", "OffsetCurve", "Parabola", ] crv = sh.Curve for typename in typelist: if type(crv) is getattr(Part, typename): break typename = None self.assignProp(selfobj, "App::PropertyString", "EdgeType", typename) self.transplant_all_attributes(selfobj, crv, "Edge") elif sh.ShapeType == "Vertex": self.assignProp(selfobj, "App::PropertyVector", "VertexPosition", sh.Point) finally: # remove properties that haven't been updated for propname in selfobj.PropertiesList: if selfobj.getGroupOfProperty(propname) == "info": if not (propname in self.updatedProperties): selfobj.removeProperty(propname)
def derivedExecute(self,obj): self.assureGenerator(obj) self.updateReadonlyness(obj) # Apply links if obj.Link: if lattice2BaseFeature.isObjectLattice(obj.Link): lattice2Executer.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': propname = "SpanEnd" obj.SpanEnd = obj.SpanStart + App.Units.Quantity('mm')*dir.Length else: propname = obj.DrivenProperty setattr(obj, propname, dir.Length) if self.generator.isPropertyControlledByGenerator(propname): lattice2Executer.warning(obj, "Property "+propname+" is driven by both generator and link. Generator has priority.") # Generate series of values self.generator.execute() values = [float(strv) for strv in obj.Values] #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 = lattice2GeomUtils.makeOrientationFromLocalAxes(ZAx= obj.Dir).multiply( lattice2GeomUtils.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 for v in values: output.append( App.Placement(obj.Point + obj.Dir*v, ori) ) return output
def derivedExecute(self,obj): self.initNewProperties(obj) outputIsLattice = lattice2BaseFeature.isObjectLattice(screen(obj.Object)) if not lattice2BaseFeature.isObjectLattice(screen(obj.Object)): if obj.ObjectTraversal == "Direct children only": objectShapes = screen(obj.Object).Shape.childShapes() if screen(obj.Object).Shape.ShapeType != "Compound": lattice2Executer.warning(obj,"shape supplied as object is not a compound. It is going to be downgraded one level down (e.g, if it is a wire, the edges are going to be enumerated as children).") elif obj.ObjectTraversal == "Recursive": objectShapes = LCE.AllLeaves(screen(obj.Object).Shape) else: raise ValueError("Traversal mode not implemented: "+obj.ObjectTraversal) else: objectPlms = lattice2BaseFeature.getPlacementsList(screen(obj.Object), obj) placements = lattice2BaseFeature.getPlacementsList(screen(obj.PlacementsTo), obj) # Precompute referencing placements = DereferenceArray(obj, placements, screen(obj.PlacementsFrom), obj.Referencing) # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements iChild = 0 numChildren = len(objectPlms) if outputIsLattice else len(objectShapes) copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying) # the essence for iPlm in range(len(placements)): if iChild == numChildren: if obj.LoopObjectSequence: iChild = 0 else: break plm = placements[iPlm] if outputIsLattice: objectPlm = objectPlms[iChild] outputPlms.append(plm.multiply(objectPlm)) else: outputShape = ShapeCopy.copyShape(objectShapes[iChild], copy_method_index, plm) # outputShape.Placement = plm.multiply(outputShape.Placement) #now done by shape copy routine outputShapes.append(outputShape) iChild += 1 if len(placements) > numChildren and not obj.LoopObjectSequence: lattice2Executer.warning(obj,"There are fewer children to populate, than placements to be populated (%1, %2). Extra placements will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements)))) if len(placements) < numChildren: lattice2Executer.warning(obj,"There are more children to populate, than placements to be populated (%1, %2). Extra children will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements)))) if outputIsLattice: return outputPlms else: obj.Shape = Part.makeCompound(outputShapes) return None
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 lattice2BaseFeature.isObjectLattice(lattice): lattice2Executer.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)
def execute(self, selfobj): self.assureProperties(selfobj) #validity check if isObjectLattice(screen(selfobj.Object)): import lattice2Executer lattice2Executer.warning( selfobj, "A generic shape is expected, but a placement/array was supplied. It will be treated as a generic shape." ) lnkobj = screen(selfobj.Object) sh = lnkobj.Shape # subsequencing full_link = (lnkobj, selfobj.SubNames) if selfobj.Looping == 'Single': lnkseq = [full_link] else: lnkseq = LSS.Subsequence_auto(full_link, selfobj.CompoundTraversal, selfobj.Looping) # main code seq_packs = [ ] #pack = single item of subsequence. Pack contains list of elements that were selected. shape_count = 0 for lnk in lnkseq: # loop over subsequence (if Looping == 'Single', this loop will only loop once) # extract the pack assert ( lnk[0] is lnkobj ) # all links should point to elements of one object anyway subnames = lnk[1] pack = [ ] #acculumator, to eventually become a compound of shapes for this subsequence item for subname in subnames: subname = subname.strip() if len(subname) == 0: raise ValueError("Empty subname! Not allowed.") if 'Face' in subname: # manual handling of standard cases, because support for negative indexing is needed index = int(subname.replace('Face', '')) - 1 pack.append(sh.Faces[index]) elif 'Edge' in subname: index = int(subname.replace('Edge', '')) - 1 pack.append(sh.Edges[index]) elif 'Vertex' in subname: index = int(subname.replace('Vertex', '')) - 1 pack.append(sh.Vertexes[index]) else: #fail-safe. non-standard sublink. import lattice2Executer lattice2Executer.warning( selfobj, "Unexpected subelement name: " + subname + ". Trying to extract it with .Shape.getElement()...") pack.append(sh.getElement(subname)) shape_count += len(pack) # convert list into compound if len(pack) == 1: pack = ShapeCopy.transformCopy(pack[0]) else: pack = Part.makeCompound(pack) # accumulate seq_packs.append(pack) # convert list into compound if len(seq_packs) == 1: seq_packs = seq_packs[0] else: seq_packs = Part.makeCompound(seq_packs) if shape_count == 0: # no shapes collected, FAIL! scale = 1.0 try: if screen(selfobj.Object): scale = screen( selfobj.Object ).Shape.BoundBox.DiagonalLength / math.sqrt(3) except Exception as err: App.Console.PrintError( selfobj.Name + ": Failed to estimate size of marker shape") if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError( 'Nothing is linked, apparently!' ) #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. # done! selfobj.Shape = seq_packs
def execute(self,selfobj): self.updatedProperties = set() try: if LBF.isObjectLattice(screen(selfobj.Object)): plms = LBF.getPlacementsList(screen(selfobj.Object)) self.assignProp(selfobj,"App::PropertyInteger","NumberOfPlacements",len(plms)) for i in range( min( len(plms), 10 ) ): self.assignProp(selfobj,"App::PropertyPlacement","Placement"+str(i),plms[i]) else: sh = screen(selfobj.Object).Shape self.assignProp(selfobj,"App::PropertyString","ShapeType", sh.ShapeType) if sh.ShapeType == "Compound" or sh.ShapeType == "CompSolid" or sh.ShapeType == "Shell" or sh.ShapeType == "Wire": self.assignProp(selfobj,"App::PropertyInteger",sh.ShapeType+"NumChildren",len(sh.childShapes(False,False))) if sh.ShapeType == "Compound": max_depth = 0 num_leaves = 0 last_leaf = None for (child, msg, it) in LCE.CompoundExplorer(sh): if it.curDepth() > max_depth: max_depth = it.curDepth() if msg == LCE.CompoundExplorer.MSG_LEAF: last_leaf = child num_leaves += 1 self.assignProp(selfobj,"App::PropertyInteger","CompoundNestingDepth", max_depth) self.assignProp(selfobj,"App::PropertyInteger","CompoundNumLeaves", num_leaves) if num_leaves == 1: self.assignProp(selfobj,"App::PropertyString","ShapeType", sh.ShapeType + "(" + last_leaf.ShapeType + ")") sh = last_leaf self.transplant_all_attributes(selfobj,sh,"Shape", withdraw_set= set(["ShapeType", "Content", "Module", "TypeId"])) if sh.ShapeType == "Face": typelist = ["BSplineSurface", "BezierSurface", "Cone", "Cylinder", "OffsetSurface", "Plane", "PlateSurface", "RectangularTrimmedSurface", "Sphere", "SurfaceOfExtrusion", "SurfaceOfRevolution", "Toroid", ] surf = sh.Surface for typename in typelist: if type(surf) is getattr(Part, typename): break typename = None self.assignProp(selfobj,"App::PropertyString","FaceType",typename) self.transplant_all_attributes(selfobj,surf,"Face") elif sh.ShapeType == "Edge": typelist = ["Arc", "ArcOfCircle", "ArcOfEllipse", "ArcOfHyperbola", "ArcOfParabola", "BSplineCurve", "BezierCurve", "Circle", "Ellipse", "Hyperbola", "Line", "OffsetCurve", "Parabola", ] crv = sh.Curve for typename in typelist: if type(crv) is getattr(Part, typename): break typename = None self.assignProp(selfobj,"App::PropertyString","EdgeType",typename) self.transplant_all_attributes(selfobj,crv,"Edge") elif sh.ShapeType == "Vertex": self.assignProp(selfobj,"App::PropertyVector","VertexPosition",sh.Point) finally: #remove properties that haven't been updated for propname in selfobj.PropertiesList: if selfobj.getGroupOfProperty(propname) == "info": if not (propname in self.updatedProperties): selfobj.removeProperty(propname)
def derivedExecute(self,obj): # cache stuff base = screen(obj.Base).Shape if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)): lattice2Executer.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 # construct 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
def execute(self, obj): #validity check if isObjectLattice(screen(obj.Base)): import lattice2Executer lattice2Executer.warning( obj, "A generic shape is expected, but an array of placements was supplied. It will be treated as a generic shape." ) rst = [] #variable to receive the final list of shapes shps = screen(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: if len(r_v) == 2: r_v.append( "" ) # fix issue #1: instead of checking length here and there, simply add the missing field =) 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 range(0, len(shps)): if not flags[i]: rst.append(shps[i]) elif obj.FilterType == 'collision-pass': stencil = screen(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 range(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 range(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 screen(obj.Base).Shape.isNull(): scale = screen( obj.Base).Shape.BoundBox.DiagonalLength / math.sqrt( 3) / math.sqrt(len(shps)) if scale < DistConfusion * 100: scale = 1.0 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 = ShapeCopy.transformCopy(sh) sh.Placement = obj.Placement obj.Shape = sh return
def derivedExecute(self,obj): # cache stuff if lattice2BaseFeature.isObjectLattice(obj.ShapeLink): lattice2Executer.warning(obj,"ShapeLink points to a placement/array of placements. The placement/array will be reinterpreted as a generic shape; the results may be unexpected.") base = obj.ShapeLink.Shape if obj.CompoundTraversal == "Use as a whole": baseChildren = [base] else: if base.ShapeType != 'Compound': base = Part.makeCompound([base]) if obj.CompoundTraversal == "Recursive": baseChildren = LCE.AllLeaves(base) else: baseChildren = base.childShapes() #cache mode comparisons, for speed posIsNone = obj.TranslateMode == '(none)' posIsParent = obj.TranslateMode == 'parent' posIsChild = obj.TranslateMode == 'child' posIsCenterM = obj.TranslateMode == 'child.CenterOfMass' posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox' posIsVertex = obj.TranslateMode == 'child.Vertex' oriIsNone = obj.OrientMode == '(none)' oriIsParent = obj.OrientMode == 'parent' 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 posIsParent: 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 lattice2BoundBox bb = lattice2BoundBox.getPrecisionBoundBox(child) pos = bb.Center elif posIsVertex: v = child.Vertexes[obj.TranslateElementIndex - 1] pos = v.Point else: raise ValueError(obj.Name + ": translation mode not implemented: "+obj.TranslateMode) if oriIsNone: pass elif oriIsParent: ori = base.Placement.Rotation elif oriIsChild: ori = child.Placement.Rotation elif oriIsInertial: leaves = LCE.AllLeaves(child) if len(leaves)>1: raise ValueError(obj.Name + ": 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(obj.Name + ": orientation mode not implemented: "+obj.OrientMode) plm = App.Placement(pos, ori) outputPlms.append(plm) return outputPlms
def derivedExecute(self,obj): #validity check if not lattice2BaseFeature.isObjectLattice(obj.Base): lattice2Executer.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 lattice2BaseFeature.isObjectLattice(obj.Tool): lattice2Executer.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.TranslateMode == 'keep' posIsProjected = obj.TranslateMode == 'projected' posIsMixed = obj.TranslateMode == '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.TranslateMode ) 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
def Activated(self): sel = FreeCADGui.Selection.getSelectionEx()[0] isLattice = lattice2BaseFeature.isObjectLattice(sel.Object) strStructure = [] if not hasattr(sel.Object, "Shape"): strStructure = ["<object has no shape!>"] else: if sel.Object.Shape.isNull(): strStructure.append("<NULL SHAPE!>") else: for (child, msg, it) in LCE.CompoundExplorer(sel.Object.Shape): #child is a shape. #msg is int equal to one of three constants: # CompoundExplorer.MSG_LEAF - child is a leaf (non-compound) # CompoundExplorer.MSG_DIVEDOWN - child is a compound that is about to be traversed # CompoundExplorer.MSG_BUBBLEUP - child is a compound that was just finished traversing #it is reference to iterator class (can be useful to extract current depth, or index stack) if msg == LCE.CompoundExplorer.MSG_LEAF or msg == LCE.CompoundExplorer.MSG_DIVEDOWN: try: strMsg = ' ' * it.curDepth() + shapeInfoString( child) if msg == LCE.CompoundExplorer.MSG_DIVEDOWN: strMsg += ":" except Exception as err: strMsg = "ERROR: " + str(err) strStructure.append(strMsg) strSubInfo = [] if sel.HasSubObjects: subNames = sel.SubElementNames subObjects = sel.SubObjects for i in range(0, len(subNames)): strMsg = subNames[i] + ": " child = subObjects[i] try: strMsg += shapeInfoString(child) except Exception as err: strMsg += "ERROR: " + str(err) strSubInfo.append(strMsg) allText = u'' if sel.HasSubObjects: allText += u"Selected " + str(len( sel.SubElementNames)) + u" subelements:\n" allText += u'\n'.join(strSubInfo) + u'\n\n' allText += u'Selected document object:\n' allText += u' Name = ' + sel.Object.Name + u'\n' allText += u' Label = ' + sel.Object.Label + u'\n' allText += u' Is placement/array = ' + repr(isLattice) + u'\n' allText += u'Structure: \n' allText += u'\n'.join(strStructure) mb = QtGui.QMessageBox() mb.setIcon(mb.Icon.Information) lines = allText.split(u"\n") if len(lines) > 30: lines = lines[0:30] lines.append(u"...") mb.setText(u"\n".join(lines)) mb.setWindowTitle(translate("Lattice2_Inspect", "Selection info", None)) btnClose = mb.addButton(QtGui.QMessageBox.StandardButton.Close) btnCopy = mb.addButton("Copy to clipboard", QtGui.QMessageBox.ButtonRole.ActionRole) mb.setDefaultButton(btnClose) mb.exec_() if mb.clickedButton() is btnCopy: cb = QtGui.QClipboard() cb.setText(allText)
def derivedExecute(self, selfobj): if selfobj.Recomputing == "Disabled": raise ValueError( selfobj.Name + ": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it." ) try: # do the subsequencing in this document first, to verify stuff is set up correctly, and to obtain sequence length if self.isVerbose(): print("In-place pre-subsequencing, for early check") n_seq, subs_linkdict = self.makeSubsequence( selfobj, screen(selfobj.ObjectToLoopOver)) bGui = bool( App.GuiUp ) and Executer.globalIsCreatingLatticeFeature #disabled for most recomputes, because it causes a crash if property edits are approved by hitting Enter if bGui: import PySide progress = PySide.QtGui.QProgressDialog( u"Recomputing " + selfobj.Label, u"Abort", 0, n_seq + 1) progress.setModal(True) progress.show() doc1 = selfobj.Document doc2 = App.newDocument() object_to_take_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error object_to_loop_in_doc2 = None try: if self.isVerbose(): print( "Copying object with dependencies to a temporary document..." ) doc2.copyObject(screen(selfobj.ObjectToTake), True) if self.isVerbose(): print("Enabling nested para/toposeries, if any...") #if there are nested para/toposeries in the dependencies, make sure to enable them for objd2 in doc2.Objects: if hasattr(objd2, "Recomputing"): try: objd2.Recomputing = "Enabled" objd2.purgeTouched() except exception: Executer.warning( selfobj, "Failed to enable recomputing of " + objd2.Name) object_to_take_in_doc2 = doc2.getObject( screen(selfobj.ObjectToTake).Name) object_to_loop_in_doc2 = doc2.getObject( screen(selfobj.ObjectToLoopOver).Name) if bGui: progress.setValue(1) if self.isVerbose(): print("Repeating subsequencing in temporary document...") n_seq, subs_linkdict = self.makeSubsequence( selfobj, object_to_loop_in_doc2) output_shapes = [] for i in range(n_seq): if self.isVerbose(): print("Computing {x}/{y}".format(x=i + 1, y=n_seq)) for key in subs_linkdict: writeProperty(doc2, key[0], key[1], subs_linkdict[key][i]) #recompute doc2.recompute() #get shape shape = None for obj in doc2.Objects: if 'Invalid' in obj.State: Executer.error( obj, "Recomputing shape for subsequence index " + repr(i) + " failed.") scale = 1.0 try: if not screen( selfobj.ObjectToTake).Shape.isNull(): scale = screen( selfobj.ObjectToTake ).Shape.BoundBox.DiagonalLength / math.sqrt( 3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 shape = markers.getNullShapeShape(scale) if shape is None: shape = object_to_take_in_doc2.Shape.copy() output_shapes.append(shape) #update progress if bGui: progress.setValue(progress.value() + 1) if progress.wasCanceled(): raise Executer.CancelError() finally: #delete all references, before destroying the document. Probably not required, but to be sure... if self.isVerbose(): print("Cleanup...") del (object_to_take_in_doc2) del (object_to_loop_in_doc2) doc2_name = doc2.Name del (doc2) App.closeDocument(doc2_name) if bGui: progress.setValue(n_seq + 1) selfobj.Shape = Part.makeCompound(output_shapes) output_is_lattice = lattice2BaseFeature.isObjectLattice( screen(selfobj.ObjectToTake)) if 'Auto' in selfobj.isLattice: new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' if selfobj.isLattice != new_isLattice: #check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) selfobj.isLattice = new_isLattice finally: if selfobj.Recomputing == "Recompute Once": selfobj.Recomputing = "Disabled" return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
def derivedExecute(self, obj): #validity check if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)): lattice2Executer.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(screen(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: if len(r_v) == 2: r_v.append( "" ) # fix issue #1: instead of checking length here and there, simply add the missing field =) 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 range(0, len(input)): if not flags[i]: output.append(input[i]) elif obj.FilterType == 'collision-pass': stencil = screen(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 range(0, len(input)): if obj.FilterType == 'window-distance': pnt = Part.Vertex(input[i].Base) vals[i] = pnt.distToShape(screen(obj.Stencil).Shape)[0] valFrom = obj.WindowFrom valTo = obj.WindowTo for i in range(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
def derivedExecute(self,obj): # cache stuff if lattice2BaseFeature.isObjectLattice(screen(obj.ShapeLink)): lattice2Executer.warning(obj,"ShapeLink points to a placement/array of placements. The placement/array will be reinterpreted as a generic shape; the results may be unexpected.") base = screen(obj.ShapeLink).Shape if obj.CompoundTraversal == "Use as a whole": baseChildren = [base] else: if base.ShapeType != 'Compound': base = Part.makeCompound([base]) if obj.CompoundTraversal == "Recursive": baseChildren = LCE.AllLeaves(base) else: baseChildren = base.childShapes() #cache mode comparisons, for speed posIsNone = obj.TranslateMode == '(none)' posIsParent = obj.TranslateMode == 'parent' posIsChild = obj.TranslateMode == 'child' posIsCenterM = obj.TranslateMode == 'child.CenterOfMass' posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox' posIsVertex = obj.TranslateMode == 'child.Vertex' oriIsNone = obj.OrientMode == '(none)' oriIsParent = obj.OrientMode == 'parent' 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 posIsParent: 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 lattice2BoundBox bb = lattice2BoundBox.getPrecisionBoundBox(child) pos = bb.Center elif posIsVertex: v = child.Vertexes[obj.TranslateElementIndex - 1] pos = v.Point else: raise ValueError(obj.Name + ": translation mode not implemented: "+obj.TranslateMode) if oriIsNone: pass elif oriIsParent: ori = base.Placement.Rotation elif oriIsChild: ori = child.Placement.Rotation elif oriIsInertial: leaves = LCE.AllLeaves(child) if len(leaves)>1: raise ValueError(obj.Name + ": 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(obj.Name + ": orientation mode not implemented: "+obj.OrientMode) plm = App.Placement(pos, ori) outputPlms.append(plm) return outputPlms