def _sort_edges(edges): ''' Sort a continuous path of edges ''' path_shapes = [] for path_edge in edges: path_shapes.append(path_edge.toShape()) result = Part.sortEdges(path_shapes)[0] #if the first sorted edge is not the same as the first drawn edge, #reverse the sorted list if not path_shapes[0].Vertexes[0] in result[0].Vertexes: for _x in range(0, len(edges)): edges[_x].reverse() path_shapes = [] for path_edge in edges: path_shapes.append(path_edge.toShape()) result = Part.sortEdges(path_shapes)[0] return result
def execute(self, obj): e, w = self.getShape(obj) params = [] if hasattr(obj, "Values"): params = self.parse_values(e, obj.Values) if params == []: if w: obj.Shape = obj.Source[0].Shape else: obj.Shape = e return if params[0] > e.FirstParameter: params.insert(0, e.FirstParameter) if params[-1] < e.LastParameter: params.append(e.LastParameter) if w: # No subshape given, take wire 1 edges = w.Edges for i in range(len(params)): p = e.valueAt(params[i]) d, pts, info = Part.Vertex(p).distToShape(w) #print(info) if info[0][3] == "Edge": n = info[0][4] nw = w.Edges[n].split(info[0][5]) nw.Placement = w.Edges[n].Placement if len(nw.Edges) == 2: edges[n] = nw.Edges[0] edges.insert(n + 1, nw.Edges[1]) #print([e.Length for e in edges]) se = Part.sortEdges(edges) if len(se) > 1: FreeCAD.Console.PrintError( "Split curve : failed to build temp Wire !") #print(se) w = Part.Wire(se[0]) else: edges = [] for i in range(len(params) - 1): c = e.Curve.trim(params[i], params[i + 1]) edges.append(c.toShape()) se = Part.sortEdges(edges) if len(se) > 1: FreeCAD.Console.PrintError( "Split curve : failed to build final Wire !") wires = [] for el in se: wires.append(Part.Wire(el)) w = Part.Compound(wires) else: w = Part.Wire(se[0]) if w.isValid(): obj.Shape = w else: FreeCAD.Console.PrintError("Split curve : Invalid Wire !") obj.Shape = e
def opExecute(self, obj): PathLog.track(obj.Label) (depth, offset) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool) PathLog.track(obj.Label, depth, offset) self.basewires = [] self.adjusted_basewires = [] wires = [] for base, subs in obj.Base: edges = [] basewires = [] for f in subs: sub = base.Shape.getElement(f) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: basewires.extend(sub.Wires) else: basewires.append(Part.Wire(sub.Edges)) self.edges = edges for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) self.basewires.extend(basewires) for w in self.adjustWirePlacement(obj, base, basewires): self.adjusted_basewires.append(w) wire = PathOpTools.offsetWire(w, base.Shape, offset, True) if wire: wires.append(wire) self.wires = wires self.buildpathocc(obj, wires, [depth], True)
def get_face_boundary_rectangle(face): """Returns the 3D boundary wire of the 2D boundingbox of a face Input : Topo face Output : Topo Wire""" lines = get_face_2d_boundary(face) edges = [l.toShape(face.Surface) for l in lines] return Part.Wire(Part.sortEdges(edges)[0])
def map_shape(face, shape, transfer): """ mapped_shape = map_shape(face, shapes, transfer) Maps the shape on the target face transfer is a nurbs rectangle that has the same parameters as the target face. shape is projected onto transfer, to get the 2D geometry. """ proj = transfer.project(shape.Edges) new_edges = [] for e in proj.Edges: try: c2d, fp, lp = transfer.curveOnSurface(e) ne = c2d.toShape(face.Surface, fp, lp) new_edges.append(ne) except TypeError: debug("Failed to get 2D curve") #sorted_edges = Part.sortEdges(new_edges) #wirelist = [Part.Wire(el) for el in sorted_edges] if len(new_edges) == 0: return [] else: se = Part.sortEdges(new_edges) if len(se) > 1: wires = [] for el in se: wires.append(Part.Wire(el)) return Part.Compound(wires) else: return Part.Wire(se[0])
def extendWire(feature, wire, length): '''extendWire(wire, length) ... return a closed Wire which extends wire by length''' try: off2D = wire.makeOffset2D(length) except Exception as e: msg = "\nThe selected face cannot be used.\nYou must select the bottom face of the pocket area.\nextendWire() in PathPocketShape.py" PathLog.error(e) PathLog.error(msg) return False else: endPts = endPoints(wire) edges = [ e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts) ] wires = [Part.Wire(e) for e in Part.sortEdges(edges)] offset = selectOffsetWire(feature, wires) ePts = endPoints(offset) l0 = (ePts[0] - endPts[0]).Length l1 = (ePts[1] - endPts[0]).Length edges = wire.Edges if l0 < l1: edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1]))) else: edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1]))) return Part.Wire(edges)
def map_shape(self, shape, quad, face, fillfaces=False): if not isinstance(shape, Part.Shape): return [] # proj = quad.project(shape.Edges) new_edges = [] for oe in shape.Edges: # debug("original edge has : {} Pcurves : {}".format(_utils.nb_pcurves(oe), oe.curveOnSurface(0))) proj = quad.project([oe]) for e in proj.Edges: # debug("edge on quad has : {} Pcurves : {}".format(_utils.nb_pcurves(e), e.curveOnSurface(0))) c2d, fp, lp = quad.curveOnSurface(e) if oe.isClosed() and not c2d.isClosed(): self.force_closed_bspline2d(c2d) ne = c2d.toShape(face.Surface, fp, lp) # debug("edge on face has : {} Pcurves : {}".format(_utils.nb_pcurves(ne), ne.curveOnSurface(0))) # debug(ne.Placement) # debug(face.Placement) # ne.Placement = face.Placement vt = ne.getTolerance(1, Part.Vertex) et = ne.getTolerance(1, Part.Edge) if vt < et: ne.fixTolerance(et, Part.Vertex) # debug("fixing tolerance : {0:e} -> {1:e}".format(vt,et)) new_edges.append(ne) # else: # except TypeError: # error("Failed to get 2D curve") sorted_edges = Part.sortEdges(new_edges) wirelist = [Part.Wire(el) for el in sorted_edges] if fillfaces: return self.build_faces(wirelist, face) else: return wirelist
def extendWire(feature, wire, length): '''extendWire(wire, length) ... return a closed Wire which extends wire by length''' try: off2D = wire.makeOffset2D(length) except Exception as e: PathLog.error("extendWire(): wire.makeOffset2D()") PathLog.error(e) return False else: endPts = endPoints(wire) edges = [e for e in off2D.Edges if not isinstance(e.Curve, Part.Circle) or not includesPoint(e.Curve.Center, endPts)] wires = [Part.Wire(e) for e in Part.sortEdges(edges)] offset = selectOffsetWire(feature, wires) ePts = endPoints(offset) try: l0 = (ePts[0] - endPts[0]).Length except Exception as ee: PathLog.error("extendWire(): (ePts[0] - endPts[0]).Length") PathLog.error(ee) return False else: l1 = (ePts[1] - endPts[0]).Length edges = wire.Edges if l0 < l1: edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1]))) else: edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1]))) return Part.Wire(edges)
def extendWire(feature, wire, length): '''extendWire(wire, length) ... return a closed Wire which extends wire by length''' PathLog.track(length) if length and length != 0: off2D = wire.makeOffset2D(length) endPts = endPoints(wire) if endPts: edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)] wires = [Part.Wire(e) for e in Part.sortEdges(edges)] offset = selectOffsetWire(feature, wires) ePts = endPoints(offset) if ePts and len(ePts) > 1: l0 = (ePts[0] - endPts[0]).Length l1 = (ePts[1] - endPts[0]).Length edges = wire.Edges if l0 < l1: edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1]))) else: edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1]))) return Part.Wire(edges) return None
def map_shape(self, shape, quad, face, fillfaces=False): if not isinstance(shape, Part.Shape): return [] #proj = quad.project(shape.Edges) new_edges = [] for oe in shape.Edges: if True: e = quad.project([oe]).Edges[0] c2d, fp, lp = quad.curveOnSurface(e) if oe.isClosed() and not c2d.isClosed(): self.force_closed_bspline2d(c2d) ne = c2d.toShape(face.Surface, fp, lp) # toShape produces 1e-5 tolerance #debug(ne.Placement) #debug(face.Placement) #ne.Placement = face.Placement vt = ne.getTolerance(1, Part.Vertex) et = ne.getTolerance(1, Part.Edge) if vt < et: ne.fixTolerance(et, Part.Vertex) #debug("fixing tolerance : {0:e} -> {1:e}".format(vt,et)) new_edges.append(ne) else: #except TypeError: error("Failed to get 2D curve") sorted_edges = Part.sortEdges(new_edges) wirelist = [Part.Wire(el) for el in sorted_edges] if fillfaces: return self.build_faces(wirelist, face) else: return wirelist
def getWire(self): PathLog.track() if PathGeom.isRoughly(0, self.length.Value) or not self.sub: PathLog.debug("no extension, length=%.2f, sub=%s" % (self.length.Value, self.sub)) return None feature = self.obj.Shape.getElement(self.feature) edges = self._getEdges() sub = Part.Wire(Part.sortEdges(edges)[0]) if 1 == len(edges): PathLog.debug("Extending single edge wire") edge = edges[0] if Part.Circle == type(edge.Curve): circle = edge.Curve # for a circle we have to figure out if it's a hole or a cylinder p0 = edge.valueAt(edge.FirstParameter) normal = (edge.Curve.Center - p0).normalize() direction = self._getDirectedNormal(p0, normal) if direction is None: return None if PathGeom.pointsCoincide(normal, direction): r = circle.Radius - self.length.Value else: r = circle.Radius + self.length.Value # assuming the offset produces a valid circle - go for it if r > 0: e3 = Part.makeCircle(r, circle.Center, circle.Axis, edge.FirstParameter * 180 / math.pi, edge.LastParameter * 180 / math.pi) if endPoints(edge): # need to construct the arc slice e0 = Part.makeLine(edge.valueAt(edge.FirstParameter), e3.valueAt(e3.FirstParameter)) e2 = Part.makeLine(edge.valueAt(edge.LastParameter), e3.valueAt(e3.LastParameter)) return Part.Wire([e0, edge, e2, e3]) return Part.Wire([e3]) # the extension is bigger than the hole - so let's just cover the whole hole if endPoints(edge): # if the resulting arc is smaller than the radius, create a pie slice PathLog.track() center = circle.Center e0 = Part.makeLine(center, edge.valueAt(edge.FirstParameter)) e2 = Part.makeLine(edge.valueAt(edge.LastParameter), center) return Part.Wire([e0, edge, e2]) PathLog.track() return Part.Wire([edge]) else: PathLog.track(self.feature, self.sub, type(edge.Curve), endPoints(edge)) direction = self._getDirection(sub) if direction is None: return None # return self._extendEdge(feature, edge, direction) return self._extendEdge(feature, edges[0], direction) return extendWire(feature, sub, self.length.Value)
def mergeWires(list_of_edges_wires, flag_single = False, split_connections = []): edges = [] for sh in list_of_edges_wires: edges.extend(sh.Edges) if flag_single: return Part.Wire(edges) else: groups = splitIntoGroupsBySharing(edges, lambda sh: sh.Vertexes, split_connections) return Part.makeCompound([Part.Wire(Part.sortEdges(group)[0]) for group in groups])
def opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() jobshapes = [] if len(obj.Base) >= 1: # user has selected specific subelements wires = [] for base, subs in obj.Base: edges = [] basewires = [] for feature in subs: sub = base.Shape.getElement(feature) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: basewires.extend(sub.Wires) else: basewires.append(Part.Wire(sub.Edges)) for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) wires.extend(basewires) jobshapes.append(Part.makeCompound(wires)) else: # Use the Job Base object for base in self.model: PathLog.track(base.Label) if base.isDerivedFrom('Part::Part2DObject'): jobshapes.append(base.Shape) elif base.isDerivedFrom('Sketcher::SketchObject'): jobshapes.append(base.Shape) elif hasattr(base, 'ArrayType'): jobshapes.append(base.Shape) elif isinstance(base.Proxy, ArchPanel.PanelSheet): for tag in self.model[0].Proxy.getTags(self.model[0], transform=True): tagWires = [] for w in tag.Wires: tagWires.append(Part.Wire(w.Edges)) jobshapes.append(Part.makeCompound(tagWires)) if len(jobshapes) > 0: PathLog.debug('processing {} jobshapes'.format(len(jobshapes))) PathLog.track() wires = [] for shape in jobshapes: PathLog.debug('jobshape has {} edges'.format(len(shape.Edges))) self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) shapeWires = shape.Wires self.buildpathocc(obj, shape.Wires, self.getZValues(obj)) wires.extend(shapeWires) self.wires = wires # the last command is a move to clearance, which is automatically added by PathOp if self.commandlist: self.commandlist.pop()
def opExecute(self, obj): PathLog.track(obj.Label) (depth, offset) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool) PathLog.track(obj.Label, depth, offset) self.basewires = [] # pylint: disable=attribute-defined-outside-init self.adjusted_basewires = [] # pylint: disable=attribute-defined-outside-init wires = [] for base, subs in obj.Base: edges = [] basewires = [] for f in subs: sub = base.Shape.getElement(f) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: basewires.extend(sub.Wires) else: basewires.append(Part.Wire(sub.Edges)) self.edges = edges # pylint: disable=attribute-defined-outside-init for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) self.basewires.extend(basewires) for w in basewires: self.adjusted_basewires.append(w) wire = PathOpTools.offsetWire(w, base.Shape, offset, True) if wire: wires.append(wire) forward = True if obj.Direction == 'CCW': forward = False zValues = [] z = 0 if obj.StepDown.Value != 0: while z + obj.StepDown.Value < depth: z = z + obj.StepDown.Value zValues.append(z) zValues.append(depth) PathLog.track(obj.Label, depth, zValues) self.wires = wires # pylint: disable=attribute-defined-outside-init self.buildpathocc(obj, wires, zValues, True, forward) # the last command is a move to clearance, which is automatically added by PathOp if self.commandlist: self.commandlist.pop()
def execute(self, obj): edges = [] for g in obj.Geometry: if hasattr(g, 'Construction') and not g.Construction: #try: edges.append(g.toShape()) #except AttributeError: #debug("Failed to convert %s to BSpline"%str(g)) if edges: c = Part.Compound([]) se = Part.sortEdges(edges) for l in se: c.add(Part.Wire(l)) obj.Shape = c
def createItemForBaseModel(self, base, sub, edges, extensions): PathLog.track() ext = _Extension(self.obj, base, sub, None) item = QtGui.QStandardItem() item.setData(sub, QtCore.Qt.EditRole) item.setData(ext, self.DataObject) item.setSelectable(False) extendCorners = self.form.extendCorners.isChecked() def createSubItem(label, ext0): self.switch.addChild(ext0.root) item0 = QtGui.QStandardItem() item0.setData(label, QtCore.Qt.EditRole) item0.setData(ext0, self.DataObject) item0.setCheckable(True) for e in extensions: if e.obj == base and e.sub == label: item0.setCheckState(QtCore.Qt.Checked) ext0.enable() break item.appendRow([item0]) extensionEdges = {} for edge in base.Shape.getElement(sub).Edges: for (e, label) in edges: if edge.isSame(e): ext0 = _Extension(self.obj, base, sub, label) if ext0.isValid(): extensionEdges[e] = label[4:] if not extendCorners: createSubItem(label, ext0) break if extendCorners: def edgesMatchShape(e0, e1): return PathGeom.edgesMatch(e0, e1) or PathGeom.edgesMatch(e0, PathGeom.flipEdge(e1)) self.extensionEdges = extensionEdges # pylint: disable=attribute-defined-outside-init for edgeList in Part.sortEdges(list(extensionEdges.keys())): self.edgeList = edgeList # pylint: disable=attribute-defined-outside-init if len(edgeList) == 1: label = "Edge%s" % [extensionEdges[keyEdge] for keyEdge in extensionEdges.keys() if edgesMatchShape(keyEdge, edgeList[0])][0] else: label = "Wire(%s)" % ','.join(sorted([extensionEdges[keyEdge] for e in edgeList for keyEdge in extensionEdges.keys() if edgesMatchShape(e, keyEdge)], key=lambda s: int(s))) # pylint: disable=unnecessary-lambda ext0 = _Extension(self.obj, base, sub, label) createSubItem(label, ext0) return item
def parse(pathobj): """accepts a Path object. Returns a list of wires""" feedcommands = PathGeom.CmdMove rapidcommands = PathGeom.CmdMoveRapid edges = [] objlist = [] # Gotta start somewhere. Assume 0,0,0 curPoint = FreeCAD.Vector(0, 0, 0) for c in pathobj.Path.Commands: PathLog.debug("{} -> {}".format(curPoint, c)) if "Z" in c.Parameters: newparams = c.Parameters newparams.pop("Z", None) flatcommand = Path.Command(c.Name, newparams) c.Parameters = newparams else: flatcommand = c # ignore gcode that isn't moving if flatcommand.Name not in feedcommands + rapidcommands: PathLog.debug("non move") continue # ignore pure vertical feed and rapid if (flatcommand.Parameters.get("X", curPoint.x) == curPoint.x and flatcommand.Parameters.get("Y", curPoint.y) == curPoint.y): PathLog.debug("vertical") continue # feeding move. Build an edge if flatcommand.Name in feedcommands: edges.append(PathGeom.edgeForCmd(flatcommand, curPoint)) PathLog.debug("feeding move") # update the curpoint curPoint.x = flatcommand.Parameters.get("X", curPoint.x) curPoint.y = flatcommand.Parameters.get("Y", curPoint.y) if len(edges) > 0: candidates = Part.sortEdges(edges) for c in candidates: obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Wire") obj.Shape = Part.Wire(c) objlist.append(obj) return objlist
def shape(self): proj1 = self.shape1.toNurbs().extrude(self.dir1) proj2 = self.shape2.toNurbs().extrude(self.dir2) curves = list() for f1 in proj1.Faces: for f2 in proj2.Faces: curves += f1.Surface.intersectSS(f2.Surface) intersect = [c.toShape() for c in curves] edges = [] for sh in intersect: if isinstance(sh, Part.Edge) and sh.Length > 1e-7: edges.append(sh) se = Part.sortEdges(edges) wires = [] for el in se: wires.append(Part.Wire(el)) return Part.Compound(wires)
def opExecute(self, obj): PathLog.track(obj.Label) (depth, offset) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool) PathLog.track(obj.Label, depth, offset) self.basewires = [] self.adjusted_basewires = [] wires = [] for base, subs in obj.Base: edges = [] basewires = [] for f in subs: sub = base.Shape.getElement(f) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: basewires.extend(sub.Wires) else: basewires.append(Part.Wire(sub.Edges)) self.edges = edges for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) self.basewires.extend(basewires) for w in basewires: self.adjusted_basewires.append(w) wire = PathOpTools.offsetWire(w, base.Shape, offset, True) if wire: wires.append(wire) zValues = [] z = 0 if obj.StepDown.Value != 0: while z + obj.StepDown.Value < depth: z = z + obj.StepDown.Value zValues.append(z) zValues.append(depth) PathLog.track(obj.Label, depth, zValues) self.wires = wires self.buildpathocc(obj, wires, zValues, True) # the last command is a move to clearance, which is automatically added by PathOp if self.commandlist: self.commandlist.pop()
def approximate(self, obj, input_shape): pts = False input_edges = None if isinstance(input_shape, (list, tuple)): #debug(isinstance(input_shape[0],Base.Vector)) if isinstance(input_shape[0], Base.Vector): pts = input_shape elif isinstance(input_shape[0], Part.Edge): input_edges = input_shape else: input_edges = input_shape.Edges if not obj.Active: return Part.Compound(input_shape) edges = list() if input_edges: for e in input_edges: pts = e.discretize(obj.Samples) bs = Part.BSplineCurve() bs.approximate(Points=pts, DegMin=obj.DegreeMin, DegMax=obj.DegreeMax, Tolerance=obj.ApproxTolerance, Continuity=obj.Continuity, ParamType=obj.Parametrization) edges.append(bs.toShape()) se = Part.sortEdges(edges) wires = [] for el in se: if len(el) > 1: wires.append(Part.Wire(el)) else: wires.append(el[0]) if len(wires) > 1: return Part.Compound(wires) else: return wires[0] elif pts: bs = Part.BSplineCurve() bs.approximate(Points=pts, DegMin=obj.DegreeMin, DegMax=obj.DegreeMax, Tolerance=obj.ApproxTolerance, Continuity=obj.Continuity, ParamType=obj.Parametrization) return bs.toShape()
def execute(self, obj): # discretizza gli elementi che compongono il bordo separatamente (dopo averli ordinati) points = [] # Ordino le Edges, utile per evitare problemi con le sezioni che spesso hanno le edges disordinate Edges = Part.sortEdges(obj.Wire.Shape.Edges)[0] for edges in Edges: # aggiungo i punti escluso l'ultimo if (points != []): points.pop() # elimino l'ultimo punto se la lista non è vuota for e in (edges.discretize(Deflection=obj.Deflection)): points.append(e) # Creo la BSpline bspline = Part.BSplineCurve() bspline.interpolate(points) # creo la Shape obj.Shape = bspline.toShape()
def join_multi_edges(edgelist,closed=False): good_edges = list() last = edgelist[0] remaining = edgelist[1:] res = [] while len(remaining) > 0: rejected = list() closest_dist = 1e50 closest_edge = None # closest_pts = None # closest_info = None for e in remaining: d,p,i = e.distToShape(last) if closest_dist > d: closest_dist = d if closest_edge: rejected.append(closest_edge) closest_edge = e # closest_pts = p # closest_info = i else: rejected.append(e) res = join_2_edges(last,closest_edge) #print(last,closest_edge,res) last = res[-1] good_edges.extend(res[:-1]) remaining = rejected if res: good_edges.append(res[-1]) se = Part.sortEdges(good_edges) wires = list() for group in se: print("Wire has %d edges"%len(group)) wires.append(Part.Wire(group)) if closed: for w in wires: if not w.isClosed(): ov = w.OrderedVertexes d,p,i = ov[0].distToShape(ov[-1]) w.add(Part.makeLine(p[0][0],p[0][1])) return(Part.Compound(wires))
def join_multi_edges(edgelist, closed=False): good_edges = list() last = edgelist[0] remaining = edgelist[1:] res = [] while len(remaining) > 0: rejected = list() closest_dist = 1e50 closest_edge = None # closest_pts = None # closest_info = None for e in remaining: d, p, i = e.distToShape(last) if closest_dist > d: closest_dist = d if closest_edge: rejected.append(closest_edge) closest_edge = e # closest_pts = p # closest_info = i else: rejected.append(e) res = join_2_edges(last, closest_edge) #print(last.distToShape(closest_edge)) last = res[-1] good_edges.extend(res[:-1]) remaining = rejected if res: good_edges.append(res[-1]) se = Part.sortEdges(good_edges) wires = list() for group in se: print("Wire has %d edges" % len(group)) wires.append(Part.Wire(group)) if closed: for w in wires: if not w.isClosed(): ov = w.OrderedVertexes d, p, i = ov[0].distToShape(ov[-1]) w.add(Part.makeLine(p[0][0], p[0][1])) return Part.Compound(wires)
def extendWire(feature, wire, length): '''extendWire(wire, length) ... return a closed Wire which extends wire by length''' off2D = wire.makeOffset2D(length) endPts = endPoints(wire) edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)] wires = [Part.Wire(e) for e in Part.sortEdges(edges)] offset = selectOffsetWire(feature, wires) ePts = endPoints(offset) l0 = (ePts[0] - endPts[0]).Length l1 = (ePts[1] - endPts[0]).Length edges = wire.Edges if l0 < l1: edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1]))) else: edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1]))) return Part.Wire(edges)
def sweep_wire(self, w, solid=False): faces = [] for e in w.Edges: faces.append(self.sweep_edge(e, solid)) shell = Part.Shell(faces) shell.sewShape() if solid: cyl = Part.makeCylinder(self.max_radius * 2, self.nb_of_turns * self.lead) cyl.Placement = self._placement.multiply( FreeCAD.Placement(FreeCAD.Vector(), FreeCAD.Vector(1, 0, 0), -90)) common = cyl.common(shell) cut_faces = common.Faces new_edges = [] for e1 in common.Edges: found = False for e2 in shell.Edges: if nurbs_tools.is_same(e1.Curve, e2.Curve, tol=1e-7, full=False): found = True #print("found similar edges") continue if not found: new_edges.append(e1) #print(len(Part.sortEdges(new_edges))) el1, el2 = Part.sortEdges(new_edges)[0:2] f1 = Part.makeFace(Part.Wire(el1), 'Part::FaceMakerSimple') f2 = Part.makeFace(Part.Wire(el2), 'Part::FaceMakerSimple') cut_faces.extend([f1, f2]) try: shell = Part.Shell(cut_faces) shell.sewShape() return Part.Solid(shell) except Part.OCCError: print("Failed to create solid") return Part.Compound(cut_faces) return shell
def execute(self, obj): debug("* trimFace execute *") if not obj.Tool: debug("No tool") return if not obj.PickedPoint: debug("No PickedPoint") return if not obj.Face: debug("No Face") return if not (obj.DirVector or obj.Direction): debug("No Direction") return face = self.getFace(obj.Face) v = self.getVector(obj) v.normalize() debug("Vector : {}".format(str(v))) wires = [ Part.Wire(el) for el in Part.sortEdges(self.getEdges(obj.Tool)) ] union = Part.Compound(wires + [face]) d = 2 * union.BoundBox.DiagonalLength cuttool = [] for w in wires: w.translate(v * d) cuttool.append(w.extrude(-v * d * 2)) # Part.show(cuttool) bf = splitAPI.slice(face, cuttool, "Split", 1e-6) debug("shape has {} faces".format(len(bf.Faces))) u = obj.PickedPoint.x v = obj.PickedPoint.y for f in bf.Faces: if f.isPartOfDomain(u, v): obj.Shape = f return
def extendWire(feature, wire, length): """extendWire(wire, length) ... return a closed Wire which extends wire by length""" PathLog.track(length) if not length or length == 0: return None try: off2D = wire.makeOffset2D(length) except FreeCAD.Base.FreeCADError as ee: PathLog.debug(ee) return None endPts = endPoints(wire) # Assumes wire is NOT closed if endPts: edges = [ e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts) ] wires = [Part.Wire(e) for e in Part.sortEdges(edges)] offset = selectOffsetWire(feature, wires) ePts = endPoints(offset) if ePts and len(ePts) > 1: l0 = (ePts[0] - endPts[0]).Length l1 = (ePts[1] - endPts[0]).Length edges = wire.Edges if l0 < l1: edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1]))) else: edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0]))) edges.extend(offset.Edges) edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1]))) return Part.Wire(edges) return None
def opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() job = PathUtils.findParentJob(obj) jobshapes = [] zValues = self.getZValues(obj) try: if len(self.model) == 1 and self.model[0].isDerivedFrom('Sketcher::SketchObject') or \ self.model[0].isDerivedFrom('Part::Part2DObject') or \ hasattr(self.model[0], 'ArrayType'): PathLog.track() self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) # we only consider the outer wire if this is a Face wires = [] for w in self.model[0].Shape.Wires: wires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, wires, zValues) self.wires = wires elif len(self.model) == 1 and isinstance( self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet PathLog.track() wires = [] for tag in self.model[0].Proxy.getTags(self.model[0], transform=True): self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) tagWires = [] for w in tag.Wires: tagWires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, tagWires, zValues) wires.extend(tagWires) self.wires = wires elif obj.Base: PathLog.track() wires = [] for base, subs in obj.Base: edges = [] basewires = [] for feature in subs: sub = base.Shape.getElement(feature) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: basewires.extend(sub.Wires) else: basewires.append(Part.Wire(sub.Edges)) for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) wires.extend(basewires) self.buildpathocc(obj, wires, zValues) self.wires = wires elif not obj.BaseShapes: PathLog.track() if not obj.Base and not obj.BaseShapes: for base in self.model: PathLog.track(base.Label) if base.isDerivedFrom('Part::Part2DObject'): jobshapes.append(base) if not jobshapes: raise ValueError( translate( 'PathEngrave', "Unknown baseobject type for engraving (%s)") % (obj.Base)) if obj.BaseShapes or jobshapes: PathLog.track() wires = [] for shape in obj.BaseShapes + jobshapes: PathLog.track(shape.Label) shapeWires = shape.Shape.Wires self.buildpathocc(obj, shapeWires, zValues) wires.extend(shapeWires) self.wires = wires # the last command is a move to clearance, which is automatically added by PathOp if self.commandlist: self.commandlist.pop() except Exception as e: PathLog.error(e) traceback.print_exc() PathLog.error( translate( 'PathEngrave', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.' ))
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 opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() job = PathUtils.findParentJob(obj) jobshapes = [] zValues = self.getZValues(obj) try: if len(self.model) == 1 and self.model[0].isDerivedFrom('Sketcher::SketchObject') or \ self.model[0].isDerivedFrom('Part::Part2DObject') or \ hasattr(self.model[0], 'ArrayType'): PathLog.track() self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) # we only consider the outer wire if this is a Face wires = [] for w in self.model[0].Shape.Wires: wires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, wires, zValues) self.wires = wires elif len(self.model) == 1 and isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet PathLog.track() wires = [] for tag in self.model[0].Proxy.getTags(self.model[0], transform=True): self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) tagWires = [] for w in tag.Wires: tagWires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, tagWires, zValues) wires.extend(tagWires) self.wires = wires elif obj.Base: PathLog.track() wires = [] for base, subs in obj.Base: edges = [] basewires = [] for feature in subs: sub = base.Shape.getElement(feature) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: basewires.extend(sub.Wires) else: basewires.append(Part.Wire(sub.Edges)) for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) wires.extend(basewires) self.buildpathocc(obj, wires, zValues) self.wires = wires elif not obj.BaseShapes: PathLog.track() if not obj.Base and not obj.BaseShapes: for base in self.model: PathLog.track(base.Label) if base.isDerivedFrom('Part::Part2DObject'): jobshapes.append(base) if not jobshapes: raise ValueError(translate('PathEngrave', "Unknown baseobject type for engraving (%s)") % (obj.Base)) if obj.BaseShapes or jobshapes: PathLog.track() wires = [] for shape in obj.BaseShapes + jobshapes: PathLog.track(shape.Label) shapeWires = shape.Shape.Wires self.buildpathocc(obj, shapeWires, zValues) wires.extend(shapeWires) self.wires = wires # the last command is a move to clearance, which is automatically added by PathOp if self.commandlist: self.commandlist.pop() except Exception as e: PathLog.error(e) traceback.print_exc() PathLog.error(translate('PathEngrave', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.'))
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" import Part,DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self,obj) if data: if not isinstance(data[0],list): # multifuses not considered here return data length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value if not height: for p in obj.InList: if Draft.getType(p) in ["Floor","BuildingPart"]: if p.Height.Value: height = p.Height.Value if not height: return None if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None self.basewires = None # build wall layers layers = [] if hasattr(obj,"Material"): if obj.Material: if hasattr(obj.Material,"Materials"): varwidth = 0 restwidth = width - sum(obj.Material.Thicknesses) if restwidth > 0: varwidth = [t for t in obj.Material.Thicknesses if t == 0] if varwidth: varwidth = restwidth/len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) elif varwidth: layers.append(varwidth) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None elif obj.Face > 0: if len(obj.Base.Shape.Faces) >= obj.Face: face = obj.Base.Shape.Faces[obj.Face-1] # this wall is based on a specific face of its base object normal = face.normalAt(0,0) if normal.getAngle(Vector(0,0,1)) > math.pi/4: normal.multiply(width) base = face.extrude(normal) if obj.Align == "Center": base.translate(normal.negative().multiply(0.5)) elif obj.Align == "Right": base.translate(normal.negative()) else: normal.multiply(height) base = face.extrude(normal) base,placement = self.rebase(base) return (base,normal,placement) elif obj.Base.Shape.Faces: if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: base,placement = self.rebase(obj.Base.Shape) elif len(obj.Base.Shape.Edges) == 1: self.basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # self.basewires = obj.Base.Shape.Wires self.basewires = [] for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) if self.basewires and width: if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 baseface = None for i,wire in enumerate(self.basewires): e = wire.Edges[0] if isinstance(e.Curve,Part.Circle): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.negative() off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": if layers: off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w1 = DraftGeomUtils.offsetWire(wire,d1) layeroffset += layers[i] off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w2 = DraftGeomUtils.offsetWire(wire,d1) else: dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) if sh: sh.fix(0.1,0,1) # fixes self-intersecting wires f = Part.Face(sh) if baseface: if layers: baseface.append(f) else: baseface = baseface.fuse(f) # baseface = baseface.removeSplitter() s = DraftGeomUtils.removeSplitter(baseface) if s: baseface = s else: if layers: baseface = [f] else: baseface = f if baseface: base,placement = self.rebase(baseface) else: if layers: totalwidth = sum(layers) offset = 0 base = [] for l in layers: l2 = length/2 or 0.5 w1 = -totalwidth/2 + offset w2 = w1 + l v1 = Vector(-l2,w1,0) v2 = Vector(l2,w1,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base.append(Part.Face(Part.makePolygon([v1,v2,v3,v4,v1]))) offset += l else: l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) placement = FreeCAD.Placement() if base and placement: extrusion = normal.multiply(height) return (base,extrusion,placement) return None
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" import Part,DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self,obj) if data: if not isinstance(data[0],list): # multifuses not considered here return data length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value if not height: for p in obj.InList: if Draft.getType(p) in ["Floor","BuildingPart"]: if p.Height.Value: height = p.Height.Value if not height: return None if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None self.basewires = None # build wall layers layers = [] if hasattr(obj,"Material"): if obj.Material: if hasattr(obj.Material,"Materials"): varwidth = 0 restwidth = width - sum(obj.Material.Thicknesses) if restwidth > 0: varwidth = [t for t in obj.Material.Thicknesses if t == 0] if varwidth: varwidth = restwidth/len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) elif varwidth: layers.append(varwidth) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None elif obj.Face > 0: if len(obj.Base.Shape.Faces) >= obj.Face: face = obj.Base.Shape.Faces[obj.Face-1] # this wall is based on a specific face of its base object if obj.Normal != Vector(0,0,0): normal = face.normalAt(0,0) if normal.getAngle(Vector(0,0,1)) > math.pi/4: normal.multiply(width) base = face.extrude(normal) if obj.Align == "Center": base.translate(normal.negative().multiply(0.5)) elif obj.Align == "Right": base.translate(normal.negative()) else: normal.multiply(height) base = face.extrude(normal) base,placement = self.rebase(base) return (base,normal,placement) elif obj.Base.Shape.Faces: if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: base,placement = self.rebase(obj.Base.Shape) elif len(obj.Base.Shape.Edges) == 1: self.basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # self.basewires = obj.Base.Shape.Wires self.basewires = [] for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) if self.basewires and width: if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 baseface = None for i,wire in enumerate(self.basewires): e = wire.Edges[0] if isinstance(e.Curve,Part.Circle): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.negative() off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": if layers: off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w1 = DraftGeomUtils.offsetWire(wire,d1) layeroffset += layers[i] off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w2 = DraftGeomUtils.offsetWire(wire,d1) else: dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) if sh: sh.fix(0.1,0,1) # fixes self-intersecting wires f = Part.Face(sh) if baseface: if layers: baseface.append(f) else: baseface = baseface.fuse(f) # baseface = baseface.removeSplitter() s = DraftGeomUtils.removeSplitter(baseface) if s: baseface = s else: if layers: baseface = [f] else: baseface = f if baseface: base,placement = self.rebase(baseface) else: if layers: totalwidth = sum(layers) offset = 0 base = [] for l in layers: l2 = length/2 or 0.5 w1 = -totalwidth/2 + offset w2 = w1 + l v1 = Vector(-l2,w1,0) v2 = Vector(l2,w1,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base.append(Part.Face(Part.makePolygon([v1,v2,v3,v4,v1]))) offset += l else: l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) placement = FreeCAD.Placement() if base and placement: extrusion = normal.multiply(height) if placement.Rotation.Angle > 0: extrusion = placement.inverse().Rotation.multVec(extrusion) return (base,extrusion,placement) return None
def processExtrudedSketch(extrudeObj, sketchObj, xmlVol): from .exportGDML import insertXMLvolume, exportPosition, addVolRef, \ quaternion2XYZ sortededges = Part.sortEdges(sketchObj.Shape.Edges) # sort by largest area to smallest area sortEdgelistsByBoundingBoxArea(sortededges) # getCurve returns one of the sub classes of ClosedCurve that # knows how to export the specifc closed edges # Make names based on Extrude name # curves = [getCurve(edges, extrudeObj.Label + str(i)) for i, edges in enumerate(sortededges)] if extrudeObj.Symmetric is True: height = extrudeObj.LengthFwd.Value else: height = extrudeObj.LengthFwd.Value + extrudeObj.LengthRev.Value eName = extrudeObj.Label # get a list of curves (instances of class ClosedCurve) for each set of closed edges curves = [ getExtrudedCurve(eName + str(i), edges, height) for i, edges in enumerate(sortededges) ] # build a generalized binary tree of closed curves. root = Node(curves[0], None, 0) for c in curves[1:]: root.insert(c) # Traverse the tree. The list returned is a list of [Node, parity], where parity = 0, says add to parent, 1 mean subtract lst = root.preOrderTraversal(root) rootnode = lst[0][0] rootCurve = rootnode.closedCurve rootCurve.export() # a curve is created with a unique name firstName = rootCurve.name booleanName = firstName rootPos = rootCurve.position rootRot = rootCurve.rotation # for now consider only angle of rotation about z-axis for c in lst[1:]: node = c[0] parity = c[1] curve = node.closedCurve curve.export() if parity == 0: boolType = 'union' secondName = curve.name secondPos = curve.position else: boolType = 'subtraction' secondName = curve.name + '_s' # scale solids along z, so it punches thru scaleUp(secondName, curve.name, 1.10) secondPos = curve.position - Vector(0, 0, 0.01 * height) booleanName = curve.name + '_bool' boolSolid = ET.SubElement(solids, boolType, {'name': booleanName}) ET.SubElement(boolSolid, 'first', {'ref': firstName}) ET.SubElement(boolSolid, 'second', {'ref': secondName}) relativePosition = secondPos - rootPos zAngle = curve.rotation[2] - rootRot[2] posName = curve.name + '_pos' rotName = curve.name + '_rot' exportDefine(posName, relativePosition) # position of second relative to first ET.SubElement(define, 'rotation', { 'name': rotName, 'unit': 'deg', 'x': '0', 'y': '0', 'z': str(zAngle) }) ET.SubElement(boolSolid, 'positionref', {'ref': posName}) ET.SubElement(boolSolid, 'rotationref', {'ref': rotName}) firstName = booleanName # now create logical and physical volumes for the last boolean. # Because the position of each closed curve might not be at the # origin, whereas primitives (tubes, cones, etc, are created centered at # the origin, we need to shift the position of the very first node by its # position, in addition to the shift by the Extrusion placement volName = booleanName + 'Vol' # booleanName is name of last boolean newvol = insertXMLvolume(volName) addVolRef(newvol, volName, extrudeObj, booleanName) # ET.SubElement(newvol,'materialref',{'ref': 'G4_Si'}) # ET.SubElement(newvol,'solidref',{'ref': booleanName}) pvol = ET.SubElement(xmlVol, 'physvol', {'name': 'PV' + volName}) ET.SubElement(pvol, 'volumeref', {'ref': volName}) extrudePosition = extrudeObj.Placement.Base if extrudeObj.Symmetric is False: if extrudeObj.Reversed is False: zoffset = Vector(0, 0, extrudeObj.LengthRev.Value) else: zoffset = Vector(0, 0, extrudeObj.LengthFwd.Value) else: zoffset = Vector(0, 0, extrudeObj.LengthFwd.Value / 2) angles = quaternion2XYZ(extrudeObj.Placement.Rotation) # need to add rotations of elliptical tubes. Assume extrusion is on z-axis # Probably wil not work in general zAngle = angles[2] + rootRot[2] print(rootPos) print(rootCurve.name) print(rootCurve.position) rootPos = rotatedPos(rootCurve, extrudeObj.Placement.Rotation) print(rootPos) volPos = extrudePosition + rootPos - zoffset print(volPos) exportPosition(volName, pvol, volPos) rotName = booleanName + '_rot' ET.SubElement( define, 'rotation', { 'name': rotName, 'unit': 'deg', 'x': str(-angles[0]), 'y': str(-angles[1]), 'z': str(-zAngle) }) ET.SubElement(pvol, 'rotationref', {'ref': rotName})
def opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() job = PathUtils.findParentJob(obj) if job and job.Base: obj.BaseObject = job.Base zValues = [] if obj.StepDown.Value != 0: z = obj.StartDepth.Value - obj.StepDown.Value while z > obj.FinalDepth.Value: zValues.append(z) z -= obj.StepDown.Value zValues.append(obj.FinalDepth.Value) self.zValues = zValues try: if self.baseobject.isDerivedFrom('Sketcher::SketchObject') or \ self.baseobject.isDerivedFrom('Part::Part2DObject') or \ hasattr(self.baseobject, 'ArrayType'): PathLog.track() self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) # we only consider the outer wire if this is a Face wires = [] for w in self.baseobject.Shape.Wires: wires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, wires, zValues) self.wires = wires elif isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet PathLog.track() wires = [] for tag in self.baseobject.Proxy.getTags(self.baseobject, transform=True): self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) tagWires = [] for w in tag.Wires: tagWires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, tagWires, zValues) wires.extend(tagWires) self.wires = wires elif obj.Base: PathLog.track() wires = [] for base, subs in obj.Base: edges = [] #for sub in subs: # edges.extend(base.Shape.getElement(sub).Edges) #shapeWires = adjustWirePlacement(obj, base, TechDraw.edgeWalker(edges)) #wires.extend(shapeWires) for feature in subs: sub = base.Shape.getElement(feature) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: wires.extend(sub.Wires) else: wires.append(Part.Wire(sub.Edges)) for edgelist in Part.sortEdges(edges): wires.append(Part.Wire(edgelist)) wires = adjustWirePlacement(obj, base, wires) self.buildpathocc(obj, wires, zValues) self.wires = wires elif not obj.BaseShapes: PathLog.track() raise ValueError(translate('PathEngrave', "Unknown baseobject type for engraving (%s)") % (obj.Base)) if obj.BaseShapes: PathLog.track() wires = [] for shape in obj.BaseShapes: shapeWires = adjustWirePlacement(obj, shape, shape.Shape.Wires) self.buildpathocc(obj, shapeWires, zValues) wires.extend(shapeWires) self.wires = wires except Exception as e: PathLog.error(e) traceback.print_exc() PathLog.error(translate('PathEngrave', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.'))
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 offsetWire(wire, base, offset, forward): '''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly. The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting happens in the XY plane. ''' PathLog.track('offsetWire') if 1 == len(wire.Edges): edge = wire.Edges[0] curve = edge.Curve if Part.Circle == type(curve) and wire.isClosed(): # it's a full circle and there are some problems with that, see # http://www.freecadweb.org/wiki/Part%20Offset2D # it's easy to construct them manually though z = -1 if forward else 1 edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z)) if base.isInside(edge.Vertexes[0].Point, offset/2, True): if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius): # offsetting a hole by its own radius (or more) makes the hole vanish return None edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) w = Part.Wire([edge]) return w if Part.Line == type(curve) or Part.LineSegment == type(curve): # offsetting a single edge doesn't work because there is an infinite # possible planes into which the edge could be offset # luckily, the plane here must be the XY-plane ... p0 = edge.Vertexes[0].Point v0 = edge.Vertexes[1].Point - p0 n = v0.cross(FreeCAD.Vector(0, 0, 1)) o = n.normalize() * offset edge.translate(o) # offset edde the other way if the result is inside if base.isInside(edge.valueAt((edge.FirstParameter + edge.LastParameter) / 2), offset / 2, True): edge.translate(-2 * o) # flip the edge if it's not on the right side of the original edge if forward is not None: v1 = edge.Vertexes[1].Point - p0 left = PathGeom.Side.Left == PathGeom.Side.of(v0, v1) if left != forward: edge = PathGeom.flipEdge(edge) return Part.Wire([edge]) # if we get to this point the assumption is that makeOffset2D can deal with the edge pass owire = orientWire(wire.makeOffset2D(offset), True) debugWire('makeOffset2D_%d' % len(wire.Edges), owire) if wire.isClosed(): if not base.isInside(owire.Edges[0].Vertexes[0].Point, offset/2, True): PathLog.track('closed - outside') return orientWire(owire, forward) PathLog.track('closed - inside') try: owire = wire.makeOffset2D(-offset) except: # most likely offsetting didn't work because the wire is a hole # and the offset is too big - making the hole vanish return None # For negative offsets (holes) 'forward' is the other way if forward is None: return orientWire(owire, None) return orientWire(owire, not forward) # An edge is considered to be inside of shape if the mid point is inside # Of the remaining edges we take the longest wire to be the engraving side # Looking for a circle with the start vertex as center marks and end # starting from there follow the edges until a circle with the end vertex as center is found # if the traversed edges include any oof the remainig from above, all those edges are remaining # this is to also include edges which might partially be inside shape # if they need to be discarded, split, that should happen in a post process # Depending on the Axis of the circle, and which side remains we know if the wire needs to be flipped # first, let's make sure all edges are oriented the proper way edges = _orientEdges(wire.Edges) # determine the start and end point start = edges[0].firstVertex().Point end = edges[-1].lastVertex().Point debugWire('wire', wire) debugWire('wedges', Part.Wire(edges)) # find edges that are not inside the shape common = base.common(owire) insideEndpoints = [e.lastVertex().Point for e in common.Edges] insideEndpoints.append(common.Edges[0].firstVertex().Point) def isInside(edge): p0 = edge.firstVertex().Point p1 = edge.lastVertex().Point for p in insideEndpoints: if PathGeom.pointsCoincide(p, p0, 0.01) or PathGeom.pointsCoincide(p, p1, 0.01): return True return False outside = [e for e in owire.Edges if not isInside(e)] # discard all edges that are not part of the longest wire longestWire = None for w in [Part.Wire(el) for el in Part.sortEdges(outside)]: if not longestWire or longestWire.Length < w.Length: longestWire = w debugWire('outside', Part.Wire(outside)) debugWire('longest', longestWire) def isCircleAt(edge, center): '''isCircleAt(edge, center) ... helper function returns True if edge is a circle at the given center.''' if Part.Circle == type(edge.Curve) or Part.ArcOfCircle == type(edge.Curve): return PathGeom.pointsCoincide(edge.Curve.Center, center) return False # split offset wire into edges to the left side and edges to the right side collectLeft = False collectRight = False leftSideEdges = [] rightSideEdges = [] # traverse through all edges in order and start collecting them when we encounter # an end point (circle centered at one of the end points of the original wire). # should we come to an end point and determine that we've already collected the # next side, we're done for e in (owire.Edges + owire.Edges): if isCircleAt(e, start): if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)): if not collectLeft and leftSideEdges: break collectLeft = True collectRight = False else: if not collectRight and rightSideEdges: break collectLeft = False collectRight = True elif isCircleAt(e, end): if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)): if not collectRight and rightSideEdges: break collectLeft = False collectRight = True else: if not collectLeft and leftSideEdges: break collectLeft = True collectRight = False elif collectLeft: leftSideEdges.append(e) elif collectRight: rightSideEdges.append(e) debugWire('left', Part.Wire(leftSideEdges)) debugWire('right', Part.Wire(rightSideEdges)) # figure out if all the left sided edges or the right sided edges are the ones # that are 'outside'. However, we return the full side. edges = leftSideEdges for e in longestWire.Edges: for e0 in rightSideEdges: if PathGeom.edgesMatch(e, e0): edges = rightSideEdges PathLog.debug("#use right side edges") if not forward: PathLog.debug("#reverse") edges.reverse() return orientWire(Part.Wire(edges), None) # at this point we have the correct edges and they are in the order for forward # traversal (climb milling). If that's not what we want just reverse the order, # orientWire takes care of orienting the edges appropriately. PathLog.debug("#use left side edges") if not forward: PathLog.debug("#reverse") edges.reverse() return orientWire(Part.Wire(edges), None)
def opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() job = PathUtils.findParentJob(obj) if job and job.Base: obj.BaseObject = job.Base zValues = [] if obj.StepDown.Value != 0: z = obj.StartDepth.Value - obj.StepDown.Value while z > obj.FinalDepth.Value: zValues.append(z) z -= obj.StepDown.Value zValues.append(obj.FinalDepth.Value) self.zValues = zValues try: if self.baseobject.isDerivedFrom('Sketcher::SketchObject') or \ self.baseobject.isDerivedFrom('Part::Part2DObject') or \ hasattr(self.baseobject, 'ArrayType'): PathLog.track() self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) # we only consider the outer wire if this is a Face wires = [] for w in self.baseobject.Shape.Wires: wires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, wires, zValues) self.wires = wires elif isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet PathLog.track() wires = [] for tag in self.baseobject.Proxy.getTags(self.baseobject, transform=True): self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) tagWires = [] for w in tag.Wires: tagWires.append(Part.Wire(w.Edges)) self.buildpathocc(obj, tagWires, zValues) wires.extend(tagWires) self.wires = wires elif obj.Base: PathLog.track() wires = [] for base, subs in obj.Base: edges = [] #for sub in subs: # edges.extend(base.Shape.getElement(sub).Edges) #shapeWires = adjustWirePlacement(obj, base, TechDraw.edgeWalker(edges)) #wires.extend(shapeWires) for feature in subs: sub = base.Shape.getElement(feature) if type(sub) == Part.Edge: edges.append(sub) elif sub.Wires: wires.extend(sub.Wires) else: wires.append(Part.Wire(sub.Edges)) for edgelist in Part.sortEdges(edges): wires.append(Part.Wire(edgelist)) wires = adjustWirePlacement(obj, base, wires) self.buildpathocc(obj, wires, zValues) self.wires = wires elif not obj.BaseShapes: PathLog.track() raise ValueError( translate('PathEngrave', "Unknown baseobject type for engraving (%s)") % (obj.Base)) if obj.BaseShapes: PathLog.track() wires = [] for shape in obj.BaseShapes: shapeWires = adjustWirePlacement(obj, shape, shape.Shape.Wires) self.buildpathocc(obj, shapeWires, zValues) wires.extend(shapeWires) self.wires = wires except Exception as e: PathLog.error(e) traceback.print_exc() PathLog.error( translate( 'PathEngrave', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.' ))
def Activated(self): b = [] selection = FreeCADGui.Selection.getSelectionEx() if selection: if len(selection) == 4: base = FreeCAD.ActiveDocument.getObject( (selection[0].ObjectName)) b = base.WingPanels _rootRib = FreeCAD.ActiveDocument.getObject( (selection[1].ObjectName)) _path = FreeCAD.ActiveDocument.getObject( (selection[2].ObjectName)) base.WingEdges.append(_path) _envelope = FreeCAD.ActiveDocument.getObject( (selection[3].ObjectName)) base.WingEdges.append(_envelope) _leadingedge = FreeCAD.ActiveDocument.getObject( (selection[3].ObjectName)) # A netoyer en doublon wingsketch = FreeCAD.ActiveDocument.getObject( (selection[3].ObjectName)) # Separate leadInEdge & trailingEdge edges = Part.sortEdges( wingsketch.Shape.Edges ) # Separate, #edges=Part.sortEdges(wingsketch.Shape.Edges) # deprecated ? paths = Part.__sortEdges__(_path.Shape.Edges) leadInEdges = edges[0] #id the leading edge trailingEdges = edges[1] #id the trailing edge FreeCAD.Console.PrintMessage("Edges number : " + str(len(edges)) + "\n") if len(edges) != 2: FreeCAD.Console.PrintMessage("Edges must be 2 and not " + str(len(edges)) + "\n") return nbOfPanels = len(leadInEdges) FreeCAD.Console.PrintMessage( "-------------------- Wing Panel --------------------" + "\n") FreeCAD.Console.PrintMessage(" Rib :" + str(_rootRib.Label) + "\n") FreeCAD.Console.PrintMessage(" Wing :" + str(base.Label) + "\n") FreeCAD.Console.PrintMessage(" Path :" + str(_path.Label) + "\n") FreeCAD.Console.PrintMessage(" envelope :" + str(_envelope.Label) + "\n") FreeCAD.Console.PrintMessage(" envelope placement:" + str(_envelope.Placement) + "\n") FreeCAD.Console.PrintMessage(" Number of Panels :" + str(nbOfPanels) + "\n") pmin = 0 pmax = 0 for i in range(0, nbOfPanels): # for each panel pmin = 0 #pmax pmax = leadInEdges[i].Length param = leadInEdges[i].getParameterByLength(pmin) param2 = leadInEdges[i].getParameterByLength(pmax) direction = leadInEdges[i].tangentAt(param) posvec = leadInEdges[i].valueAt(param) posvec2 = leadInEdges[i].valueAt(param2) #normal = CurvedShapes.getNormal(obj.Base) #rotaxis = normal.cross(direction) #angle = math.degrees(normal.getAngle(direction)) bbox = leadInEdges[i].BoundBox bbox2 = trailingEdges[i].BoundBox FreeCAD.Console.PrintMessage(" Panel n° " + str(i) + " ------------------------\n") FreeCAD.Console.PrintMessage(" leadInEdges id :" + str(leadInEdges[i]) + "\n") FreeCAD.Console.PrintMessage(" leadInEdges Length :" + str(leadInEdges[i].Length) + "\n") FreeCAD.Console.PrintMessage(" trailingEdges id :" + str(trailingEdges[i]) + "\n") FreeCAD.Console.PrintMessage(" trailingEdges Length :" + str(trailingEdges[i].Length) + "\n") FreeCAD.Console.PrintMessage(" direction :" + str(direction) + "\n") FreeCAD.Console.PrintMessage(" pmin : " + str(pmin) + "\n") FreeCAD.Console.PrintMessage(" Param : " + str(param) + "\n") FreeCAD.Console.PrintMessage(" Position : " + str(posvec) + "\n") FreeCAD.Console.PrintMessage(" Position2 : " + str(posvec2) + "\n") FreeCAD.Console.PrintMessage( " BoundBox leadInEdges : " + str(bbox) + "\n") FreeCAD.Console.PrintMessage( " BoundBox trailingEdges : " + str(bbox2) + "\n") FreeCADGui.activateWorkbench("DraftWorkbench") plane = WorkingPlane.plane() FreeCAD.DraftWorkingPlane = plane #workplane = WorkingPlane.plane() workplane = FreeCAD.DraftWorkingPlane v1 = FreeCAD.Vector( 0, 1, 0).normalize() #paths[i].tangentAt(param).normalize() v2 = FreeCAD.Vector(0, 0, 1).normalize() #workplane.alignToPointAndAxis(v1, v2, 0) #FreeCAD.DraftWorkingPlane.alignToPointAndAxis(v1, v2, 0) #FreeCADGui.Snapper.toggleGrid() FreeCAD.Console.PrintMessage(" V1 : " + str(v1) + "\n") FreeCAD.Console.PrintMessage(" V2 : " + str(v2) + "\n") # workplane.alignToPointAndAxis( v1, v2, 0) #FreeCADGui.Snapper.toggleGrid() FreeCADGui.activeDocument().activeView( ).setCameraOrientation(_path.Placement.Rotation) #paths[i]. FreeCAD.Console.PrintMessage(" pathline : " + str(paths[i]) + "\n") #pathline=Part.Line(paths[i].Geometry) myObj0 = Draft.makeSketch(paths[i], autoconstraints=True) #myObj1=Part.Line(paths[i].Content) myObj0.Label = "path" + str(i) vec = _envelope.Placement.Rotation FreeCADGui.activeDocument().activeView( ).setCameraOrientation(vec) #(0,0,0,1)) myObj1 = Draft.makeSketch(leadInEdges[i], name="leadInEdges" + str(i), autoconstraints=True) #myObj1=Part.Line() myObj1.Label = "leadInEdges" + str(i) FreeCADGui.activeDocument().activeView( ).setCameraOrientation(vec) myObj2 = Draft.makeSketch(trailingEdges[i], autoconstraints=True) myObj2.Label = "trailingEdge" + str(i) obj = FreeCAD.ActiveDocument.addObject( "Part::FeaturePython", "WingPanel" + str(i)) WingPanel(obj, _rootRib, myObj0, _envelope, myObj1, myObj2, 200, 100, 100, 0, 0) #WingPanel(obj,_rootRib,_path,_envelope,myObj1,myObj2,200,100,100,0,0) #WingPanel(obj,_rootRib,_path,_envelope,leadInEdges[i],trailingEdges[i],200,100,100,0,0) #WingPanel(obj,_rootRib,_path,leadInEdges[i],trailingEdges[i],200,100,100,0,0) ViewProviderPanel(obj.ViewObject) b.append(obj) FreeCAD.ActiveDocument.recompute() else: #---------------------création des nervures temporaires #_rootRib=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","RibRoot_") #WingRib(_rootRib,"/Users/fredericnivoix/Library/Preferences/FreeCAD/Mod/AirPlaneDesign/wingribprofil/naca/naca2412.dat",False,0,200,0,0,0,0,0,0) #ViewProviderWingRib(_rootRib.ViewObject) #_tipRib=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","RibTip_") #WingRib(_tipRib,"/Users/fredericnivoix/Library/Preferences/FreeCAD/Mod/AirPlaneDesign/wingribprofil/naca/naca2412.dat",False,0,200,0,500,0,0,0,0) #ViewProviderWingRib(_tipRib.ViewObject) #---------- obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "WingPanel") WingPanel(obj, None, None, None, None, None, 200, 100, 100, 0, 0) ViewProviderPanel(obj.ViewObject) if selection: #selection==None : try: #b=base.WingPanels #b.append(obj) base.WingPanels = b except: print("The selection is not a wing") FreeCAD.Gui.activeDocument().activeView().viewAxonometric() FreeCAD.Gui.SendMsgToActiveView("ViewFit")