def snapToPolar(self,point,last): "snaps to polar lines from the given point" if self.isEnabled('ortho'): if last: vecs = [] if hasattr(FreeCAD,"DraftWorkingPlane"): ax = [FreeCAD.DraftWorkingPlane.u, FreeCAD.DraftWorkingPlane.v, FreeCAD.DraftWorkingPlane.axis] else: ax = [FreeCAD.Vector(1,0,0), FreeCAD.Vector(0,1,0), FreeCAD.Vector(0,0,1)] for a in self.polarAngles: if a == 90: vecs.extend([ax[0],fcvec.neg(ax[0])]) vecs.extend([ax[1],fcvec.neg(ax[1])]) else: v = fcvec.rotate(ax[0],math.radians(a),ax[2]) vecs.extend([v,fcvec.neg(v)]) v = fcvec.rotate(ax[1],math.radians(a),ax[2]) vecs.extend([v,fcvec.neg(v)]) for v in vecs: de = Part.Line(last,last.add(v)).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('ortho') return np,de return point,None
def getbase(wire): "returns a full shape from a base wire" dvec = fcgeo.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(obj.Width) w2 = fcgeo.offsetWire(wire, dvec) sh = fcgeo.bind(wire, w2) elif obj.Align == "Right": dvec = dvec.multiply(obj.Width) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire, dvec) sh = fcgeo.bind(wire, w2) elif obj.Align == "Center": dvec = dvec.multiply(obj.Width / 2) w1 = fcgeo.offsetWire(wire, dvec) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire, dvec) sh = fcgeo.bind(w1, w2) # fixing self-intersections sh.fix(0.1, 0, 1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh
def getbase(wire): "returns a full shape from a base wire" dvec = fcgeo.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = fcgeo.offsetWire(wire,dvec) w1 = Part.Wire(fcgeo.sortEdges(wire.Edges)) sh = fcgeo.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire,dvec) w1 = Part.Wire(fcgeo.sortEdges(wire.Edges)) sh = fcgeo.bind(w1,w2) elif obj.Align == "Center": dvec = dvec.multiply(width/2) w1 = fcgeo.offsetWire(wire,dvec) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire,dvec) sh = fcgeo.bind(w1,w2) # fixing self-intersections sh.fix(0.1,0,1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh
def removeShape(objs,mark=True): '''takes an arch object (wall or structure) built on a cubic shape, and removes the inner shape, keeping its length, width and height as parameters.''' from draftlibs import fcgeo if not isinstance(objs,list): objs = [objs] for obj in objs: if fcgeo.isCubic(obj.Shape): dims = fcgeo.getCubicDimensions(obj.Shape) if dims: name = obj.Name tp = Draft.getType(obj) print tp if tp == "Structure": FreeCAD.ActiveDocument.removeObject(name) import ArchStructure str = ArchStructure.makeStructure(length=dims[1],width=dims[2],height=dims[3],name=name) str.Placement = dims[0] elif tp == "Wall": FreeCAD.ActiveDocument.removeObject(name) import ArchWall length = dims[1] width = dims[2] v1 = Vector(length/2,0,0) v2 = fcvec.neg(v1) v1 = dims[0].multVec(v1) v2 = dims[0].multVec(v2) line = Draft.makeLine(v1,v2) wal = ArchWall.makeWall(line,width=width,height=dims[3],name=name) else: if mark: obj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0)
def getClosestAxis(self, point): "returns which of the workingplane axes is closest from the given vector" ax = point.getAngle(self.u) ay = point.getAngle(self.v) az = point.getAngle(self.axis) bx = point.getAngle(fcvec.neg(self.u)) by = point.getAngle(fcvec.neg(self.v)) bz = point.getAngle(fcvec.neg(self.axis)) b = min(ax, ay, az, bx, by, bz) if b in [ax, bx]: return "x" elif b in [ay, by]: return "y" elif b in [az, bz]: return "z" else: return None
def getClosestAxis(self,point): "returns which of the workingplane axes is closest from the given vector" ax = point.getAngle(self.u) ay = point.getAngle(self.v) az = point.getAngle(self.axis) bx = point.getAngle(fcvec.neg(self.u)) by = point.getAngle(fcvec.neg(self.v)) bz = point.getAngle(fcvec.neg(self.axis)) b = min(ax,ay,az,bx,by,bz) if b in [ax,bx]: return "x" elif b in [ay,by]: return "y" elif b in [az,bz]: return "z" else: return None
def renderWireframeSVG(self, objs, direction): os = objs[:] if os: sh = os.pop().Shape for o in os: sh = sh.fuse(o.Shape) result = Drawing.projectToSVG(sh, fcvec.neg(direction)) if result: result = result.replace('stroke-width="0.35"', 'stroke-width="0.01 px"') return result return ''
def calc(self): import Part if (self.p1 != None) and (self.p2 != None): points = [fcvec.tup(self.p1,True),fcvec.tup(self.p2,True),\ fcvec.tup(self.p1,True),fcvec.tup(self.p2,True)] if self.p3 != None: p1 = self.p1 p4 = self.p2 if fcvec.equals(p1,p4): proj = None else: base = Part.Line(p1,p4).toShape() proj = fcgeo.findDistance(self.p3,base) if not proj: p2 = p1 p3 = p4 else: p2 = p1.add(fcvec.neg(proj)) p3 = p4.add(fcvec.neg(proj)) points = [fcvec.tup(p1),fcvec.tup(p2),fcvec.tup(p3),fcvec.tup(p4)] self.coords.point.setValues(0,4,points)
def updateSVG(self, obj, join=False): "encapsulates a svg fragment into a transformation node" import Part from draftlibs import fcgeo if hasattr(obj, "Source"): if obj.Source: if obj.Source.Objects: svg = "" # generating SVG linewidth = obj.LineWidth / obj.Scale if obj.RenderingMode == "Solid": # render using the Arch Vector Renderer import ArchVRM render = ArchVRM.Renderer() render.setWorkingPlane(obj.Source.Placement) render.addObjects(obj.Source.Objects) render.cut(obj.Source.Shape) svg += render.getViewSVG(linewidth=linewidth) svg += render.getSectionSVG(linewidth=linewidth * 2) # print render.info() else: # render using the Drawing module shapes = [] for o in obj.Source.Objects: if o.isDerivedFrom("Part::Feature"): shapes.append(o.Shape) if shapes: base = shape.pop() for sh in shapes: base = base.fuse(sh) svgf = Drawing.projectToSVG(base, fcvec.neg(direction)) if svgf: svgf = svgf.replace('stroke-width="0.35"', 'stroke-width="' + str(linewidth) + 'px"') svg += svgf result = "" result += '<g id="' + obj.Name + '"' result += ' transform="' result += "rotate(" + str(obj.Rotation) + "," + str(obj.X) + "," + str(obj.Y) + ") " result += "translate(" + str(obj.X) + "," + str(obj.Y) + ") " result += "scale(" + str(obj.Scale) + "," + str(-obj.Scale) + ")" result += '">\n' result += svg result += "</g>\n" # print "complete node:",result return result return ""
def update(self, point): "this function is called by the Snapper when the mouse is moved" b = self.points[0] n = FreeCAD.DraftWorkingPlane.axis bv = point.sub(b) dv = bv.cross(n) dv = fcvec.scaleTo(dv, self.Width / 2) if self.Align == "Center": self.tracker.update([b, point]) elif self.Align == "Left": self.tracker.update([b.add(dv), point.add(dv)]) else: dv = fcvec.neg(dv) self.tracker.update([b.add(dv), point.add(dv)])
def update(self,point): "this function is called by the Snapper when the mouse is moved" b = self.points[0] n = FreeCAD.DraftWorkingPlane.axis bv = point.sub(b) dv = bv.cross(n) dv = fcvec.scaleTo(dv,self.Width/2) if self.Align == "Center": self.tracker.update([b,point]) elif self.Align == "Left": self.tracker.update([b.add(dv),point.add(dv)]) else: dv = fcvec.neg(dv) self.tracker.update([b.add(dv),point.add(dv)])
def getSubVolume(self, base, width, delta=None): "returns a subvolume from a base object" import Part max_length = 0 for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w f = Part.Face(f) n = f.normalAt(0, 0) v1 = fcvec.scaleTo(n, width) f.translate(v1) v2 = fcvec.neg(v1) v2 = fcvec.scale(v1, -2) f = f.extrude(v2) if delta: f.translate(delta) return f
def getSubVolume(self,base,width,delta=None): "returns a subvolume from a base object" import Part max_length = 0 for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w f = Part.Face(f) n = f.normalAt(0,0) v1 = fcvec.scaleTo(n,width) f.translate(v1) v2 = fcvec.neg(v1) v2 = fcvec.scale(v1,-2) f = f.extrude(v2) if delta: f.translate(delta) return f
def renderOutlineSVG(self, objs, direction): plane = None plane = WorkingPlane.plane() if direction != Vector(0, 0, 0): plane.alignToPointAndAxis(Vector(0, 0, 0), fcvec.neg(direction), 0) else: direction = Vector(0, 0, -1) faces = [] for obj in objs: for face in obj.Shape.Faces: normal = face.normalAt(0, 0) if normal.getAngle(direction) > math.pi / 2: faces.append(face) print "faces:", faces if faces: base = faces.pop() for face in faces: base = base.oldFuse(face) result = self.getPath(base, plane) return result
def removeShape(objs, mark=True): '''takes an arch object (wall or structure) built on a cubic shape, and removes the inner shape, keeping its length, width and height as parameters.''' from draftlibs import fcgeo if not isinstance(objs, list): objs = [objs] for obj in objs: if fcgeo.isCubic(obj.Shape): dims = fcgeo.getCubicDimensions(obj.Shape) if dims: name = obj.Name tp = Draft.getType(obj) print tp if tp == "Structure": FreeCAD.ActiveDocument.removeObject(name) import ArchStructure str = ArchStructure.makeStructure(length=dims[1], width=dims[2], height=dims[3], name=name) str.Placement = dims[0] elif tp == "Wall": FreeCAD.ActiveDocument.removeObject(name) import ArchWall length = dims[1] width = dims[2] v1 = Vector(length / 2, 0, 0) v2 = fcvec.neg(v1) v1 = dims[0].multVec(v1) v2 = dims[0].multVec(v2) line = Draft.makeLine(v1, v2) wal = ArchWall.makeWall(line, width=width, height=dims[3], name=name) else: if mark: obj.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0)
def renderClassicSVG(self, objs, direction, base=None): """returns an svg fragment from a SectionPlane object, a direction vector and optionally a base point""" def intersection(p1, p2, p3, p4): "returns the intersection of line (p1,p2) with plane (p3,p4)" # http://paulbourke.net/geometry/planeline/ dn = p4.dot(p2.sub(p1)) if dn != 0: u = (p4.dot(p3.sub(p1))) / dn p = p1.add((p2.sub(p1)).scale(u, u, u)) return p else: # line is parallel to normal vp = fcvec.project(p3.sub(p1), p2.sub(p1)) l = vp.Length if vp.getAngle(p2.sub(p1)) > 1: l = -l return fcvec.scaleTo(p2.sub(p1), l) def getFirstIndex(list1, list2): "returns the first index from list2 where there is an item of list1" for i1 in range(len(list1)): for i2 in range(len(list2)): if list1[i1].hashCode() == list2[i2].hashCode(): return i2 return None def getLastIndex(list1, list2): "returns the last index from list2 where there is an item of list1" i = None for i1 in range(len(list1)): for i2 in range(len(list2)): if list1[i1].hashCode() == list2[i2].hashCode(): i = i2 return i def findPrevious(base, dir, faces): "returns the highest index in faces that is crossed by the given line" for i in range(len(faces) - 1, -1, -1): print "p1:", base, " p2: ", base.add(dir) obb = faces[i].BoundBox print "bo: ", obb op = intersection(base, base.add(dir), faces[i].CenterOfMass, faces[i].normalAt(0, 0)) print "int:", op if obb.isInside(op): dv = op.sub(base) if dv.getAngle(dir) < math.pi / 2: return i return None def findNext(base, dir, faces): "returns the lowest index in faces that is crossed by the given line" for i in range(len(faces)): obb = faces[i].BoundBox op = intersection(base, base.add(dir), faces[i].CenterOfMass, faces[i].normalAt(0, 0)) if obb.isInside(op): dv = op.sub(base) if dv.getAngle(dir) > math.pi / 2: return i return None print "getting representation at ", direction, " =======================================>" # using Draft WorkingPlane plane = None plane = WorkingPlane.plane() if direction != Vector(0, 0, 0): plane.alignToPointAndAxis(Vector(0, 0, 0), fcvec.neg(direction), 0) else: direction = Vector(0, 0, -1) print "plane:", plane sortedFaces = [] if not base: # getting the base point = first point from the bounding box bb = FreeCAD.BoundBox() for o in objs: bb.add(o.Shape.BoundBox) rad = bb.DiagonalLength / 2 rv = bb.Center.add(direction) rv = fcvec.scaleTo(rv, rad) rv = fcvec.neg(rv) base = bb.Center.add(rv) print "base:", base # getting faces unsortedFaces = [] notFoundFaces = [] for o in objs: unsortedFaces.append(o.Name) unsortedFaces.extend(o.Shape.Faces[:]) print "analyzing ", len(unsortedFaces), " faces" for face in unsortedFaces: if isinstance(face, str): print "OBJECT ", face, " =======================================>" continue print "testing face ", unsortedFaces.index(face) # testing if normal points outwards normal = face.normalAt(0, 0) if normal.getAngle(direction) <= math.pi / 2: print "normal pointing outwards" continue fprev = 0 fnext = len(sortedFaces) notFound = True print "checking ", len(face.Vertexes), " verts" for v in face.Vertexes: vprev = findPrevious(v.Point, direction, sortedFaces) vnext = findNext(v.Point, direction, sortedFaces) print "temp indexes:", vprev, vnext if (vprev != None): notfound = False if (vprev > fprev): fprev = vprev if (vnext != None): notfound = False if (vnext < fnext): fnext = vnext print "fprev:", fprev print "fnext:", fnext print "notFound", notFound if fnext < fprev: raise "Error, impossible index" elif fnext == fprev: sortedFaces.insert(fnext, face) else: sortedFaces.insert(fnext, face) print len(sortedFaces), " sorted faces:", sortedFaces # building SVG representation in correct order svg = '' for f in sortedFaces: svg += self.getPath(f, plane) return svg