def rebase(self,shape): import DraftGeomUtils,math if not isinstance(shape,list): shape = [shape] if hasattr(shape[0],"CenterOfMass"): v = shape[0].CenterOfMass else: v = shape[0].BoundBox.Center n = DraftGeomUtils.getNormal(shape[0]) r = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),n) if round(r.Angle,8) == round(math.pi,8): r = FreeCAD.Rotation() shapes = [] for s in shape: s = s.copy() s.translate(v.negative()) s.rotate(FreeCAD.Vector(0,0,0),r.inverted().Axis,math.degrees(r.inverted().Angle)) shapes.append(s) p = FreeCAD.Placement() p.Base = v p.Rotation = r if len(shapes) == 1: return (shapes[0],p) else: return(shapes,p)
def rebase(self, shape): import DraftGeomUtils, math if not isinstance(shape, list): shape = [shape] if hasattr(shape[0], "CenterOfMass"): v = shape[0].CenterOfMass else: v = shape[0].BoundBox.Center n = DraftGeomUtils.getNormal(shape[0]) r = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), n) if round(r.Angle, 8) == round(math.pi, 8): r = FreeCAD.Rotation() shapes = [] for s in shape: s = s.copy() s.translate(v.negative()) s.rotate(FreeCAD.Vector(0, 0, 0), r.inverted().Axis, math.degrees(r.inverted().Angle)) shapes.append(s) p = FreeCAD.Placement() p.Base = v p.Rotation = r if len(shapes) == 1: return (shapes[0], p) else: return (shapes, p)
def rebase(self,shape): """returns a shape that is a copy of the original shape but centered on the (0,0) origin, and a placement that is needed to reposition that shape to its original location/orientation""" import DraftGeomUtils,math if not isinstance(shape,list): shape = [shape] if hasattr(shape[0],"CenterOfMass"): v = shape[0].CenterOfMass else: v = shape[0].BoundBox.Center n = DraftGeomUtils.getNormal(shape[0]) r = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),n) if round(abs(r.Angle),8) == round(math.pi,8): r = FreeCAD.Rotation() shapes = [] for s in shape: s = s.copy() s.translate(v.negative()) s.rotate(FreeCAD.Vector(0,0,0),r.Axis,math.degrees(-r.Angle)) shapes.append(s) p = FreeCAD.Placement() p.Base = v p.Rotation = r if len(shapes) == 1: return (shapes[0],p) else: return(shapes,p)
def updateWire(self,v): pts = self.obj.Points editPnt = v#self.invpl.multVec(v) # DNC: allows to close the curve by placing ends close to each other tol = 0.001 if ( ( self.editing == 0 ) and ( (editPnt - pts[-1]).Length < tol) ) or ( self.editing == len(pts) - 1 ) and ( (editPnt - pts[0]).Length < tol): self.obj.Closed = True # DNC: fix error message if edited point coincides with one of the existing points if ( editPnt in pts ) == True: # checks if point enter is equal to other, this could cause a OCC problem FreeCAD.Console.PrintMessage(translate("draft", "Is not possible to have two coincident points in this object, please try again.")+"\n") if Draft.getType(self.obj) in ["BezCurve"]: self.resetTrackers() else: self.trackers[self.editing].set(self.pl.multVec(self.obj.Points[self.editing])) return if Draft.getType(self.obj) in ["BezCurve"]: pts = self.recomputePointsBezier(pts,self.editing,v,self.obj.Degree,moveTrackers=False) # check that the new point lies on the plane of the wire import DraftGeomUtils, DraftVecUtils if self.obj.Closed: n = DraftGeomUtils.getNormal(self.obj.Shape) dv = editPnt.sub(pts[self.editing]) rn = DraftVecUtils.project(dv,n) if dv.Length: editPnt = editPnt.add(rn.negative()) pts[self.editing] = editPnt self.obj.Points = pts self.trackers[self.editing].set(self.pl.multVec(v))
def Activated(self): doc = FreeCAD.ActiveDocument pg = FreeCAD.ParamGet("User parameter:Plugins/MeshRemodel") line_width = pg.GetFloat("LineWidth", 5.0) point_size = pg.GetFloat("PointSize", 4.0) #QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) #add_varargs_method("makeCircle",&Module::makeCircle, # "makeCircle(radius,[pnt,dir,angle1,angle2]) -- Make a circle with a given radius\n" # "By default pnt=Vector(0,0,0), dir=Vector(0,0,1), angle1=0 and angle2=360" #); modifiers = QtGui.QApplication.keyboardModifiers() poly = Part.makePolygon(self.pts) #Part.show(poly) normal = DraftGeomUtils.getNormal(poly) A = self.pts[0] B = self.pts[1] C = self.pts[2] if gu.isColinear(A, B, C): FreeCAD.Console.PrintError( "MeshRemodel Error: Cannot make arc/circle from 3 colinear points\n" ) return center = gu.circumcenter(A, B, C) radius = gu.circumradius(A, B, C) doc.openTransaction("Create circle") circle = Part.makeCircle(radius, center, normal) circName = "MR_Ref" if not modifiers == QtCore.Qt.ShiftModifier.__or__( QtCore.Qt.ControlModifier): Part.show(circle, "MR_Circle") circName = doc.ActiveObject.Name doc.ActiveObject.ViewObject.LineWidth = line_width FreeCAD.Console.PrintMessage(circName + ": radius = " + str(radius) + "\n center at " + str(center) + "\n") Gui.Selection.clearSelection() Gui.Selection.addSelection(doc.getObject(circName)) if modifiers == QtCore.Qt.ControlModifier or modifiers == QtCore.Qt.ControlModifier.__or__( QtCore.Qt.ShiftModifier): Part.show( Part.Point(center).toShape(), circName + "_Ctr" ) #show the center point on ctrl click or shift+ctrl click doc.ActiveObject.ViewObject.PointSize = point_size doc.recompute() doc.commitTransaction() #QtGui.QApplication.restoreOverrideCursor() return
def rebase(self,shape): import DraftGeomUtils,math if hasattr(shape,"CenterOfMass"): v = shape.CenterOfMass else: v = shape.BoundBox.Center n = DraftGeomUtils.getNormal(shape) r = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),n) if round(r.Angle,8) == round(math.pi,8): r = FreeCAD.Rotation() shape = shape.copy() shape.translate(v.negative()) shape.rotate(FreeCAD.Vector(0,0,0),r.inverted().Axis,math.degrees(r.inverted().Angle)) p = FreeCAD.Placement() p.Base = v p.Rotation = r return (shape,p)
def execute(self, obj): import Part, DraftGeomUtils, math pl = obj.Placement w = self.getWire(obj) if not w: FreeCAD.Console.PrintError( translate("Arch", "Unable to build the base path") + "\n") return if obj.OffsetStart.Value: e = w.Edges[0] v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(obj.OffsetStart.Value) e = Part.LineSegment(e.Vertexes[0].Point.add(v), e.Vertexes[-1].Point).toShape() w = Part.Wire([e] + w.Edges[1:]) if obj.OffsetEnd.Value: e = w.Edges[-1] v = e.Vertexes[0].Point.sub(e.Vertexes[-1].Point).normalize() v.multiply(obj.OffsetEnd.Value) e = Part.LineSegment(e.Vertexes[-1].Point.add(v), e.Vertexes[0].Point).toShape() w = Part.Wire(w.Edges[:-1] + [e]) p = self.getProfile(obj) if not p: FreeCAD.Console.PrintError( translate("Arch", "Unable to build the profile") + "\n") return # move and rotate the profile to the first point delta = w.Vertexes[0].Point - p.CenterOfMass p.translate(delta) v1 = w.Vertexes[1].Point - w.Vertexes[0].Point v2 = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(v2, v1) p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) try: sh = w.makePipeShell([p], True, False, 2) except: FreeCAD.Console.PrintError( translate("Arch", "Unable to build the pipe") + "\n") else: obj.Shape = sh if obj.Base: obj.Length = w.Length else: obj.Placement = pl
def Activated(self): doc = FreeCAD.ActiveDocument pg = FreeCAD.ParamGet("User parameter:Plugins/MeshRemodel") line_width = pg.GetFloat("LineWidth", 5.0) point_size = pg.GetFloat("PointSize", 4.0) modifiers = QtGui.QApplication.keyboardModifiers() poly = Part.makePolygon(self.pts) # Part.show(poly) normal = DraftGeomUtils.getNormal(poly) A = self.pts[0] B = self.pts[1] C = self.pts[2] if gu.isColinear(A, B, C): FreeCAD.Console.PrintError( "MeshRemodel Error: Cannot make arc/circle from 3 colinear points\n" ) return center = gu.circumcenter(A, B, C) radius = gu.circumradius(A, B, C) doc.openTransaction("Create Arc") arc = Part.ArcOfCircle(A, B, C) #on ctrl+shift click we only show center arcName = "MR_Ref" if not modifiers == QtCore.Qt.ControlModifier.__or__( QtCore.Qt.ShiftModifier): Part.show(arc.toShape(), "MR_Arc") arcName = doc.ActiveObject.Name doc.ActiveObject.ViewObject.LineWidth = line_width FreeCAD.Console.PrintMessage(arcName + ": radius = " + str(radius) + "\n center at " + str(center) + "\n") Gui.Selection.clearSelection() Gui.Selection.addSelection(doc.getObject(arcName)) if modifiers == QtCore.Qt.ControlModifier or modifiers == QtCore.Qt.ControlModifier.__or__( QtCore.Qt.ShiftModifier): Part.show(Part.Point(center).toShape(), arcName + "_Ctr") #show the center point doc.ActiveObject.ViewObject.PointSize = point_size doc.recompute() doc.commitTransaction() #QtGui.QApplication.restoreOverrideCursor() return
def getRebarsSpanAxis(rebar): """getRebarsSpanAxis(Rebar): Returns span axis of rebars. """ if (Draft.getType(rebar.Base) == "Wire" or rebar.Base.Shape.ShapeType == "Wire") and len( rebar.Base.Shape.Wires[0].Edges ) != 1: # Draft Wires can have "wrong" placement # This works fine instead for straight rebars i.e. for rebars having # base wire with only one edge axis = DraftGeomUtils.getNormal(rebar.Base.Shape) else: axis = rebar.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) if hasattr(rebar, "Direction"): if not DraftVecUtils.isNull(rebar.Direction): axis = FreeCAD.Vector(rebar.Direction) axis.normalize() return axis
def orientWP(self): """Orient the working plane.""" import DraftGeomUtils if hasattr(App, "DraftWorkingPlane"): if len(self.node) > 1 and self.obj: n = DraftGeomUtils.getNormal(self.obj.Shape) if not n: n = App.DraftWorkingPlane.axis p = self.node[-1] v = self.node[-2].sub(self.node[-1]) v = v.negative() if not self.oldWP: self.oldWP = App.DraftWorkingPlane.getParameters() App.DraftWorkingPlane.alignToPointAndAxis(p, n, upvec=v) if hasattr(Gui, "Snapper"): Gui.Snapper.setGrid() Gui.Snapper.restack() if self.planetrack: self.planetrack.set(self.node[-1])
def execute(self,obj): import Part,DraftGeomUtils,math pl = obj.Placement w = self.getWire(obj) if not w: FreeCAD.Console.PrintError(translate("Arch","Unable to build the base path")+"\n") return if obj.OffsetStart.Value: e = w.Edges[0] v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(obj.OffsetStart.Value) e = Part.LineSegment(e.Vertexes[0].Point.add(v),e.Vertexes[-1].Point).toShape() w = Part.Wire([e]+w.Edges[1:]) if obj.OffsetEnd.Value: e = w.Edges[-1] v = e.Vertexes[0].Point.sub(e.Vertexes[-1].Point).normalize() v.multiply(obj.OffsetEnd.Value) e = Part.LineSegment(e.Vertexes[-1].Point.add(v),e.Vertexes[0].Point).toShape() w = Part.Wire(w.Edges[:-1]+[e]) p = self.getProfile(obj) if not p: FreeCAD.Console.PrintError(translate("Arch","Unable to build the profile")+"\n") return # move and rotate the profile to the first point delta = w.Vertexes[0].Point-p.CenterOfMass p.translate(delta) v1 = w.Vertexes[1].Point-w.Vertexes[0].Point v2 = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(v2,v1) p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) try: sh = w.makePipeShell([p],True,False,2) except: FreeCAD.Console.PrintError(translate("Arch","Unable to build the pipe")+"\n") else: obj.Shape = sh if obj.Base: obj.Length = w.Length else: obj.Placement = pl
def execute(self,obj): tol = 1 # tolerance for alignment. This is only visual, we can keep it low... ptol = 0.001 # tolerance for coincident points import math,Part,DraftGeomUtils,ArchCommands if len(obj.Pipes) < 2: return if len(obj.Pipes) > 3: FreeCAD.Console.PrintWarning(translate("Arch","Only the 3 first wires will be connected")+"\n") if obj.Radius.Value == 0: return wires = [] order = [] for o in obj.Pipes: wires.append(o.Proxy.getWire(o)) if wires[0].Vertexes[0].Point.sub(wires[1].Vertexes[0].Point).Length <= ptol: order = ["start","start"] point = wires[0].Vertexes[0].Point elif wires[0].Vertexes[0].Point.sub(wires[1].Vertexes[-1].Point).Length <= ptol: order = ["start","end"] point = wires[0].Vertexes[0].Point elif wires[0].Vertexes[-1].Point.sub(wires[1].Vertexes[-1].Point).Length <= ptol: order = ["end","end"] point = wires[0].Vertexes[-1].Point elif wires[0].Vertexes[-1].Point.sub(wires[1].Vertexes[0].Point).Length <= ptol: order = ["end","start"] point = wires[0].Vertexes[-1].Point else: FreeCAD.Console.PrintError(translate("Arch","Common vertex not found")+"\n") return if order[0] == "start": v1 = wires[0].Vertexes[1].Point.sub(wires[0].Vertexes[0].Point).normalize() else: v1 = wires[0].Vertexes[-2].Point.sub(wires[0].Vertexes[-1].Point).normalize() if order[1] == "start": v2 = wires[1].Vertexes[1].Point.sub(wires[1].Vertexes[0].Point).normalize() else: v2 = wires[1].Vertexes[-2].Point.sub(wires[1].Vertexes[-1].Point).normalize() p = obj.Pipes[0].Proxy.getProfile(obj.Pipes[0]) p = Part.Face(p) if len(obj.Pipes) == 2: if obj.ConnectorType != "Corner": obj.ConnectorType = "Corner" if round(v1.getAngle(v2),tol) in [0,round(math.pi,tol)]: FreeCAD.Console.PrintError(translate("Arch","Pipes are already aligned")+"\n") return normal = v2.cross(v1) offset = math.tan(math.pi/2-v1.getAngle(v2)/2)*obj.Radius.Value v1.multiply(offset) v2.multiply(offset) self.setOffset(obj.Pipes[0],order[0],offset) self.setOffset(obj.Pipes[1],order[1],offset) # find center perp = v1.cross(normal).normalize() perp.multiply(obj.Radius.Value) center = point.add(v1).add(perp) # move and rotate the profile to the first point delta = point.add(v1)-p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(vp,v1) p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) sh = p.revolve(center,normal,math.degrees(math.pi-v1.getAngle(v2))) #sh = Part.makeCompound([sh]+[Part.Vertex(point),Part.Vertex(point.add(v1)),Part.Vertex(center),Part.Vertex(point.add(v2))]) else: if obj.ConnectorType != "Tee": obj.ConnectorType = "Tee" if wires[2].Vertexes[0].Point == point: order.append("start") elif wires[0].Vertexes[-1].Point == point: order.append("end") else: FreeCAD.Console.PrintError(translate("Arch","Common vertex not found")+"\n") if order[2] == "start": v3 = wires[2].Vertexes[1].Point.sub(wires[2].Vertexes[0].Point).normalize() else: v3 = wires[2].Vertexes[-2].Point.sub(wires[2].Vertexes[-1].Point).normalize() if round(v1.getAngle(v2),tol) in [0,round(math.pi,tol)]: pair = [v1,v2,v3] elif round(v1.getAngle(v3),tol) in [0,round(math.pi,tol)]: pair = [v1,v3,v2] elif round(v2.getAngle(v3),tol) in [0,round(math.pi,tol)]: pair = [v2,v3,v1] else: FreeCAD.Console.PrintError(translate("Arch","At least 2 pipes must aligned")+"\n") return offset = obj.Radius.Value v1.multiply(offset) v2.multiply(offset) v3.multiply(offset) self.setOffset(obj.Pipes[0],order[0],offset) self.setOffset(obj.Pipes[1],order[1],offset) self.setOffset(obj.Pipes[2],order[2],offset) normal = pair[0].cross(pair[2]) # move and rotate the profile to the first point delta = point.add(pair[0])-p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(vp,pair[0]) p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) t1 = p.extrude(pair[1].multiply(2)) # move and rotate the profile to the second point delta = point.add(pair[2])-p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(vp,pair[2]) p.rotate(p.CenterOfMass,rot.Axis,math.degrees(rot.Angle)) t2 = p.extrude(pair[2].negative().multiply(2)) # create a cut plane cp = Part.makePolygon([point,point.add(pair[0]),point.add(normal),point]) cp = Part.Face(cp) if cp.normalAt(0,0).getAngle(pair[2]) < math.pi/2: cp.reverse() cf, cv, invcv = ArchCommands.getCutVolume(cp,t2) t2 = t2.cut(cv) sh = t1.fuse(t2) obj.Shape = sh
def execute(self,obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj,"ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: edges = obj.Base.Shape.Edges if hasattr(obj,"Edges"): if obj.Edges == "Vertical edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Top Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z,reverse=True) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] elif obj.Edges == "Bottom Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] for e in edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj,"BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append(DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage(translate("Arch","Crossing point not found in profile.")+"\n") basepoint = basepointliste[0] else : basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: if hasattr(obj,"Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) s = s.removeSplitter() obj.Shape = s obj.Placement = pl return obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def make_sketch(objectslist, autoconstraints=False, addTo=None, delete=False, name="Sketch", radiusPrecision=-1): """makeSketch(objectslist,[autoconstraints],[addTo],[delete],[name],[radiusPrecision]) Makes a Sketch objectslist with the given Draft objects. Parameters ---------- objectlist: can be single or list of objects of Draft type objects, Part::Feature, Part.Shape, or mix of them. autoconstraints(False): if True, constraints will be automatically added to wire nodes, rectangles and circles. addTo(None) : if set to an existing sketch, geometry will be added to it instead of creating a new one. delete(False): if True, the original object will be deleted. If set to a string 'all' the object and all its linked object will be deleted name('Sketch'): the name for the new sketch object radiusPrecision(-1): If <0, disable radius constraint. If =0, add indiviaul radius constraint. If >0, the radius will be rounded according to this precision, and 'Equal' constraint will be added to curve with equal radius within precision. """ if not App.ActiveDocument: App.Console.PrintError("No active document. Aborting\n") return import Part from Sketcher import Constraint import Sketcher StartPoint = 1 EndPoint = 2 MiddlePoint = 3 deletable = None if not isinstance(objectslist, (list, tuple)): objectslist = [objectslist] for obj in objectslist: if isinstance(obj, Part.Shape): shape = obj elif not hasattr(obj, 'Shape'): App.Console.PrintError(translate("draft", "not shape found")) return None else: shape = obj.Shape if not DraftGeomUtils.isPlanar(shape): App.Console.PrintError( translate("draft", "All Shapes must be co-planar")) return None if addTo: nobj = addTo else: nobj = App.ActiveDocument.addObject("Sketcher::SketchObject", name) deletable = nobj if App.GuiUp: nobj.ViewObject.Autoconstraints = False # Collect constraints and add in one go to improve performance constraints = [] radiuses = {} def addRadiusConstraint(edge): try: if radiusPrecision < 0: return if radiusPrecision == 0: constraints.append( Constraint('Radius', nobj.GeometryCount - 1, edge.Curve.Radius)) return r = round(edge.Curve.Radius, radiusPrecision) constraints.append( Constraint('Equal', radiuses[r], nobj.GeometryCount - 1)) except KeyError: radiuses[r] = nobj.GeometryCount - 1 constraints.append(Constraint('Radius', nobj.GeometryCount - 1, r)) except AttributeError: pass def convertBezier(edge): if DraftGeomUtils.geomType(edge) == "BezierCurve": return (edge.Curve.toBSpline(edge.FirstParameter, edge.LastParameter).toShape()) else: return (edge) rotation = None for obj in objectslist: ok = False tp = utils.get_type(obj) if tp in ["Circle", "Ellipse"]: if obj.Shape.Edges: if rotation is None: rotation = obj.Placement.Rotation edge = obj.Shape.Edges[0] if len(edge.Vertexes) == 1: newEdge = DraftGeomUtils.orientEdge(edge) nobj.addGeometry(newEdge) else: # make new ArcOfCircle circle = DraftGeomUtils.orientEdge(edge) angle = edge.Placement.Rotation.Angle axis = edge.Placement.Rotation.Axis circle.Center = DraftVecUtils.rotate( edge.Curve.Center, -angle, axis) first = math.radians(obj.FirstAngle) last = math.radians(obj.LastAngle) arc = Part.ArcOfCircle(circle, first, last) nobj.addGeometry(arc) addRadiusConstraint(edge) ok = True elif tp == "Rectangle": if rotation is None: rotation = obj.Placement.Rotation if obj.FilletRadius.Value == 0: for edge in obj.Shape.Edges: nobj.addGeometry(DraftGeomUtils.orientEdge(edge)) if autoconstraints: last = nobj.GeometryCount - 1 segs = [last - 3, last - 2, last - 1, last] if obj.Placement.Rotation.Q == (0, 0, 0, 1): constraints.append( Constraint("Coincident", last - 3, EndPoint, last - 2, StartPoint)) constraints.append( Constraint("Coincident", last - 2, EndPoint, last - 1, StartPoint)) constraints.append( Constraint("Coincident", last - 1, EndPoint, last, StartPoint)) constraints.append( Constraint("Coincident", last, EndPoint, last - 3, StartPoint)) constraints.append(Constraint("Horizontal", last - 3)) constraints.append(Constraint("Vertical", last - 2)) constraints.append(Constraint("Horizontal", last - 1)) constraints.append(Constraint("Vertical", last)) ok = True elif tp in ["Wire", "Polygon"]: if obj.FilletRadius.Value == 0: closed = False if tp == "Polygon": closed = True elif hasattr(obj, "Closed"): closed = obj.Closed if obj.Shape.Edges: if (len(obj.Shape.Vertexes) < 3): e = obj.Shape.Edges[0] nobj.addGeometry( Part.LineSegment(e.Curve, e.FirstParameter, e.LastParameter)) else: # Use the first three points to make a working plane. We've already # checked to make sure everything is coplanar plane = Part.Plane( *[i.Point for i in obj.Shape.Vertexes[:3]]) normal = plane.Axis if rotation is None: axis = App.Vector(0, 0, 1).cross(normal) angle = DraftVecUtils.angle( normal, App.Vector(0, 0, 1)) * App.Units.Radian rotation = App.Rotation(axis, angle) for edge in obj.Shape.Edges: # edge.rotate(App.Vector(0,0,0), rotAxis, rotAngle) edge = DraftGeomUtils.orientEdge(edge, normal) nobj.addGeometry(edge) if autoconstraints: last = nobj.GeometryCount segs = list( range(last - len(obj.Shape.Edges), last - 1)) for seg in segs: constraints.append( Constraint("Coincident", seg, EndPoint, seg + 1, StartPoint)) if DraftGeomUtils.isAligned( nobj.Geometry[seg], "x"): constraints.append( Constraint("Vertical", seg)) elif DraftGeomUtils.isAligned( nobj.Geometry[seg], "y"): constraints.append( Constraint("Horizontal", seg)) if closed: constraints.append( Constraint("Coincident", last - 1, EndPoint, segs[0], StartPoint)) ok = True elif tp == "BSpline": if obj.Shape.Edges: nobj.addGeometry(obj.Shape.Edges[0].Curve) nobj.exposeInternalGeometry(nobj.GeometryCount - 1) ok = True elif tp == "BezCurve": if obj.Shape.Edges: bez = obj.Shape.Edges[0].Curve bsp = bez.toBSpline(bez.FirstParameter, bez.LastParameter) nobj.addGeometry(bsp) nobj.exposeInternalGeometry(nobj.GeometryCount - 1) ok = True elif tp == 'Shape' or hasattr(obj, 'Shape'): shape = obj if tp == 'Shape' else obj.Shape if not DraftGeomUtils.isPlanar(shape): App.Console.PrintError( translate( "draft", "The given object is not planar and cannot be converted into a sketch." )) return None if rotation is None: #rotation = obj.Placement.Rotation norm = DraftGeomUtils.getNormal(shape) if norm: rotation = App.Rotation(App.Vector(0, 0, 1), norm) else: App.Console.PrintWarning( translate( "draft", "Unable to guess the normal direction of this object" )) rotation = App.Rotation() norm = obj.Placement.Rotation.Axis if not shape.Wires: for e in shape.Edges: # unconnected edges newedge = convertBezier(e) nobj.addGeometry( DraftGeomUtils.orientEdge(newedge, norm, make_arc=True)) addRadiusConstraint(newedge) # if not addTo: # nobj.Placement.Rotation = DraftGeomUtils.calculatePlacement(shape).Rotation if autoconstraints: for wire in shape.Wires: last_count = nobj.GeometryCount edges = wire.OrderedEdges for edge in edges: newedge = convertBezier(edge) nobj.addGeometry( DraftGeomUtils.orientEdge(newedge, norm, make_arc=True)) addRadiusConstraint(newedge) for i, g in enumerate(nobj.Geometry[last_count:]): if edges[i].Closed: continue seg = last_count + i if DraftGeomUtils.isAligned(g, "x"): constraints.append(Constraint("Vertical", seg)) elif DraftGeomUtils.isAligned(g, "y"): constraints.append(Constraint("Horizontal", seg)) if seg == nobj.GeometryCount - 1: if not wire.isClosed(): break g2 = nobj.Geometry[last_count] seg2 = last_count else: seg2 = seg + 1 g2 = nobj.Geometry[seg2] end1 = g.value(g.LastParameter) start2 = g2.value(g2.FirstParameter) if DraftVecUtils.equals(end1, start2): constraints.append( Constraint("Coincident", seg, EndPoint, seg2, StartPoint)) continue end2 = g2.value(g2.LastParameter) start1 = g.value(g.FirstParameter) if DraftVecUtils.equals(end2, start1): constraints.append( Constraint("Coincident", seg, StartPoint, seg2, EndPoint)) elif DraftVecUtils.equals(start1, start2): constraints.append( Constraint("Coincident", seg, StartPoint, seg2, StartPoint)) elif DraftVecUtils.equals(end1, end2): constraints.append( Constraint("Coincident", seg, EndPoint, seg2, EndPoint)) else: for wire in shape.Wires: for edge in wire.OrderedEdges: newedge = convertBezier(edge) nobj.addGeometry( DraftGeomUtils.orientEdge(newedge, norm, make_arc=True)) ok = True gui_utils.format_object(nobj, obj) if ok and delete and hasattr(obj, 'Shape'): doc = obj.Document def delObj(obj): if obj.InList: App.Console.PrintWarning( translate( "draft", "Cannot delete object {} with dependency". format(obj.Label)) + "\n") else: doc.removeObject(obj.Name) try: if delete == 'all': objs = [obj] while objs: obj = objs[0] objs = objs[1:] + obj.OutList delObj(obj) else: delObj(obj) except Exception as ex: App.Console.PrintWarning( translate( "draft", "Failed to delete object {}: {}".format( obj.Label, ex)) + "\n") if rotation: nobj.Placement.Rotation = rotation else: print("-----error!!! rotation is still None...") nobj.addConstraint(constraints) return nobj
def execute(self, obj): import Part, DraftGeomUtils, math pl = obj.Placement w = self.getWire(obj) if not w: FreeCAD.Console.PrintError( translate("Arch", "Unable to build the base path") + "\n") return if obj.OffsetStart.Value: e = w.Edges[0] v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(obj.OffsetStart.Value) e = Part.LineSegment(e.Vertexes[0].Point.add(v), e.Vertexes[-1].Point).toShape() w = Part.Wire([e] + w.Edges[1:]) if obj.OffsetEnd.Value: e = w.Edges[-1] v = e.Vertexes[0].Point.sub(e.Vertexes[-1].Point).normalize() v.multiply(obj.OffsetEnd.Value) e = Part.LineSegment(e.Vertexes[-1].Point.add(v), e.Vertexes[0].Point).toShape() w = Part.Wire(w.Edges[:-1] + [e]) p = self.getProfile(obj) if not p: FreeCAD.Console.PrintError( translate("Arch", "Unable to build the profile") + "\n") return # move and rotate the profile to the first point if hasattr(p, "CenterOfMass"): c = p.CenterOfMass else: c = p.BoundBox.Center delta = w.Vertexes[0].Point - c p.translate(delta) import Draft if Draft.getType(obj.Base) == "BezCurve": v1 = obj.Base.Placement.multVec( obj.Base.Points[1]) - w.Vertexes[0].Point else: v1 = w.Vertexes[1].Point - w.Vertexes[0].Point v2 = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(v2, v1) p.rotate(w.Vertexes[0].Point, rot.Axis, math.degrees(rot.Angle)) shapes = [] try: if p.Faces: for f in p.Faces: sh = w.makePipeShell([f.OuterWire], True, False, 2) for shw in f.Wires: if shw.hashCode() != f.OuterWire.hashCode(): sh2 = w.makePipeShell([shw], True, False, 2) sh = sh.cut(sh2) shapes.append(sh) elif p.Wires: for pw in p.Wires: sh = w.makePipeShell([pw], True, False, 2) shapes.append(sh) except Exception: FreeCAD.Console.PrintError( translate("Arch", "Unable to build the pipe") + "\n") else: if len(shapes) == 0: return elif len(shapes) == 1: sh = shapes[0] else: sh = Part.makeCompound(shapes) obj.Shape = sh if obj.Base: obj.Length = w.Length else: obj.Placement = pl
def execute(self, obj): tol = 1 # tolerance for alignment. This is only visual, we can keep it low... ptol = 0.001 # tolerance for coincident points import math, Part, DraftGeomUtils, ArchCommands if len(obj.Pipes) < 2: return if len(obj.Pipes) > 3: FreeCAD.Console.PrintWarning( translate("Arch", "Only the 3 first wires will be connected") + "\n") if obj.Radius.Value == 0: return wires = [] order = [] for o in obj.Pipes: wires.append(o.Proxy.getWire(o)) if wires[0].Vertexes[0].Point.sub( wires[1].Vertexes[0].Point).Length <= ptol: order = ["start", "start"] point = wires[0].Vertexes[0].Point elif wires[0].Vertexes[0].Point.sub( wires[1].Vertexes[-1].Point).Length <= ptol: order = ["start", "end"] point = wires[0].Vertexes[0].Point elif wires[0].Vertexes[-1].Point.sub( wires[1].Vertexes[-1].Point).Length <= ptol: order = ["end", "end"] point = wires[0].Vertexes[-1].Point elif wires[0].Vertexes[-1].Point.sub( wires[1].Vertexes[0].Point).Length <= ptol: order = ["end", "start"] point = wires[0].Vertexes[-1].Point else: FreeCAD.Console.PrintError( translate("Arch", "Common vertex not found") + "\n") return if order[0] == "start": v1 = wires[0].Vertexes[1].Point.sub( wires[0].Vertexes[0].Point).normalize() else: v1 = wires[0].Vertexes[-2].Point.sub( wires[0].Vertexes[-1].Point).normalize() if order[1] == "start": v2 = wires[1].Vertexes[1].Point.sub( wires[1].Vertexes[0].Point).normalize() else: v2 = wires[1].Vertexes[-2].Point.sub( wires[1].Vertexes[-1].Point).normalize() p = obj.Pipes[0].Proxy.getProfile(obj.Pipes[0]) p = Part.Face(p) if len(obj.Pipes) == 2: if obj.ConnectorType != "Corner": obj.ConnectorType = "Corner" if round(v1.getAngle(v2), tol) in [0, round(math.pi, tol)]: FreeCAD.Console.PrintError( translate("Arch", "Pipes are already aligned") + "\n") return normal = v2.cross(v1) offset = math.tan(math.pi / 2 - v1.getAngle(v2) / 2) * obj.Radius.Value v1.multiply(offset) v2.multiply(offset) self.setOffset(obj.Pipes[0], order[0], offset) self.setOffset(obj.Pipes[1], order[1], offset) # find center perp = v1.cross(normal).normalize() perp.multiply(obj.Radius.Value) center = point.add(v1).add(perp) # move and rotate the profile to the first point delta = point.add(v1) - p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(vp, v1) p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) sh = p.revolve(center, normal, math.degrees(math.pi - v1.getAngle(v2))) #sh = Part.makeCompound([sh]+[Part.Vertex(point),Part.Vertex(point.add(v1)),Part.Vertex(center),Part.Vertex(point.add(v2))]) else: if obj.ConnectorType != "Tee": obj.ConnectorType = "Tee" if wires[2].Vertexes[0].Point == point: order.append("start") elif wires[0].Vertexes[-1].Point == point: order.append("end") else: FreeCAD.Console.PrintError( translate("Arch", "Common vertex not found") + "\n") if order[2] == "start": v3 = wires[2].Vertexes[1].Point.sub( wires[2].Vertexes[0].Point).normalize() else: v3 = wires[2].Vertexes[-2].Point.sub( wires[2].Vertexes[-1].Point).normalize() if round(v1.getAngle(v2), tol) in [0, round(math.pi, tol)]: pair = [v1, v2, v3] elif round(v1.getAngle(v3), tol) in [0, round(math.pi, tol)]: pair = [v1, v3, v2] elif round(v2.getAngle(v3), tol) in [0, round(math.pi, tol)]: pair = [v2, v3, v1] else: FreeCAD.Console.PrintError( translate("Arch", "At least 2 pipes must align") + "\n") return offset = obj.Radius.Value v1.multiply(offset) v2.multiply(offset) v3.multiply(offset) self.setOffset(obj.Pipes[0], order[0], offset) self.setOffset(obj.Pipes[1], order[1], offset) self.setOffset(obj.Pipes[2], order[2], offset) normal = pair[0].cross(pair[2]) # move and rotate the profile to the first point delta = point.add(pair[0]) - p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(vp, pair[0]) p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) t1 = p.extrude(pair[1].multiply(2)) # move and rotate the profile to the second point delta = point.add(pair[2]) - p.CenterOfMass p.translate(delta) vp = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(vp, pair[2]) p.rotate(p.CenterOfMass, rot.Axis, math.degrees(rot.Angle)) t2 = p.extrude(pair[2].negative().multiply(2)) # create a cut plane cp = Part.makePolygon( [point, point.add(pair[0]), point.add(normal), point]) cp = Part.Face(cp) if cp.normalAt(0, 0).getAngle(pair[2]) < math.pi / 2: cp.reverse() cf, cv, invcv = ArchCommands.getCutVolume(cp, t2) t2 = t2.cut(cv) sh = t1.fuse(t2) obj.Shape = sh
def execute(self, obj): pl = obj.Placement if obj.Source: base = None if Draft.getType(obj.Source) == "Panel": import Part, DraftGeomUtils baseobj = None if obj.Source.CloneOf: baseobj = obj.Source.CloneOf.Base if obj.Source.Base: baseobj = obj.Source.Base if baseobj: if baseobj.isDerivedFrom("Part::Feature"): if baseobj.Shape.Solids: return else: base = Part.makeCompound(baseobj.Shape.Wires) n = None for w in base.Wires: n = DraftGeomUtils.getNormal(w) if n: break if not n: n = Vector(0, 0, 1) base.translate(base.Vertexes[0].Point.negative()) r = FreeCAD.Rotation(n, Vector(0, 0, 1)) base.rotate(Vector(0, 0, 0), r.Axis, math.degrees(r.Angle)) elif baseobj.isDerivedFrom("Mesh::Feature"): return else: l2 = obj.Source.Length / 2 w2 = obj.Source.Width / 2 v1 = Vector(-l2, -w2, 0) v2 = Vector(l2, -w2, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base = Part.makePolygon([v1, v2, v3, v4, v1]) if base: self.outline = base if obj.FontFile and obj.TagText and obj.TagSize.Value: if obj.TagPosition.Length == 0: pos = base.BoundBox.Center else: pos = obj.TagPosition if obj.TagText == "%tag%": string = obj.Source.Tag elif obj.TagText == "%label%": string = obj.Source.Label elif obj.TagText == "%description%": string = obj.Source.Description else: string = obj.TagText chars = [] for char in Part.makeWireString( string, obj.FontFile, obj.TagSize.Value, 0): chars.extend(char) textshape = Part.Compound(chars) textshape.translate(pos.sub(textshape.BoundBox.Center)) textshape.rotate(textshape.BoundBox.Center, Vector(0, 0, 1), obj.TagRotation.Value) self.tag = textshape base = Part.Compound([base, textshape]) else: base = Part.Compound([base]) obj.Shape = base obj.Placement = pl
def execute(self, obj): if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) angle = bvec.getAngle(axis) if round(angle, Draft.precision()) != 0: if round(angle, Draft.precision()) != round( math.pi, Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate( DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self, obj): pl = obj.Placement if obj.Source: base = None if Draft.getType(obj.Source) == "Panel": import Part,DraftGeomUtils baseobj = None if obj.Source.CloneOf: baseobj = obj.Source.CloneOf.Base if obj.Source.Base: baseobj = obj.Source.Base if baseobj: if baseobj.isDerivedFrom("Part::Feature"): if baseobj.Shape.Solids: return else: base = Part.makeCompound(baseobj.Shape.Wires) n = None for w in base.Wires: n = DraftGeomUtils.getNormal(w) if n: break if not n: n = Vector(0,0,1) base.translate(base.Vertexes[0].Point.negative()) r = FreeCAD.Rotation(n,Vector(0,0,1)) base.rotate(Vector(0,0,0),r.Axis,math.degrees(r.Angle)) elif baseobj.isDerivedFrom("Mesh::Feature"): return else: l2 = obj.Source.Length/2 w2 = obj.Source.Width/2 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) if base: self.outline = base if obj.FontFile and obj.TagText and obj.TagSize.Value: if obj.TagPosition.Length == 0: pos = base.BoundBox.Center else: pos = obj.TagPosition if obj.TagText == "%tag%": string = obj.Source.Tag elif obj.TagText == "%label%": string = obj.Source.Label elif obj.TagText == "%description%": string = obj.Source.Description else: string = obj.TagText chars = [] for char in Part.makeWireString(string,obj.FontFile,obj.TagSize.Value,0): chars.extend(char) textshape = Part.Compound(chars) textshape.translate(pos.sub(textshape.BoundBox.Center)) textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value) self.tag = textshape base = Part.Compound([base,textshape]) else: base = Part.Compound([base]) obj.Shape = base obj.Placement = pl
def execute(self,obj): "creates the structure shape" import Part, DraftGeomUtils if self.clone(obj): return normal,length,width,height = self.getDefaultValues(obj) # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return if not obj.Base.Shape.isValid(): if not obj.Base.Shape.Solids: # let pass invalid objects if they have solids... return if hasattr(obj,"Tool"): if obj.Tool: try: base = obj.Tool.Shape.copy().makePipe(obj.Base.Shape.copy()) except Part.OCCError: FreeCAD.Console.PrintError(translate("Arch","Error: The base shape couldn't be extruded along this tool object")) return if not base: if not height: return if obj.Normal == Vector(0,0,0): if len(obj.Base.Shape.Faces) > 0 : normal=obj.Base.Shape.Faces[0].normalAt(.5,.5) else: normal = DraftGeomUtils.getNormal(obj.Base.Shape) if not normal: normal = FreeCAD.Vector(0,0,1) #p = FreeCAD.Placement(obj.Base.Placement) #normal = p.Rotation.multVec(normal) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): try: base = Part.Face(base.Wires[0]) base = base.extrude(normal) except Part.OCCError: FreeCAD.Console.PrintError(obj.Label+" : "+str(translate("Arch","Unable to extrude the base shape\n"))) return elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids and (not sh.isNull()): base = sh else: FreeCAD.Console.PrintWarning(str(translate("Arch","This mesh is an invalid solid"))) obj.Base.ViewObject.show() else: base = self.getProfiles(obj) if base: if length > height: normal = normal.multiply(length) else: normal = normal.multiply(height) base = Part.Face(base[0]) base = base.extrude(normal) base = self.processSubShapes(obj,base,pl) self.applyShape(obj,base,pl)
def execute(self, obj): "creates the structure shape" import Part, DraftGeomUtils if self.clone(obj): return normal, length, width, height = self.getDefaultValues(obj) # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return if not obj.Base.Shape.isValid(): if not obj.Base.Shape.Solids: # let pass invalid objects if they have solids... return if hasattr(obj, "Tool"): if obj.Tool: try: base = obj.Tool.Shape.copy().makePipe( obj.Base.Shape.copy()) except Part.OCCError: FreeCAD.Console.PrintError( translate( "Arch", "Error: The base shape couldn't be extruded along this tool object" )) return if not base: if not height: return if obj.Normal == Vector(0, 0, 0): if len(obj.Base.Shape.Faces) > 0: normal = obj.Base.Shape.Faces[0].normalAt(.5, .5) else: normal = DraftGeomUtils.getNormal(obj.Base.Shape) if not normal: normal = FreeCAD.Vector(0, 0, 1) #p = FreeCAD.Placement(obj.Base.Placement) #normal = p.Rotation.multVec(normal) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): try: base = Part.Face(base.Wires[0]) base = base.extrude(normal) except Part.OCCError: FreeCAD.Console.PrintError( obj.Label + " : " + str( translate( "Arch", "Unable to extrude the base shape\n" ))) return elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids and ( not sh.isNull()): base = sh else: FreeCAD.Console.PrintWarning( str( translate( "Arch", "This mesh is an invalid solid"))) obj.Base.ViewObject.show() else: base = self.getProfiles(obj) if base: if length > height: normal = normal.multiply(length) else: normal = normal.multiply(height) base = Part.Face(base[0]) base = base.extrude(normal) base = self.processSubShapes(obj, base, pl) self.applyShape(obj, base, pl)
def getRebarData(self,obj): if not obj.Host: return if Draft.getType(obj.Host) != "Structure": return if not obj.Host.Shape: return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = obj.Host wire = obj.Base.Shape.Wires[0] if Draft.getType(obj.Base) == "Wire": # Draft Wires can have "wrong" placement import DraftGeomUtils axis = DraftGeomUtils.getNormal(obj.Base.Shape) else: axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,-1)) size = (ArchCommands.projectToVector(father.Shape.copy(),axis)).Length if hasattr(obj,"Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if hasattr(obj,"Distance"): if obj.Distance.Value: size = obj.Distance.Value if hasattr(obj,"Rounding"): if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire,radius) wires = [] if obj.Amount == 1: offset = DraftVecUtils.scaleTo(axis,size/2) wire.translate(offset) wires.append(wire) else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis,obj.OffsetStart.Value) else: baseoffset = None if obj.ViewObject.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) vinterval = DraftVecUtils.scaleTo(axis,interval) for i in range(obj.Amount): if i == 0: if baseoffset: wire.translate(baseoffset) wires.append(wire) else: wire = wire.copy() wire.translate(vinterval) wires.append(wire) return [wires,obj.Diameter.Value/2]
def execute(self,obj): if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self, obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj, "ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply( baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj, "BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append( DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage( translate( "Arch", "Crossing point not found in profile.\n")) basepoint = basepointliste[0] else: basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) angle = bvec.getAngle(axis) if round(angle, Draft.precision()) != 0: if round(angle, Draft.precision()) != round( math.pi, Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate( DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self,obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj,"ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj,"BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append(DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage(translate("Arch","Crossing point not found in profile.\n")) basepoint = basepointliste[0] else : basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self,obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj,"ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: edges = obj.Base.Shape.Edges if hasattr(obj,"Edges"): if obj.Edges == "Vertical edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Top Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z,reverse=True) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] elif obj.Edges == "Bottom Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] for e in edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj,"BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append(DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage(translate("Arch","Crossing point not found in profile.\n")) basepoint = basepointliste[0] else : basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: if hasattr(obj,"Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) s = s.removeSplitter() obj.Shape = s obj.Placement = pl return obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self, obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.Shape: return if obj.Profile.Shape.findPlane() is None: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj, "ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply( baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) edges = obj.Base.Shape.Edges if hasattr(obj, "Edges"): if obj.Edges == "Vertical edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(0, 1, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] elif obj.Edges == "Horizontal edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(1, 0, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] elif obj.Edges == "Top Horizontal edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(1, 0, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] edges = sorted(edges, key=lambda x: x.CenterOfMass.z, reverse=True) z = edges[0].CenterOfMass.z edges = [ e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001 ] elif obj.Edges == "Bottom Horizontal edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(1, 0, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] edges = sorted(edges, key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z edges = [ e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001 ] for e in edges: bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() rot = None # New rotation. # Supplying FreeCAD.Rotation() with two parallel vectors and # a null vector may seem strange, but the function is perfectly # able to handle this. Its algorithm will use default axes in # such cases. if obj.Align: if normal is None: rot = FreeCAD.Rotation(FreeCAD.Vector(), bvec, bvec, "ZYX") else: rot = FreeCAD.Rotation(FreeCAD.Vector(), normal, bvec, "ZYX") profile.Placement.Rotation = rot if hasattr(obj, "BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.Placement.Base] for edge in edges: basepointliste.append( DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage( translate("Arch", "Crossing point not found in profile.") + "\n") basepoint = basepointliste[0] else: basepoint = profile.Placement.Base delta = bpoint.sub(basepoint) # Translation vector. if obj.Offset and (not DraftVecUtils.isNull(obj.Offset)): if rot is None: delta = delta + obj.Offset else: delta = delta + rot.multVec(obj.Offset) profile.translate(delta) if obj.Rotation: profile.rotate(bpoint, bvec, obj.Rotation) # profile = wire.makePipeShell([profile], True, False, 2) TODO buggy profile = profile.extrude(bvec) shapes.append(profile) if shapes: if hasattr(obj, "Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) s = s.removeSplitter() obj.Shape = s obj.Placement = pl return obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def getRebarData(self, obj): #if not obj.Host: # return #if Draft.getType(obj.Host) != "Structure": # return #if not obj.Host.Shape: # return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = None if obj.Host: father = obj.Host wire = obj.Base.Shape.Wires[0] axis = None if Draft.getType( obj.Base) == "Wire": # Draft Wires can have "wrong" placement import DraftGeomUtils axis = DraftGeomUtils.getNormal(obj.Base.Shape) if not axis: axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector( 0, 0, -1)) size = 0 if father: size = (ArchCommands.projectToVector(father.Shape.copy(), axis)).Length if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value if hasattr(obj, "Rounding"): if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire, radius) wires = [] if obj.Amount == 1: if size and father: offset = DraftVecUtils.scaleTo(axis, size / 2) else: offset = FreeCAD.Vector() wire.translate(offset) wires.append(wire) else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) vinterval = DraftVecUtils.scaleTo(axis, interval) for i in range(obj.Amount): if i == 0: if baseoffset: wire.translate(baseoffset) wires.append(wire) else: wire = wire.copy() wire.translate(vinterval) wires.append(wire) return [wires, obj.Diameter.Value / 2]
def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align, mode = 'Original', forceNormal=False, normalOverride=None): """Calculates the placements of a shape along a given path so that each copy will be distributed evenly""" import Part import DraftGeomUtils closedpath = DraftGeomUtils.isReallyClosed(pathwire) normal = DraftGeomUtils.getNormal(pathwire) if forceNormal and normalOverride: normal = normalOverride path = Part.__sortEdges__(pathwire.Edges) ends = [] cdist = 0 for e in path: # find cumulative edge end distance cdist += e.Length ends.append(cdist) placements = [] # place the start shape pt = path[0].Vertexes[0].Point placements.append(calculatePlacement( shapeRotation, path[0], 0, pt, xlate, align, normal, mode, forceNormal)) # closed path doesn't need shape on last vertex if not(closedpath): # place the end shape pt = path[-1].Vertexes[-1].Point placements.append(calculatePlacement( shapeRotation, path[-1], path[-1].Length, pt, xlate, align, normal, mode, forceNormal)) if count < 3: return placements # place the middle shapes if closedpath: stop = count else: stop = count - 1 step = float(cdist) / stop remains = 0 travel = step for i in range(1, stop): # which edge in path should contain this shape? # avoids problems with float math travel > ends[-1] iend = len(ends) - 1 for j in range(0, len(ends)): if travel <= ends[j]: iend = j break # place shape at proper spot on proper edge remains = ends[iend] - travel offset = path[iend].Length - remains pt = path[iend].valueAt(getParameterFromV0(path[iend], offset)) placements.append(calculatePlacement( shapeRotation, path[iend], offset, pt, xlate, align, normal, mode, forceNormal)) travel += step return placements