def derivedExecute(self,obj): #validity check nonLattices = [] for iArr in range(0, len(obj.Links)): link = obj.Links[iArr] if not latticeBaseFeature.isObjectLattice(link): nonLattices.append(link.Label) if len(nonLattices) > 0: latticeExecuter.warning(obj, "Only lattice objects are expected to be linked as arrays in JoinArrays. There are " +len(nonLattices)+" objects which are not lattice objects. Results may me unexpected.") #extract placements listlistPlms = [] lengths = [] for link in obj.Links: leaves = LCE.AllLeaves(link.Shape) listlistPlms.append([child.Placement for child in leaves]) lengths.append(len(leaves)) #processing output = [] #list of placements if obj.Interleave: for l in lengths[1:]: if l != lengths[0]: latticeExecuter.warning(obj,"Array lengths are unequal: "+repr(lengths)+". Interleaving will be inconsistent.") break for iItem in range(0,max(lengths)): for list in listlistPlms: if iItem < len(list): output.append(list[iItem]) else: for list in listlistPlms: output.extend(list) return output
def Activated(self): if len(FreeCADGui.Selection.getSelection()) == 1 : FreeCAD.ActiveDocument.openTransaction("Explode Array") latticeExecuter.globalIsCreatingLatticeFeature = True try: obj = FreeCADGui.Selection.getSelection()[0] if not latticeBaseFeature.isObjectLattice(obj): latticeExecuter.warning(None,"ExplodeArray expects a lattice object; a generic shape was provided instead. Results may be unexpected.") sh = obj.Shape n_elem = len(LCE.AllLeaves(sh)) latticeExecuter.globalIsCreatingLatticeFeature = False for i in range(0, n_elem): af = makeArrayFilter(name = 'Element') af.Label = u'Element' + unicode(i) af.Base = obj af.FilterType = 'specific items' af.items = str(i) af.SingleByDesign = True af.ViewObject.DontUnhideOnDelete = True FreeCAD.ActiveDocument.recompute() obj.ViewObject.hide() except Exception: FreeCAD.ActiveDocument.abortTransaction() raise finally: latticeExecuter.globalIsCreatingLatticeFeature = False FreeCAD.ActiveDocument.commitTransaction() else: mb = QtGui.QMessageBox() mb.setIcon(mb.Icon.Warning) mb.setText(translate("Lattice_ArrayFilter", "Select a lattice object, first!", None)) mb.setWindowTitle(translate("Lattice_ArrayFilter","Bad selection", None)) mb.exec_()
def derivedExecute(self,obj): #validity check if not latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.") output = [] #variable to receive the final list of placements leaves = LCE.AllLeaves(obj.Base.Shape) input = [leaf.Placement for leaf in leaves] if obj.FilterType == 'bypass': output = input elif obj.FilterType == 'specific items': flags = [False] * len(input) ranges = obj.items.split(';') for r in ranges: r_v = r.split(':') if len(r_v) == 1: i = int(r_v[0]) output.append(input[i]) flags[i] = True elif len(r_v) == 2 or len(r_v) == 3: ifrom = None if len(r_v[0].strip()) == 0 else int(r_v[0]) ito = None if len(r_v[1].strip()) == 0 else int(r_v[1]) istep = None if len(r_v[2].strip()) == 0 else int(r_v[2]) output=output+input[ifrom:ito:istep] for b in flags[ifrom:ito:istep]: b = True else: raise ValueError('index range cannot be parsed:'+r) if obj.Invert : output = [] for i in xrange(0,len(input)): if not flags[i]: output.append(input[i]) elif obj.FilterType == 'collision-pass': stencil = obj.Stencil.Shape for plm in input: pnt = Part.Vertex(plm.Base) d = pnt.distToShape(stencil) if bool(d[0] < DistConfusion) ^ bool(obj.Invert): output.append(plm) elif obj.FilterType == 'window-distance': vals = [0.0] * len(input) for i in xrange(0,len(input)): if obj.FilterType == 'window-distance': pnt = Part.Vertex(input[i].Base) vals[i] = pnt.distToShape(obj.Stencil.Shape)[0] valFrom = obj.WindowFrom valTo = obj.WindowTo for i in xrange(0,len(input)): if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert: output.append(input[i]) else: raise ValueError('Filter mode not implemented:'+obj.FilterType) return output
def derivedExecute(self, obj): # cache stuff base = obj.Base.Shape tool = obj.Tool.Shape if tool.ShapeType != 'Compound': tool = Part.makeCompound([tool]) if obj.FlattenToolHierarchy: toolChildren = LCE.AllLeaves(tool) else: toolChildren = tool.childShapes() # validity logic if not latticeBaseFeature.isObjectLattice(obj.Tool): latticeExecuter.warning( obj, 'Tool is not a lattice object. Results may be unexpected.\n') outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base) plmMatcher = App.Placement( ) #extra placement, that makes first item to preserve its original placement if obj.KeepBaseFirstItemPos: plmMatcher = toolChildren[0].Placement.inverse() # Pre-collect base placement list, if base is a lattice. For speed. if outputIsLattice: baseLeaves = LCE.AllLeaves(base) basePlms = [] for leaf in baseLeaves: basePlms.append(plmMatcher.multiply(leaf.Placement)) baseLeaves = None #free memory # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements # the essence for toolChild in toolChildren: #cache some stuff toolPlm = toolChild.Placement if outputIsLattice: for basePlm in basePlms: outputPlms.append(toolPlm.multiply(basePlm)) else: outputShape = base.copy() outputShape.Placement = toolPlm.multiply( plmMatcher.multiply(outputShape.Placement)) outputShapes.append(outputShape) if outputIsLattice: return outputPlms else: obj.Shape = Part.makeCompound(outputShapes) return None
def derivedExecute(self,obj): # cache stuff base = obj.Base.Shape tool = obj.Tool.Shape if tool.ShapeType != 'Compound': tool = Part.makeCompound([tool]) if obj.FlattenToolHierarchy: toolChildren = LCE.AllLeaves(tool) else: toolChildren = tool.childShapes() # validity logic if not latticeBaseFeature.isObjectLattice(obj.Tool): latticeExecuter.warning(obj, 'Tool is not a lattice object. Results may be unexpected.\n') outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base) plmMatcher = App.Placement() #extra placement, that makes first item to preserve its original placement if obj.KeepBaseFirstItemPos: plmMatcher = toolChildren[0].Placement.inverse() # Pre-collect base placement list, if base is a lattice. For speed. if outputIsLattice: baseLeaves = LCE.AllLeaves(base) basePlms = [] for leaf in baseLeaves: basePlms.append(plmMatcher.multiply(leaf.Placement)) baseLeaves = None #free memory # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements # the essence for toolChild in toolChildren: #cache some stuff toolPlm = toolChild.Placement if outputIsLattice: for basePlm in basePlms: outputPlms.append(toolPlm.multiply(basePlm)) else: outputShape = base.copy() outputShape.Placement = toolPlm.multiply(plmMatcher.multiply(outputShape.Placement)) outputShapes.append(outputShape) if outputIsLattice: return outputPlms else: obj.Shape = Part.makeCompound(outputShapes) return None
def execute(self, obj): # please, don't override. Override derivedExecute instead. plms = self.derivedExecute(obj) if plms is not None: obj.NumElements = len(plms) shapes = [] markerSize = obj.MarkerSize if markerSize < DistConfusion: markerSize = getMarkerSizeEstimate(plms) marker = latticeMarkers.getPlacementMarker( scale=markerSize, markerID=obj.MarkerShape) #FIXME: make hierarchy-aware if obj.SingleByDesign: if len(plms) != 1: latticeExecuter.warning( obj, "Multiple placements are being fed, but object is single by design. Only fisrt placement will be used..." ) obj.Shape = marker.copy() obj.Placement = plms[0] else: for plm in plms: sh = marker.copy() sh.Placement = plm shapes.append(sh) if len(shapes) == 0: obj.Shape = latticeMarkers.getNullShapeShape(markerSize) raise ValueError( 'Lattice object is null' ) #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. sh = Part.makeCompound(shapes) obj.Shape = sh if obj.isLattice == 'Auto-Off': obj.isLattice = 'Auto-On' else: # DerivedExecute didn't return anything. Thus we assume it # has assigned the shape, and thus we don't do anything. # Moreover, we assume that it is no longer a lattice object, so: if obj.isLattice == 'Auto-On': obj.isLattice = 'Auto-Off' obj.NumElements = len(obj.Shape.childShapes(False, False)) return
def execute(self,obj): # please, don't override. Override derivedExecute instead. plms = self.derivedExecute(obj) if plms is not None: obj.NumElements = len(plms) shapes = [] markerSize = obj.MarkerSize if markerSize < DistConfusion: markerSize = getMarkerSizeEstimate(plms) marker = latticeMarkers.getPlacementMarker(scale= markerSize, markerID= obj.MarkerShape) #FIXME: make hierarchy-aware if obj.SingleByDesign: if len(plms) != 1: latticeExecuter.warning(obj,"Multiple placements are being fed, but object is single by design. Only fisrt placement will be used...") obj.Shape = marker.copy() obj.Placement = plms[0] else: for plm in plms: sh = marker.copy() sh.Placement = plm shapes.append(sh) if len(shapes) == 0: obj.Shape = latticeMarkers.getNullShapeShape(markerSize) raise ValueError('Lattice object is null') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. sh = Part.makeCompound(shapes) obj.Shape = sh if obj.isLattice == 'Auto-Off': obj.isLattice = 'Auto-On' else: # DerivedExecute didn't return anything. Thus we assume it # has assigned the shape, and thus we don't do anything. # Moreover, we assume that it is no longer a lattice object, so: if obj.isLattice == 'Auto-On': obj.isLattice = 'Auto-Off' obj.NumElements = len(obj.Shape.childShapes(False,False)) return
def derivedExecute(self,obj): # cache stuff base = obj.Base.Shape if not latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n") baseChildren = LCE.AllLeaves(base) #cache mode comparisons, for speed posIsInvert = obj.TranslateMode == 'invert' posIsKeep = obj.TranslateMode == 'keep' posIsReset = obj.TranslateMode == 'reset' oriIsInvert = obj.OrientMode == 'invert' oriIsKeep = obj.OrientMode == 'keep' oriIsReset = obj.OrientMode == 'reset' # initialize output containers and loop variables outputPlms = [] #list of placements # the essence for child in baseChildren: pos = App.Vector() ori = App.Rotation() inverted = child.Placement.inverse() if posIsInvert: pos = inverted.Base elif posIsKeep: pos = child.Placement.Base elif posIsReset: pass if oriIsInvert: ori = inverted.Rotation elif oriIsKeep: ori = child.Placement.Rotation elif oriIsReset: pass plm = App.Placement(pos, ori) outputPlms.append(plm) return outputPlms
def derivedExecute(self,obj): #validity check if not latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.") toolShape = obj.Tool.Shape if latticeBaseFeature.isObjectLattice(obj.Tool): latticeExecuter.warning(obj,"A lattice object was provided as Tool. It will be converted into points; orientations will be ignored.") leaves = LCE.AllLeaves(toolShape) points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves] toolShape = Part.makeCompound(points) leaves = LCE.AllLeaves(obj.Base.Shape) input = [leaf.Placement for leaf in leaves] output = [] #variable to receive the final list of placements #cache settings elev = float(obj.PosElevation) posIsKeep = obj.PositionMode == 'keep' posIsProjected = obj.PositionMode == 'projected' posIsMixed = obj.PositionMode == 'mixed' mixF = float(obj.PosMixFraction) oriIsKeep = obj.OrientMode == 'keep' oriIsAlongGap = obj.OrientMode == 'along gap' oriIsTangentPlane = obj.OrientMode == 'tangent plane' oriIsAlongU = obj.OrientMode == 'along u' oriIsAlongV = obj.OrientMode == 'along v' isMultiSol = obj.Multisolution == 'use all' for plm in input: v = Part.Vertex(plm.Base) projection = v.distToShape(toolShape) (dist, gaps, infos) = projection for iSol in range(0,len(gaps)): (posKeep, posPrj) = gaps[iSol] (dummy, dummy, dummy, el_topo, el_index, el_params) = infos[iSol] # Fetch all possible parameters (some may not be required, depending on modes) normal = posKeep - posPrj if normal.Length < DistConfusion: normal = None tangU = None tangV = None if el_topo == 'Face': face = toolShape.Faces[el_index] if normal is None: normal = face.normalAt(*el_params) (tangU, tangV) = face.tangentAt(*el_params) elif el_topo == "Edge": edge = toolShape.Edges[el_index] tangU = edge.tangentAt(el_params) if normal is not None: normal.normalize() #mode logic - compute new placement if posIsKeep: pos = plm.Base elif posIsProjected: pos = posPrj elif posIsMixed: pos = posKeep*mixF + posPrj*(1-mixF) else: raise ValueError("Positioning mode not implemented: " + obj.PositionMode ) if abs(elev) > DistConfusion: if normal is None: raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for nonzero position elevation.") pos += normal * elev if oriIsKeep: ori = plm.Rotation elif oriIsAlongGap: if normal is None: raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'") ori = Utils.makeOrientationFromLocalAxesUni("X",XAx= normal*(-1.0)) elif oriIsTangentPlane: if normal is None: raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'") ori = Utils.makeOrientationFromLocalAxesUni("Z",ZAx= normal) elif oriIsAlongU: if normal is None: raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'") if tangU is None: raise ValueError("TangentU vector not available for point on " + el_topo +". TangentU vector is required for orientation mode '"+obj.OrientMode+"'") ori = Utils.makeOrientationFromLocalAxesUni("ZX",ZAx= normal, XAx= tangU) elif oriIsAlongV: if normal is None: raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'") if tangV is None: raise ValueError("TangentV vector not available for point on " + el_topo +". TangentV vector is required for orientation mode '"+obj.OrientMode+"'") ori = Utils.makeOrientationFromLocalAxesUni("ZX",ZAx= normal, XAx= tangV) else: raise ValueError("Orientation mode not implemented: " + obj.OrientMode ) output.append(App.Placement(pos,ori)) if not isMultiSol: break return output
def execute(self,obj): #validity check if isObjectLattice(obj.Base): import latticeExecuter latticeExecuter.warning(obj,"A generic shape is expected, but a lattice object was supplied. It will be treated as a generic shape.") rst = [] #variable to receive the final list of shapes shps = obj.Base.Shape.childShapes() if obj.FilterType == 'bypass': rst = shps elif obj.FilterType == 'specific items': rst = [] flags = [False] * len(shps) ranges = obj.items.split(';') for r in ranges: r_v = r.split(':') if len(r_v) == 1: i = int(r_v[0]) rst.append(shps[i]) flags[i] = True elif len(r_v) == 2 or len(r_v) == 3: ifrom = None if len(r_v[0].strip()) == 0 else int(r_v[0]) ito = None if len(r_v[1].strip()) == 0 else int(r_v[1]) istep = None if len(r_v[2].strip()) == 0 else int(r_v[2]) rst=rst+shps[ifrom:ito:istep] for b in flags[ifrom:ito:istep]: b = True else: raise ValueError('index range cannot be parsed:'+r) if obj.Invert : rst = [] for i in xrange(0,len(shps)): if not flags[i]: rst.append(shps[i]) elif obj.FilterType == 'collision-pass': stencil = obj.Stencil.Shape for s in shps: d = s.distToShape(stencil) if bool(d[0] < DistConfusion) ^ bool(obj.Invert): rst.append(s) elif obj.FilterType == 'window-volume' or obj.FilterType == 'window-area' or obj.FilterType == 'window-length' or obj.FilterType == 'window-distance': vals = [0.0] * len(shps) for i in xrange(0,len(shps)): if obj.FilterType == 'window-volume': vals[i] = shps[i].Volume elif obj.FilterType == 'window-area': vals[i] = shps[i].Area elif obj.FilterType == 'window-length': vals[i] = shps[i].Length elif obj.FilterType == 'window-distance': vals[i] = shps[i].distToShape(obj.Stencil.Shape)[0] maxval = max(vals) if obj.Stencil: if obj.FilterType == 'window-volume': vals[i] = obj.Stencil.Shape.Volume elif obj.FilterType == 'window-area': vals[i] = obj.Stencil.Shape.Area elif obj.FilterType == 'window-length': vals[i] = obj.Stencil.Shape.Length if obj.OverrideMaxVal: maxval = obj.OverrideMaxVal valFrom = obj.WindowFrom / 100.0 * maxval valTo = obj.WindowTo / 100.0 * maxval for i in xrange(0,len(shps)): if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert: rst.append(shps[i]) else: raise ValueError('Filter mode not implemented:'+obj.FilterType) if len(rst) == 0: scale = 1.0 if not obj.Base.Shape.isNull(): scale = obj.Base.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps)) if scale < DistConfusion * 100: scale = 1.0 print scale obj.Shape = markers.getNullShapeShape(scale) raise ValueError('Nothing passes through the filter') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. if len(rst) > 1: obj.Shape = Part.makeCompound(rst) else: # don't make compound of one shape, output it directly sh = rst[0] sh.transformShape(sh.Placement.toMatrix(),True) #True = make copy sh.Placement = FreeCAD.Placement() obj.Shape = sh return
def derivedExecute(self, obj): #validity check if not latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning( obj, "A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected." ) toolShape = obj.Tool.Shape if latticeBaseFeature.isObjectLattice(obj.Tool): latticeExecuter.warning( obj, "A lattice object was provided as Tool. It will be converted into points; orientations will be ignored." ) leaves = LCE.AllLeaves(toolShape) points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves] toolShape = Part.makeCompound(points) leaves = LCE.AllLeaves(obj.Base.Shape) input = [leaf.Placement for leaf in leaves] output = [] #variable to receive the final list of placements #cache settings elev = float(obj.PosElevation) posIsKeep = obj.PositionMode == 'keep' posIsProjected = obj.PositionMode == 'projected' posIsMixed = obj.PositionMode == 'mixed' mixF = float(obj.PosMixFraction) oriIsKeep = obj.OrientMode == 'keep' oriIsAlongGap = obj.OrientMode == 'along gap' oriIsTangentPlane = obj.OrientMode == 'tangent plane' oriIsAlongU = obj.OrientMode == 'along u' oriIsAlongV = obj.OrientMode == 'along v' isMultiSol = obj.Multisolution == 'use all' for plm in input: v = Part.Vertex(plm.Base) projection = v.distToShape(toolShape) (dist, gaps, infos) = projection for iSol in range(0, len(gaps)): (posKeep, posPrj) = gaps[iSol] (dummy, dummy, dummy, el_topo, el_index, el_params) = infos[iSol] # Fetch all possible parameters (some may not be required, depending on modes) normal = posKeep - posPrj if normal.Length < DistConfusion: normal = None tangU = None tangV = None if el_topo == 'Face': face = toolShape.Faces[el_index] if normal is None: normal = face.normalAt(*el_params) (tangU, tangV) = face.tangentAt(*el_params) elif el_topo == "Edge": edge = toolShape.Edges[el_index] tangU = edge.tangentAt(el_params) if normal is not None: normal.normalize() #mode logic - compute new placement if posIsKeep: pos = plm.Base elif posIsProjected: pos = posPrj elif posIsMixed: pos = posKeep * mixF + posPrj * (1 - mixF) else: raise ValueError("Positioning mode not implemented: " + obj.PositionMode) if abs(elev) > DistConfusion: if normal is None: raise ValueError( "Normal vector not available for a placement resting on " + el_topo + ". Normal vector is required for nonzero position elevation." ) pos += normal * elev if oriIsKeep: ori = plm.Rotation elif oriIsAlongGap: if normal is None: raise ValueError( "Normal vector not available for a placement resting on " + el_topo + ". Normal vector is required for orientation mode '" + obj.OrientMode + "'") ori = Utils.makeOrientationFromLocalAxesUni("X", XAx=normal * (-1.0)) elif oriIsTangentPlane: if normal is None: raise ValueError( "Normal vector not available for a placement resting on " + el_topo + ". Normal vector is required for orientation mode '" + obj.OrientMode + "'") ori = Utils.makeOrientationFromLocalAxesUni("Z", ZAx=normal) elif oriIsAlongU: if normal is None: raise ValueError( "Normal vector not available for a placement resting on " + el_topo + ". Normal vector is required for orientation mode '" + obj.OrientMode + "'") if tangU is None: raise ValueError( "TangentU vector not available for point on " + el_topo + ". TangentU vector is required for orientation mode '" + obj.OrientMode + "'") ori = Utils.makeOrientationFromLocalAxesUni("ZX", ZAx=normal, XAx=tangU) elif oriIsAlongV: if normal is None: raise ValueError( "Normal vector not available for a placement resting on " + el_topo + ". Normal vector is required for orientation mode '" + obj.OrientMode + "'") if tangV is None: raise ValueError( "TangentV vector not available for point on " + el_topo + ". TangentV vector is required for orientation mode '" + obj.OrientMode + "'") ori = Utils.makeOrientationFromLocalAxesUni("ZX", ZAx=normal, XAx=tangV) else: raise ValueError("Orientation mode not implemented: " + obj.OrientMode) output.append(App.Placement(pos, ori)) if not isMultiSol: break return output
def derivedExecute(self, obj): # cache stuff base = obj.Base.Shape if not latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning( obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n" ) input = [leaf.Placement for leaf in LCE.AllLeaves(base)] if len(input) < 2: raise ValueError( "At least 2 placements ar needed to interpolate; there are just " + str(len(input)) + " in base array.") if obj.NumberSamples < 2: raise ValueError("Can output no less than 2 samples; " + str(obj.NumberSamples) + " was requested.") #cache mode comparisons, for speed posIsInterpolate = obj.TranslateMode == 'interpolate' posIsReset = obj.TranslateMode == 'reset' oriIsInterpolate = obj.OrientMode == 'interpolate' oriIsReset = obj.OrientMode == 'reset' # construct interpolation functions # prepare lists of input samples IArray = [float(i) for i in range(0, len(input))] XArray = [plm.Base.x for plm in input] YArray = [plm.Base.y for plm in input] ZArray = [plm.Base.z for plm in input] QArrays = [[], [], [], []] prevQ = [0.0] * 4 for plm in input: Q = plm.Rotation.Q #test if quaernion has changed sign compared to previous one. # Quaternions of opposite sign are equivalent in terms of rotation, # but sign changes confuse interpolation, so we are detecting sign # changes and discarding them if dotProduct(Q, prevQ) < -ParaConfusion: Q = [-v for v in Q] for iQ in [0, 1, 2, 3]: QArrays[iQ].append(Q[iQ]) prevQ = Q # constuct function objects if posIsInterpolate: FX = LIU.InterpolateF(IArray, XArray) FY = LIU.InterpolateF(IArray, YArray) FZ = LIU.InterpolateF(IArray, ZArray) if oriIsInterpolate: FQs = [] for iQ in [0, 1, 2, 3]: FQs.append(LIU.InterpolateF(IArray, QArrays[iQ])) # initialize output containers and loop variables outputPlms = [] #list of placements for i_output in range(0, math.trunc(obj.NumberSamples + ParaConfusion)): i_input = float(i_output) / (obj.NumberSamples - 1) * (len(input) - 1) pos = App.Vector() ori = App.Rotation() if posIsInterpolate: pos = App.Vector(FX.value(i_input), FY.value(i_input), FZ.value(i_input)) if oriIsInterpolate: ori = App.Rotation(FQs[0].value(i_input), FQs[1].value(i_input), FQs[2].value(i_input), FQs[3].value(i_input)) plm = App.Placement(pos, ori) outputPlms.append(plm) return outputPlms
def derivedExecute(self,obj): # cache stuff base = obj.Base.Shape if base.ShapeType != 'Compound': base = Part.makeCompound([base]) if obj.FlattenBaseHierarchy: baseChildren = LCE.AllLeaves(base) else: baseChildren = base.childShapes() tool = obj.Tool.Shape if tool.ShapeType != 'Compound': tool = Part.makeCompound([tool]) if obj.FlattenToolHierarchy: toolChildren = LCE.AllLeaves(tool) else: toolChildren = tool.childShapes() iBase = 0 isMult = obj.Operation == 'MultiplyPlacements' # cache mode comparisons to speed them up isAvg = obj.Operation == 'AveragePlacements' isIgnore = obj.Operation == 'IgnoreBasePlacements' isOverride = obj.Operation == 'OverrideBasePlacements' #mode validity logic if not latticeBaseFeature.isObjectLattice(obj.Tool): latticeExecuter.warning(obj, 'Tool is not a lattice object. Results may be unexpected.\n') outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base) if isOverride and outputIsLattice: latticeExecuter.warning(obj, 'Base is a lattice object. OverrideBasePlacements operation requires a generic compound as Base. So, the lattice is being treated as a generic compound.\n') outputIsLattice = False # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements bFirst = True plmMatcher = App.Placement() #extra placement, that aligns first tool member and first base member # the essence for toolChild in toolChildren: # early test for termination if iBase > len(baseChildren)-1: if obj.LoopSequence: iBase = 0 else: FreeCAD.Console.PrintWarning(obj.Name+': There are '+str(len(toolChildren)-len(baseChildren))+ ' more placements in Tool than children in Base. Those placements'+ ' were dropped.\n') break #cache some stuff basePlm = baseChildren[iBase].Placement toolPlm = toolChild.Placement if not outputIsLattice: outputShape = baseChildren[iBase].copy() #prepare alignment placement if bFirst: bFirst = False if obj.KeepBaseFirstItemPos: plmMatcher = toolPlm.inverse() #mode logic if isMult: outPlm = toolPlm.multiply(plmMatcher.multiply(basePlm)) elif isAvg: plm1 = toolPlm plm2 = plmMatcher.multiply(basePlm) transl = plm1.Base*0.5 + plm2.Base*0.5 a1,b1,c1,d1 = plm1.Rotation.Q a2,b2,c2,d2 = plm2.Rotation.Q rot = App.Rotation(a1+a2,b1+b2,c1+c2,d1+d2) #no divide-by-two, because FreeCAD will normalize the quaternion automatically outPlm = App.Placement(transl,rot) elif isIgnore: outPlm = toolPlm elif isOverride: assert(not outputIsLattice) outputShape.transformShape(toolPlm.inverse.multiply(plmMatcher.multiply(basePlm))) outPlm = toolPlm if outputIsLattice: outputPlms.append(outPlm) else: outputShape.Placement = outPlm outputShapes.append(outputShape) iBase += 1 if outputIsLattice: return outputPlms else: obj.Shape = Part.makeCompound(outputShapes)
def derivedExecute(self,obj): # Fill in (update read-only) properties that are driven by the mode. self.updateReadOnlyness(obj) if obj.Mode == 'SpanN': n = obj.NumberPolar if obj.EndInclusive: n -= 1 if n == 0: n = 1 obj.AngleStep = (obj.AngleSpanEnd - obj.AngleSpanStart)/n elif obj.Mode == 'StepN': n = obj.NumberPolar if obj.EndInclusive: n -= 1 obj.AngleSpanEnd = obj.AngleSpanStart + obj.AngleStep*n elif obj.Mode == 'SpanStep': nfloat = float((obj.AngleSpanEnd - obj.AngleSpanStart) / obj.AngleStep) n = math.trunc(nfloat - ParaConfusion) + 1 if obj.EndInclusive and abs(nfloat-round(nfloat)) <= ParaConfusion: n = n + 1 obj.NumberPolar = n # Apply links if obj.AxisLink: if latticeBaseFeature.isObjectLattice(obj.AxisLink): latticeExecuter.warning(obj,"For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape.") #resolve the link if len(obj.AxisLinkSubelement) > 0: linkedShape = obj.AxisLink.Shape.getElement(obj.AxisLinkSubelement) else: linkedShape = obj.AxisLink.Shape #Type check if linkedShape.ShapeType != 'Edge': raise ValueError('Axis link must be an edge; it is '+linkedShape.ShapeType+' instead.') #prepare dir = App.Vector() point = App.Vector() if isinstance(linkedShape.Curve, Part.Line): dir = linkedShape.Curve.EndPoint - linkedShape.Curve.StartPoint point = linkedShape.Curve.StartPoint elif isinstance(linkedShape.Curve, Part.Circle): dir = linkedShape.Curve.Axis point = linkedShape.Curve.Center else: raise ValueError("Edge " + repr(linkedShape) + " can't be used to derive an axis. It must be either a line or a circle/arc.") #apply if obj.AxisDirIsDriven: obj.AxisDir = dir if obj.AxisPointIsDriven: obj.AxisPoint = point # Generate the actual array. We can use Step and N directly to # completely avoid mode logic, since we had updated them # cache properties into variables step = float(obj.AngleStep) startAng = float(obj.AngleSpanStart) + step*float(obj.Offset) n = int(obj.NumberPolar) radius = float(obj.Radius) # compute initial vector. It is to be perpendicular to Axis rot_ini = latticeGeomUtils.makeOrientationFromLocalAxes(ZAx= obj.AxisDir) overallPlacement = App.Placement(obj.AxisPoint, rot_ini) # Make the array output = [] # list of placements if obj.Mode != "Spreadsheet": for i in range(0, n): ang = startAng + step*i p = Part.Vertex() localrot = App.Rotation(App.Vector(0,0,1), ang) localtransl = localrot.multVec(App.Vector(radius,0,0)) localplm = App.Placement(localtransl, localrot) resultplm = overallPlacement.multiply(localplm) if obj.OrientMode == 'None': resultplm.Rotation = App.Rotation() output.append(resultplm) else: #parse address addr = obj.CellStart #assuming only two letter column if addr[1].isalpha(): col = addr[0:2] row = addr[2:] else: col = addr[0:1] row = addr[1:] row = int(row) #loop until the value an't be read out while True: try: ang = obj.SpreadSheet.get(col+str(row)) except ValueError: break ang = float(ang) p = Part.Vertex() localrot = App.Rotation(App.Vector(0,0,1), ang) localtransl = localrot.multVec(App.Vector(radius,0,0)) localplm = App.Placement(localtransl, localrot) output.append( overallPlacement.multiply(localplm) ) row += 1 return output
def execute(self,obj): nOfStrings = len(obj.Strings) lattice = obj.ArrayLink if lattice is None: plms = [App.Placement() for i in range(0,nOfStrings)] else: if not latticeBaseFeature.isObjectLattice(lattice): latticeExecuter.warning(obj,"ShapeString's link to array must point to a lattice. It points to a generic shape. Results may be unexpected.") leaves = LCE.AllLeaves(lattice.Shape) plms = [leaf.Placement for leaf in leaves] #update foolObj's properties for (proptype, propname, group, hint) in self.foolObj.properties: if propname != "String": #ignore "String", that will be taken care of in the following loop setattr(self.foolObj, propname, getattr(obj, propname)) self.foolObj.FontFile = findFont(obj.FontFile) obj.FullPathToFont = self.foolObj.FontFile shapes = [] for i in range( 0 , min(len(plms),len(obj.Strings)) ): if len(obj.Strings[i]) > 0: #generate shapestring using Draft self.foolObj.String = obj.Strings[i] self.foolObj.Shape = None self.draft_shape_string.execute(self.foolObj) shape = self.foolObj.Shape #calculate alignment point if obj.XAlign == 'None' and obj.YAlign == 'None': pass #need not calculate boundbox else: if obj.AlignPrecisionBoundBox: bb = getPrecisionBoundBox(shape) else: bb = shape.BoundBox alignPnt = App.Vector() if obj.XAlign == 'Left': alignPnt.x = bb.XMin elif obj.XAlign == 'Right': alignPnt.x = bb.XMax elif obj.XAlign == 'Middle': alignPnt.x = bb.Center.x if obj.YAlign == 'Bottom': alignPnt.y = bb.YMin elif obj.YAlign == 'Top': alignPnt.y = bb.YMax elif obj.YAlign == 'Middle': alignPnt.y = bb.Center.y #Apply alignment shape.Placement = App.Placement(alignPnt*(-1.0), App.Rotation()).multiply(shape.Placement) #Apply placement from array shape.Placement = plms[i].multiply(shape.Placement) shapes.append(shape.copy()) if len(shapes) == 0: scale = 1.0 if lattice is not None: scale = lattice.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps)) if scale < DistConfusion * 100: scale = 1.0 obj.Shape = markers.getNullShapeShape(scale) raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. obj.Shape = Part.makeCompound(shapes)
def derivedExecute(self,obj): # cache stuff if latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning(obj,"Base is a lattice object. Since a non-lattice object is required by arrayFromShape tool, the results may be unexpected.") base = obj.Base.Shape if obj.WholeObject: baseChildren = [base] #if obj.FlattenBaseHierarchy: # latticeExecuter.warning(obj, "FlattenBaseHierarchy is ignored because WholeObject is set to True") else: if base.ShapeType != 'Compound': base = Part.makeCompound([base]) if obj.FlattenBaseHierarchy: baseChildren = LCE.AllLeaves(base) else: baseChildren = base.childShapes() #cache mode comparisons, for speed posIsNone = obj.TranslateMode == '(none)' posIsBase = obj.TranslateMode == 'base' posIsChild = obj.TranslateMode == 'child' posIsCenterM = obj.TranslateMode == 'child.CenterOfMass' posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox' posIsVertex = obj.TranslateMode == 'child.Vertex' oriIsNone = obj.OrientMode == '(none)' oriIsBase = obj.OrientMode == 'base' oriIsChild = obj.OrientMode == 'child' oriIsInertial = obj.OrientMode == 'child.InertiaAxes' oriIsEdge = obj.OrientMode == 'child.Edge' oriIsFace = obj.OrientMode == 'child.FaceAxis' # initialize output containers and loop variables outputPlms = [] #list of placements # the essence for child in baseChildren: pos = App.Vector() ori = App.Rotation() if posIsNone: pass elif posIsBase: pos = base.Placement.Base elif posIsChild: pos = child.Placement.Base elif posIsCenterM: leaves = LCE.AllLeaves(child) totalW = 0 weightAttrib = {"Vertex":"", "Edge":"Length", "Wire":"Length", "Face":"Area", "Shell":"Area", "Solid":"Volume", "CompSolid":""}[leaves[0].ShapeType] #Center of mass of a compound is a weghted average of centers # of mass of individual objects. for leaf in leaves: w = 1.0 if not weightAttrib else (getattr(leaf, weightAttrib)) if leaf.ShapeType == 'Vertex': leafCM = leaf.Point #elif child.ShapeType == 'CompSolid': #todo else: leafCM = leaf.CenterOfMass pos += leafCM * w totalW += w pos = pos * (1.0/totalW) elif posIsCenterBB: import latticeBoundBox bb = latticeBoundBox.getPrecisionBoundBox(child) pos = bb.Center elif posIsVertex: v = child.Vertexes[obj.TranslateElementIndex - 1] pos = v.Point else: raise ValueError("latticePolarArrayFromShape: translation mode not implemented: "+obj.TranslateMode) if oriIsNone: pass elif oriIsBase: ori = base.Placement.Rotation elif oriIsChild: ori = child.Placement.Rotation elif oriIsInertial: leaves = LCE.AllLeaves(child) if len(leaves)>1: raise ValueError("latticePolarArrayFromShape: calculation of principal axes of compounds is not supported yet") props = leaves[0].PrincipalProperties XAx = props['FirstAxisOfInertia'] ZAx = props['ThirdAxisOfInertia'] ori = Utils.makeOrientationFromLocalAxes(ZAx, XAx) elif oriIsEdge: edge = child.Edges[obj.OrientElementIndex - 1] XAx = edge.Curve.tangent(edge.Curve.FirstParameter)[0] ori1 = Utils.makeOrientationFromLocalAxes(ZAx= XAx) ori2 = Utils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0),XAx= App.Vector(0,0,1)) ori = ori1.multiply(ori2) elif oriIsFace: face = child.Faces[obj.OrientElementIndex - 1] ZAx = face.Surface.Axis else: raise ValueError("latticePolarArrayFromShape: rientation mode not implemented: "+obj.OrientMode) plm = App.Placement(pos, ori) outputPlms.append(plm) return outputPlms
def execute(self, obj): #validity check if isObjectLattice(obj.Base): import latticeExecuter latticeExecuter.warning( obj, "A generic shape is expected, but a lattice object was supplied. It will be treated as a generic shape." ) rst = [] #variable to receive the final list of shapes shps = obj.Base.Shape.childShapes() if obj.FilterType == 'bypass': rst = shps elif obj.FilterType == 'specific items': rst = [] flags = [False] * len(shps) ranges = obj.items.split(';') for r in ranges: r_v = r.split(':') if len(r_v) == 1: i = int(r_v[0]) rst.append(shps[i]) flags[i] = True elif len(r_v) == 2 or len(r_v) == 3: ifrom = None if len(r_v[0].strip()) == 0 else int(r_v[0]) ito = None if len(r_v[1].strip()) == 0 else int(r_v[1]) istep = None if len(r_v[2].strip()) == 0 else int(r_v[2]) rst = rst + shps[ifrom:ito:istep] for b in flags[ifrom:ito:istep]: b = True else: raise ValueError('index range cannot be parsed:' + r) if obj.Invert: rst = [] for i in xrange(0, len(shps)): if not flags[i]: rst.append(shps[i]) elif obj.FilterType == 'collision-pass': stencil = obj.Stencil.Shape for s in shps: d = s.distToShape(stencil) if bool(d[0] < DistConfusion) ^ bool(obj.Invert): rst.append(s) elif obj.FilterType == 'window-volume' or obj.FilterType == 'window-area' or obj.FilterType == 'window-length' or obj.FilterType == 'window-distance': vals = [0.0] * len(shps) for i in xrange(0, len(shps)): if obj.FilterType == 'window-volume': vals[i] = shps[i].Volume elif obj.FilterType == 'window-area': vals[i] = shps[i].Area elif obj.FilterType == 'window-length': vals[i] = shps[i].Length elif obj.FilterType == 'window-distance': vals[i] = shps[i].distToShape(obj.Stencil.Shape)[0] maxval = max(vals) if obj.Stencil: if obj.FilterType == 'window-volume': maxval = obj.Stencil.Shape.Volume elif obj.FilterType == 'window-area': maxval = obj.Stencil.Shape.Area elif obj.FilterType == 'window-length': maxval = obj.Stencil.Shape.Length if obj.OverrideMaxVal: maxval = obj.OverrideMaxVal valFrom = obj.WindowFrom / 100.0 * maxval valTo = obj.WindowTo / 100.0 * maxval for i in xrange(0, len(shps)): if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert: rst.append(shps[i]) else: raise ValueError('Filter mode not implemented:' + obj.FilterType) if len(rst) == 0: scale = 1.0 if not obj.Base.Shape.isNull(): scale = obj.Base.Shape.BoundBox.DiagonalLength / math.sqrt( 3) / math.sqrt(len(shps)) if scale < DistConfusion * 100: scale = 1.0 print scale obj.Shape = markers.getNullShapeShape(scale) raise ValueError( 'Nothing passes through the filter' ) #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. if len(rst) > 1: obj.Shape = Part.makeCompound(rst) else: # don't make compound of one shape, output it directly sh = rst[0] sh.transformShape(sh.Placement.toMatrix(), True) #True = make copy sh.Placement = FreeCAD.Placement() obj.Shape = sh return
def derivedExecute(self,obj): self.updateReadOnlyness(obj) # Apply links if obj.Link: if latticeBaseFeature.isObjectLattice(obj.Link): latticeExecuter.warning(obj,"For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape.") #resolve the link if len(obj.LinkSubelement) > 0: linkedShape = obj.Link.Shape.getElement(obj.LinkSubelement) else: linkedShape = obj.Link.Shape #Type check if linkedShape.ShapeType != 'Edge': raise ValueError('Axis link must be an edge; it is '+linkedShape.ShapeType+' instead.') if type(linkedShape.Curve) is not Part.Line: raise ValueError('Axis link must be a line; it is '+type(linkedShape.Curve)+' instead.') #obtain dir = linkedShape.Curve.EndPoint - linkedShape.Curve.StartPoint point = linkedShape.Curve.StartPoint if not obj.Reverse else linkedShape.Curve.EndPoint if obj.DirIsDriven: obj.Dir = dir if obj.PointIsDriven: obj.Point = point if obj.DrivenProperty != 'None': if obj.DrivenProperty == 'Span': obj.SpanEnd = obj.SpanStart + App.Units.Quantity('mm')*dir.Length else: setattr(obj, obj.DrivenProperty, dir.Length) # Fill in (update read-only) properties that are driven by the mode. if obj.Mode == 'SpanN': n = obj.NumberLinear if obj.EndInclusive: n -= 1 if n == 0: n = 1 obj.Step = (obj.SpanEnd - obj.SpanStart)/n if obj.DrivenProperty == 'Step' and obj.Link: latticeExecuter.warning(obj,"Step property is being driven by both the link and the selected mode. Mode has priority.") elif obj.Mode == 'StepN': n = obj.NumberLinear if obj.EndInclusive: n -= 1 obj.SpanEnd = obj.SpanStart + obj.Step*n if 'Span' in obj.DrivenProperty and obj.Link: latticeExecuter.warning(obj,"SpanEnd property is being driven by both the link and the selected mode. Mode has priority.") elif obj.Mode == 'SpanStep': nfloat = float((obj.SpanEnd - obj.SpanStart) / obj.Step) n = math.trunc(nfloat - ParaConfusion) + 1 if obj.EndInclusive and abs(nfloat-round(nfloat)) <= ParaConfusion: n = n + 1 obj.NumberLinear = n # Generate the actual array. We can use Step and N directly to # completely avoid mode logic, since we had updated them # cache properties into variables step = float(obj.Step) start = float(obj.SpanStart) + step*float(obj.Offset) n = int(obj.NumberLinear) #Apply reversal if obj.Reverse: obj.Dir = obj.Dir*(-1.0) if not(obj.DirIsDriven and obj.Link): obj.Reverse = False # precompute orientation if obj.OrientMode == 'Along axis': ori = latticeGeomUtils.makeOrientationFromLocalAxes(ZAx= obj.Dir).multiply( latticeGeomUtils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0), XAx= App.Vector(0,0,1)) ) else: ori = App.Rotation() dir = obj.Dir dir.normalize() # Make the array output = [] # list of placements if obj.Mode != "Spreadsheet": for i in range(0, n): position = start + step*i output.append( App.Placement(obj.Point + obj.Dir*position, ori) ) else: #parse address addr = obj.CellStart #assuming only two letter column if addr[1].isalpha(): col = addr[0:2] row = addr[2:] else: col = addr[0:1] row = addr[1:] row = int(row) #loop until the value can't be read out while True: try: position = obj.SpreadSheet.get(col+str(row)) except ValueError: break position = float(position) output.append( App.Placement(obj.Point + obj.Dir*position, ori) ) row += 1 return output
def derivedExecute(self, obj): # cache stuff base = obj.Base.Shape if base.ShapeType != 'Compound': base = Part.makeCompound([base]) if obj.FlattenBaseHierarchy: baseChildren = LCE.AllLeaves(base) else: baseChildren = base.childShapes() tool = obj.Tool.Shape if tool.ShapeType != 'Compound': tool = Part.makeCompound([tool]) if obj.FlattenToolHierarchy: toolChildren = LCE.AllLeaves(tool) else: toolChildren = tool.childShapes() iBase = 0 isMult = obj.Operation == 'MultiplyPlacements' # cache mode comparisons to speed them up isAvg = obj.Operation == 'AveragePlacements' isIgnore = obj.Operation == 'IgnoreBasePlacements' isOverride = obj.Operation == 'OverrideBasePlacements' #mode validity logic if not latticeBaseFeature.isObjectLattice(obj.Tool): latticeExecuter.warning( obj, 'Tool is not a lattice object. Results may be unexpected.\n') outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base) if isOverride and outputIsLattice: latticeExecuter.warning( obj, 'Base is a lattice object. OverrideBasePlacements operation requires a generic compound as Base. So, the lattice is being treated as a generic compound.\n' ) outputIsLattice = False # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements bFirst = True plmMatcher = App.Placement( ) #extra placement, that aligns first tool member and first base member # the essence for toolChild in toolChildren: # early test for termination if iBase > len(baseChildren) - 1: if obj.LoopSequence: iBase = 0 else: FreeCAD.Console.PrintWarning( obj.Name + ': There are ' + str(len(toolChildren) - len(baseChildren)) + ' more placements in Tool than children in Base. Those placements' + ' were dropped.\n') break #cache some stuff basePlm = baseChildren[iBase].Placement toolPlm = toolChild.Placement if not outputIsLattice: outputShape = baseChildren[iBase].copy() #prepare alignment placement if bFirst: bFirst = False if obj.KeepBaseFirstItemPos: plmMatcher = toolPlm.inverse() #mode logic if isMult: outPlm = toolPlm.multiply(plmMatcher.multiply(basePlm)) elif isAvg: plm1 = toolPlm plm2 = plmMatcher.multiply(basePlm) transl = plm1.Base * 0.5 + plm2.Base * 0.5 a1, b1, c1, d1 = plm1.Rotation.Q a2, b2, c2, d2 = plm2.Rotation.Q rot = App.Rotation( a1 + a2, b1 + b2, c1 + c2, d1 + d2 ) #no divide-by-two, because FreeCAD will normalize the quaternion automatically outPlm = App.Placement(transl, rot) elif isIgnore: outPlm = toolPlm elif isOverride: assert (not outputIsLattice) outputShape.transformShape( toolPlm.inverse.multiply(plmMatcher.multiply(basePlm))) outPlm = toolPlm if outputIsLattice: outputPlms.append(outPlm) else: outputShape.Placement = outPlm outputShapes.append(outputShape) iBase += 1 if outputIsLattice: return outputPlms else: obj.Shape = Part.makeCompound(outputShapes)
def derivedExecute(self,obj): # cache stuff base = obj.Base.Shape if not latticeBaseFeature.isObjectLattice(obj.Base): latticeExecuter.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n") input = [leaf.Placement for leaf in LCE.AllLeaves(base)] if len(input) < 2: raise ValueError("At least 2 placements ar needed to interpolate; there are just "+str(len(input))+" in base array.") if obj.NumberSamples < 2: raise ValueError("Can output no less than 2 samples; "+str(obj.NumberSamples)+" was requested.") #cache mode comparisons, for speed posIsInterpolate = obj.TranslateMode == 'interpolate' posIsReset = obj.TranslateMode == 'reset' oriIsInterpolate = obj.OrientMode == 'interpolate' oriIsReset = obj.OrientMode == 'reset' # construct interpolation functions # prepare lists of input samples IArray = [float(i) for i in range(0,len(input))] XArray = [plm.Base.x for plm in input] YArray = [plm.Base.y for plm in input] ZArray = [plm.Base.z for plm in input] QArrays = [[],[],[],[]] prevQ = [0.0]*4 for plm in input: Q = plm.Rotation.Q #test if quaernion has changed sign compared to previous one. # Quaternions of opposite sign are equivalent in terms of rotation, # but sign changes confuse interpolation, so we are detecting sign # changes and discarding them if dotProduct(Q,prevQ) < -ParaConfusion: Q = [-v for v in Q] for iQ in [0,1,2,3]: QArrays[iQ].append( Q[iQ] ) prevQ = Q # constuct function objects if posIsInterpolate: FX = LIU.InterpolateF(IArray,XArray) FY = LIU.InterpolateF(IArray,YArray) FZ = LIU.InterpolateF(IArray,ZArray) if oriIsInterpolate: FQs = [] for iQ in [0,1,2,3]: FQs.append(LIU.InterpolateF(IArray,QArrays[iQ])) # initialize output containers and loop variables outputPlms = [] #list of placements for i_output in range(0,math.trunc(obj.NumberSamples+ParaConfusion)): i_input = float(i_output) / (obj.NumberSamples-1) * (len(input)-1) pos = App.Vector() ori = App.Rotation() if posIsInterpolate: pos = App.Vector(FX.value(i_input), FY.value(i_input), FZ.value(i_input)) if oriIsInterpolate: ori = App.Rotation(FQs[0].value(i_input), FQs[1].value(i_input), FQs[2].value(i_input), FQs[3].value(i_input)) plm = App.Placement(pos, ori) outputPlms.append(plm) return outputPlms
def execute(self,obj): nOfStrings = len(obj.Strings) lattice = obj.ArrayLink if lattice is None: plms = [App.Placement() for i in range(0,nOfStrings)] else: if not latticeBaseFeature.isObjectLattice(lattice): latticeExecuter.warning(obj,"ShapeString's link to array must point to a lattice. It points to a generic shape. Results may be unexpected.") leaves = LCE.AllLeaves(lattice.Shape) plms = [leaf.Placement for leaf in leaves] #update foolObj's properties self.makeFoolObj(obj) #make sure we have one - fixes defunct Lattice ShapeString after save-load for (proptype, propname, group, hint) in self.foolObj.properties: if propname != "String": #ignore "String", that will be taken care of in the following loop setattr(self.foolObj, propname, getattr(obj, propname)) self.foolObj.FontFile = findFont(obj.FontFile) obj.FullPathToFont = self.foolObj.FontFile shapes = [] for i in range( 0 , min(len(plms),len(obj.Strings)) ): if len(obj.Strings[i]) > 0: #generate shapestring using Draft self.foolObj.String = obj.Strings[i] self.foolObj.Shape = None self.draft_shape_string.execute(self.foolObj) shape = self.foolObj.Shape #calculate alignment point if obj.XAlign == 'None' and obj.YAlign == 'None': pass #need not calculate boundbox else: if obj.AlignPrecisionBoundBox: bb = getPrecisionBoundBox(shape) else: bb = shape.BoundBox alignPnt = App.Vector() if obj.XAlign == 'Left': alignPnt.x = bb.XMin elif obj.XAlign == 'Right': alignPnt.x = bb.XMax elif obj.XAlign == 'Middle': alignPnt.x = bb.Center.x if obj.YAlign == 'Bottom': alignPnt.y = bb.YMin elif obj.YAlign == 'Top': alignPnt.y = bb.YMax elif obj.YAlign == 'Middle': alignPnt.y = bb.Center.y #Apply alignment shape.Placement = App.Placement(alignPnt*(-1.0), App.Rotation()).multiply(shape.Placement) #Apply placement from array shape.Placement = plms[i].multiply(shape.Placement) shapes.append(shape.copy()) if len(shapes) == 0: scale = 1.0 if lattice is not None: scale = lattice.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps)) if scale < DistConfusion * 100: scale = 1.0 obj.Shape = markers.getNullShapeShape(scale) raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. obj.Shape = Part.makeCompound(shapes)