def execute(self,obj): rst = [] pieces = LCE.AllLeaves(screen(obj.Base).Shape) cutters = LCE.AllLeaves(screen(obj.Tool).Shape) # prepare cutter shapes by converting them to solids cutters_solids = [] for cutter in cutters: if cutter.ShapeType == "Face": cutter = Part.makeShell([cutter]) if cutter.ShapeType == "Shell": cutter = Part.makeSolid(cutter) if cutter.ShapeType == "Solid": # all right, nothing to do cutters_solids.append(cutter) else: raise TypeError("Cannot slice with shape of type '{typ}'".format(typ= cutter.ShapeType)) # cut everything successively with one cutter at a time for cutter in cutters_solids: pieces_old = pieces pieces = [] for piece in pieces_old: pieces_1 = LCE.AllLeaves(piece.cut(cutter)) pieces_2 = LCE.AllLeaves(piece.common(cutter)) # when cutting with shells, and object doesn't intersect cutter, duplicates are sometimes produced. This is probably as occ bug. # But we can filter the duplicates out. The trick is to test, if the result of a cut is the same as the original, which can be done by simply comparing masses. pieces_12 = pieces_1+pieces_2 all_same = True for piece_test in pieces_12: if float_fuzzy_equal(piece_test.Mass, piece.Mass, 1e-9): # piece doesn't intersect cutter (probably). # this test may fail, if the piece cut off by the cutter is very small.... So we discard cut result only if all masses are equal (no smaller objects are found) pass else: all_same = False break if all_same: #the object doesn't intersect with cutter. Special processing, to remove duplicates if we are cutting with shells. pieces += [piece] else: pieces += pieces_1 + pieces_2 if obj.Refine: pieces_old = pieces pieces = [] for piece in pieces_old: pieces.append(piece.removeSplitter()) obj.Shape = Part.makeCompound(pieces)
def resolveSingleSublink(lnk): if lnk is None: raise ValueError("resolveSingleSublink: link is empty") obj, sub = lnk if len(sub)>1: raise ValueError("Too many subelements linked: num. Maximum: 1".format(num= len(sub))) sh = obj.Shape if len(sub) == 0 or sub[0] == '' else obj.Shape.getElement(sub[0]) shs = LCE.AllLeaves(sh) #if whole object is linked, it may be a compound containing the shape of interest. Explode it. if len(shs) != 1: raise ValueError("Linked is {num} shapes, but should be exactly one.".format(num= len(shs))) return shs[0]
def getPlacementsList(documentObject, context=None, suppressWarning=False): '''getPlacementsList(documentObject, context = None): extract list of placements from an array object. Context is an object to report as context, when displaying a warning if the documentObject happens to be a non-lattice.''' if not isObjectLattice(documentObject): if not suppressWarning: lattice2Executer.warning( context, documentObject.Name + " is not a placement or an array of placements. Results may be unexpected." ) if documentObject.isDerivedFrom( 'App::Placement') or documentObject.isDerivedFrom( 'PartDesign::CoordinateSystem'): return [documentObject.Placement] leaves = LCE.AllLeaves(documentObject.Shape) return [leaf.Placement for leaf in leaves]
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): #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 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): #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.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 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 execute(self,obj): rst = [] #variable to receive the final list of shapes shp = screen(obj.Base).Shape if obj.Mode == 'bypass': rst = [shp] elif obj.Mode == 'Leaves': rst = LCE.AllLeaves(shp) elif obj.Mode == 'CompSolids': rst = shp.CompSolids elif obj.Mode == 'Solids': rst = shp.Solids elif obj.Mode == 'Shells': rst = shp.Shells elif obj.Mode == 'OpenWires': openWires = [] shells = shp.Shells for shell in shells: openEdges = shell.getFreeEdges().childShapes() if len(openEdges) > 1: # edges need to be fused into wires clusters = Part.getSortedClusters(openEdges) wires = [Part.Wire(cluster) for cluster in clusters] else: wires = openEdges openWires.extend(wires) rst = openWires elif obj.Mode == 'Faces': rst = shp.Faces elif obj.Mode == 'Wires': rst = shp.Wires elif obj.Mode == 'Edges': rst = shp.Edges elif obj.Mode == 'Seam edges': rst = getAllSeams(shp) elif obj.Mode == 'Non-seam edges': seams = getAllSeams(shp) edges = shp.Edges rst = [] for e in edges: bIsSeam = False for s in seams: if e.isSame(s): bIsSeam = True break if not bIsSeam: rst.append(e) elif obj.Mode == 'Vertices': rst = shp.Vertexes else: raise ValueError('Downgrade mode not implemented:'+obj.Mode) if len(rst) == 0: scale = 1.0 if not screen(obj.Base).Shape.isNull(): scale = screen(obj.Base).Shape.BoundBox.DiagonalLength/math.sqrt(3) if scale < DistConfusion * 100: scale = 1.0 obj.Shape = markers.getNullShapeShape(scale) raise ValueError('Downgrade output is null') #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(rst) return
def execute(self, obj): 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() N = len(baseChildren) orients = [] if obj.OrientMode == "global": orients = [App.Placement()] * N elif obj.OrientMode == "local of compound": orients = [screen(obj.ShapeLink).Placement] * N elif obj.OrientMode == "local of child": orients = [child.Placement for child in baseChildren] elif obj.OrientMode == "use OrientLink": orients = LBF.getPlacementsList(screen(obj.OrientLink), context=obj) if len(orients) == N: pass elif len(orients) > N: Executer.warning( obj, "Array of placements linked in OrientLink has more placements (" + str(len(orients)) + ") than bounding boxes to be constructed (" + str(len(baseChildren)) + "). Extra placements will be dropped.") elif len(orients) == 1: orients = [orients[0]] * N else: raise ValueError( obj.Name + ": Array of placements linked in OrientLink has not enough placements (" + str(len(orients)) + ") than bounding boxes to be constructed (" + str(len(baseChildren)) + ").") else: raise ValueError(obj.Name + ": OrientMode " + obj.OrientMode + " not implemented =(") # mark placements with no rotation for i in range(N): Q = orients[i].Rotation.Q # Quaternions for zero rotation are either (0,0,0,1) or (0,0,0,-1). For non-zero # rotations, some of first three values will be nonzero, and fourth value will # not be equal to 1. While it's enough to compare absolute value of fourth value # to 1, precision is seriously lost in such comparison, so we are checking if # first three values are zero instead. if abs(Q[0]) + abs(Q[1]) + abs(Q[2]) < ParaConfusion: orients[i] = None from lattice2ShapeCopy import shallowCopy boxes_shapes = [] for i in range(N): child = baseChildren[i] if orients[i] is not None: child = shallowCopy(child) child.Placement = orients[i].inverse().multiply( child.Placement) if obj.Precision: bb = getPrecisionBoundBox(child) else: bb = child.BoundBox bb = scaledBoundBox(bb, obj.ScaleFactor) bb.enlarge(obj.Padding) bb_shape = boundBox2RealBox(bb) if orients[i] is not None: bb_shape.transformShape(orients[i].toMatrix(), True) boxes_shapes.append(bb_shape) #Fill in read-only properties if N == 1: obj.Size = App.Vector(bb.XLength, bb.YLength, bb.ZLength) cnt = bb.Center if orients[0] is not None: cnt = orients[0].multVec(cnt) obj.Center = cnt else: obj.Size = App.Vector() obj.Center = App.Vector() if obj.CompoundTraversal == "Use as a whole": assert (N == 1) obj.Shape = boxes_shapes[0] else: obj.Shape = Part.makeCompound(boxes_shapes)
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 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