def setFromSelection(self): """Fill the nodes according to the selected geometry.""" import DraftGeomUtils sel = Gui.Selection.getSelectionEx() if len(sel) == 1: if len(sel[0].SubElementNames) == 1: if "Edge" in sel[0].SubElementNames[0]: edge = sel[0].SubObjects[0] n = int(sel[0].SubElementNames[0].lstrip("Edge")) - 1 self.indices.append(n) if DraftGeomUtils.geomType(edge) == "Line": self.node.extend( [edge.Vertexes[0].Point, edge.Vertexes[1].Point]) v1 = None v2 = None for i, v in enumerate(sel[0].Object.Shape.Vertexes): if v.Point == edge.Vertexes[0].Point: v1 = i if v.Point == edge.Vertexes[1].Point: v2 = i if (v1 is not None) and (v2 is not None): self.link = [sel[0].Object, v1, v2] elif DraftGeomUtils.geomType(edge) == "Circle": self.node.extend( [edge.Curve.Center, edge.Vertexes[0].Point]) self.edges = [edge] self.arcmode = "diameter" self.link = [sel[0].Object, n]
def getPathData(self, w): """Returns a SVG path data string from a 2D wire The Y Axis in the SVG Coordinate system is reversed from the FreeCAD Coordinate System. So we change the y coordinates accordingly """ def toCommand(command, x, y): return '%s %s %s ' % (command, toNumberString(x), toNumberString(-y)) edges = Part.__sortEdges__(w.Edges) v = edges[0].Vertexes[0].Point svg = toCommand('M', v.x, v.y) for e in edges: if (DraftGeomUtils.geomType(e) == "Line") or (DraftGeomUtils.geomType(e) == "BSplineCurve"): v = e.Vertexes[-1].Point svg += toCommand('L', v.x, v.y) elif DraftGeomUtils.geomType(e) == "Circle": r = e.Curve.Radius v = e.Vertexes[-1].Point svg += 'A %s %s 0 0 1 %s %s ' % (toNumberString(r), toNumberString(r), toNumberString(v.x), toNumberString(-v.y)) if len(edges) > 1: svg += 'Z ' return svg
def export(exportList, filename): "called when freecad exports a file" faces = [] edges = [] # getting faces and edges for ob in exportList: if ob.Shape.Faces: for f in ob.Shape.Faces: faces.append(f) else: for e in ob.Shape.Edges: edges.append(e) if not (edges or faces): print "oca: found no data to export" return # writing file oca = pythonopen(filename, 'wb') oca.write("#oca file generated from FreeCAD\r\n") oca.write("# edges\r\n") count = 1 for e in edges: if DraftGeomUtils.geomType(e) == "Line": oca.write("L" + str(count) + "=") oca.write(writepoint(e.Vertexes[0].Point)) oca.write(" ") oca.write(writepoint(e.Vertexes[-1].Point)) oca.write("\r\n") elif DraftGeomUtils.geomType(e) == "Circle": if (len(e.Vertexes) > 1): oca.write("C" + str(count) + "=ARC ") oca.write(writepoint(e.Vertexes[0].Point)) oca.write(" ") oca.write(writepoint(DraftGeomUtils.findMidpoint(e))) oca.write(" ") oca.write(writepoint(e.Vertexes[-1].Point)) else: oca.write("C" + str(count) + "= ") oca.write(writepoint(e.Curve.Center)) oca.write(" ") oca.write(str(e.Curve.Radius)) oca.write("\r\n") count += 1 oca.write("# faces\r\n") for f in faces: oca.write("A" + str(count) + "=S(POL") for v in f.Vertexes: oca.write(" ") oca.write(writepoint(v.Point)) oca.write(" ") oca.write(writepoint(f.Vertexes[0].Point)) oca.write(")\r\n") count += 1 # closing oca.close() FreeCAD.Console.PrintMessage("successfully exported " + filename)
def export(exportList,filename): "called when freecad exports a file" faces = [] edges = [] # getting faces and edges for ob in exportList: if ob.Shape.Faces: for f in ob.Shape.Faces: faces.append(f) else: for e in ob.Shape.Edges: edges.append(e) if not (edges or faces): print("oca: found no data to export") return # writing file oca = pythonopen(filename,'wb') oca.write("#oca file generated from FreeCAD\r\n") oca.write("# edges\r\n") count = 1 for e in edges: if DraftGeomUtils.geomType(e) == "Line": oca.write("L"+str(count)+"=") oca.write(writepoint(e.Vertexes[0].Point)) oca.write(" ") oca.write(writepoint(e.Vertexes[-1].Point)) oca.write("\r\n") elif DraftGeomUtils.geomType(e) == "Circle": if (len(e.Vertexes) > 1): oca.write("C"+str(count)+"=ARC ") oca.write(writepoint(e.Vertexes[0].Point)) oca.write(" ") oca.write(writepoint(DraftGeomUtils.findMidpoint(e))) oca.write(" ") oca.write(writepoint(e.Vertexes[-1].Point)) else: oca.write("C"+str(count)+"= ") oca.write(writepoint(e.Curve.Center)) oca.write(" ") oca.write(str(e.Curve.Radius)) oca.write("\r\n") count += 1 oca.write("# faces\r\n") for f in faces: oca.write("A"+str(count)+"=S(POL") for v in f.Vertexes: oca.write(" ") oca.write(writepoint(v.Point)) oca.write(" ") oca.write(writepoint(f.Vertexes[0].Point)) oca.write(")\r\n") count += 1 # closing oca.close() FreeCAD.Console.PrintMessage("successfully exported "+filename)
def execute(self, obj): """ Set start point and end point according to the linked geometry""" if obj.LinkedGeometry: if len(obj.LinkedGeometry) == 1: lobj = obj.LinkedGeometry[0][0] lsub = obj.LinkedGeometry[0][1] if len(lsub) == 1: if "Edge" in lsub[0]: n = int(lsub[0][4:]) - 1 edge = lobj.Shape.Edges[n] if DraftGeomUtils.geomType(edge) == "Line": obj.Start = edge.Vertexes[0].Point obj.End = edge.Vertexes[-1].Point elif DraftGeomUtils.geomType(edge) == "Circle": c = edge.Curve.Center r = edge.Curve.Radius a = edge.Curve.Axis ray = obj.Dimline.sub(c).projectToPlane( App.Vector(0, 0, 0), a) if (ray.Length == 0): ray = a.cross(App.Vector(1, 0, 0)) if (ray.Length == 0): ray = a.cross(App.Vector(0, 1, 0)) ray = DraftVecUtils.scaleTo(ray, r) if hasattr(obj, "Diameter"): if obj.Diameter: obj.Start = c.add(ray.negative()) obj.End = c.add(ray) else: obj.Start = c obj.End = c.add(ray) elif len(lsub) == 2: if ("Vertex" in lsub[0]) and ("Vertex" in lsub[1]): n1 = int(lsub[0][6:]) - 1 n2 = int(lsub[1][6:]) - 1 obj.Start = lobj.Shape.Vertexes[n1].Point obj.End = lobj.Shape.Vertexes[n2].Point elif len(obj.LinkedGeometry) == 2: lobj1 = obj.LinkedGeometry[0][0] lobj2 = obj.LinkedGeometry[1][0] lsub1 = obj.LinkedGeometry[0][1] lsub2 = obj.LinkedGeometry[1][1] if (len(lsub1) == 1) and (len(lsub2) == 1): if ("Vertex" in lsub1[0]) and ("Vertex" in lsub2[1]): n1 = int(lsub1[0][6:]) - 1 n2 = int(lsub2[0][6:]) - 1 obj.Start = lobj1.Shape.Vertexes[n1].Point obj.End = lobj2.Shape.Vertexes[n2].Point # set the distance property total_len = (obj.Start.sub(obj.End)).Length if round(obj.Distance.Value, utils.precision()) != round( total_len, utils.precision()): obj.Distance = total_len if App.GuiUp: if obj.ViewObject: obj.ViewObject.update()
def measure_one_obj_edge(obj, subelement, dim_point, diameter=False): """Measure one object with one subelement, a straight or circular edge. Parameters ---------- obj: Part::Feature The object that is measured. subelement: str The subelement that is measured, for example, `'Edge1'`. dim_line: Base::Vector3 A point through which the dimension goes through. """ start = App.Vector() end = App.Vector() if "Edge" in subelement: n = int(subelement[4:]) - 1 edge = obj.Shape.Edges[n] if DraftGeomUtils.geomType(edge) == "Line": start = edge.Vertexes[0].Point end = edge.Vertexes[-1].Point elif DraftGeomUtils.geomType(edge) == "Circle": center = edge.Curve.Center radius = edge.Curve.Radius axis = edge.Curve.Axis dim_line = dim_point.sub(center) # The ray is projected to the plane on which the circle lies, # but if the projection is not successful, try in the other planes ray = dim_line.projectToPlane(App.Vector(0, 0, 0), axis) if ray.Length == 0: ray = axis.cross(App.Vector(1, 0, 0)) if ray.Length == 0: ray = axis.cross(App.Vector(0, 1, 0)) # The ray is made as large as the arc's radius # and optionally the diameter ray = DraftVecUtils.scaleTo(ray, radius) if diameter: # The start and end points lie on the arc start = center.add(ray.negative()) end = center.add(ray) else: # The start is th center and the end lies on the arc start = center end = center.add(ray) return start, end
def onChanged(self, obj, prop): if prop == "Length": if obj.Base and obj.Length.Value: if obj.Base.isDerivedFrom("Part::Feature"): if len(obj.Base.Shape.Edges) == 1: import DraftGeomUtils e = obj.Base.Shape.Edges[0] if DraftGeomUtils.geomType(e) == "Line": if e.Length != obj.Length.Value: v = e.Vertexes[-1].Point.sub( e.Vertexes[0].Point) v.normalize() v.multiply(obj.Length.Value) p2 = e.Vertexes[0].Point.add(v) if Draft.getType(obj.Base) == "Wire": obj.Base.End = p2 elif Draft.getType(obj.Base) == "Sketch": obj.Base.movePoint(0, 2, p2, 0) else: FreeCAD.Console.PrintError( translate( "Arch", "Error: Unable to modify the base object of this wall" ) + "\n") self.hideSubobjects(obj, prop) ArchComponent.Component.onChanged(self, obj, prop)
def onChanged(self,obj,prop): if prop == "Length": if obj.Base and obj.Length.Value and hasattr(self,"oldLength") and (self.oldLength != None) and (self.oldLength != obj.Length.Value): if obj.Base.isDerivedFrom("Part::Feature"): if len(obj.Base.Shape.Edges) == 1: import DraftGeomUtils e = obj.Base.Shape.Edges[0] if DraftGeomUtils.geomType(e) == "Line": if e.Length != obj.Length.Value: v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point) v.normalize() v.multiply(obj.Length.Value) p2 = e.Vertexes[0].Point.add(v) if Draft.getType(obj.Base) == "Wire": #print "modifying p2" obj.Base.End = p2 elif Draft.getType(obj.Base) == "Sketch": try: obj.Base.movePoint(0,2,p2,0) except: print("Debug: The base sketch of this wall could not be changed, because the sketch has not been edited yet in this session (this is a bug in FreeCAD). Try entering and exiting edit mode in this sketch first, and then changing the wall length should work.") else: FreeCAD.Console.PrintError(translate("Arch","Error: Unable to modify the base object of this wall")+"\n") self.hideSubobjects(obj,prop) ArchComponent.Component.onChanged(self,obj,prop)
def getIndices(shape,offset): "returns a list with 2 lists: vertices and face indexes, offsetted with the given amount" vlist = [] elist = [] flist = [] for v in shape.Vertexes: vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offset) ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offset) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str(findVert(vdata,shape.Vertexes) + offset) flist.append(fi) else: fi = "" # OCC vertices are unsorted. We need to sort in the right order... edges = DraftGeomUtils.sortEdges(f.Wire.Edges) #print edges for e in edges: #print e.Vertexes[0].Point,e.Vertexes[1].Point v = e.Vertexes[0] fi += " " + str(findVert(v,shape.Vertexes) + offset) flist.append(fi) return vlist,elist,flist
def getIndices(shape,offset): "returns a list with 2 lists: vertices and face indexes, offsetted with the given amount" vlist = [] elist = [] flist = [] curves = None if isinstance(shape,Part.Shape): for e in shape.Edges: try: if not isinstance(e.Curve,Part.LineSegment): if not curves: curves = shape.tessellate(1) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating\n").decode('utf8')) break except: # unimplemented curve type curves = shape.tessellate(1) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating\n").decode('utf8')) break elif isinstance(shape,Mesh.Mesh): curves = shape.Topology if curves: for v in curves[0]: vlist.append(" "+str(round(v.x,p))+" "+str(round(v.y,p))+" "+str(round(v.z,p))) for f in curves[1]: fi = "" for vi in f: fi += " " + str(vi + offset) flist.append(fi) else: for v in shape.Vertexes: vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offset) ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offset) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str(findVert(vdata,shape.Vertexes) + offset) flist.append(fi) else: fi = "" for e in f.OuterWire.OrderedEdges: #print(e.Vertexes[0].Point,e.Vertexes[1].Point) v = e.Vertexes[0] ind = findVert(v,shape.Vertexes) if ind == None: return None,None,None fi += " " + str(ind + offset) flist.append(fi) return vlist,elist,flist
def getIndices(shape,offset): "returns a list with 2 lists: vertices and face indexes, offsetted with the given amount" vlist = [] elist = [] flist = [] curves = None if isinstance(shape,Part.Shape): for e in shape.Edges: try: if not isinstance(e.Curve,Part.LineSegment): if not curves: curves = shape.tessellate(1) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") break except: # unimplemented curve type curves = shape.tessellate(1) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") break elif isinstance(shape,Mesh.Mesh): curves = shape.Topology if curves: for v in curves[0]: vlist.append(" "+str(round(v.x,p))+" "+str(round(v.y,p))+" "+str(round(v.z,p))) for f in curves[1]: fi = "" for vi in f: fi += " " + str(vi + offset) flist.append(fi) else: for v in shape.Vertexes: vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offset) ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offset) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str(findVert(vdata,shape.Vertexes) + offset) flist.append(fi) else: fi = "" for e in f.OuterWire.OrderedEdges: #print(e.Vertexes[0].Point,e.Vertexes[1].Point) v = e.Vertexes[0] ind = findVert(v,shape.Vertexes) if ind == None: return None,None,None fi += " " + str(ind + offset) flist.append(fi) return vlist,elist,flist
def getIndices(shape, offset): "returns a list with 2 lists: vertices and face indexes, offsetted with the given amount" vlist = [] elist = [] flist = [] curves = None for e in shape.Edges: if not isinstance(e.Curve, Part.Line): if not curves: curves = shape.tessellate(1) FreeCAD.Console.PrintWarning( translate( "Arch", "Found a shape containing curves, triangulating\n")) if curves: for v in curves[0]: vlist.append(" " + str(round(v.x, p)) + " " + str(round(v.y, p)) + " " + str(round(v.z, p))) for f in curves[1]: fi = "" for vi in f: fi += " " + str(vi + offset) flist.append(fi) else: for v in shape.Vertexes: vlist.append(" " + str(round(v.X, p)) + " " + str(round(v.Y, p)) + " " + str(round(v.Z, p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str( findVert(e.Vertexes[0], shape.Vertexes) + offset) ei += " " + str( findVert(e.Vertexes[-1], shape.Vertexes) + offset) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str( findVert(vdata, shape.Vertexes) + offset) flist.append(fi) else: fi = "" # OCC vertices are unsorted. We need to sort in the right order... edges = DraftGeomUtils.sortEdges(f.OuterWire.Edges) #print edges for e in edges: #print e.Vertexes[0].Point,e.Vertexes[1].Point v = e.Vertexes[0] fi += " " + str(findVert(v, shape.Vertexes) + offset) flist.append(fi) return vlist, elist, flist
def snapToPerpendicular(self,shape,last): "returns a list of perpendicular snap locations" snaps = [] if self.isEnabled("perpendicular"): if last: if isinstance(shape,Part.Edge): if DraftGeomUtils.geomType(shape) == "Line": np = self.getPerpendicular(shape,last) elif DraftGeomUtils.geomType(shape) == "Circle": dv = last.sub(shape.Curve.Center) dv = DraftVecUtils.scaleTo(dv,shape.Curve.Radius) np = (shape.Curve.Center).add(dv) elif DraftGeomUtils.geomType(shape) == "BSplineCurve": pr = shape.Curve.parameter(last) np = shape.Curve.value(pr) else: return snaps snaps.append([np,'perpendicular',np]) return snaps
def snapToPerpendicular(self, shape, last): "returns a list of perpendicular snap locations" snaps = [] if self.isEnabled("perpendicular"): if last: if isinstance(shape, Part.Edge): if DraftGeomUtils.geomType(shape) == "Line": np = self.getPerpendicular(shape, last) elif DraftGeomUtils.geomType(shape) == "Circle": dv = last.sub(shape.Curve.Center) dv = DraftVecUtils.scaleTo(dv, shape.Curve.Radius) np = (shape.Curve.Center).add(dv) elif DraftGeomUtils.geomType(shape) == "BSplineCurve": pr = shape.Curve.parameter(last) np = shape.Curve.value(pr) else: return snaps snaps.append([np, 'perpendicular', np]) return snaps
def getPathData(self,w): "Returns a SVG path data string from a 2D wire" def tostr(val): return str(round(val,DraftVecUtils.precision())) edges = DraftGeomUtils.sortEdges(w.Edges) v = edges[0].Vertexes[0].Point svg = 'M '+ tostr(v.x) +' '+ tostr(v.y) + ' ' for e in edges: if (DraftGeomUtils.geomType(e) == "Line") or (DraftGeomUtils.geomType(e) == "BSplineCurve"): v = e.Vertexes[-1].Point svg += 'L '+ tostr(v.x) +' '+ tostr(v.y) + ' ' elif DraftGeomUtils.geomType(e) == "Circle": r = e.Curve.Radius v = e.Vertexes[-1].Point svg += 'A '+ tostr(r) + ' '+ tostr(r) +' 0 0 1 '+ tostr(v.x) +' ' svg += tostr(v.y) + ' ' if len(edges) > 1: svg += 'Z ' return svg
def getIndices(shape,offset): "returns a list with 2 lists: vertices and face indexes, offsetted with the given amount" vlist = [] elist = [] flist = [] curves = None for e in shape.Edges: try: if not isinstance(e.Curve,Part.Line): if not curves: curves = shape.tessellate(1) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating\n")) break except: # unimplemented curve type curves = shape.tessellate(1) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating\n")) break if curves: for v in curves[0]: vlist.append(" "+str(round(v.x,p))+" "+str(round(v.y,p))+" "+str(round(v.z,p))) for f in curves[1]: fi = "" for vi in f: fi += " " + str(vi + offset) flist.append(fi) else: for v in shape.Vertexes: vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offset) ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offset) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str(findVert(vdata,shape.Vertexes) + offset) flist.append(fi) else: fi = "" # OCC vertices are unsorted. We need to sort in the right order... edges = DraftGeomUtils.sortEdges(f.OuterWire.Edges) #print edges for e in edges: #print e.Vertexes[0].Point,e.Vertexes[1].Point v = e.Vertexes[0] fi += " " + str(findVert(v,shape.Vertexes) + offset) flist.append(fi) return vlist,elist,flist
def shapify(obj): """Transform a parametric object into a static, non-parametric shape. Parameters ---------- obj : App::DocumentObject Any type of scripted object. This object will be removed, and a non-parametric object with the same topological shape (`Part::TopoShape`) will be created. Returns ------- Part::Feature The new object that takes `obj.Shape` as its own. Depending on the contents of the Shape, the resulting object will be named `'Face'`, `'Solid'`, `'Compound'`, `'Shell'`, `'Wire'`, `'Line'`, `'Circle'`, or the name returned by `get_real_name(obj.Name)`. If there is a problem with `obj.Shape`, it will return `None`, and the original object will not be modified. """ try: shape = obj.Shape except Exception: return None if len(shape.Faces) == 1: name = "Face" elif len(shape.Solids) == 1: name = "Solid" elif len(shape.Solids) > 1: name = "Compound" elif len(shape.Faces) > 1: name = "Shell" elif len(shape.Wires) == 1: name = "Wire" elif len(shape.Edges) == 1: import DraftGeomUtils if DraftGeomUtils.geomType(shape.Edges[0]) == "Line": name = "Line" else: name = "Circle" else: name = getRealName(obj.Name) App.ActiveDocument.removeObject(obj.Name) newobj = App.ActiveDocument.addObject("Part::Feature", name) newobj.Shape = shape return newobj
def is_linked_to_circle(self): _obj = self.Object if _obj.LinkedGeometry and len(_obj.LinkedGeometry) == 1: lobj = _obj.LinkedGeometry[0][0] lsub = _obj.LinkedGeometry[0][1] if len(lsub) == 1 and "Edge" in lsub[0]: n = int(lsub[0][4:]) - 1 edge = lobj.Shape.Edges[n] if DraftGeomUtils.geomType(edge) == "Circle": return True return False
def draftify(objectslist, makeblock=False, delete=True): """draftify(objectslist,[makeblock],[delete]) Turn each object of the given list (objectslist can also be a single object) into a Draft parametric wire. TODO: support more objects Parameters ---------- objectslist : makeblock : bool If makeblock is True, multiple objects will be grouped in a block. delete : bool If delete = False, old objects are not deleted """ import Part import DraftGeomUtils if not isinstance(objectslist, list): objectslist = [objectslist] newobjlist = [] for obj in objectslist: if hasattr(obj, 'Shape'): for cluster in Part.getSortedClusters(obj.Shape.Edges): w = Part.Wire(cluster) if DraftGeomUtils.hasCurves(w): if (len(w.Edges) == 1) and (DraftGeomUtils.geomType( w.Edges[0]) == "Circle"): nobj = makeCircle(w.Edges[0]) else: nobj = App.ActiveDocument.addObject( "Part::Feature", obj.Name) nobj.Shape = w else: nobj = makeWire(w) newobjlist.append(nobj) gui_utils.format_object(nobj, obj) # sketches are always in wireframe mode. In Draft we don't like that! if App.GuiUp: nobj.ViewObject.DisplayMode = "Flat Lines" if delete: App.ActiveDocument.removeObject(obj.Name) if makeblock: return makeBlock(newobjlist) else: if len(newobjlist) == 1: return newobjlist[0] return newobjlist
def addCircle(radius, placement=None, face=None, startangle=None, endangle=None, support=None): import Part, DraftGeomUtils if placement: typecheck([(placement, FreeCAD.Placement)], "makeCircle") if startangle != endangle: n = "Arc" else: n = "Circle" obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython", n) _Circle(obj) if face != None: obj.MakeFace = face if isinstance(radius, Part.Edge): edge = radius if DraftGeomUtils.geomType(edge) == "Circle": obj.Radius = edge.Curve.Radius placement = FreeCAD.Placement(edge.Placement) delta = edge.Curve.Center.sub(placement.Base) placement.move(delta) if len(edge.Vertexes) > 1: ref = placement.multVec(FreeCAD.Vector(1, 0, 0)) v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center) v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center) a1 = -math.degrees(DraftVecUtils.angle(v1, ref)) a2 = -math.degrees(DraftVecUtils.angle(v2, ref)) obj.FirstAngle = a1 obj.LastAngle = a2 else: obj.Radius = radius if (startangle != None) and (endangle != None): if startangle == -0: startangle = 0 obj.FirstAngle = startangle obj.LastAngle = endangle obj.Support = support if placement: obj.Placement = placement if gui: _ViewProviderDraft(obj.ViewObject) formatObject(obj) select(obj) FreeCAD.ActiveDocument.recompute() return obj
def onChanged(self,obj,prop): if prop == "Length": if obj.Base and obj.Length.Value: if obj.Base.isDerivedFrom("Part::Feature"): if len(obj.Base.Shape.Edges) == 1: import DraftGeomUtils e = obj.Base.Shape.Edges[0] if DraftGeomUtils.geomType(e) == "Line": if e.Length != obj.Length.Value: v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point) v.normalize() v.multiply(obj.Length.Value) p2 = e.Vertexes[0].Point.add(v) if Draft.getType(obj.Base) == "Wire": obj.Base.End = p2 elif Draft.getType(obj.Base) == "Sketch": obj.Base.movePoint(0,2,p2,0) else: FreeCAD.Console.PrintError(translate("Arch","Error: Unable to modify the base object of this wall")+"\n") self.hideSubobjects(obj,prop) ArchComponent.Component.onChanged(self,obj,prop)
def getIndices(shape, offset): "returns a list with 2 lists: vertices and face indexes, offsetted with the given amount" vlist = [] elist = [] flist = [] for v in shape.Vertexes: vlist.append(" " + str(round(v.X, p)) + " " + str(round(v.Y, p)) + " " + str(round(v.Z, p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str( findVert(e.Vertexes[0], shape.Vertexes) + offset) ei += " " + str( findVert(e.Vertexes[-1], shape.Vertexes) + offset) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str(findVert(vdata, shape.Vertexes) + offset) flist.append(fi) else: fi = "" # OCC vertices are unsorted. We need to sort in the right order... edges = DraftGeomUtils.sortEdges(f.Wire.Edges) #print edges for e in edges: #print e.Vertexes[0].Point,e.Vertexes[1].Point v = e.Vertexes[0] fi += " " + str(findVert(v, shape.Vertexes) + offset) flist.append(fi) return vlist, elist, flist
def getStirrupSVGData(rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style): """getStirrupSVGData(StirrupRebar, ViewPlane, RebarsSVG, RebarsStrokeWidth, RebarsColorStyle): Returns dictionary containing stirrup svg data. rebars_color_style can be: - "shape color" to select color of rebar shape - color name or hex value of color Returns dictionary format: { "svg": stirrup_svg, "visibility": is_rebar_visible, } """ rebars_color = getRebarColor(rebar, rebars_color_style) stirrup_svg = ElementTree.Element("g", attrib={"id": str(rebar.Name)}) is_rebar_visible = False drawing_plane_normal = view_plane.axis stirrup_span_axis = getRebarsSpanAxis(rebar) if round(drawing_plane_normal.cross(stirrup_span_axis).Length) == 0: basewire = rebar.Base.Shape.Wires[0].copy() basewire.Placement = rebar.PlacementList[0].multiply( basewire.Placement) edges = Part.__sortEdges__( DraftGeomUtils.filletWire( basewire, rebar.Rounding * rebar.Diameter.Value, ).Edges) for edge in edges: if DraftGeomUtils.geomType(edge) == "Line": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) edge_svg = getLineSVG(p1, p2, rebars_stroke_width, rebars_color) if is_rebar_visible or not isLineInSVG(p1, p2, rebars_svg): stirrup_svg.append(edge_svg) is_rebar_visible = True elif DraftGeomUtils.geomType(edge) == "Circle": edge_svg = getRoundEdgeSVG(edge, view_plane, rebars_stroke_width, rebars_color) if is_rebar_visible or not isRoundCornerInSVG( edge, rebar.Rounding * rebar.Diameter.Value, view_plane, rebars_svg, ): stirrup_svg.append(edge_svg) is_rebar_visible = True else: if round(stirrup_span_axis.cross(view_plane.u).Length) == 0: stirrup_alignment = "V" else: stirrup_alignment = "H" basewire = DraftGeomUtils.filletWire( rebar.Base.Shape.Wires[0], rebar.Rounding * rebar.Diameter.Value) for placement in rebar.PlacementList: wire = basewire.copy() wire.Placement = placement.multiply(basewire.Placement) p1, p2 = getStirrupSVGPoints(wire, stirrup_alignment, view_plane) rebar_svg = getLineSVG(p1, p2, rebars_stroke_width, rebars_color) if not isLineInSVG(p1, p2, rebars_svg): is_rebar_visible = True if is_rebar_visible: stirrup_svg.append(rebar_svg) return { "svg": stirrup_svg, "visibility": is_rebar_visible, }
def convertBezier(edge): if DraftGeomUtils.geomType(edge) == "BezierCurve": return (edge.Curve.toBSpline(edge.FirstParameter, edge.LastParameter).toShape()) else: return (edge)
def make_circle(radius, placement=None, face=None, startangle=None, endangle=None, support=None): """make_circle(radius, [placement, face, startangle, endangle]) or make_circle(edge,[face]): Creates a circle object with given parameters. Parameters ---------- radius : the radius of the circle. placement : If placement is given, it is used. face : Bool If face is False, the circle is shown as a wireframe, otherwise as a face. startangle : start angle of the arc (in degrees) endangle : end angle of the arc (in degrees) if startangle and endangle are equal, a circle is created, if they are different an arc is created edge : edge.Curve must be a 'Part.Circle' the circle is created from the given edge support : TODO: Describe """ if not App.ActiveDocument: App.Console.PrintError("No active document. Aborting\n") return if placement: type_check([(placement,App.Placement)], "make_circle") if startangle != endangle: _name = "Arc" else: _name = "Circle" obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", _name) Circle(obj) if face != None: obj.MakeFace = face if isinstance(radius,Part.Edge): edge = radius if DraftGeomUtils.geomType(edge) == "Circle": obj.Radius = edge.Curve.Radius placement = App.Placement(edge.Placement) delta = edge.Curve.Center.sub(placement.Base) placement.move(delta) # Rotation of the edge rotOk = App.Rotation(edge.Curve.XAxis, edge.Curve.YAxis, edge.Curve.Axis, "ZXY") placement.Rotation = rotOk if len(edge.Vertexes) > 1: v0 = edge.Curve.XAxis v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center) v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center) # Angle between edge.Curve.XAxis and the vector from center to start of arc a0 = math.degrees(App.Vector.getAngle(v0, v1)) # Angle between edge.Curve.XAxis and the vector from center to end of arc a1 = math.degrees(App.Vector.getAngle(v0, v2)) obj.FirstAngle = a0 obj.LastAngle = a1 else: obj.Radius = radius if (startangle != None) and (endangle != None): if startangle == -0: startangle = 0 obj.FirstAngle = startangle obj.LastAngle = endangle obj.Support = support if placement: obj.Placement = placement if App.GuiUp: ViewProviderDraft(obj.ViewObject) format_object(obj) select(obj) return obj
def getRebarShapeSVG( rebar, view_direction: Union[FreeCAD.Vector, WorkingPlane.Plane] = FreeCAD.Vector(0, 0, 0), include_mark: bool = True, stirrup_extended_edge_offset: float = 2, rebar_stroke_width: float = 0.35, rebar_color_style: str = "shape color", include_dimensions: bool = True, rebar_dimension_units: str = "mm", rebar_length_dimension_precision: int = 0, include_units_in_dimension_label: bool = False, bent_angle_dimension_exclude_list: Union[Tuple[float, ...], List[float]] = ( 45, 90, 180, ), dimension_font_family: str = "DejaVu Sans", dimension_font_size: float = 2, helical_rebar_dimension_label_format: str = "%L,r=%R,pitch=%P", scale: float = 1, max_height: float = 0, max_width: float = 0, side_padding: float = 1, horizontal_shape: bool = False, ) -> ElementTree.Element: """Generate and return rebar shape svg. Parameters ---------- rebar: <ArchRebar._Rebar> or <rebar2.BaseRebar> Rebar to generate its shape svg. view_direction: FreeCAD.Vector or WorkingPlane.Plane, optional The view point direction for rebar shape. Default is FreeCAD.Vector(0, 0, 0) to automatically choose view_direction. include_mark: bool, optional If True, then rebar.Mark will be included in rebar shape svg. Default is True. stirrup_extended_edge_offset: float, optional The offset of extended end edges of stirrup, so that end edges of stirrup with 90 degree bent angle do not overlap with stirrup edges. Default is 2. rebar_stroke_width: float, optional The stroke-width of rebar in svg. Default is 0.35 rebar_color_style: {"shape color", "color_name", "hex_value_of_color"} The color style of rebar. "shape color" means select color of rebar shape. include_dimensions: bool, optional If True, then each rebar edge dimensions and bent angle dimensions will be included in rebar shape svg. rebar_dimension_units: str, optional The units to be used for rebar length dimensions. Default is "mm". rebar_length_dimension_precision: int, optional The number of decimals that should be shown for rebar length as dimension label. Set it to None to use user preferred unit precision from FreeCAD unit preferences. Default is 0 include_units_in_dimension_label: bool, optional If it is True, then rebar length units will be shown in dimension label. Default is False. bent_angle_dimension_exclude_list: list or tuple of float, optional The list of bent angles to not include their dimensions. Default is (45, 90, 180). dimension_font_family: str, optional The font-family of dimension text. Default is "DejaVu Sans". dimension_font_size: float, optional The font-size of dimension text. Default is 2 helical_rebar_dimension_label_format: str, optional The format of helical rebar dimension label. %L -> Length of helical rebar %R -> Helix radius of helical rebar %P -> Helix pitch of helical rebar Default is "%L,r=%R,pitch=%P". scale: float, optional The scale value to scale rebar svg. The scale parameter helps to scale down rebar_stroke_width and dimension_font_size to make them resolution independent. If max_height or max_width is set to non-zero value, then scale parameter will be ignored. Default is 1 max_height: float, optional The maximum height of rebar shape svg. Default is 0 to set rebar shape svg height based on scale parameter. max_width: float, optional The maximum width of rebar shape svg. Default is 0 to set rebar shape svg width based on scale parameter. side_padding: float, optional The padding on each side of rebar shape. Default is 1. horizontal_shape: bool, optional If True, then rebar shape will be made horizontal by rotating max length edge of rebar shape. Default is False. Returns ------- ElementTree.Element The generated rebar shape svg. """ if isinstance(view_direction, FreeCAD.Vector): if DraftVecUtils.isNull(view_direction): if (hasattr(rebar, "RebarShape") and rebar.RebarShape == "HelicalRebar"): view_direction = rebar.Base.Placement.Rotation.multVec( FreeCAD.Vector(0, -1, 0)) if hasattr(rebar, "Direction") and not DraftVecUtils.isNull( rebar.Direction): view_direction = FreeCAD.Vector(rebar.Direction) view_direction.normalize() else: view_direction = getRebarsSpanAxis(rebar) view_plane = getSVGPlaneFromAxis(view_direction) elif isinstance(view_direction, WorkingPlane.Plane): view_plane = view_direction else: FreeCAD.Console.PrintError( "Invalid view_direction type. Supported view_direction types: " "FreeCAD.Vector, WorkingPlane.Plane\n") return ElementTree.Element("g") if rebar_length_dimension_precision is None: # Get user preferred unit precision precision: int = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Units").GetInt("Decimals") else: precision = abs(int(rebar_length_dimension_precision)) rebar_color = getRebarColor(rebar, rebar_color_style) # Create required svg elements svg = getSVGRootElement() rebar_shape_svg = ElementTree.Element("g", attrib={"id": str(rebar.Name)}) svg.append(rebar_shape_svg) rebar_edges_svg = ElementTree.Element("g") edge_dimension_svg = ElementTree.Element("g") rebar_shape_svg.extend([rebar_edges_svg, edge_dimension_svg]) # Get basewire and fillet_basewire (basewire with round edges) basewire = rebar.Base.Shape.Wires[0].copy() fillet_radius = rebar.Rounding * rebar.Diameter.Value if fillet_radius: fillet_basewire = DraftGeomUtils.filletWire(basewire, fillet_radius) else: fillet_basewire = basewire ( rebar_shape_min_x, rebar_shape_min_y, rebar_shape_max_x, rebar_shape_max_y, ) = getVertexesMinMaxXY(fillet_basewire.Vertexes, view_plane) # If rebar shape should be horizontal and its width is less than its # height, then we should rotate basewire to make rebar shape horizontal rebar_shape_rotation_angle = 0 if horizontal_shape: line_type_edges = [ edge for edge in basewire.Edges if DraftGeomUtils.geomType(edge) == "Line" ] if line_type_edges: max_length_edge = max(line_type_edges, key=lambda x: x.Length) rebar_shape_rotation_angle = math.degrees( DraftVecUtils.angle( max_length_edge.lastVertex().Point.sub( max_length_edge.firstVertex().Point), view_plane.u, view_plane.axis, )) elif (rebar_shape_max_x - rebar_shape_min_x) < (rebar_shape_max_y - rebar_shape_min_y): rebar_shape_rotation_angle = -90 basewire.rotate(basewire.CenterOfMass, view_plane.axis, rebar_shape_rotation_angle) fillet_radius = rebar.Rounding * rebar.Diameter.Value if fillet_radius: fillet_basewire = DraftGeomUtils.filletWire( basewire, fillet_radius) else: fillet_basewire = basewire ( rebar_shape_min_x, rebar_shape_min_y, rebar_shape_max_x, rebar_shape_max_y, ) = getVertexesMinMaxXY(fillet_basewire.Vertexes, view_plane) # Check if stirrup will be having extended edges separated apart if (hasattr(rebar, "RebarShape") and rebar.RebarShape == "Stirrup" and hasattr(rebar, "BentAngle") and rebar.BentAngle == 90): apply_stirrup_extended_edge_offset = True else: apply_stirrup_extended_edge_offset = False # Apply max_height and max_width of rebar shape svg And calculate scaling # factor rebar_shape_height = (rebar_shape_max_y - rebar_shape_min_y) or 1 rebar_shape_width = (rebar_shape_max_x - rebar_shape_min_x) or 1 h_scaling_factor = v_scaling_factor = scale if max_height: v_scaling_factor = ( max_height - dimension_font_size * ((2 if include_mark else 0) + (2 if include_dimensions else 0)) - 2 * side_padding - (stirrup_extended_edge_offset if apply_stirrup_extended_edge_offset and (round( getProjectionToSVGPlane( Part.__sortEdges__(basewire.Edges)[0].firstVertex().Point, view_plane, ).y) in (round(rebar_shape_min_y), round(rebar_shape_max_y))) else 0)) / rebar_shape_height if max_width: h_scaling_factor = ( max_width - dimension_font_size * (2 if include_dimensions else 0) - 2 * side_padding - (stirrup_extended_edge_offset if apply_stirrup_extended_edge_offset and (round( getProjectionToSVGPlane( Part.__sortEdges__(basewire.Edges)[0].firstVertex().Point, view_plane, ).x) in (round(rebar_shape_min_x), round(rebar_shape_max_x))) else 0)) / rebar_shape_width scale = min(h_scaling_factor, v_scaling_factor) svg_height = ( rebar_shape_height * scale + dimension_font_size * ((2 if include_mark else 0) + (2 if include_dimensions else 0)) + 2 * side_padding + (stirrup_extended_edge_offset if apply_stirrup_extended_edge_offset and (round( getProjectionToSVGPlane( Part.__sortEdges__(basewire.Edges)[0].firstVertex().Point, view_plane, ).y) in (round(rebar_shape_min_y), round(rebar_shape_max_y))) else 0)) svg_width = ( rebar_shape_width * scale + dimension_font_size * (2 if include_dimensions else 0) + 2 * side_padding + (stirrup_extended_edge_offset if apply_stirrup_extended_edge_offset and (round( getProjectionToSVGPlane( Part.__sortEdges__(basewire.Edges)[0].firstVertex().Point, view_plane, ).x) in (round(rebar_shape_min_x), round(rebar_shape_max_x))) else 0)) # Move (min_x, min_y) point in svg plane to (0, 0) so that entire basewire # should be visible in svg view box and apply required scaling translate_x = round( -(rebar_shape_min_x - (dimension_font_size if include_dimensions else 0) / scale - side_padding / scale - (stirrup_extended_edge_offset / scale if apply_stirrup_extended_edge_offset and (round( getProjectionToSVGPlane( Part.__sortEdges__(basewire.Edges)[0].firstVertex().Point, view_plane, ).x) == round(rebar_shape_min_x)) else 0))) translate_y = round( -(rebar_shape_min_y - ((2 if include_mark else 0) + (1 if include_dimensions else 0)) * dimension_font_size / scale - side_padding / scale - (stirrup_extended_edge_offset / scale if apply_stirrup_extended_edge_offset and (round( getProjectionToSVGPlane( Part.__sortEdges__(basewire.Edges)[0].firstVertex().Point, view_plane, ).y) == round(rebar_shape_min_y)) else 0))) rebar_shape_svg.set( "transform", "scale({}) translate({} {})".format(scale, translate_x, translate_y), ) svg.set("width", "{}mm".format(round(svg_width))) svg.set("height", "{}mm".format(round(svg_height))) svg.set("viewBox", "0 0 {} {}".format(round(svg_width), round(svg_height))) # Scale down rebar_stroke_width and dimension_font_size to make them # resolution independent rebar_stroke_width /= scale dimension_font_size /= scale # Include rebar.Mark in rebar shape svg if include_mark: if hasattr(rebar, "Mark"): mark = rebar.Mark elif hasattr(rebar, "MarkNumber"): mark = rebar.MarkNumber else: mark = "" rebar_shape_svg.append( getSVGTextElement( mark, rebar_shape_min_x, rebar_shape_min_y - (0.5 + bool(include_dimensions)) * dimension_font_size, dimension_font_family, 1.5 * dimension_font_size, )) if hasattr(rebar, "RebarShape") and rebar.RebarShape == "HelicalRebar": helical_rebar_shape_svg = Draft.getSVG( rebar, direction=view_plane, linewidth=rebar_stroke_width, fillstyle="none", color=rebar_color, ) if helical_rebar_shape_svg: helical_rebar_shape_svg_element = ElementTree.fromstring( "<g>{}</g>".format(helical_rebar_shape_svg)) rebar_edges_svg.append(helical_rebar_shape_svg_element) helical_rebar_center = getProjectionToSVGPlane( rebar.Base.Shape.CenterOfMass, view_plane) helical_rebar_shape_svg_element.set( "transform", "rotate({} {} {})".format( rebar_shape_rotation_angle, helical_rebar_center.x, helical_rebar_center.y, ), ) if include_dimensions: # Create rebar dimension svg top_mid_point = FreeCAD.Vector( (rebar_shape_min_x + rebar_shape_max_x) / 2, rebar_shape_min_y) helical_rebar_length = str( round( FreeCAD.Units.Quantity("{}mm".format( rebar.Base.Shape.Wires[0].Length)).getValueAs( rebar_dimension_units).Value, precision, )) helix_radius = str( round( rebar.Base.Radius.getValueAs(rebar_dimension_units).Value, precision, )) helix_pitch = str( round( rebar.Base.Pitch.getValueAs(rebar_dimension_units).Value, precision, )) if "." in helical_rebar_length: helical_rebar_length = helical_rebar_length.rstrip("0").rstrip( ".") if "." in helix_radius: helix_radius = helix_radius.rstrip("0").rstrip(".") if "." in helix_pitch: helix_pitch = helix_pitch.rstrip("0").rstrip(".") if include_units_in_dimension_label: helical_rebar_length += rebar_dimension_units helix_radius += rebar_dimension_units helix_pitch += rebar_dimension_units edge_dimension_svg.append( getSVGTextElement( helical_rebar_dimension_label_format.replace( "%L", helical_rebar_length).replace( "%R", helix_radius).replace("%P", helix_pitch), top_mid_point.x, top_mid_point.y - rebar_stroke_width * 2, dimension_font_family, dimension_font_size, "middle", )) else: if stirrup_extended_edge_offset and apply_stirrup_extended_edge_offset: basewire = getBasewireOfStirrupWithExtendedEdges( rebar, view_plane, stirrup_extended_edge_offset / scale) basewire.rotate( basewire.CenterOfMass, view_plane.axis, rebar_shape_rotation_angle, ) fillet_radius = rebar.Rounding * rebar.Diameter.Value if fillet_radius: fillet_basewire = DraftGeomUtils.filletWire( basewire, fillet_radius) else: fillet_basewire = basewire edges = Part.__sortEdges__(fillet_basewire.Edges) straight_edges = Part.__sortEdges__(rebar.Base.Shape.Wires[0].Edges) for edge in list(straight_edges): if DraftGeomUtils.geomType(edge) != "Line": straight_edges.remove(edge) current_straight_edge_index = 0 for edge_index, edge in enumerate(edges): if DraftGeomUtils.geomType(edge) == "Line": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) # Create Edge svg if round(p1.x) == round(p2.x) and round(p1.y) == round(p2.y): edge_svg = getPointSVG(p1, radius=2 * rebar_stroke_width, fill=rebar_color) else: edge_svg = getLineSVG(p1, p2, rebar_stroke_width, rebar_color) if include_dimensions: # Create edge dimension svg mid_point = FreeCAD.Vector((p1.x + p2.x) / 2, (p1.y + p2.y) / 2) dimension_rotation = (math.degrees( math.atan((p2.y - p1.y) / (p2.x - p1.x))) if round(p2.x) != round(p1.x) else -90) edge_length = str( round( FreeCAD.Units.Quantity("{}mm".format( straight_edges[current_straight_edge_index]. Length)).getValueAs( rebar_dimension_units).Value, precision, )) if "." in edge_length: edge_length = edge_length.rstrip("0").rstrip(".") if include_units_in_dimension_label: edge_length += rebar_dimension_units edge_dimension_svg.append( getSVGTextElement( edge_length, mid_point.x, mid_point.y - rebar_stroke_width * 2, dimension_font_family, dimension_font_size, "middle", )) edge_dimension_svg[-1].set( "transform", "rotate({} {} {})".format( dimension_rotation, round(mid_point.x), round(mid_point.y), ), ) current_straight_edge_index += 1 if (0 <= edge_index - 1 and DraftGeomUtils.geomType( edges[edge_index - 1]) == "Line"): radius = max(fillet_radius, dimension_font_size * 0.8) bent_angle_svg = getEdgesAngleSVG( edges[edge_index - 1], edge, radius, view_plane, dimension_font_family, dimension_font_size * 0.8, bent_angle_dimension_exclude_list, 0.2 / scale, ) edge_dimension_svg.append(bent_angle_svg) elif DraftGeomUtils.geomType(edge) == "Circle": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) if round(p1.x) == round(p2.x) or round(p1.y) == round(p2.y): edge_svg = getLineSVG(p1, p2, rebar_stroke_width, rebar_color) else: edge_svg = getRoundEdgeSVG(edge, view_plane, rebar_stroke_width, rebar_color) if include_dimensions: # Create bent angle svg if 0 <= edge_index - 1 and edge_index + 1 < len(edges): prev_edge = edges[edge_index - 1] next_edge = edges[edge_index + 1] if (DraftGeomUtils.geomType(prev_edge) == DraftGeomUtils.geomType(next_edge) == "Line"): radius = max(fillet_radius, dimension_font_size * 0.8) bent_angle_svg = getEdgesAngleSVG( prev_edge, next_edge, radius, view_plane, dimension_font_family, dimension_font_size * 0.8, bent_angle_dimension_exclude_list, 0.2 / scale, ) edge_dimension_svg.append(bent_angle_svg) else: edge_svg = ElementTree.Element("g") rebar_edges_svg.append(edge_svg) return svg
def export(exportList, filename): """Export the OCA file with a given list of objects. The objects must be edges or faces, in order to be processed and exported. Parameters ---------- exportList : list List of document objects to export. filename : str Path to the new file. Returns ------- None If `exportList` doesn't have shapes to export. """ faces = [] edges = [] # getting faces and edges for ob in exportList: if ob.Shape.Faces: for f in ob.Shape.Faces: faces.append(f) else: for e in ob.Shape.Edges: edges.append(e) if not (edges or faces): FCC.PrintMessage( translate("importOCA", "OCA: found no data to export") + "\n") return # writing file oca = pythonopen(filename, 'w') oca.write("#oca file generated from FreeCAD\r\n") oca.write("# edges\r\n") count = 1 for e in edges: if DraftGeomUtils.geomType(e) == "Line": oca.write("L" + str(count) + "=") oca.write(writepoint(e.Vertexes[0].Point)) oca.write(" ") oca.write(writepoint(e.Vertexes[-1].Point)) oca.write("\r\n") elif DraftGeomUtils.geomType(e) == "Circle": if len(e.Vertexes) > 1: oca.write("C" + str(count) + "=ARC ") oca.write(writepoint(e.Vertexes[0].Point)) oca.write(" ") oca.write(writepoint(DraftGeomUtils.findMidpoint(e))) oca.write(" ") oca.write(writepoint(e.Vertexes[-1].Point)) else: oca.write("C" + str(count) + "= ") oca.write(writepoint(e.Curve.Center)) oca.write(" ") oca.write(str(e.Curve.Radius)) oca.write("\r\n") count += 1 oca.write("# faces\r\n") for f in faces: oca.write("A" + str(count) + "=S(POL") for v in f.Vertexes: oca.write(" ") oca.write(writepoint(v.Point)) oca.write(" ") oca.write(writepoint(f.Vertexes[0].Point)) oca.write(")\r\n") count += 1 # closing oca.close() FCC.PrintMessage( translate("importOCA", "successfully exported") + " " + filename + "\n")
def snap(self,screenpos,lastpoint=None,active=True,constrain=False,noTracker=False): """snap(screenpos,lastpoint=None,active=True,constrain=False,noTracker=False): returns a snapped point from the given (x,y) screenpos (the position of the mouse cursor), active is to activate active point snapping or not (passive), lastpoint is an optional other point used to draw an imaginary segment and get additional snap locations. Constrain can be True to constrain the point against the closest working plane axis. Screenpos can be a list, a tuple or a coin.SbVec2s object. If noTracker is True, the tracking line is not displayed.""" global Part, DraftGeomUtils import Part, DraftGeomUtils if not hasattr(self,"toolbar"): self.makeSnapToolBar() mw = getMainWindow() bt = mw.findChild(QtGui.QToolBar,"Draft Snap") if not bt: mw.addToolBar(self.toolbar) else: if Draft.getParam("showSnapBar"): bt.show() def cstr(point): "constrains if needed" if constrain or self.mask: fpt = self.constrain(point,lastpoint) else: self.unconstrain() fpt = point if self.radiusTracker: self.radiusTracker.update(fpt) return fpt snaps = [] self.snapInfo = None # type conversion if needed if isinstance(screenpos,list): screenpos = tuple(screenpos) elif isinstance(screenpos,coin.SbVec2s): screenpos = tuple(screenpos.getValue()) elif not isinstance(screenpos,tuple): print "snap needs valid screen position (list, tuple or sbvec2s)" return None # setup trackers if needed self.setTrackers() # getting current snap Radius self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) if self.radiusTracker: self.radiusTracker.update(self.radius) self.radiusTracker.off() # activate snap oldActive = False if Draft.getParam("alwaysSnap"): oldActive = active active = True if not self.active: active = False self.setCursor('passive') if self.tracker: self.tracker.off() if self.extLine: self.extLine.off() if self.trackLine: self.trackLine.off() point = self.getApparentPoint(screenpos[0],screenpos[1]) # setup a track line if we got a last point if lastpoint: if not self.trackLine: self.trackLine = DraftTrackers.lineTracker() self.trackLine.p1(lastpoint) # check if we snapped to something self.snapInfo = Draft.get3DView().getObjectInfo((screenpos[0],screenpos[1])) # checking if parallel to one of the edges of the last objects or to a polar direction if active: eline = None point,eline = self.snapToPolar(point,lastpoint) point,eline = self.snapToExtensions(point,lastpoint,constrain,eline) if not self.snapInfo: # nothing has been snapped, check fro grid snap if active: point = self.snapToGrid(point) fp = cstr(point) if self.trackLine and lastpoint and (not noTracker): self.trackLine.p2(fp) self.trackLine.on() return fp else: # we have an object to snap to obj = FreeCAD.ActiveDocument.getObject(self.snapInfo['Object']) if not obj: return cstr(point) self.lastSnappedObject = obj if hasattr(obj.ViewObject,"Selectable"): if not obj.ViewObject.Selectable: return cstr(point) if not active: # passive snapping snaps = [self.snapToVertex(self.snapInfo)] else: # first stick to the snapped object s = self.snapToVertex(self.snapInfo) if s: point = s[0] # active snapping comp = self.snapInfo['Component'] if (Draft.getType(obj) == "Wall") and not oldActive: edges = [] for o in [obj]+obj.Additions: if Draft.getType(o) == "Wall": if o.Base: edges.extend(o.Base.Shape.Edges) for edge in edges: snaps.extend(self.snapToEndpoints(edge)) snaps.extend(self.snapToMidpoint(edge)) snaps.extend(self.snapToPerpendicular(edge,lastpoint)) snaps.extend(self.snapToIntersection(edge)) snaps.extend(self.snapToElines(edge,eline)) elif obj.isDerivedFrom("Part::Feature"): if (not self.maxEdges) or (len(obj.Edges) <= self.maxEdges): if "Edge" in comp: # we are snapping to an edge en = int(comp[4:])-1 if len(obj.Shape.Edges) > en: edge = obj.Shape.Edges[en] snaps.extend(self.snapToEndpoints(edge)) snaps.extend(self.snapToMidpoint(edge)) snaps.extend(self.snapToPerpendicular(edge,lastpoint)) #snaps.extend(self.snapToOrtho(edge,lastpoint,constrain)) # now part of snapToPolar snaps.extend(self.snapToIntersection(edge)) snaps.extend(self.snapToElines(edge,eline)) if DraftGeomUtils.geomType(edge) == "Circle": # the edge is an arc, we have extra options snaps.extend(self.snapToAngles(edge)) snaps.extend(self.snapToCenter(edge)) elif "Vertex" in comp: # directly snapped to a vertex snaps.append(self.snapToVertex(self.snapInfo,active=True)) elif comp == '': # workaround for the new view provider snaps.append(self.snapToVertex(self.snapInfo,active=True)) else: # all other cases (face, etc...) default to passive snap snapArray = [self.snapToVertex(self.snapInfo)] elif Draft.getType(obj) == "Dimension": # for dimensions we snap to their 3 points for pt in [obj.Start,obj.End,obj.Dimline]: snaps.append([pt,'endpoint',pt]) elif Draft.getType(obj) == "Mesh": # for meshes we only snap to vertices snaps.extend(self.snapToEndpoints(obj.Mesh)) elif Draft.getType(obj) == "Points": # for points we only snap to points snaps.extend(self.snapToEndpoints(obj.Points)) # updating last objects list if not self.lastObj[1]: self.lastObj[1] = obj.Name elif self.lastObj[1] != obj.Name: self.lastObj[0] = self.lastObj[1] self.lastObj[1] = obj.Name if not snaps: return cstr(point) # calculating the nearest snap point shortest = 1000000000000000000 origin = Vector(self.snapInfo['x'],self.snapInfo['y'],self.snapInfo['z']) winner = [Vector(0,0,0),None,Vector(0,0,0)] for snap in snaps: if (not snap) or (snap[0] == None): print "debug: Snapper: invalid snap point: ",snaps else: delta = snap[0].sub(origin) if delta.Length < shortest: shortest = delta.Length winner = snap # see if we are out of the max radius, if any if self.radius: dv = point.sub(winner[2]) if (dv.Length > self.radius): if (not oldActive) and self.isEnabled("passive"): winner = self.snapToVertex(self.snapInfo) # setting the cursors if self.tracker: self.tracker.setCoords(winner[2]) self.tracker.setMarker(self.mk[winner[1]]) self.tracker.on() # setting the trackline fp = cstr(winner[2]) if self.trackLine and lastpoint: self.trackLine.p2(fp) self.trackLine.on() # set the cursor self.setCursor(winner[1]) # return the final point return fp
def execute(self,obj): if obj.Base: tool = PathUtils.getLastTool(obj) if tool: radius = tool.Diameter/2 if radius < 0:# safe guard radius -= radius else: # temporary value, to be taken from the properties later on radius = 1 import Part, DraftGeomUtils if "Face" in obj.Base[1][0]: shape = getattr(obj.Base[0].Shape,obj.Base[1][0]) else: edges = [getattr(obj.Base[0].Shape,sub) for sub in obj.Base[1]] shape = Part.Wire(edges) print len(edges) # absolute coords, millimeters, cancel offsets output = "G90\nG21\nG40\n" # save tool if obj.ToolNumber > 0 and tool.ToolNumber != obj.ToolNumber: output += "M06 T" + str(tool.ToolNumber) + "\n" # build offsets offsets = [] nextradius = radius result = DraftGeomUtils.pocket2d(shape,nextradius) while result: #print "Adding " + str(len(result)) + " wires" offsets.extend(result) nextradius += radius result = DraftGeomUtils.pocket2d(shape,nextradius) # first move will be rapid, subsequent will be at feed rate first = True startPoint = None fastZPos = max(obj.StartDepth + 2, obj.RetractHeight) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() # print "startDepth: " + str(obj.StartDepth) # print "finalDepth: " + str(obj.FinalDepth) # print "stepDown: " + str(obj.StepDown) # print "finishDepth" + str(obj.FinishDepth) # print "offsets:", len(offsets) def prnt(vlu): return str("%.4f" % round(vlu, 4)) #Fraction of tool radius our plunge helix is to be #FIXME: This should be configurable plungeR = 0.75 #(minimum) Fraction of tool DIAMETER to go back and forth while ramp-plunging #FIXME: This should be configurable #FIXME: The ramp plunging should maybe even be limited to this distance; I don't know what's best rampD = 0.75 #Total offset from the desired pocket edge is tool radius plus the plunge helix radius #Any point on these curves could be the center of a plunge helixBounds = DraftGeomUtils.pocket2d(shape, tool.Diameter / 2. * (1 + plungeR)) #Try to find a location to nicely plunge, starting with a helix, then ramp #Can't do it without knowledge of a tool plungePos = None rampEdge = None if not tool: raise Error("Ramp plunge location-finding requires a tool") return else: #Since we're going to start machining either the inner-most #edge or the outer (depending on StartAt setting), try to #plunge near that location if helixBounds: #Edge is easy- pick a point on helixBounds and go with it if obj.StartAt == 'Edge': plungePos = helixBounds[0].Edges[0].Vertexes[0].Point #Center is harder- use a point from the first offset, check if it works else: plungePos = offsets[0].Edges[0].Vertexes[0].Point #If it turns out this is invalid for some reason, nuke plungePos [perp,idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges) if not perp or perp.Length < tool.Diameter / 2. * (1 + plungeR): plungePos = None #FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds #Or some math to prove that it has to be (doubt that's true) #Maybe reverse helixBounds and pick off that? #If we didn't find a place to helix, how about a ramp? if not plungePos: #Check first edge of our offsets if (offsets[0].Edges[0].Length >= tool.Diameter * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)): rampEdge = offsets[0].Edges[0] #The last edge also connects with the starting location- try that elif (offsets[0].Edges[-1].Length >= tool.Diameter * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)): rampEdge = offsets[0].Edges[-1] else: print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) #FIXME: There's got to be a smarter way to find a place to ramp #Returns gcode to perform a rapid move def rapid(x=None, y=None, z=None): retstr = "G00" if (x != None) or (y != None) or (z != None): if (x != None): retstr += " X" + str("%.4f" % x) if (y != None): retstr += " Y" + str("%.4f" % y) if (z != None): retstr += " Z" + str("%.4f" % z) else: return "" return retstr + "\n" #Returns gcode to perform a linear feed def feed(x=None, y=None, z=None): global feedxy retstr = "G01 F" if(x == None) and (y == None): retstr += str("%.4f" % obj.HorizFeed) else: retstr += str("%.4f" % obj.VertFeed) if (x != None) or (y != None) or (z != None): if (x != None): retstr += " X" + str("%.4f" % x) if (y != None): retstr += " Y" + str("%.4f" % y) if (z != None): retstr += " Z" + str("%.4f" % z) else: return "" return retstr + "\n" #Returns gcode to perform an arc #Assumes XY plane or helix around Z #Don't worry about starting Z- assume that's dealt with elsewhere def arc(cx, cy, sx, sy, ex, ey, ez=None, ccw=False): #If start/end radii aren't within eps, abort eps = 0.01 if (math.sqrt((cx - sx)**2 + (cy - sy)**2) - math.sqrt((cx - ex)**2 + (cy - ey)**2)) >= eps: print "ERROR: Illegal arc: Stand and end radii not equal" return "" #Set [C]CW and feed retstr = "" if ccw: retstr += "G03 F" else: retstr += "G02 F" retstr += str(obj.HorizFeed) #End location retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey) #Helix if requested if ez != None: retstr += " Z" + str("%.4f" % ez) #Append center offsets retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy)) return retstr + "\n" #Returns gcode to helically plunge #destZ is the milling level #startZ is the height we can safely feed down to before helix-ing def helicalPlunge(plungePos, rampangle, destZ, startZ): helixCmds = "(START HELICAL PLUNGE)\n" if(plungePos == None): raise Error("Helical plunging requires a position!") return None if(not tool): raise Error("Helical plunging requires a tool!") return None helixX = plungePos.x + tool.Diameter/2. * plungeR helixY = plungePos.y; helixCirc = math.pi * tool.Diameter * plungeR dzPerRev = math.sin(rampangle/180. * math.pi) * helixCirc #Go to the start of the helix position helixCmds += rapid(helixX, helixY) helixCmds += rapid(z=startZ) #Helix as required to get to the requested depth lastZ = startZ curZ = max(startZ-dzPerRev, destZ) done = False while not done: done = (curZ == destZ) #NOTE: FreeCAD doesn't render this, but at least LinuxCNC considers it valid #helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX, helixY, ez = curZ, ccw=True) #Use two half-helixes; FreeCAD renders that correctly, #and it fits with the other code breaking up 360-degree arcs helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - tool.Diameter * plungeR, helixY, ez = (curZ + lastZ)/2., ccw=True) helixCmds += arc(plungePos.x, plungePos.y, helixX - tool.Diameter * plungeR, helixY, helixX, helixY, ez = curZ, ccw=True) lastZ = curZ curZ = max(curZ - dzPerRev, destZ) return helixCmds #Returns commands to linearly ramp into a cut #FIXME: This ramps along the first edge, assuming it's long #enough, NOT just wiggling back and forth by ~0.75 * toolD. #Not sure if that's any worse, but it's simpler #FIXME: This code is untested def rampPlunge(edge, rampangle, destZ, startZ): rampCmds = "(START RAMP PLUNGE)\n" if(edge == None): raise Error("Ramp plunging requires an edge!") return None if(not tool): raise Error("Ramp plunging requires a tool!") sPoint = edge.Vertexes[0].Point ePoint = edge.Vertexes[1].Point #Evidently edges can get flipped- pick the right one in this case #FIXME: This is iffy code, based on what already existed in the "for vpos ..." loop below if ePoint == sPoint: #print "FLIP" ePoint = edge.Vertexes[-1].Point #print "Start: " + str(sPoint) + " End: " + str(ePoint) + " Zhigh: " + prnt(startZ) + " ZLow: " + prnt(destZ) rampDist = edge.Length rampDZ = math.sin(rampangle/180. * math.pi) * rampDist rampCmds += rapid(sPoint.x, sPoint.y) rampCmds += rapid(z=startZ) #Ramp down to the requested depth #FIXME: This might be an arc, so handle that as well lastZ = startZ curZ = max(startZ-rampDZ, destZ) done = False while not done: done = (curZ == destZ) #If it's an arc, handle it! if isinstance(edge.Curve,Part.Circle): raise Error("rampPlunge: Screw it, not handling an arc.") #Straight feed! Easy! else: rampCmds += feed(ePoint.x, ePoint.y, curZ) rampCmds += feed(sPoint.x, sPoint.y) lastZ = curZ curZ = max(curZ - rampDZ, destZ) return rampCmds #For helix-ing/ramping, know where we were last time #FIXME: Can probably get this from the "machine"? lastZ = fastZPos for vpos in frange(obj.StartDepth, obj.FinalDepth, obj.StepDown, obj.FinishDepth): # print "vpos: " + str(vpos) #Every for every depth we should helix down first = True # loop over successive wires for currentWire in offsets: # print "new line (offset)" last = None for edge in currentWire.Edges: # print "new edge" if not last: # we set the base GO to our fast move to our starting pos if first: #If we can helix, do so if plungePos: output += helicalPlunge(plungePos, 3, vpos, lastZ) #print output lastZ = vpos #Otherwise, see if we can ramp #FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) elif rampEdge: output += rampPlunge(rampEdge, 3, vpos, lastZ) lastZ = vpos #Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. #FIXME: At least not with the lazy ramp programming above... else: print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp" startPoint = edge.Vertexes[0].Point output += "G0 X" + prnt(startPoint.x) + " Y" + prnt(startPoint.y) +\ " Z" + prnt(fastZPos) + "\n" first = False #then move slow down to our starting point for our profile last = edge.Vertexes[0].Point output += "G1 X" + prnt(last.x) + " Y" + prnt(last.y) + " Z" + prnt(vpos) + "\n" #if isinstance(edge.Curve,Part.Circle): if DraftGeomUtils.geomType(edge) == "Circle": point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point # print "flipped" center = edge.Curve.Center relcenter = center.sub(last) v1 = last.sub(center) v2 = point.sub(center) if v1.cross(v2).z < 0: output += "G2" else: output += "G3" output += " X" + prnt(point.x) + " Y" + prnt(point.y) + " Z" + prnt(vpos) output += " I" + prnt(relcenter.x) + " J" +prnt(relcenter.y) + " K" + prnt(relcenter.z) output += "\n" last = point else: point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + prnt(point.x) + " Y" + prnt(point.y) + " Z" + prnt(vpos) + "\n" last = point #move back up output += "G0 Z" + prnt(fastZPos) + "\n" # print output # path = Path.Path(output) # obj.Path = path if obj.Active: path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def getPath(edges=[],wires=[],pathname=None): import Part,DraftGeomUtils svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] for w in wires: w1=w.copy() w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs=() #skipped for the first edge for edgeindex,e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' else: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG(e,drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = (getProj(c.value((c.LastParameter-\ c.FirstParameter)/2.0), plane), \ getProj(vs[-1].Point, plane)) else: endpoints = (getProj(vs[-1].Point), plane) # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -=180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' else: bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline=bspline.approximateBSpline(0.05,50, 3,'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree>3: #should not happen raise AssertionError elif bezierseg.Degree==1: edata +='L ' elif bezierseg.Degree==2: edata +='Q ' elif bezierseg.Degree==3: edata +='C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) +' '+ str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg
def proceed(self): """Proceed with execution of the command after proper selection.""" if self.call: self.view.removeEventCallback("SoEvent", self.call) sel = Gui.Selection.getSelection() if len(sel) == 2: self.trimObjects(sel) self.finish() return self.obj = sel[0] self.ui.trimUi() self.linetrack = trackers.lineTracker() import DraftGeomUtils import Part if "Shape" not in self.obj.PropertiesList: return if "Placement" in self.obj.PropertiesList: self.placement = self.obj.Placement if len(self.obj.Shape.Faces) == 1: # simple extrude mode, the object itself is extruded self.extrudeMode = True self.ghost = [trackers.ghostTracker([self.obj])] self.normal = self.obj.Shape.Faces[0].normalAt(0.5, 0.5) for v in self.obj.Shape.Vertexes: self.ghost.append(trackers.lineTracker()) elif len(self.obj.Shape.Faces) > 1: # face extrude mode, a new object is created ss = Gui.Selection.getSelectionEx()[0] if len(ss.SubObjects) == 1: if ss.SubObjects[0].ShapeType == "Face": self.obj = self.doc.addObject("Part::Feature", "Face") self.obj.Shape = ss.SubObjects[0] self.extrudeMode = True self.ghost = [trackers.ghostTracker([self.obj])] self.normal = self.obj.Shape.Faces[0].normalAt(0.5, 0.5) for v in self.obj.Shape.Vertexes: self.ghost.append(trackers.lineTracker()) else: # normal wire trimex mode self.color = self.obj.ViewObject.LineColor self.width = self.obj.ViewObject.LineWidth # self.obj.ViewObject.Visibility = False self.obj.ViewObject.LineColor = (0.5, 0.5, 0.5) self.obj.ViewObject.LineWidth = 1 self.extrudeMode = False if self.obj.Shape.Wires: self.edges = self.obj.Shape.Wires[0].Edges self.edges = Part.__sortEdges__(self.edges) else: self.edges = self.obj.Shape.Edges self.ghost = [] lc = self.color sc = (lc[0], lc[1], lc[2]) sw = self.width for e in self.edges: if DraftGeomUtils.geomType(e) == "Line": self.ghost.append( trackers.lineTracker(scolor=sc, swidth=sw)) else: self.ghost.append(trackers.arcTracker(scolor=sc, swidth=sw)) if not self.ghost: self.finish() for g in self.ghost: g.on() self.activePoint = 0 self.nodes = [] self.shift = False self.alt = False self.force = None self.cv = None self.call = self.view.addEventCallback("SoEvent", self.action) _msg(translate("draft", "Pick distance"))
def redraw(self, point, snapped=None, shift=False, alt=False, real=None): """Redraw the ghost normally.""" # initializing reverse = False for g in self.ghost: g.off() if real: newedges = [] import DraftGeomUtils import Part # finding the active point vlist = [] for e in self.edges: vlist.append(e.Vertexes[0].Point) vlist.append(self.edges[-1].Vertexes[-1].Point) if shift: npoint = self.activePoint else: npoint = DraftGeomUtils.findClosest(point, vlist) if npoint > len(self.edges) / 2: reverse = True if alt: reverse = not reverse self.activePoint = npoint # sorting out directions if reverse and (npoint > 0): npoint = npoint - 1 if (npoint > len(self.edges) - 1): edge = self.edges[-1] ghost = self.ghost[-1] else: edge = self.edges[npoint] ghost = self.ghost[npoint] if reverse: v1 = edge.Vertexes[-1].Point v2 = edge.Vertexes[0].Point else: v1 = edge.Vertexes[0].Point v2 = edge.Vertexes[-1].Point # snapping if snapped: snapped = self.doc.getObject(snapped['Object']) if hasattr(snapped, "Shape"): pts = [] for e in snapped.Shape.Edges: int = DraftGeomUtils.findIntersection(edge, e, True, True) if int: pts.extend(int) if pts: point = pts[DraftGeomUtils.findClosest(point, pts)] # modifying active edge if DraftGeomUtils.geomType(edge) == "Line": ve = DraftGeomUtils.vec(edge) chord = v1.sub(point) n = ve.cross(chord) if n.Length == 0: self.newpoint = point else: perp = ve.cross(n) proj = DraftVecUtils.project(chord, perp) self.newpoint = App.Vector.add(point, proj) dist = v1.sub(self.newpoint).Length ghost.p1(self.newpoint) ghost.p2(v2) self.ui.labelRadius.setText(translate("draft", "Distance")) self.ui.radiusValue.setToolTip( translate("draft", "The offset distance")) if real: if self.force: ray = self.newpoint.sub(v1) ray.multiply(self.force / ray.Length) self.newpoint = App.Vector.add(v1, ray) newedges.append(Part.LineSegment(self.newpoint, v2).toShape()) else: center = edge.Curve.Center rad = edge.Curve.Radius ang1 = DraftVecUtils.angle(v2.sub(center)) ang2 = DraftVecUtils.angle(point.sub(center)) _rot_rad = DraftVecUtils.rotate(App.Vector(rad, 0, 0), -ang2) self.newpoint = App.Vector.add(center, _rot_rad) self.ui.labelRadius.setText(translate("draft", "Angle")) self.ui.radiusValue.setToolTip( translate("draft", "The offset angle")) dist = math.degrees(-ang2) # if ang1 > ang2: # ang1, ang2 = ang2, ang1 # print("last calculated:", # math.degrees(-ang1), # math.degrees(-ang2)) ghost.setEndAngle(-ang2) ghost.setStartAngle(-ang1) ghost.setCenter(center) ghost.setRadius(rad) if real: if self.force: angle = math.radians(self.force) newray = DraftVecUtils.rotate(App.Vector(rad, 0, 0), -angle) self.newpoint = App.Vector.add(center, newray) chord = self.newpoint.sub(v2) perp = chord.cross(App.Vector(0, 0, 1)) scaledperp = DraftVecUtils.scaleTo(perp, rad) midpoint = App.Vector.add(center, scaledperp) _sh = Part.Arc(self.newpoint, midpoint, v2).toShape() newedges.append(_sh) ghost.on() # resetting the visible edges if not reverse: li = list(range(npoint + 1, len(self.edges))) else: li = list(range(npoint - 1, -1, -1)) for i in li: edge = self.edges[i] ghost = self.ghost[i] if DraftGeomUtils.geomType(edge) == "Line": ghost.p1(edge.Vertexes[0].Point) ghost.p2(edge.Vertexes[-1].Point) else: ang1 = DraftVecUtils.angle(edge.Vertexes[0].Point.sub(center)) ang2 = DraftVecUtils.angle(edge.Vertexes[-1].Point.sub(center)) # if ang1 > ang2: # ang1, ang2 = ang2, ang1 ghost.setEndAngle(-ang2) ghost.setStartAngle(-ang1) ghost.setCenter(edge.Curve.Center) ghost.setRadius(edge.Curve.Radius) if real: newedges.append(edge) ghost.on() # finishing if real: return newedges else: return dist
def getUShapeRebarSVGData( rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style, longitudinal_line_dia=None, ): """getUShapeRebarSVGData(UShapeRebar, ViewPlane, RebarsSVG, RebarsStrokeWidth, RebarsColorStyle, longitudinal_line_dia): Returns dictionary containing UShape rebar svg data. rebars_color_style can be: - "shape color" to select color of rebar shape - color name or hex value of color Returns dictionary format: { "svg": u_rebar_svg, "visibility": is_rebar_visible, } """ if longitudinal_line_dia is None: longitudinal_line_dia = 2 * 2 * rebars_stroke_width rebars_color = getRebarColor(rebar, rebars_color_style) u_rebar_svg = ElementTree.Element("g", attrib={"id": str(rebar.Name)}) is_rebar_visible = False drawing_plane_normal = view_plane.axis if round(drawing_plane_normal.cross(getRebarsSpanAxis(rebar)).Length) == 0: basewire = rebar.Base.Shape.Wires[0].copy() basewire.Placement = rebar.PlacementList[0].multiply( basewire.Placement) edges = Part.__sortEdges__( DraftGeomUtils.filletWire( basewire, rebar.Rounding * rebar.Diameter.Value, ).Edges) for edge in edges: if DraftGeomUtils.geomType(edge) == "Line": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) if round(p1.x) == round(p2.x) and round(p1.y) == round(p2.y): edge_svg = getPointSVG(p1, radius=longitudinal_line_dia / 2, fill=rebars_color) else: edge_svg = getLineSVG(p1, p2, rebars_stroke_width, rebars_color) if not isLineInSVG(p1, p2, rebars_svg): is_rebar_visible = True if is_rebar_visible: u_rebar_svg.append(edge_svg) is_rebar_visible = True elif DraftGeomUtils.geomType(edge) == "Circle": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) if round(p1.x) == round(p2.x) or round(p1.y) == round(p2.y): edge_svg = getLineSVG(p1, p2, rebars_stroke_width, rebars_color) if not isLineInSVG(p1, p2, rebars_svg): is_rebar_visible = True else: edge_svg = getRoundEdgeSVG(edge, view_plane, rebars_stroke_width, rebars_color) if not isRoundCornerInSVG( edge, rebar.Rounding * rebar.Diameter.Value, view_plane, rebars_svg, ): is_rebar_visible = True if is_rebar_visible: u_rebar_svg.append(edge_svg) else: basewire = rebar.Base.Shape.Wires[0] for placement in rebar.PlacementList: wire = basewire.copy() wire.Placement = placement.multiply(basewire.Placement) edges = Part.__sortEdges__( DraftGeomUtils.filletWire( wire, rebar.Rounding * rebar.Diameter.Value, ).Edges) for edge in edges: if DraftGeomUtils.geomType(edge) == "Line": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) if round(p1.x) == round(p2.x) and round(p1.y) == round( p2.y): edge_svg = getPointSVG( p1, radius=longitudinal_line_dia / 2, fill=rebars_color, ) else: edge_svg = getLineSVG(p1, p2, rebars_stroke_width, rebars_color) if not isLineInSVG(p1, p2, rebars_svg): is_rebar_visible = True if is_rebar_visible or not isLineInSVG(p1, p2, rebars_svg): u_rebar_svg.append(edge_svg) is_rebar_visible = True elif DraftGeomUtils.geomType(edge) == "Circle": p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) if round(p1.x) == round(p2.x) or round(p1.y) == round( p2.y): edge_svg = getLineSVG(p1, p2, rebars_stroke_width, rebars_color) if not isLineInSVG(p1, p2, rebars_svg): is_rebar_visible = True else: edge_svg = getRoundEdgeSVG(edge, view_plane, rebars_stroke_width, rebars_color) if not isRoundCornerInSVG( edge, rebar.Rounding * rebar.Diameter.Value, view_plane, rebars_svg, ): is_rebar_visible = True if is_rebar_visible: u_rebar_svg.append(edge_svg) return { "svg": u_rebar_svg, "visibility": is_rebar_visible, }
def getPath(edges=[], wires=[], pathname=None): svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] first = True for w in wires: w1 = w.copy() if first: first = False else: # invert further wires to create holes w1 = DraftGeomUtils.invert(w1) w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs = () #skipped for the first edge for edgeindex, e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point - previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M ' + str(v.x) + ' ' + str(v.y) + ' ' else: if (vs[0].Point - previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD, "DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0, 0, 1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal), 2) in [0, 3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG( e, drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[ 1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes ) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = [ getProj( c.value((c.LastParameter - c.FirstParameter) / 2.0), plane), getProj(vs[-1].Point, plane) ] else: endpoints = [getProj(vs[-1].Point, plane)] # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -= 180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt( e.FirstParameter + (e.LastParameter - e.FirstParameter) / 10) flag_sweep = (DraftVecUtils.angle( t1, t2, drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' ' else: bspline = e.Curve.toBSpline(e.FirstParameter, e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline = bspline.approximateBSpline( 0.05, 50, 3, 'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree > 3: #should not happen raise AssertionError elif bezierseg.Degree == 1: edata += 'L ' elif bezierseg.Degree == 2: edata += 'Q ' elif bezierseg.Degree == 3: edata += 'C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) + ' ' + str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg
def action(self, arg): """Handle the 3D scene events. This is installed as an EventCallback in the Inventor view. Parameters ---------- arg: dict Dictionary with strings that indicates the type of event received from the 3D view. """ if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection import DraftGeomUtils shift = gui_tool_utils.hasMod(arg, gui_tool_utils.MODCONSTRAIN) if self.arcmode or self.point2: gui_tool_utils.setMod(arg, gui_tool_utils.MODCONSTRAIN, False) (self.point, ctrlPoint, self.info) = gui_tool_utils.getPoint( self, arg, noTracker=(len(self.node) > 0)) if (gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT) or self.selectmode) and (len(self.node) < 3): self.dimtrack.off() if not self.altdown: self.altdown = True self.ui.switchUi(True) if hasattr(Gui, "Snapper"): Gui.Snapper.setSelectMode(True) snapped = self.view.getObjectInfo( (arg["Position"][0], arg["Position"][1])) if snapped: ob = self.doc.getObject(snapped['Object']) if "Edge" in snapped['Component']: num = int(snapped['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] v1 = ed.Vertexes[0].Point v2 = ed.Vertexes[-1].Point self.dimtrack.update([v1, v2, self.cont]) else: if self.node and (len(self.edges) < 2): self.dimtrack.on() if len(self.edges) == 2: # angular dimension self.dimtrack.off() r = self.point.sub(self.center) self.arctrack.setRadius(r.Length) a = self.arctrack.getAngle(self.point) pair = DraftGeomUtils.getBoundaryAngles(a, self.pts) if not (pair[0] < a < pair[1]): self.angledata = [ 4 * math.pi - pair[0], 2 * math.pi - pair[1] ] else: self.angledata = [ 2 * math.pi - pair[0], 2 * math.pi - pair[1] ] self.arctrack.setStartAngle(self.angledata[0]) self.arctrack.setEndAngle(self.angledata[1]) if self.altdown: self.altdown = False self.ui.switchUi(False) if hasattr(Gui, "Snapper"): Gui.Snapper.setSelectMode(False) if self.dir: _p = DraftVecUtils.project(self.point.sub(self.node[0]), self.dir) self.point = self.node[0].add(_p) if len(self.node) == 2: if self.arcmode and self.edges: cen = self.edges[0].Curve.Center rad = self.edges[0].Curve.Radius baseray = self.point.sub(cen) v2 = DraftVecUtils.scaleTo(baseray, rad) v1 = v2.negative() if shift: self.node = [cen, cen.add(v2)] self.arcmode = "radius" else: self.node = [cen.add(v1), cen.add(v2)] self.arcmode = "diameter" self.dimtrack.update(self.node) # Draw constraint tracker line. if shift and (not self.arcmode): if len(self.node) == 2: if not self.point2: self.point2 = self.node[1] else: self.node[1] = self.point2 if not self.force: _p = self.point.sub(self.node[0]) a = abs(_p.getAngle(App.DraftWorkingPlane.u)) if (a > math.pi / 4) and (a <= 0.75 * math.pi): self.force = 1 else: self.force = 2 if self.force == 1: self.node[1] = App.Vector(self.node[0].x, self.node[1].y, self.node[0].z) elif self.force == 2: self.node[1] = App.Vector(self.node[1].x, self.node[0].y, self.node[0].z) else: self.force = None if self.point2 and (len(self.node) > 1): self.node[1] = self.point2 self.point2 = None # update the dimline if self.node and not self.arcmode: self.dimtrack.update(self.node + [self.point] + [self.cont]) gui_tool_utils.redraw3DView() elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): import DraftGeomUtils if self.point: self.ui.redraw() if (not self.node) and (not self.support): gui_tool_utils.getSupport(arg) if (gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT) or self.selectmode) and (len(self.node) < 3): # print("snapped: ",self.info) if self.info: ob = self.doc.getObject(self.info['Object']) if 'Edge' in self.info['Component']: num = int( self.info['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] v1 = ed.Vertexes[0].Point v2 = ed.Vertexes[-1].Point i1 = i2 = None for i in range(len(ob.Shape.Vertexes)): if v1 == ob.Shape.Vertexes[i].Point: i1 = i if v2 == ob.Shape.Vertexes[i].Point: i2 = i if (i1 is not None) and (i2 is not None): self.indices.append(num) if not self.edges: # nothing snapped yet, we treat it # as a normal edge-snapped dimension self.node = [v1, v2] self.link = [ob, i1, i2] self.edges.append(ed) if DraftGeomUtils.geomType( ed) == "Circle": # snapped edge is an arc self.arcmode = "diameter" self.link = [ob, num] else: # there is already a snapped edge, # so we start angular dimension self.edges.append(ed) # self.node now has the 4 endpoints self.node.extend([v1, v2]) c = DraftGeomUtils.findIntersection( self.node[0], self.node[1], self.node[2], self.node[3], True, True) if c: # print("centers:",c) self.center = c[0] self.arctrack.setCenter( self.center) self.arctrack.on() for e in self.edges: for v in e.Vertexes: self.pts.append( self.arctrack.getAngle( v.Point)) self.link = [self.link[0], ob] else: _msg( translate( "draft", "Edges don't intersect!")) self.finish() return self.dimtrack.on() else: self.node.append(self.point) self.selectmode = False # print("node", self.node) self.dimtrack.update(self.node) if len(self.node) == 2: self.point2 = self.node[1] if len(self.node) == 1: self.dimtrack.on() if self.planetrack: self.planetrack.set(self.node[0]) elif len(self.node) == 2 and self.cont: self.node.append(self.cont) self.createObject() if not self.cont: self.finish() elif len(self.node) == 3: # for unlinked arc mode: # if self.arcmode: # v = self.node[1].sub(self.node[0]) # v.multiply(0.5) # cen = self.node[0].add(v) # self.node = [self.node[0], self.node[1], cen] self.createObject() if not self.cont: self.finish() elif self.angledata: self.node.append(self.point) self.createObject() self.finish()
def getIndices(obj, shape, offsetv, offsetvn): "returns a list with 2 lists: vertices and face indexes, offset with the given amount" vlist = [] vnlist = [] elist = [] flist = [] curves = None if isinstance(shape, Part.Shape): for e in shape.Edges: try: if not isinstance(e.Curve, Part.LineSegment): if not curves: if obj.isDerivedFrom("App::Link"): myshape = obj.LinkedObject.Shape.copy(False) myshape.Placement = obj.LinkPlacement else: myshape = obj.Shape.copy(False) myshape.Placement = obj.getGlobalPlacement() mesh = MeshPart.meshFromShape(Shape=myshape, LinearDeflection=0.1, AngularDeflection=0.7, Relative=True) FreeCAD.Console.PrintWarning( translate( "Arch", "Found a shape containing curves, triangulating" ) + "\n") break except: # unimplemented curve type if obj.isDerivedFrom("App::Link"): if obj.Shape: myshape = obj.Shape.copy(False) myshape.Placement = obj.LinkPlacement else: myshape = obj.Shape.copy(False) myshape.Placement = obj.getGlobalPlacement() mesh = MeshPart.meshFromShape(Shape=myshape, LinearDeflection=0.1, AngularDeflection=0.7, Relative=True) FreeCAD.Console.PrintWarning( translate( "Arch", "Found a shape containing curves, triangulating") + "\n") break elif isinstance(shape, Mesh.Mesh): curves = shape.Topology if mesh: for v in mesh.Topology[0]: vlist.append(" " + str(round(v[0], p)) + " " + str(round(v[1], p)) + " " + str(round(v[2], p))) for vn in mesh.Facets: vnlist.append(" " + str(vn.Normal[0]) + " " + str(vn.Normal[1]) + " " + str(vn.Normal[2])) for i, vn in enumerate(mesh.Topology[1]): flist.append(" " + str(vn[0] + offsetv) + "//" + str(i + offsetvn) + " " + str(vn[1] + offsetv) + "//" + str(i + offsetvn) + " " + str(vn[2] + offsetv) + "//" + str(i + offsetvn) + " ") else: if curves: for v in curves[0]: vlist.append(" " + str(round(v.x, p)) + " " + str(round(v.y, p)) + " " + str(round(v.z, p))) for f in curves[1]: fi = "" for vi in f: fi += " " + str(vi + offsetv) flist.append(fi) else: for v in shape.Vertexes: vlist.append(" " + str(round(v.X, p)) + " " + str(round(v.Y, p)) + " " + str(round(v.Z, p))) if not shape.Faces: for e in shape.Edges: if DraftGeomUtils.geomType(e) == "Line": ei = " " + str( findVert(e.Vertexes[0], shape.Vertexes) + offsetv) ei += " " + str( findVert(e.Vertexes[-1], shape.Vertexes) + offsetv) elist.append(ei) for f in shape.Faces: if len(f.Wires) > 1: # if we have holes, we triangulate tris = f.tessellate(1) for fdata in tris[1]: fi = "" for vi in fdata: vdata = Part.Vertex(tris[0][vi]) fi += " " + str( findVert(vdata, shape.Vertexes) + offsetv) flist.append(fi) else: fi = "" for e in f.OuterWire.OrderedEdges: #print(e.Vertexes[0].Point,e.Vertexes[1].Point) v = e.Vertexes[0] ind = findVert(v, shape.Vertexes) if ind is None: return None, None, None fi += " " + str(ind + offsetv) flist.append(fi) return vlist, vnlist, elist, flist
def upgrade(objects, delete=False, force=None): """upgrade(objects,delete=False,force=None) Upgrade the given object(s). Parameters ---------- objects : delete : bool If delete is True, old objects are deleted. force : string The force attribute can be used to force a certain way of upgrading. Accepted values: makeCompound, closeGroupWires, makeSolid, closeWire, turnToParts, makeFusion, makeShell, makeFaces, draftify, joinFaces, makeSketchFace, makeWires. Return ---------- Returns a dictionary containing two lists, a list of new objects and a list of objects to be deleted """ import Part import DraftGeomUtils if not isinstance(objects,list): objects = [objects] global deleteList, newList deleteList = [] addList = [] # definitions of actions to perform def turnToLine(obj): """turns an edge into a Draft line""" p1 = obj.Shape.Vertexes[0].Point p2 = obj.Shape.Vertexes[-1].Point newobj = makeLine(p1,p2) addList.append(newobj) deleteList.append(obj) return newobj def makeCompound(objectslist): """returns a compound object made from the given objects""" newobj = makeBlock(objectslist) addList.append(newobj) return newobj def closeGroupWires(groupslist): """closes every open wire in the given groups""" result = False for grp in groupslist: for obj in grp.Group: newobj = closeWire(obj) # add new objects to their respective groups if newobj: result = True grp.addObject(newobj) return result def makeSolid(obj): """turns an object into a solid, if possible""" if obj.Shape.Solids: return None sol = None try: sol = Part.makeSolid(obj.Shape) except Part.OCCError: return None else: if sol: if sol.isClosed(): newobj = App.ActiveDocument.addObject("Part::Feature","Solid") newobj.Shape = sol addList.append(newobj) deleteList.append(obj) return newobj def closeWire(obj): """closes a wire object, if possible""" if obj.Shape.Faces: return None if len(obj.Shape.Wires) != 1: return None if len(obj.Shape.Edges) == 1: return None if utils.get_type(obj) == "Wire": obj.Closed = True return True else: w = obj.Shape.Wires[0] if not w.isClosed(): edges = w.Edges p0 = w.Vertexes[0].Point p1 = w.Vertexes[-1].Point if p0 == p1: # sometimes an open wire can have its start and end points identical (OCC bug) # in that case, although it is not closed, face works... f = Part.Face(w) newobj = App.ActiveDocument.addObject("Part::Feature","Face") newobj.Shape = f else: edges.append(Part.LineSegment(p1,p0).toShape()) w = Part.Wire(Part.__sortEdges__(edges)) newobj = App.ActiveDocument.addObject("Part::Feature","Wire") newobj.Shape = w addList.append(newobj) deleteList.append(obj) return newobj else: return None def turnToParts(meshes): """turn given meshes to parts""" result = False import Arch for mesh in meshes: sh = Arch.getShapeFromMesh(mesh.Mesh) if sh: newobj = App.ActiveDocument.addObject("Part::Feature","Shell") newobj.Shape = sh addList.append(newobj) deleteList.append(mesh) result = True return result def makeFusion(obj1,obj2): """makes a Draft or Part fusion between 2 given objects""" newobj = fuse(obj1,obj2) if newobj: addList.append(newobj) return newobj return None def makeShell(objectslist): """makes a shell with the given objects""" params = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") preserveFaceColor = params.GetBool("preserveFaceColor") # True preserveFaceNames = params.GetBool("preserveFaceNames") # True faces = [] facecolors = [[], []] if (preserveFaceColor) else None for obj in objectslist: faces.extend(obj.Shape.Faces) if (preserveFaceColor): """ at this point, obj.Shape.Faces are not in same order as the original faces we might have gotten as a result of downgrade, nor do they have the same hashCode(); but they still keep reference to their original colors - capture that in facecolors. Also, cannot w/ .ShapeColor here, need a whole array matching the colors of the array of faces per object, only DiffuseColor has that """ facecolors[0].extend(obj.ViewObject.DiffuseColor) facecolors[1] = faces sh = Part.makeShell(faces) if sh: if sh.Faces: newobj = App.ActiveDocument.addObject("Part::Feature","Shell") newobj.Shape = sh if (preserveFaceNames): import re firstName = objectslist[0].Label nameNoTrailNumbers = re.sub("\d+$", "", firstName) newobj.Label = "{} {}".format(newobj.Label, nameNoTrailNumbers) if (preserveFaceColor): """ At this point, sh.Faces are completely new, with different hashCodes and different ordering from obj.Shape.Faces; since we cannot compare via hashCode(), we have to iterate and use a different criteria to find the original matching color """ colarray = [] for ind, face in enumerate(newobj.Shape.Faces): for fcind, fcface in enumerate(facecolors[1]): if ((face.Area == fcface.Area) and (face.CenterOfMass == fcface.CenterOfMass)): colarray.append(facecolors[0][fcind]) break newobj.ViewObject.DiffuseColor = colarray; addList.append(newobj) deleteList.extend(objectslist) return newobj return None def joinFaces(objectslist): """makes one big face from selected objects, if possible""" faces = [] for obj in objectslist: faces.extend(obj.Shape.Faces) u = faces.pop(0) for f in faces: u = u.fuse(f) if DraftGeomUtils.isCoplanar(faces): u = DraftGeomUtils.concatenate(u) if not DraftGeomUtils.hasCurves(u): # several coplanar and non-curved faces: they can become a Draft wire newobj = makeWire(u.Wires[0],closed=True,face=True) else: # if not possible, we do a non-parametric union newobj = App.ActiveDocument.addObject("Part::Feature","Union") newobj.Shape = u addList.append(newobj) deleteList.extend(objectslist) return newobj return None def makeSketchFace(obj): """Makes a Draft face out of a sketch""" newobj = makeWire(obj.Shape,closed=True) if newobj: newobj.Base = obj obj.ViewObject.Visibility = False addList.append(newobj) return newobj return None def makeFaces(objectslist): """make a face from every closed wire in the list""" result = False for o in objectslist: for w in o.Shape.Wires: try: f = Part.Face(w) except Part.OCCError: pass else: newobj = App.ActiveDocument.addObject("Part::Feature","Face") newobj.Shape = f addList.append(newobj) result = True if not o in deleteList: deleteList.append(o) return result def makeWires(objectslist): """joins edges in the given objects list into wires""" edges = [] for o in objectslist: for e in o.Shape.Edges: edges.append(e) try: nedges = Part.__sortEdges__(edges[:]) # for e in nedges: print("debug: ",e.Curve,e.Vertexes[0].Point,e.Vertexes[-1].Point) w = Part.Wire(nedges) except Part.OCCError: return None else: if len(w.Edges) == len(edges): newobj = App.ActiveDocument.addObject("Part::Feature","Wire") newobj.Shape = w addList.append(newobj) deleteList.extend(objectslist) return True return None # analyzing what we have in our selection edges = [] wires = [] openwires = [] faces = [] groups = [] parts = [] curves = [] facewires = [] loneedges = [] meshes = [] for ob in objects: if ob.TypeId == "App::DocumentObjectGroup": groups.append(ob) elif hasattr(ob,'Shape'): parts.append(ob) faces.extend(ob.Shape.Faces) wires.extend(ob.Shape.Wires) edges.extend(ob.Shape.Edges) for f in ob.Shape.Faces: facewires.extend(f.Wires) wirededges = [] for w in ob.Shape.Wires: if len(w.Edges) > 1: for e in w.Edges: wirededges.append(e.hashCode()) if not w.isClosed(): openwires.append(w) for e in ob.Shape.Edges: if DraftGeomUtils.geomType(e) != "Line": curves.append(e) if not e.hashCode() in wirededges: loneedges.append(e) elif ob.isDerivedFrom("Mesh::Feature"): meshes.append(ob) objects = parts #print("objects:",objects," edges:",edges," wires:",wires," openwires:",openwires," faces:",faces) #print("groups:",groups," curves:",curves," facewires:",facewires, "loneedges:", loneedges) if force: if force in ["makeCompound","closeGroupWires","makeSolid","closeWire","turnToParts","makeFusion", "makeShell","makeFaces","draftify","joinFaces","makeSketchFace","makeWires","turnToLine"]: result = eval(force)(objects) else: App.Console.PrintMessage(_tr("Upgrade: Unknown force method:")+" "+force) result = None else: # applying transformations automatically result = None # if we have a group: turn each closed wire inside into a face if groups: result = closeGroupWires(groups) if result: App.Console.PrintMessage(_tr("Found groups: closing each open object inside")+"\n") # if we have meshes, we try to turn them into shapes elif meshes: result = turnToParts(meshes) if result: App.Console.PrintMessage(_tr("Found mesh(es): turning into Part shapes")+"\n") # we have only faces here, no lone edges elif faces and (len(wires) + len(openwires) == len(facewires)): # we have one shell: we try to make a solid if (len(objects) == 1) and (len(faces) > 3): result = makeSolid(objects[0]) if result: App.Console.PrintMessage(_tr("Found 1 solidifiable object: solidifying it")+"\n") # we have exactly 2 objects: we fuse them elif (len(objects) == 2) and (not curves): result = makeFusion(objects[0],objects[1]) if result: App.Console.PrintMessage(_tr("Found 2 objects: fusing them")+"\n") # we have many separate faces: we try to make a shell elif (len(objects) > 2) and (len(faces) > 1) and (not loneedges): result = makeShell(objects) if result: App.Console.PrintMessage(_tr("Found several objects: creating a shell")+"\n") # we have faces: we try to join them if they are coplanar elif len(faces) > 1: result = joinFaces(objects) if result: App.Console.PrintMessage(_tr("Found several coplanar objects or faces: creating one face")+"\n") # only one object: if not parametric, we "draftify" it elif len(objects) == 1 and (not objects[0].isDerivedFrom("Part::Part2DObjectPython")): result = draftify(objects[0]) if result: App.Console.PrintMessage(_tr("Found 1 non-parametric objects: draftifying it")+"\n") # we have only one object that contains one edge elif (not faces) and (len(objects) == 1) and (len(edges) == 1): # we have a closed sketch: Extract a face if objects[0].isDerivedFrom("Sketcher::SketchObject") and (len(edges[0].Vertexes) == 1): result = makeSketchFace(objects[0]) if result: App.Console.PrintMessage(_tr("Found 1 closed sketch object: creating a face from it")+"\n") else: # turn to Draft line e = objects[0].Shape.Edges[0] if isinstance(e.Curve,(Part.LineSegment,Part.Line)): result = turnToLine(objects[0]) if result: App.Console.PrintMessage(_tr("Found 1 linear object: converting to line")+"\n") # we have only closed wires, no faces elif wires and (not faces) and (not openwires): # we have a sketch: Extract a face if (len(objects) == 1) and objects[0].isDerivedFrom("Sketcher::SketchObject"): result = makeSketchFace(objects[0]) if result: App.Console.PrintMessage(_tr("Found 1 closed sketch object: creating a face from it")+"\n") # only closed wires else: result = makeFaces(objects) if result: App.Console.PrintMessage(_tr("Found closed wires: creating faces")+"\n") # special case, we have only one open wire. We close it, unless it has only 1 edge!" elif (len(openwires) == 1) and (not faces) and (not loneedges): result = closeWire(objects[0]) if result: App.Console.PrintMessage(_tr("Found 1 open wire: closing it")+"\n") # only open wires and edges: we try to join their edges elif openwires and (not wires) and (not faces): result = makeWires(objects) if result: App.Console.PrintMessage(_tr("Found several open wires: joining them")+"\n") # only loneedges: we try to join them elif loneedges and (not facewires): result = makeWires(objects) if result: App.Console.PrintMessage(_tr("Found several edges: wiring them")+"\n") # all other cases, if more than 1 object, make a compound elif (len(objects) > 1): result = makeCompound(objects) if result: App.Console.PrintMessage(_tr("Found several non-treatable objects: creating compound")+"\n") # no result has been obtained if not result: App.Console.PrintMessage(_tr("Unable to upgrade these objects.")+"\n") if delete: names = [] for o in deleteList: names.append(o.Name) deleteList = [] for n in names: App.ActiveDocument.removeObject(n) gui_utils.select(addList) return [addList,deleteList]
def buildpathocc(self, obj, shape): """Build pocket Path using Native OCC algorithm.""" import Part import DraftGeomUtils from PathScripts.PathUtils import fmt, helicalPlunge, rampPlunge, depth_params FreeCAD.Console.PrintMessage(translate("PathPocket", "Generating toolpath with OCC native offsets.\n")) extraoffset = obj.MaterialAllowance.Value # Build up the offset loops output = "" if obj.Comment != "": output += '(' + str(obj.Comment)+')\n' output += 'G0 Z' + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" offsets = [] nextradius = self.radius + extraoffset result = DraftGeomUtils.pocket2d(shape, nextradius) while result: offsets.extend(result) nextradius += (self.radius * 2) * (float(obj.StepOver)/100) result = DraftGeomUtils.pocket2d(shape, nextradius) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() plungePos = None rampEdge = None if obj.UseEntry: # Try to find an entry location toold = self.radius*2 helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + obj.HelixSize)) if helixBounds: rampD = obj.RampSize if obj.StartAt == 'Edge': plungePos = helixBounds[0].Edges[0].Vertexes[0].Point else: plungePos = offsets[0].Edges[0].Vertexes[0].Point # If it turns out this is invalid for some reason, nuke plungePos [perp, idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges) if not perp or perp.Length < self.radius * (1 + obj.HelixSize): plungePos = None FreeCAD.Console.PrintError(translate("PathPocket", "Helical Entry location not found.\n")) # FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds # Or some math to prove that it has to be (doubt that's true) # Maybe reverse helixBounds and pick off that? if plungePos is None: # If we didn't find a place to helix, how about a ramp? FreeCAD.Console.PrintMessage(translate("PathPocket", "Attempting ramp entry.\n")) if (offsets[0].Edges[0].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)): rampEdge = offsets[0].Edges[0] # The last edge also connects with the starting location- try that elif (offsets[0].Edges[-1].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)): rampEdge = offsets[0].Edges[-1] else: FreeCAD.Console.PrintError(translate("PathPocket", "Ramp Entry location not found.\n")) # print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) # FIXME: There's got to be a smarter way to find a place to ramp # For helix-ing/ramping, know where we were last time # FIXME: Can probably get this from the "machine"? lastZ = obj.ClearanceHeight.Value startPoint = None depthparams = depth_params( obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) for vpos in depthparams.get_depths(): first = True # loop over successive wires for currentWire in offsets: last = None for edge in currentWire.Edges: if not last: # we set the base GO to our fast move to our starting pos if first: # If we can helix, do so if plungePos: output += helicalPlunge(plungePos, obj.RampAngle, vpos, lastZ, self.radius*2, obj.HelixSize, self.horizFeed) lastZ = vpos # Otherwise, see if we can ramp # FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) elif rampEdge: output += rampPlunge(rampEdge, obj.RampAngle, vpos, lastZ) lastZ = vpos # Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. # FIXME: At least not with the lazy ramp programming above... else: print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp" startPoint = edge.Vertexes[0].Point output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" output += "G0 X" + fmt(startPoint.x) + " Y" + fmt(startPoint.y) +\ " Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) + "\n" first = False # then move slow down to our starting point for our profile last = edge.Vertexes[0].Point output += "G1 X" + fmt(last.x) + " Y" + fmt(last.y) + " Z" + fmt(vpos) + " F" + fmt(self.vertFeed) + "\n" if DraftGeomUtils.geomType(edge) == "Circle": point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point center = edge.Curve.Center relcenter = center.sub(last) v1 = last.sub(center) v2 = point.sub(center) if v1.cross(v2).z < 0: output += "G2" else: output += "G3" output += " X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) output += " I" + fmt(relcenter.x) + " J" + fmt(relcenter.y) + " K" + fmt(relcenter.z) + " F" + fmt(self.horizFeed) output += "\n" last = point else: point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) + " F" + fmt(self.horizFeed) + "\n" last = point # move back up output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" return output
def buildpathocc(self, obj, shape): """Build pocket Path using Native OCC algorithm.""" import Part import DraftGeomUtils from PathScripts.PathUtils import fmt, helicalPlunge, rampPlunge FreeCAD.Console.PrintMessage( translate("PathPocket", "Generating toolpath with OCC native offsets.\n")) # Build up the offset loops output = "" offsets = [] nextradius = (self.radius * 2) * (float(obj.StepOver) / 100) result = DraftGeomUtils.pocket2d(shape, nextradius) print "did we get something: " + str(result) while result: print "Adding " + str(len(result)) + " wires" offsets.extend(result) nextradius += (self.radius * 2) * (float(obj.StepOver) / 100) result = DraftGeomUtils.pocket2d(shape, nextradius) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() plungePos = None rampEdge = None if obj.UseEntry: # Try to find an entry location toold = self.radius * 2 helixBounds = DraftGeomUtils.pocket2d( shape, self.radius * (1 + obj.HelixSize)) if helixBounds: rampD = obj.RampSize if obj.StartAt == 'Edge': plungePos = helixBounds[0].Edges[0].Vertexes[0].Point else: plungePos = offsets[0].Edges[0].Vertexes[0].Point # If it turns out this is invalid for some reason, nuke plungePos [perp, idx] = DraftGeomUtils.findPerpendicular( plungePos, shape.Edges) if not perp or perp.Length < self.radius * (1 + obj.HelixSize): plungePos = None FreeCAD.Console.PrintError( translate("PathPocket", "Helical Entry location not found.\n")) # FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds # Or some math to prove that it has to be (doubt that's true) # Maybe reverse helixBounds and pick off that? if plungePos is None: # If we didn't find a place to helix, how about a ramp? FreeCAD.Console.PrintMessage( translate("PathPocket", "Attempting ramp entry.\n")) if (offsets[0].Edges[0].Length >= toold * rampD) and not ( isinstance(offsets[0].Edges[0].Curve, Part.Circle)): rampEdge = offsets[0].Edges[0] # The last edge also connects with the starting location- try that elif (offsets[0].Edges[-1].Length >= toold * rampD) and not ( isinstance(offsets[0].Edges[-1].Curve, Part.Circle)): rampEdge = offsets[0].Edges[-1] else: FreeCAD.Console.PrintError( translate("PathPocket", "Ramp Entry location not found.\n")) # print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) # FIXME: There's got to be a smarter way to find a place to ramp fastZPos = obj.ClearanceHeight.Value # For helix-ing/ramping, know where we were last time # FIXME: Can probably get this from the "machine"? lastZ = fastZPos startPoint = None for vpos in PathUtils.frange(obj.StartDepth.Value, obj.FinalDepth.Value, obj.StepDown, obj.FinishDepth.Value): first = True # loop over successive wires for currentWire in offsets: #output += PathUtils.convert(currentWire.Edges, "on", 1) last = None for edge in currentWire.Edges: if not last: # we set the base GO to our fast move to our starting pos if first: # If we can helix, do so if plungePos: output += helicalPlunge( plungePos, obj.RampAngle, vpos, lastZ, self.radius * 2, obj.HelixSize, self.horizFeed) # print output lastZ = vpos # Otherwise, see if we can ramp # FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) elif rampEdge: output += rampPlunge(rampEdge, obj.RampAngle, vpos, lastZ) lastZ = vpos # Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. # FIXME: At least not with the lazy ramp programming above... else: print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp" startPoint = edge.Vertexes[0].Point output += "G0 X" + fmt(startPoint.x) + " Y" + fmt(startPoint.y) +\ " Z" + fmt(fastZPos) + "\n" first = False # then move slow down to our starting point for our profile last = edge.Vertexes[0].Point output += "G1 X" + fmt(last.x) + " Y" + fmt( last.y) + " Z" + fmt(vpos) + "\n" # if isinstance(edge.Curve,Part.Circle): if DraftGeomUtils.geomType(edge) == "Circle": point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point center = edge.Curve.Center relcenter = center.sub(last) v1 = last.sub(center) v2 = point.sub(center) if v1.cross(v2).z < 0: output += "G2" else: output += "G3" output += " X" + fmt(point.x) + " Y" + fmt( point.y) + " Z" + fmt(vpos) output += " I" + fmt(relcenter.x) + " J" + fmt( relcenter.y) + " K" + fmt(relcenter.z) output += "\n" last = point else: point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + fmt(point.x) + " Y" + fmt( point.y) + " Z" + fmt(vpos) + "\n" last = point # move back up output += "G1 Z" + fmt(fastZPos) + "\n" return output
def snapToExtensions(self,point,last,constrain,eline): "returns a point snapped to extension or parallel line to last object, if any" if self.isEnabled("extension"): tsnap = self.snapToExtOrtho(last,constrain,eline) if tsnap: if (tsnap[0].sub(point)).Length < self.radius: if self.tracker: self.tracker.setCoords(tsnap[2]) self.tracker.setMarker(self.mk[tsnap[1]]) self.tracker.on() if self.extLine: self.extLine.p2(tsnap[2]) self.extLine.on() self.setCursor(tsnap[1]) return tsnap[2],eline else: tsnap = self.snapToExtPerpendicular(last) if tsnap: if (tsnap[0].sub(point)).Length < self.radius: if self.tracker: self.tracker.setCoords(tsnap[2]) self.tracker.setMarker(self.mk[tsnap[1]]) self.tracker.on() if self.extLine: self.extLine.p2(tsnap[2]) self.extLine.on() self.setCursor(tsnap[1]) return tsnap[2],eline for o in [self.lastObj[1],self.lastObj[0]]: if o: ob = FreeCAD.ActiveDocument.getObject(o) if ob: if ob.isDerivedFrom("Part::Feature"): edges = ob.Shape.Edges if (not self.maxEdges) or (len(edges) <= self.maxEdges): for e in edges: if DraftGeomUtils.geomType(e) == "Line": np = self.getPerpendicular(e,point) if not DraftGeomUtils.isPtOnEdge(np,e): if (np.sub(point)).Length < self.radius: if self.isEnabled('extension'): if np != e.Vertexes[0].Point: if self.tracker: self.tracker.setCoords(np) self.tracker.setMarker(self.mk['extension']) self.tracker.on() if self.extLine: self.extLine.p1(e.Vertexes[0].Point) self.extLine.p2(np) self.extLine.on() self.setCursor('extension') return np,Part.Line(e.Vertexes[0].Point,np).toShape() else: if self.isEnabled('parallel'): if last: ve = DraftGeomUtils.vec(e) if not DraftVecUtils.isNull(ve): de = Part.Line(last,last.add(ve)).toShape() np = self.getPerpendicular(de,point) if (np.sub(point)).Length < self.radius: if self.tracker: self.tracker.setCoords(np) self.tracker.setMarker(self.mk['parallel']) self.tracker.on() self.setCursor('parallel') return np,de return point,eline