def getBase(self,obj,wire,normal,width,height): "returns a full shape from a base wire" import DraftGeomUtils,Part flat = False if hasattr(obj.ViewObject,"DisplayMode"): flat = (obj.ViewObject.DisplayMode == "Flat 2D") dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": dvec = dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.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 = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": dvec = dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.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 getGlobalCoords(self, point): "returns the global coordinates of the given point, taken relatively to this working plane" vx = DraftVecUtils.scale(self.u, point.x) vy = DraftVecUtils.scale(self.v, point.y) vz = DraftVecUtils.scale(self.axis, point.z) pt = (vx.add(vy)).add(vz) return pt.add(self.position)
def align(self,basepoint,align,widthvec): "moves a given basepoint according to the alignment" if align == "Center": basepoint = basepoint.add(DraftVecUtils.scale(widthvec,-0.5)) elif align == "Right": basepoint = basepoint.add(DraftVecUtils.scale(widthvec,-1)) return basepoint
def snapToPolar(self,point,last): "snaps to polar lines from the given point" if self.isEnabled('ortho') and (not self.mask): 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],ax[0].negative()]) vecs.extend([ax[1],ax[1].negative()]) else: v = DraftVecUtils.rotate(ax[0],math.radians(a),ax[2]) vecs.extend([v,v.negative()]) v = DraftVecUtils.rotate(ax[1],math.radians(a),ax[2]) vecs.extend([v,v.negative()]) for v in vecs: de = Part.Line(last,last.add(v)).toShape() np = self.getPerpendicular(de,point) if ((self.radius == 0) and (point.sub(last).getAngle(v) < 0.087)) \ or ((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 makeStraightStairsWithLanding(self,obj,edge): "builds a straight staircase with a landing in the middle" if obj.NumberOfSteps < 3: return import Part,DraftGeomUtils v = DraftGeomUtils.vec(edge) reslength = edge.Length - obj.Width.Value vLength = DraftVecUtils.scaleTo(v,float(reslength)/(obj.NumberOfSteps-2)) vLength = Vector(vLength.x,vLength.y,0) vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),obj.Width.Value) p1 = edge.Vertexes[0].Point if round(v.z,Draft.precision()) != 0: h = v.z else: h = obj.Height.Value hstep = h/obj.NumberOfSteps landing = obj.NumberOfSteps/2 p2 = p1.add(DraftVecUtils.scale(vLength,landing-1).add(Vector(0,0,landing*hstep))) p3 = p2.add(DraftVecUtils.scaleTo(vLength,obj.Width.Value)) p4 = p3.add(DraftVecUtils.scale(vLength,obj.NumberOfSteps-(landing+1)).add(Vector(0,0,(obj.NumberOfSteps-landing)*hstep))) self.makeStraightStairs(obj,Part.Line(p1,p2).toShape(),landing) self.makeStraightLanding(obj,Part.Line(p2,p3).toShape()) self.makeStraightStairs(obj,Part.Line(p3,p4).toShape(),obj.NumberOfSteps-landing)
def getIncidentAngle(self, queue): global currLocation '''returns in the incident angle in radians between the current and previous moves''' # get the vector of the last move if queue[1].Name in arccommands: print queue print currLocation arcLoc = FreeCAD.Base.Vector(queue[2].X + queue[1].I, queue[2].Y + queue[1].J, currLocation['Z']) radvector = queue[1].Placement.Base.sub(arcLoc) # vector of chord from center to point # vector of line perp to chord. v1 = radvector.cross(FreeCAD.Base.Vector(0, 0, 1)) else: v1 = queue[1].Placement.Base.sub(queue[2].Placement.Base) # get the vector of the current move if queue[0].Name in arccommands: arcLoc = FreeCAD.Base.Vector((queue[1].x + queue[0].I), (queue[1].y + queue[0].J), currLocation['Z']) radvector = queue[1].Placement.Base.sub(arcLoc) # calculate arcangle v2 = radvector.cross(FreeCAD.Base.Vector(0, 0, 1)) # if switching between G2 and G3, reverse orientation if queue[1].Name in arccommands: if queue[0].Name != queue[1].Name: v2 = D.rotate2D(v2, math.radians(180)) else: v2 = queue[0].Placement.Base.sub(queue[1].Placement.Base) incident_angle = D.angle(v1, v2, FreeCAD.Base.Vector(0, 0, -1)) return incident_angle
def getGlobalRot(self, point): "Same as getGlobalCoords, but discards the WP position" vx = DraftVecUtils.scale(self.u, point.x) vy = DraftVecUtils.scale(self.v, point.y) vz = DraftVecUtils.scale(self.axis, point.z) pt = (vx.add(vy)).add(vz) return pt
def onChanged(self,obj,prop): if prop == "Height": for child in obj.Group: if Draft.getType(child) in ["Wall","Structure"]: if not child.Height.Value: #print("Executing ",child.Label) child.Proxy.execute(child) elif prop == "Placement": if hasattr(self,"oldPlacement"): if self.oldPlacement: deltap = obj.Placement.Base.sub(self.oldPlacement.Base) if deltap.Length == 0: deltap = None deltar = self.oldPlacement.Rotation.multiply(obj.Placement.Rotation) #print "Rotation",deltar.Axis,deltar.Angle if deltar.Angle < 0.0001: deltar = None for child in obj.Group: if ((not hasattr(child,"MoveWithHost")) or child.MoveWithHost) and hasattr(child,"Placement"): #print "moving ",child.Label if deltap: child.Placement.move(deltap) if deltar: #child.Placement.Rotation = child.Placement.Rotation.multiply(deltar) - not enough, child must also move # use shape methods to obtain a correct placement import Part,math shape = Part.Shape() shape.Placement = child.Placement shape.rotate(DraftVecUtils.tup(obj.Placement.Base), DraftVecUtils.tup(deltar.Axis), math.degrees(deltar.Angle)) child.Placement = shape.Placement
def update(self,point): "sets the opposite (diagonal) point of the rectangle" diagonal = point.sub(self.origin) inpoint1 = self.origin.add(DraftVecUtils.project(diagonal,self.v)) inpoint2 = self.origin.add(DraftVecUtils.project(diagonal,self.u)) self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z) self.coords.point.set1Value(2,point.x,point.y,point.z) self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z)
def __repr__(self): return ( "Workplane x=" + str(DraftVecUtils.rounded(self.u)) + " y=" + str(DraftVecUtils.rounded(self.v)) + " z=" + str(DraftVecUtils.rounded(self.axis)) )
def getRebarData(self,obj): if len(obj.InList) != 1: return if Draft.getType(obj.InList[0]) != "Structure": return if not obj.InList[0].Shape: return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = obj.InList[0] wire = obj.Base.Shape.Wires[0] axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,-1)) size = (ArchCommands.projectToVector(father.Shape.copy(),axis)).Length if hasattr(obj,"Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if hasattr(obj,"Distance"): if obj.Distance.Value: size = obj.Distance.Value if hasattr(obj,"Rounding"): if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire,radius) wires = [] if obj.Amount == 1: offset = DraftVecUtils.scaleTo(axis,size/2) wire.translate(offset) wires.append(wire) else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis,obj.OffsetStart.Value) else: baseoffset = None interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) vinterval = DraftVecUtils.scaleTo(axis,interval) for i in range(obj.Amount): if i == 0: if baseoffset: wire.translate(baseoffset) wires.append(wire) else: wire = wire.copy() wire.translate(vinterval) wires.append(wire) return [wires,obj.Diameter.Value/2]
def convert(toolpath, Side, radius, clockwise=False, Z=0.0, firstedge=None, vf=1.0, hf=2.0): '''convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.''' last = None output = "" # create the path from the offset shape for edge in toolpath: if not last: # set the first point last = edge.Vertexes[0].Point # FreeCAD.Console.PrintMessage("last pt= " + str(last)+ "\n") output += "G1 X" + str(fmt(last.x)) + " Y" + str(fmt(last.y)) + \ " Z" + str(fmt(Z)) + " F" + str(vf) + "\n" if isinstance(edge.Curve, Part.Circle): # FreeCAD.Console.PrintMessage("arc\n") arcstartpt = edge.valueAt(edge.FirstParameter) midpt = edge.valueAt( (edge.FirstParameter + edge.LastParameter) * 0.5) arcendpt = edge.valueAt(edge.LastParameter) # arcchkpt = edge.valueAt(edge.LastParameter * .99) if DraftVecUtils.equals(last, arcstartpt): startpt = arcstartpt endpt = arcendpt else: startpt = arcendpt endpt = arcstartpt center = edge.Curve.Center relcenter = center.sub(last) # FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n") # FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n") # FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n") arc_cw = check_clockwise( [(startpt.x, startpt.y), (midpt.x, midpt.y), (endpt.x, endpt.y)]) # FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n") if arc_cw: output += "G2" else: output += "G3" output += " X" + str(fmt(endpt.x)) + " Y" + \ str(fmt(endpt.y)) + " Z" + str(fmt(Z)) + " F" + str(hf) output += " I" + str(fmt(relcenter.x)) + " J" + \ str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z)) output += "\n" last = endpt # FreeCAD.Console.PrintMessage("last pt arc= " + str(last)+ "\n") else: point = edge.Vertexes[-1].Point if DraftVecUtils.equals(point, last): # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + str(fmt(point.x)) + " Y" + str(fmt(point.y)) + \ " Z" + str(fmt(Z)) + " F" + str(hf) + "\n" last = point # FreeCAD.Console.PrintMessage("line\n") # FreeCAD.Console.PrintMessage("last pt line= " + str(last)+ "\n") return output
def edge_to_path(lastpt, edge, Z): if isinstance(edge.Curve, Part.Circle): # FreeCAD.Console.PrintMessage("arc\n") arcstartpt = edge.valueAt(edge.FirstParameter) midpt = edge.valueAt( (edge.FirstParameter + edge.LastParameter) * 0.5) arcendpt = edge.valueAt(edge.LastParameter) # arcchkpt = edge.valueAt(edge.LastParameter * .99) if DraftVecUtils.equals(lastpt, arcstartpt): startpt = arcstartpt endpt = arcendpt else: startpt = arcendpt endpt = arcstartpt center = edge.Curve.Center relcenter = center.sub(lastpt) # start point and end point fall together in the given output precision? if fmt(startpt.x) == fmt(endpt.x) and fmt(startpt.y) == fmt(endpt.y): if edge.Length < 0.5 * 2 * math.pi * edge.Curve.Radius: # because it is a very small circle -> omit, as that gcode would produce a full circle return endpt, "" else: # it is an actual full circle, emit a line for this pass # FreeCAD.Console.PrintMessage("arc startpt= " + str(startpt)+ "\n") # FreeCAD.Console.PrintMessage("arc midpt= " + str(midpt)+ "\n") # FreeCAD.Console.PrintMessage("arc endpt= " + str(endpt)+ "\n") arc_cw = check_clockwise( [(startpt.x, startpt.y), (midpt.x, midpt.y), (endpt.x, endpt.y)]) # FreeCAD.Console.PrintMessage("arc_cw="+ str(arc_cw)+"\n") if arc_cw: output = "G2" else: output = "G3" output += " X" + str(fmt(endpt.x)) + " Y" + \ str(fmt(endpt.y)) + " Z" + str(fmt(Z)) + " F" + str(hf) output += " I" + str(fmt(relcenter.x)) + " J" + \ str(fmt(relcenter.y)) + " K" + str(fmt(relcenter.z)) output += "\n" lastpt = endpt # FreeCAD.Console.PrintMessage("last pt arc= " + str(lastpt)+ "\n") else: point = edge.Vertexes[-1].Point if DraftVecUtils.equals(point, lastpt): # edges can come flipped point = edge.Vertexes[0].Point output = "G1 X" + str(fmt(point.x)) + " Y" + str(fmt(point.y)) + \ " Z" + str(fmt(Z)) + " F" + str(hf) + "\n" lastpt = point # FreeCAD.Console.PrintMessage("line\n") # FreeCAD.Console.PrintMessage("last pt line= " + str(lastpt)+ "\n") return lastpt, output
def getProj(vec, plane): if not plane: return vec nx = DraftVecUtils.project(vec,plane.u) lx = nx.Length if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx ny = DraftVecUtils.project(vec,plane.v) ly = ny.Length if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly #if techdraw: buggy - we now simply do it at the end # ly = -ly return Vector(lx,ly,0)
def projectPointOld(self, p, direction=None): '''project point onto plane, default direction is orthogonal. Obsolete''' if not direction: direction = self.axis t = Vector(direction) #t.normalize() a = round(t.getAngle(self.axis),DraftVecUtils.precision()) pp = round((math.pi)/2,DraftVecUtils.precision()) if a == pp: return p t.multiply(self.offsetToPoint(p, direction)) return p.add(t)
def getCoupleFacesEquerre(self, faces): listeCouple = [] lenfaces = len(faces) faces.append(faces[0]) for n in range(lenfaces): norm2 = faces[n+1].normalAt(0,0) norm1 = faces[n].normalAt(0,0) norm0 = faces[n-1].normalAt(0,0) if abs(round(math.degrees(DraftVecUtils.angle(norm1,norm0)))) == 90.: listeCouple.append([faces[n],faces[n-1]]) if abs(round(math.degrees(DraftVecUtils.angle(norm1,norm2)))) == 90.: listeCouple.append([faces[n],faces[n+1]]) return listeCouple
def isInside(self,point): "returns True if the given point is inside the rectangle" vp = point.sub(self.p1()) uv = self.p2().sub(self.p1()) vv = self.p4().sub(self.p1()) uvp = DraftVecUtils.project(vp,uv) vvp = DraftVecUtils.project(vp,vv) if uvp.getAngle(uv) < 1: if vvp.getAngle(vv) < 1: if uvp.Length <= uv.Length: if vvp.Length <= vv.Length: return True return False
def calc(self): import Part if (self.p1 != None) and (self.p2 != None): points = [ DraftVecUtils.tup(self.p1, True), DraftVecUtils.tup(self.p2, True), DraftVecUtils.tup(self.p1, True), DraftVecUtils.tup(self.p2, True), ] if self.p3 != None: p1 = self.p1 p4 = self.p2 if DraftVecUtils.equals(p1, p4): proj = None else: base = Part.Line(p1, p4).toShape() proj = DraftGeomUtils.findDistance(self.p3, base) if not proj: p2 = p1 p3 = p4 else: p2 = p1.add(proj.negative()) p3 = p4.add(proj.negative()) points = [DraftVecUtils.tup(p1), DraftVecUtils.tup(p2), DraftVecUtils.tup(p3), DraftVecUtils.tup(p4)] self.coords.point.setValues(0, 4, points)
def isSameEdge(e1, e2): """isSameEdge(e1,e2): return True if the 2 edges are both lines or arcs/circles and have the same points - inspired by Yorik's function isSameLine""" if not (isinstance(e1.Curve, Part.Line) or isinstance(e1.Curve, Part.Circle)): return False if not (isinstance(e2.Curve, Part.Line) or isinstance(e2.Curve, Part.Circle)): return False if type(e1.Curve) != type(e2.Curve): return False if isinstance(e1.Curve, Part.Line): if (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[0].Point)) and \ (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[-1].Point)): return True elif (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[0].Point)) and \ (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[-1].Point)): return True if isinstance(e1.Curve, Part.Circle): center = False radius = False endpts = False if e1.Curve.Center == e2.Curve.Center: center = True if e1.Curve.Radius == e2.Curve.Radius: radius = True if (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[0].Point)) and \ (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[-1].Point)): endpts = True elif (DraftVecUtils.equals(e1.Vertexes[-1].Point, e2.Vertexes[0].Point)) and \ (DraftVecUtils.equals(e1.Vertexes[0].Point, e2.Vertexes[-1].Point)): endpts = True if (center and radius and endpts): return True return False
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 = DraftVecUtils.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 = DraftVecUtils.neg(dv) self.tracker.update([b.add(dv),point.add(dv)])
def projectPoint(self, p, direction=None): """project point onto plane, default direction is orthogonal""" if not direction: direction = self.axis lp = self.getLocalCoords(p) gp = self.getGlobalCoords(Vector(lp.x, lp.y, 0)) a = direction.getAngle(gp.sub(p)) if a > math.pi / 2: direction = DraftVecUtils.neg(direction) a = math.pi - a ld = self.getLocalRot(direction) gd = self.getGlobalRot(Vector(ld.x, ld.y, 0)) hyp = abs(math.tan(a) * lp.z) return gp.add(DraftVecUtils.scaleTo(gd, hyp))
def createGeometry(self,obj): import Part, DraftGeomUtils pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if hasattr(obj,"WindowParts"): if obj.WindowParts and (len(obj.WindowParts)%5 == 0): shapes = [] for i in range(len(obj.WindowParts)/5): wires = [] wstr = obj.WindowParts[(i*5)+2].split(',') for s in wstr: j = int(s[4:]) if obj.Base.Shape.Wires: if len(obj.Base.Shape.Wires) >= j: wires.append(obj.Base.Shape.Wires[j]) if wires: max_length = 0 for w in wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength ext = w wires.remove(ext) shape = Part.Face(ext) norm = shape.normalAt(0,0) thk = float(obj.WindowParts[(i*5)+3]) if thk: exv = DraftVecUtils.scaleTo(norm,thk) shape = shape.extrude(exv) for w in wires: f = Part.Face(w) f = f.extrude(exv) shape = shape.cut(f) if obj.WindowParts[(i*5)+4]: zof = float(obj.WindowParts[(i*5)+4]) if zof: zov = DraftVecUtils.scaleTo(norm,zof) shape.translate(zov) shapes.append(shape) if shapes: base = Part.makeCompound(shapes) if not DraftGeomUtils.isNull(pl): base.Placement = pl base = self.processSubShapes(obj,base) if base: if not base.isNull(): obj.Shape = base
def getCutVolume(cutplane, shapes): """getCutVolume(cutplane,shapes): returns a cut face and a cut volume from the given shapes and the given cutting plane""" import Part placement = FreeCAD.Placement(cutplane.Placement) # building boundbox bb = shapes[0].BoundBox for sh in shapes[1:]: bb.add(sh.BoundBox) bb.enlarge(1) um = vm = wm = 0 ax = placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) u = placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) v = placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) if not bb.isCutPlane(placement.Base, ax): FreeCAD.Console.PrintMessage(str(translate("Arch", "No objects are cut by the plane"))) return None, None, None else: corners = [ FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMax), ] for c in corners: dv = c.sub(placement.Base) um1 = DraftVecUtils.project(dv, u).Length um = max(um, um1) vm1 = DraftVecUtils.project(dv, v).Length vm = max(vm, vm1) wm1 = DraftVecUtils.project(dv, ax).Length wm = max(wm, wm1) p1 = FreeCAD.Vector(-um, vm, 0) p2 = FreeCAD.Vector(um, vm, 0) p3 = FreeCAD.Vector(um, -vm, 0) p4 = FreeCAD.Vector(-um, -vm, 0) cutface = Part.makePolygon([p1, p2, p3, p4, p1]) cutface = Part.Face(cutface) cutface.Placement = placement cutnormal = DraftVecUtils.scaleTo(ax, wm) cutvolume = cutface.extrude(cutnormal) cutnormal = DraftVecUtils.neg(cutnormal) invcutvolume = cutface.extrude(cutnormal) return cutface, cutvolume, invcutvolume
def getLocalRot(self,point): "Same as getLocalCoords, but discards the WP position" xv = DraftVecUtils.project(point,self.u) x = xv.Length if xv.getAngle(self.u) > 1: x = -x yv = DraftVecUtils.project(point,self.v) y = yv.Length if yv.getAngle(self.v) > 1: y = -y zv = DraftVecUtils.project(point,self.axis) z = zv.Length if zv.getAngle(self.axis) > 1: z = -z return Vector(x,y,z)
def getPlacement(entity): "returns a placement from the given entity" if DEBUG: print "getting placement ",entity if not entity: return None pl = None if entity.type == "IFCAXIS2PLACEMENT3D": x = getVector(entity.RefDirection) z = getVector(entity.Axis) y = z.cross(x) loc = getVector(entity.Location) m = DraftVecUtils.getPlaneRotation(x,y,z) pl = FreeCAD.Placement(m) pl.move(loc) elif entity.type == "IFCLOCALPLACEMENT": pl = getPlacement(entity.PlacementRelTo) relpl = getPlacement(entity.RelativePlacement) if pl and relpl: pl = relpl.multiply(pl) elif relpl: pl = relpl elif entity.type == "IFCCARTESIANPOINT": loc = getVector(entity) pl = FreeCAD.Placement() pl.move(loc) if DEBUG: print "made placement for",entity.id,":",pl return pl
def rotateSel(sel, n): p = sel.Object.Placement loc = sel.Object.Placement.Base r = axis.cross(n) # rotation axis a = DraftVecUtils.angle(n, axis, r) * 180 / math.pi PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a)) Draft.rotate(sel.Object, a, axis=r)
def getPoint(self,point=None,obj=None): "this function is called by the snapper when it has a 3D point" self.tracker.finalize() if point == None: return FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Structure"))) FreeCADGui.addModule("Arch") if self.Profile: pr = Presets[self.Profile] FreeCADGui.doCommand('p = Arch.makeProfile('+str(pr[2])+','+str(pr[3])+','+str(pr[4])+','+str(pr[5])+')') if self.Length == pr[2]: # vertical FreeCADGui.doCommand('s = Arch.makeStructure(p,height='+str(self.Height)+')') else: # horizontal FreeCADGui.doCommand('s = Arch.makeStructure(p,height='+str(self.Length)+')') FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)') FreeCADGui.doCommand('s.Profile = "'+pr[1]+'"') else: FreeCADGui.doCommand('s = Arch.makeStructure(length='+str(self.Length)+',width='+str(self.Width)+',height='+str(self.Height)+')') FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point)) FreeCADGui.doCommand('s.Placement.Rotation=FreeCAD.DraftWorkingPlane.getRotation().Rotation') FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() if self.continueCmd: self.Activated()
def getRoofPaneProject(self, i): self.ptsPaneProject=[] profilCurrent = self.findProfil(i) profilBack1 = self.findProfil(i-1) profilNext1 = self.findProfil(i+1) if profilCurrent["angle"] != 90.: if profilNext1["angle"] == 90. : self.nextPignon(i) elif profilNext1["height"] == profilCurrent["height"] : self.nextSameHeight(i) elif profilNext1["height"] < profilCurrent["height"] : self.nextSmaller(i) elif profilNext1["height"] > profilCurrent["height"] : self.nextHigher(i) else: print("Arch Roof : Case Not implemented") if profilBack1["angle"] == 90. : self.backPignon(i) elif profilBack1["height"] == profilCurrent["height"] : self.backSameHeight(i) elif profilBack1["height"] < profilCurrent["height"] : self.backSmaller(i) elif profilBack1["height"] > profilCurrent["height"] : self.backHigher(i) else: print("Arch Roof : Case Not implemented") else: self.ptsPaneProject=[] self.ptsPaneProject = DraftVecUtils.removeDoubles(self.ptsPaneProject) profilCurrent["points"] = self.ptsPaneProject
def execute(self, obj): if obj.Source: if hasattr(obj.Source.Proxy,"BaseProfile"): p = obj.Source.Proxy.BaseProfile n = obj.Source.Proxy.ExtrusionVector import Drawing svg1 = "" svg2 = "" result = "" svg1 = Drawing.projectToSVG(p,DraftVecUtils.neg(n)) if svg1: w = str(obj.LineWidth/obj.Scale) #don't let linewidth be influenced by the scale... svg1 = svg1.replace('stroke-width="0.35"','stroke-width="'+w+'"') svg1 = svg1.replace('stroke-width="1"','stroke-width="'+w+'"') svg1 = svg1.replace('stroke-width:0.01','stroke-width:'+w) svg1 = svg1.replace('scale(1,-1)','scale('+str(obj.Scale)+',-'+str(obj.Scale)+')') if obj.Source.Tag: svg2 = '<text id="tag'+obj.Name+'"' svg2 += ' fill="'+Draft.getrgb(obj.TextColor)+'"' svg2 += ' font-size="'+str(obj.FontSize)+'"' svg2 += ' style="text-anchor:start;text-align:left;' svg2 += ' font-family:'+obj.FontName+'" ' svg2 += ' transform="translate(' + str(obj.TextX) + ',' + str(obj.TextY) + ')">' svg2 += '<tspan>'+obj.Source.Tag+'</tspan></text>\n' 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 += '">\n ' result += svg1 result += svg2 result += '</g>' obj.ViewResult = result
def rotate(self, axis, angle): "rotates the ghost of a given angle" self.trans.rotation.setValue(coin.SbVec3f(DraftVecUtils.tup(axis)), angle)
def calcEdgeGeometry(self, edges, i): self.profilsDico[i]["edge"] = edges[i] vec = edges[i].Vertexes[-1].Point.sub(edges[i].Vertexes[0].Point) self.profilsDico[i]["vec"] = vec rot = math.degrees(DraftVecUtils.angle(vec)) self.profilsDico[i]["rot"] = rot
def onChanged(self, vobj, prop): if prop == "LineColor": if hasattr(vobj, "LineColor"): l = vobj.LineColor self.mat1.diffuseColor.setValue([l[0], l[1], l[2]]) self.mat2.diffuseColor.setValue([l[0], l[1], l[2]]) elif prop == "Transparency": if hasattr(vobj, "Transparency"): self.mat2.transparency.setValue(vobj.Transparency / 100.0) elif prop in ["DisplayLength", "DisplayHeight", "ArrowSize"]: if hasattr(vobj, "DisplayLength") and hasattr( vobj, "DisplayHeight"): ld = vobj.DisplayLength.Value / 2 hd = vobj.DisplayHeight.Value / 2 elif hasattr(vobj, "DisplaySize"): # old objects ld = vobj.DisplaySize.Value / 2 hd = vobj.DisplaySize.Value / 2 else: ld = 1 hd = 1 verts = [] fverts = [] for v in [[-ld, -hd], [ld, -hd], [ld, hd], [-ld, hd]]: if hasattr(vobj, "ArrowSize"): l1 = vobj.ArrowSize.Value if vobj.ArrowSize.Value > 0 else 0.1 else: l1 = 0.1 l2 = l1 / 3 pl = FreeCAD.Placement(vobj.Object.Placement) p1 = pl.multVec(Vector(v[0], v[1], 0)) p2 = pl.multVec(Vector(v[0], v[1], -l1)) p3 = pl.multVec(Vector(v[0] - l2, v[1], -l1 + l2)) p4 = pl.multVec(Vector(v[0] + l2, v[1], -l1 + l2)) p5 = pl.multVec(Vector(v[0], v[1] - l2, -l1 + l2)) p6 = pl.multVec(Vector(v[0], v[1] + l2, -l1 + l2)) verts.extend([[p1.x, p1.y, p1.z], [p2.x, p2.y, p2.z]]) fverts.append([p1.x, p1.y, p1.z]) verts.extend([[p2.x, p2.y, p2.z], [p3.x, p3.y, p3.z], [p4.x, p4.y, p4.z], [p2.x, p2.y, p2.z]]) verts.extend([[p2.x, p2.y, p2.z], [p5.x, p5.y, p5.z], [p6.x, p6.y, p6.z], [p2.x, p2.y, p2.z]]) verts.extend(fverts + [fverts[0]]) self.lcoords.point.setValues(verts) self.fcoords.point.setValues(fverts) elif prop == "LineWidth": self.drawstyle.lineWidth = vobj.LineWidth elif prop in ["CutView", "CutMargin"]: if hasattr(vobj, "CutView") and FreeCADGui.ActiveDocument.ActiveView: sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() if vobj.CutView: if self.clip: sg.removeChild(self.clip) self.clip = None for o in Draft.getGroupContents(vobj.Object.Objects, walls=True): if hasattr(o.ViewObject, "Lighting"): o.ViewObject.Lighting = "One side" self.clip = coin.SoClipPlane() self.clip.on.setValue(True) norm = vobj.Object.Proxy.getNormal(vobj.Object) mp = vobj.Object.Shape.CenterOfMass mp = DraftVecUtils.project(mp, norm) dist = mp.Length #- 0.1 # to not clip exactly on the section object norm = norm.negative() marg = 1 if hasattr(vobj, "CutMargin"): marg = vobj.CutMargin.Value if mp.getAngle(norm) > 1: dist += marg dist = -dist else: dist -= marg plane = coin.SbPlane(coin.SbVec3f(norm.x, norm.y, norm.z), dist) self.clip.plane.setValue(plane) sg.insertChild(self.clip, 0) else: if self.clip: sg.removeChild(self.clip) self.clip = None return
def __repr__(self): return "Workplane x=" + str(DraftVecUtils.rounded( self.u)) + " y=" + str(DraftVecUtils.rounded( self.v)) + " z=" + str(DraftVecUtils.rounded(self.axis))
def getPerpendicular(self, edge, pt): "returns a point on an edge, perpendicular to the given point" dv = pt.sub(edge.Vertexes[0].Point) nv = DraftVecUtils.project(dv, DraftGeomUtils.vec(edge)) np = (edge.Vertexes[0].Point).add(nv) return np
def rotate(objectslist, angle, center=App.Vector(0, 0, 0), axis=App.Vector(0, 0, 1), copy=False): """rotate(objects,angle,[center,axis,copy]) Rotates the objects contained in objects (that can be a list of objects or an object) of the given angle (in degrees) around the center, using axis as a rotation axis. Parameters ---------- objectslist : list angle : rotation angle (in degrees) center : Base.Vector axis : Base.Vector If axis is omitted, the rotation will be around the vertical Z axis. copy : bool If copy is True, the actual objects are not moved, but copies are created instead. Return ---------- The objects (or their copies) are returned. """ import Part utils.type_check([(copy, bool)], "rotate") if not isinstance(objectslist, list): objectslist = [objectslist] objectslist.extend(groups.get_movable_children(objectslist)) newobjlist = [] newgroups = {} objectslist = utils.filter_objects_for_modifiers(objectslist, copy) if copy: doc = App.ActiveDocument for obj in objectslist: if obj.isDerivedFrom("App::DocumentObjectGroup") \ and obj.Name not in newgroups.keys(): newgroups[obj.Name] = doc.addObject( obj.TypeId, utils.get_real_name(obj.Name)) for obj in objectslist: newobj = None # real_center and real_axis are introduced to take into account # the possibility that object is inside an App::Part if hasattr(obj, "getGlobalPlacement"): ci = obj.getGlobalPlacement().inverse().multVec(center) real_center = obj.Placement.multVec(ci) ai = obj.getGlobalPlacement().inverse().Rotation.multVec(axis) real_axis = obj.Placement.Rotation.multVec(ai) else: real_center = center real_axis = axis if obj.isDerivedFrom("App::Annotation"): # TODO: this is very different from how move handle annotations # maybe we can uniform the two methods if copy: newobj = make_copy.make_copy(obj) else: newobj = obj if axis.normalize() == App.Vector(1, 0, 0): newobj.ViewObject.RotationAxis = "X" newobj.ViewObject.Rotation = angle elif axis.normalize() == App.Vector(0, 1, 0): newobj.ViewObject.RotationAxis = "Y" newobj.ViewObject.Rotation = angle elif axis.normalize() == App.Vector(0, -1, 0): newobj.ViewObject.RotationAxis = "Y" newobj.ViewObject.Rotation = -angle elif axis.normalize() == App.Vector(0, 0, 1): newobj.ViewObject.RotationAxis = "Z" newobj.ViewObject.Rotation = angle elif axis.normalize() == App.Vector(0, 0, -1): newobj.ViewObject.RotationAxis = "Z" newobj.ViewObject.Rotation = -angle elif utils.get_type(obj) == "Point": if copy: newobj = make_copy.make_copy(obj) else: newobj = obj v = App.Vector(newobj.X, newobj.Y, newobj.Z) rv = v.sub(real_center) rv = DraftVecUtils.rotate(rv, math.radians(angle), real_axis) v = real_center.add(rv) newobj.X = v.x newobj.Y = v.y newobj.Z = v.z elif obj.isDerivedFrom("App::DocumentObjectGroup"): if copy: newobj = newgroups[obj.Name] else: newobj = obj elif hasattr(obj, "Placement"): # App.Console.PrintMessage("placement rotation\n") if copy: newobj = make_copy.make_copy(obj) else: newobj = obj # Workaround for `faulty` implementation of Base.Placement.rotate(center, axis, angle). # See: https://forum.freecadweb.org/viewtopic.php?p=613196#p613196 offset_rotation = App.Placement(App.Vector(0, 0, 0), App.Rotation(real_axis, angle), real_center) newobj.Placement = offset_rotation * newobj.Placement elif hasattr(obj, "Shape"): if copy: newobj = make_copy.make_copy(obj) else: newobj = obj shape = newobj.Shape.copy() shape.rotate(real_center, real_axis, angle) newobj.Shape = shape if newobj is not None: newobjlist.append(newobj) if copy: for parent in obj.InList: if parent.isDerivedFrom("App::DocumentObjectGroup") \ and (parent in objectslist): newgroups[parent.Name].addObject(newobj) if utils.get_type(parent) == "Layer": parent.Proxy.addObject(parent, newobj) if copy and utils.get_param("selectBaseObjects", False): gui_utils.select(objectslist) else: gui_utils.select(newobjlist) if len(newobjlist) == 1: return newobjlist[0] return newobjlist
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 trimObjects(self, objectslist): """Attempt to trim two objects together.""" import Part import DraftGeomUtils wires = [] for obj in objectslist: if not utils.getType(obj) in ["Wire", "Circle"]: _err( translate( "draft", "Unable to trim these objects, " "only Draft wires and arcs are supported.")) return if len(obj.Shape.Wires) > 1: _err( translate( "draft", "Unable to trim these objects, " "too many wires")) return if len(obj.Shape.Wires) == 1: wires.append(obj.Shape.Wires[0]) else: wires.append(Part.Wire(obj.Shape.Edges)) ints = [] edge1 = None edge2 = None for i1, e1 in enumerate(wires[0].Edges): for i2, e2 in enumerate(wires[1].Edges): i = DraftGeomUtils.findIntersection(e1, e2, dts=False) if len(i) == 1: ints.append(i[0]) edge1 = i1 edge2 = i2 if not ints: _err(translate("draft", "These objects don't intersect.")) return if len(ints) != 1: _err(translate("draft", "Too many intersection points.")) return v11 = wires[0].Vertexes[0].Point v12 = wires[0].Vertexes[-1].Point v21 = wires[1].Vertexes[0].Point v22 = wires[1].Vertexes[-1].Point if DraftVecUtils.closest(ints[0], [v11, v12]) == 1: last1 = True else: last1 = False if DraftVecUtils.closest(ints[0], [v21, v22]) == 1: last2 = True else: last2 = False for i, obj in enumerate(objectslist): if i == 0: ed = edge1 la = last1 else: ed = edge2 la = last2 if utils.getType(obj) == "Wire": if la: pts = obj.Points[:ed + 1] + ints else: pts = ints + obj.Points[ed + 1:] obj.Points = pts else: vec = ints[0].sub(obj.Placement.Base) vec = obj.Placement.inverse().Rotation.multVec(vec) _x = App.Vector(1, 0, 0) _ang = -DraftVecUtils.angle(vec, obj.Placement.Rotation.multVec(_x), obj.Shape.Edges[0].Curve.Axis) ang = math.degrees(_ang) if la: obj.LastAngle = ang else: obj.FirstAngle = ang self.doc.recompute()
def getProfiles(self,obj,noplacement=False): "Returns the base profile(s) of this component, if applicable" wires = [] n,l,w,h = self.getDefaultValues(obj) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: base = obj.Base.Shape.copy() if noplacement: base.Placement = FreeCAD.Placement() if not base.Solids: if base.Faces: import DraftGeomUtils if not DraftGeomUtils.isCoplanar(base.Faces): return [] return [base] basewires = [] if not base.Wires: if len(base.Edges) == 1: import Part basewires = [Part.Wire(base.Edges)] else: basewires = base.Wires if basewires: import DraftGeomUtils,DraftVecUtils,Part for wire in 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(n) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if hasattr(obj,"Align"): if obj.Align == "Left": dvec.multiply(w) if hasattr(obj,"Offset"): if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec.multiply(w) dvec = dvec.negative() if hasattr(obj,"Offset"): if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": dvec.multiply(w/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) if sh: wires.append(sh) else: wires.append(wire) elif Draft.getType(obj) in ["Wall","Structure"]: if (Draft.getType(obj) == "Structure") and (l > h): if noplacement: h2 = h/2 or 0.5 w2 = w/2 or 0.5 v1 = Vector(-h2,-w2,0) v2 = Vector(h2,-w2,0) v3 = Vector(h2,w2,0) v4 = Vector(-h2,w2,0) else: h2 = h/2 or 0.5 w2 = w/2 or 0.5 v1 = Vector(0,-w2,-h2) v2 = Vector(0,-w2,h2) v3 = Vector(0,w2,h2) v4 = Vector(0,w2,-h2) else: l2 = l/2 or 0.5 w2 = w/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) import Part base = Part.makePolygon([v1,v2,v3,v4,v1]) return [base] return wires
def get_DXF(obj, direction=None): """getDXF(object,[direction]): returns a DXF entity from the given object. If direction is given, the object is projected in 2D.""" plane = None result = "" if obj.isDerivedFrom("Drawing::View") or obj.isDerivedFrom( "TechDraw::DrawView"): if obj.Source.isDerivedFrom("App::DocumentObjectGroup"): for o in obj.Source.Group: result += getDXF(o, obj.Direction) else: result += getDXF(obj.Source, obj.Direction) return result if direction: if isinstance(direction, App.Vector): import WorkingPlane if direction != App.Vector(0, 0, 0): plane = WorkingPlane.Plane() plane.alignToPointAndAxis(App.Vector(0, 0, 0), direction) def getProj(vec): if not plane: return vec nx = DraftVecUtils.project(vec, plane.u) ny = DraftVecUtils.project(vec, plane.v) return App.Vector(nx.Length, ny.Length, 0) if getType(obj) in ["Dimension", "LinearDimension"]: p1 = getProj(obj.Start) p2 = getProj(obj.End) p3 = getProj(obj.Dimline) result += "0\nDIMENSION\n8\n0\n62\n0\n3\nStandard\n70\n1\n" result += "10\n" + str(p3.x) + "\n20\n" + str(p3.y) + "\n30\n" + str( p3.z) + "\n" result += "13\n" + str(p1.x) + "\n23\n" + str(p1.y) + "\n33\n" + str( p1.z) + "\n" result += "14\n" + str(p2.x) + "\n24\n" + str(p2.y) + "\n34\n" + str( p2.z) + "\n" elif getType(obj) == "Annotation": p = getProj(obj.Position) count = 0 for t in obj.LabeLtext: result += "0\nTEXT\n8\n0\n62\n0\n" result += "10\n" + str( p.x) + "\n20\n" + str(p.y + count) + "\n30\n" + str(p.z) + "\n" result += "40\n1\n" result += "1\n" + str(t) + "\n" result += "7\nSTANDARD\n" count += 1 elif hasattr(obj, 'Shape'): # TODO do this the Draft way, for ex. using polylines and rectangles import Drawing import DraftVecUtils if not direction: direction = FreeCAD.Vector(0, 0, -1) if DraftVecUtils.isNull(direction): direction = FreeCAD.Vector(0, 0, -1) try: d = Drawing.projectToDXF(obj.Shape, direction) except: print("Draft.getDXF: Unable to project ", obj.Label, " to ", direction) else: result += d else: print("Draft.getDXF: Unsupported object: ", obj.Label) return result
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) == "Floor": 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 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: basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # basewires = obj.Base.Shape.Wires basewires = [ Part.Wire(cluster) for cluster in Part.getSortedClusters(obj.Base.Shape.Edges) ] if basewires and width: if (len(basewires) == 1) and layers: basewires = [basewires[0] for l in layers] layeroffset = 0 baseface = None for i, wire in enumerate(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 getCutVolume(cutplane, shapes): """getCutVolume(cutplane,shapes): returns a cut face and a cut volume from the given shapes and the given cutting plane""" import Part if not isinstance(shapes, list): shapes = [shapes] # building boundbox bb = shapes[0].BoundBox for sh in shapes[1:]: bb.add(sh.BoundBox) bb.enlarge(1) # building cutplane space placement = None um = vm = wm = 0 try: if hasattr(cutplane, "Shape"): p = cutplane.Shape.copy().Faces[0] else: p = cutplane.copy().Faces[0] except: FreeCAD.Console.PrintMessage(translate("Arch", "Invalid cutplane")) return None, None, None ce = p.CenterOfMass ax = p.normalAt(0, 0) u = p.Vertexes[1].Point.sub(p.Vertexes[0].Point).normalize() v = u.cross(ax) if not bb.isCutPlane(ce, ax): FreeCAD.Console.PrintMessage( translate("Arch", "No objects are cut by the plane")) return None, None, None else: corners = [ FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMax) ] for c in corners: dv = c.sub(ce) um1 = DraftVecUtils.project(dv, u).Length um = max(um, um1) vm1 = DraftVecUtils.project(dv, v).Length vm = max(vm, vm1) wm1 = DraftVecUtils.project(dv, ax).Length wm = max(wm, wm1) vu = DraftVecUtils.scaleTo(u, um) vui = vu.negative() vv = DraftVecUtils.scaleTo(v, vm) vvi = vv.negative() p1 = ce.add(vu.add(vvi)) p2 = ce.add(vu.add(vv)) p3 = ce.add(vui.add(vv)) p4 = ce.add(vui.add(vvi)) cutface = Part.makePolygon([p1, p2, p3, p4, p1]) cutface = Part.Face(cutface) cutnormal = DraftVecUtils.scaleTo(ax, wm) cutvolume = cutface.extrude(cutnormal) cutnormal = cutnormal.negative() invcutvolume = cutface.extrude(cutnormal) return cutface, cutvolume, invcutvolume
def compare(self, face1, face2): "zsorts two faces. Returns 1 if face1 is closer, 2 if face2 is closer, 0 otherwise" #print face1,face2 if not face1: if DEBUG: print "Warning, undefined face!" return 31 elif not face2: if DEBUG: print "Warning, undefined face!" return 32 # theory from # http://www.siggraph.org/education/materials/HyperGraph/scanline/visibility/painter.htm # and practical application http://vrm.ao2.it/ (blender vector renderer) b1 = face1[0].BoundBox b2 = face2[0].BoundBox # test 1: if faces don't overlap, no comparison possible if DEBUG: print "doing test 1" if b1.XMax < b2.XMin: return 0 if b1.XMin > b2.XMax: return 0 if b1.YMax < b2.YMin: return 0 if b1.YMin > b2.YMax: return 0 if DEBUG: print "failed, faces bboxes are not distinct" # test 2: if Z bounds dont overlap, it's easy to know the closest if DEBUG: print "doing test 2" if b1.ZMax < b2.ZMin: return 2 if b2.ZMax < b1.ZMin: return 1 if DEBUG: print "failed, faces Z are not distinct" # test 3: all verts of face1 are in front or behind the plane of face2 if DEBUG: print "doing test 3" norm = face2[0].normalAt(0, 0) behind = 0 front = 0 for v in face1[0].Vertexes: dv = v.Point.sub(face2[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv, norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print "front: ", front, " behind: ", behind if behind == len(face1[0].Vertexes): return 2 elif front == len(face1[0].Vertexes): return 1 if DEBUG: print "failed, cannot say if face 1 is in front or behind" # test 4: all verts of face2 are in front or behind the plane of face1 if DEBUG: print "doing test 4" norm = face1[0].normalAt(0, 0) behind = 0 front = 0 for v in face2[0].Vertexes: dv = v.Point.sub(face1[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv, norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print "front: ", front, " behind: ", behind if behind == len(face2[0].Vertexes): return 1 elif front == len(face2[0].Vertexes): return 2 if DEBUG: print "failed, cannot say if face 2 is in front or behind" # test 5: see if faces projections don't overlap, vertexwise if DEBUG: print "doing test 5" if not self.zOverlaps(face1, face2): return 0 elif not self.zOverlaps(face2, face1): return 0 if DEBUG: print "failed, faces are overlapping" if DEBUG: print "Houston, all tests passed, and still no results" return 0
def execute(self, obj): if self.clone(obj): return if not obj.Base: FreeCAD.Console.PrintError( "No Base, return without a rebar shape for {}.\n".format( obj.Name)) return if not obj.Base.Shape: FreeCAD.Console.PrintError( "No Shape in Base, return without a rebar shape for {}.\n". format(obj.Name)) return if not obj.Base.Shape.Wires: FreeCAD.Console.PrintError( "No Wires in Shape of Base, return without a rebar shape for {}.\n" .format(obj.Name)) return if not obj.Diameter.Value: FreeCAD.Console.PrintError( "No Diameter Value, return without a rebar shape for {}.\n". format(obj.Name)) return if not obj.Amount: return FreeCAD.Console.PrintError( "No Amount, return without a rebar shape for {}.\n".format( obj.Name)) father = obj.Host fathershape = None if not father: # support for old-style rebars if obj.InList: if hasattr(obj.InList[0], "Armatures"): if obj in obj.InList[0].Armatures: father = obj.InList[0] if father: if hasattr(father, 'Shape'): fathershape = father.Shape wire = obj.Base.Shape.Wires[0] if hasattr(obj, "Rounding"): #print(obj.Rounding) if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire, radius) bpoint, bvec = self.getBaseAndAxis(wire) if not bpoint: return axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) if fathershape: size = (ArchCommands.projectToVector(fathershape.copy(), axis)).Length else: size = 1 if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if fathershape: size = (ArchCommands.projectToVector( fathershape.copy(), axis)).Length else: size = 1 if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value spacinglist = None if hasattr(obj, "CustomSpacing"): if obj.CustomSpacing: spacinglist = strprocessOfCustomSpacing(obj.CustomSpacing) influenceArea = sum( spacinglist) - spacinglist[0] / 2 - spacinglist[-1] / 2 # Drop this check to solve issue as discussed here: https://github.com/FreeCAD/FreeCAD/pull/2550 # if (obj.OffsetStart.Value + obj.OffsetEnd.Value) > size: # return # all tests ok! if hasattr(obj, "Length"): length = getLengthOfRebar(obj) if length: obj.Length = length pl = obj.Placement import Part circle = Part.makeCircle(obj.Diameter.Value / 2, bpoint, bvec) circle = Part.Wire(circle) try: bar = wire.makePipeShell([circle], True, False, 2) basewire = wire.copy() except Part.OCCError: print("Arch: error sweeping rebar profile along the base sketch") return # building final shape shapes = [] placementlist = [] self.wires = [] rot = FreeCAD.Rotation() if obj.Amount == 1: if obj.ViewObject: barplacement = CalculatePlacement(obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.ViewObject.RebarShape) else: barplacement = CalculatePlacement(obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) if hasattr(obj, "Spacing"): obj.Spacing = 0 else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if obj.ViewObject and obj.ViewObject.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) for i in range(obj.Amount): if obj.ViewObject: barplacement = CalculatePlacement( obj.Amount, i + 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.ViewObject.RebarShape) else: barplacement = CalculatePlacement(obj.Amount, i + 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) if hasattr(obj, "Spacing"): obj.Spacing = interval # Calculate placement of bars from custom spacing. if spacinglist: placementlist[:] = [] if obj.ViewObject.RebarShape == "Stirrup": reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) # Avoid unnecessary checks to pass like. For eg.: when we have values # like influenceArea is 100.00001 and reqInflueneArea is 100 if round(influenceArea) > round(reqInfluenceArea): FreeCAD.Console.PrintWarning( "Influence area of rebars is greater than " + str(reqInfluenceArea) + ".\n") elif round(influenceArea) < round(reqInfluenceArea): FreeCAD.Console.PrintWarning( "Last span is greater that end offset.\n") for i in range(len(spacinglist)): if i == 0: barplacement = CustomSpacingPlacement( spacinglist, 1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) else: barplacement = CustomSpacingPlacement( spacinglist, i + 1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) obj.Amount = len(spacinglist) obj.Spacing = 0 obj.PlacementList = placementlist for i in range(len(obj.PlacementList)): if i == 0: bar.Placement = obj.PlacementList[i] shapes.append(bar) basewire.Placement = obj.PlacementList[i] self.wires.append(basewire) else: bar = bar.copy() bar.Placement = obj.PlacementList[i] shapes.append(bar) w = basewire.copy() w.Placement = obj.PlacementList[i] self.wires.append(w) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl obj.TotalLength = obj.Length * len(obj.PlacementList)
def getRebarData(self, obj): if not obj.Host: return if Draft.getType(obj.Host) != "Structure": return if not obj.Host.Shape: return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = obj.Host wire = obj.Base.Shape.Wires[0] if Draft.getType( obj.Base) == "Wire": # Draft Wires can have "wrong" placement import DraftGeomUtils axis = DraftGeomUtils.getNormal(obj.Base.Shape) else: axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector( 0, 0, -1)) size = (ArchCommands.projectToVector(father.Shape.copy(), axis)).Length if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value if hasattr(obj, "Rounding"): if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire, radius) wires = [] if obj.Amount == 1: offset = DraftVecUtils.scaleTo(axis, size / 2) wire.translate(offset) wires.append(wire) else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if obj.ViewObject.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) vinterval = DraftVecUtils.scaleTo(axis, interval) for i in range(obj.Amount): if i == 0: if baseoffset: wire.translate(baseoffset) wires.append(wire) else: wire = wire.copy() wire.translate(vinterval) wires.append(wire) return [wires, obj.Diameter.Value / 2]
def findIntersection(edge1, edge2, infinite1=False, infinite2=False, ex1=False, ex2=False, dts=True, findAll=False): """Return a list containing the intersection points of 2 edges. You can also feed 4 points instead of `edge1` and `edge2`. If `dts` is used, `Shape.distToShape()` is used, which can be buggy. """ def getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2): if pt1: # first check if we don't already have coincident endpoints if pt1 in [pt3, pt4]: return [pt1] elif (pt2 in [pt3, pt4]): return [pt2] norm1 = pt2.sub(pt1).cross(pt3.sub(pt1)) norm2 = pt2.sub(pt4).cross(pt3.sub(pt4)) if not DraftVecUtils.isNull(norm1): try: norm1.normalize() except Part.OCCError: return [] if not DraftVecUtils.isNull(norm2): try: norm2.normalize() except Part.OCCError: return [] if DraftVecUtils.isNull(norm1.cross(norm2)): vec1 = pt2.sub(pt1) vec2 = pt4.sub(pt3) if DraftVecUtils.isNull(vec1) or DraftVecUtils.isNull(vec2): return [] # One of the lines has zero-length try: vec1.normalize() vec2.normalize() except Part.OCCError: return [] norm3 = vec1.cross(vec2) denom = norm3.x + norm3.y + norm3.z if not DraftVecUtils.isNull(norm3) and denom != 0: k = ((pt3.z - pt1.z) * (vec2.x - vec2.y) + (pt3.y - pt1.y) * (vec2.z - vec2.x) + (pt3.x - pt1.x) * (vec2.y - vec2.z))/denom vec1.scale(k, k, k) intp = pt1.add(vec1) if infinite1 is False and not isPtOnEdge(intp, edge1): return [] if infinite2 is False and not isPtOnEdge(intp, edge2): return [] return [intp] else: return [] # Lines have same direction else: return [] # Lines aren't on same plane # First, check bound boxes if (isinstance(edge1, Part.Edge) and isinstance(edge2, Part.Edge) and (not infinite1) and (not infinite2)): if not edge1.BoundBox.intersect(edge2.BoundBox): return [] # bound boxes don't intersect # First, try to use distToShape if possible if (dts and isinstance(edge1, Part.Edge) and isinstance(edge2, Part.Edge) and (not infinite1) and (not infinite2)): dist, pts, geom = edge1.distToShape(edge2) sol = [] if round(dist, precision()) == 0: for p in pts: if p not in sol: sol.append(p[0]) return sol pt1 = None if isinstance(edge1, FreeCAD.Vector) and isinstance(edge2, FreeCAD.Vector): # we got points directly pt1 = edge1 pt2 = edge2 pt3 = infinite1 pt4 = infinite2 infinite1 = ex1 infinite2 = ex2 return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) elif (geomType(edge1) == "Line") and (geomType(edge2) == "Line"): # we have 2 straight lines pt1, pt2, pt3, pt4 = [edge1.Vertexes[0].Point, edge1.Vertexes[1].Point, edge2.Vertexes[0].Point, edge2.Vertexes[1].Point] return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) elif ((geomType(edge1) == "Circle") and (geomType(edge2) == "Line") or (geomType(edge1) == "Line") and (geomType(edge2) == "Circle")): # deals with an arc or circle and a line edges = [edge1, edge2] for edge in edges: if geomType(edge) == "Line": line = edge else: arc = edge dirVec = vec(line) dirVec.normalize() pt1 = line.Vertexes[0].Point pt2 = line.Vertexes[1].Point pt3 = arc.Vertexes[0].Point pt4 = arc.Vertexes[-1].Point center = arc.Curve.Center int = [] # first check for coincident endpoints if DraftVecUtils.equals(pt1, pt3) or DraftVecUtils.equals(pt1, pt4): if findAll: int.append(pt1) else: return [pt1] elif pt2 in [pt3, pt4]: if findAll: int.append(pt2) else: return [pt2] if DraftVecUtils.isNull(pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)): # Line and Arc are on same plane dOnLine = center.sub(pt1).dot(dirVec) onLine = FreeCAD.Vector(dirVec) onLine.scale(dOnLine, dOnLine, dOnLine) toLine = pt1.sub(center).add(onLine) if toLine.Length < arc.Curve.Radius: dOnLine = (arc.Curve.Radius**2 - toLine.Length**2)**(0.5) onLine = FreeCAD.Vector(dirVec) onLine.scale(dOnLine, dOnLine, dOnLine) int += [center.add(toLine).add(onLine)] onLine = FreeCAD.Vector(dirVec) onLine.scale(-dOnLine, -dOnLine, -dOnLine) int += [center.add(toLine).add(onLine)] elif round(toLine.Length - arc.Curve.Radius, precision()) == 0: int = [center.add(toLine)] else: return [] else: # Line isn't on Arc's plane if dirVec.dot(arc.Curve.Axis) != 0: toPlane = FreeCAD.Vector(arc.Curve.Axis) toPlane.normalize() d = pt1.dot(toPlane) if not d: return [] dToPlane = center.sub(pt1).dot(toPlane) toPlane = FreeCAD.Vector(pt1) toPlane.scale(dToPlane/d, dToPlane/d, dToPlane/d) ptOnPlane = toPlane.add(pt1) if round(ptOnPlane.sub(center).Length - arc.Curve.Radius, precision()) == 0: int = [ptOnPlane] else: return [] else: return [] if infinite1 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge1): del int[i] if infinite2 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge2): del int[i] return int elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Circle"): # deals with 2 arcs or circles cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center rad1, rad2 = edge1.Curve.Radius, edge2.Curve.Radius axis1, axis2 = edge1.Curve.Axis, edge2.Curve.Axis c2c = cent2.sub(cent1) if cent1.sub(cent2).Length == 0: # circles are concentric return [] if DraftVecUtils.isNull(axis1.cross(axis2)): if round(c2c.dot(axis1), precision()) == 0: # circles are on same plane dc2c = c2c.Length if not DraftVecUtils.isNull(c2c): c2c.normalize() if (round(rad1 + rad2 - dc2c, precision()) < 0 or round(rad1 - dc2c - rad2, precision()) > 0 or round(rad2 - dc2c - rad1, precision()) > 0): return [] else: norm = c2c.cross(axis1) if not DraftVecUtils.isNull(norm): norm.normalize() if DraftVecUtils.isNull(norm): x = 0 else: x = (dc2c**2 + rad1**2 - rad2**2) / (2*dc2c) y = abs(rad1**2 - x**2)**(0.5) c2c.scale(x, x, x) if round(y, precision()) != 0: norm.scale(y, y, y) int = [cent1.add(c2c).add(norm)] int += [cent1.add(c2c).sub(norm)] else: int = [cent1.add(c2c)] else: return [] # circles are on parallel planes else: # circles aren't on same plane axis1.normalize() axis2.normalize() U = axis1.cross(axis2) V = axis1.cross(U) dToPlane = c2c.dot(axis2) d = V.add(cent1).dot(axis2) V.scale(dToPlane/d, dToPlane/d, dToPlane/d) PtOn2Planes = V.add(cent1) planeIntersectionVector = U.add(PtOn2Planes) intTemp = findIntersection(planeIntersectionVector, edge1, True, True) int = [] for pt in intTemp: if round(pt.sub(cent2).Length-rad2, precision()) == 0: int += [pt] if infinite1 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge1): del int[i] if infinite2 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge2): del int[i] return int else: print("DraftGeomUtils: Unsupported curve type: " "(" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")") return []
def constrain(self, point, basepoint=None, axis=None): '''constrain(point,basepoint=None,axis=None: Returns a constrained point. Axis can be "x","y" or "z" or a custom vector. If None, the closest working plane axis will be picked. Basepoint is the base point used to figure out from where the point must be constrained. If no basepoint is given, the current point is used as basepoint.''' # without the Draft module fully loaded, no axes system!" if not hasattr(FreeCAD, "DraftWorkingPlane"): return point point = Vector(point) # setup trackers if needed if not self.constrainLine: self.constrainLine = DraftTrackers.lineTracker(dotted=True) # setting basepoint if not basepoint: if not self.basepoint: self.basepoint = point else: self.basepoint = basepoint delta = point.sub(self.basepoint) # setting constraint axis if self.mask: self.affinity = self.mask if not self.affinity: self.affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(delta) if isinstance(axis, FreeCAD.Vector): self.constraintAxis = axis elif axis == "x": self.constraintAxis = FreeCAD.DraftWorkingPlane.u elif axis == "y": self.constraintAxis = FreeCAD.DraftWorkingPlane.v elif axis == "z": self.constraintAxis = FreeCAD.DraftWorkingPlane.axis else: if self.affinity == "x": self.constraintAxis = FreeCAD.DraftWorkingPlane.u elif self.affinity == "y": self.constraintAxis = FreeCAD.DraftWorkingPlane.v else: self.constraintAxis = FreeCAD.DraftWorkingPlane.axis # calculating constrained point cdelta = DraftVecUtils.project(delta, self.constraintAxis) npoint = self.basepoint.add(cdelta) # setting constrain line if self.constrainLine: if point != npoint: self.constrainLine.p1(point) self.constrainLine.p2(npoint) self.constrainLine.on() else: self.constrainLine.off() return npoint
def connect(edges, closed=False): """Connect the edges in the given list by their intersections.""" nedges = [] v2 = None for i in range(len(edges)): curr = edges[i] # print("debug: DraftGeomUtils.connect edge ", i, " : ", # curr.Vertexes[0].Point, curr.Vertexes[-1].Point) if i > 0: prev = edges[i-1] else: if closed: prev = edges[-1] else: prev = None if i < (len(edges)-1): _next = edges[i+1] else: if closed: _next = edges[0] else: _next = None if prev: # print("debug: DraftGeomUtils.connect prev : ", # prev.Vertexes[0].Point, prev.Vertexes[-1].Point) # If the edge pairs has intersection and if there is prev v2 # (prev v2 was calculated intersection), do not calculate # again, just use it as current v1 - avoid chance of slight # difference in result. And, if edge pairs # has no intersection (parallel edges, line # - arc do no intersect, etc.), so just just current # edge endpoints as v1 and connect these 2 non-intersecting # edges # Seem have chance that 2 parallel edges offset same width, # result in 2 colinear edges - Wall / DraftGeomUtils # seem make them 1 edge and thus 1 vertical plane i = findIntersection(curr, prev, True, True) if i: if v2: v1 = v2 else: v1 = i[DraftVecUtils.closest(curr.Vertexes[0].Point, i)] else: v1 = curr.Vertexes[0].Point nedges.append(Part.LineSegment(v2, v1).toShape()) else: v1 = curr.Vertexes[0].Point if _next: # print("debug: DraftGeomUtils.connect _next : ", # _next.Vertexes[0].Point, _next.Vertexes[-1].Point) i = findIntersection(curr, _next, True, True) if i: v2 = i[DraftVecUtils.closest(curr.Vertexes[-1].Point, i)] else: v2 = curr.Vertexes[-1].Point else: v2 = curr.Vertexes[-1].Point if geomType(curr) == "Line": if v1 != v2: nedges.append(Part.LineSegment(v1, v2).toShape()) elif geomType(curr) == "Circle": if v1 != v2: nedges.append(Part.Arc(v1, findMidpoint(curr), v2).toShape()) try: return Part.Wire(nedges) except: print("DraftGeomUtils.connect: unable to connect edges") for e in nedges: print(e.Curve, " ", e.Vertexes[0].Point, " ", e.Vertexes[-1].Point) return None
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 isinstance(e.Curve, Part.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
def scale(objectslist, scale=App.Vector(1, 1, 1), center=App.Vector(0, 0, 0), copy=False): """scale(objects, scale, [center], copy) Scales the objects contained in objects (that can be a list of objects or an object) of the given around given center. Parameters ---------- objectlist : list scale : Base.Vector Scale factors defined by a given vector (in X, Y, Z directions). objectlist : Base.Vector Center of the scale operation. copy : bool If copy is True, the actual objects are not scaled, but copies are created instead. Return ---------- The objects (or their copies) are returned. """ if not isinstance(objectslist, list): objectslist = [objectslist] newobjlist = [] for obj in objectslist: if copy: newobj = make_copy.make_copy(obj) else: newobj = obj if hasattr(obj, 'Shape'): scaled_shape = obj.Shape.copy() m = App.Matrix() m.move(center.negative()) m.scale(scale.x, scale.y, scale.z) m.move(center) scaled_shape = scaled_shape.transformGeometry(m) if utils.get_type(obj) == "Rectangle": p = [] for v in scaled_shape.Vertexes: p.append(v.Point) pl = obj.Placement.copy() pl.Base = p[0] diag = p[2].sub(p[0]) bb = p[1].sub(p[0]) bh = p[3].sub(p[0]) nb = DraftVecUtils.project(diag, bb) nh = DraftVecUtils.project(diag, bh) if obj.Length < 0: l = -nb.Length else: l = nb.Length if obj.Height < 0: h = -nh.Length else: h = nh.Length newobj.Length = l newobj.Height = h tr = p[0].sub(obj.Shape.Vertexes[0].Point) # unused? newobj.Placement = pl elif utils.get_type(obj) == "Wire" or utils.get_type(obj) == "BSpline": for index, point in enumerate(newobj.Points): scale_vertex(newobj, index, scale, center) elif hasattr(obj, 'Shape'): newobj.Shape = scaled_shape elif hasattr(obj, "Position"): d = obj.Position.sub(center) newobj.Position = center.add( App.Vector(d.x * scale.x, d.y * scale.y, d.z * scale.z)) elif hasattr(obj, "Placement"): d = obj.Placement.Base.sub(center) newobj.Placement.Base = center.add( App.Vector(d.x * scale.x, d.y * scale.y, d.z * scale.z)) if hasattr(obj, "Height"): obj.setExpression('Height', None) obj.Height = obj.Height * scale.y if hasattr(obj, "Width"): obj.setExpression('Width', None) obj.Width = obj.Width * scale.x if hasattr(obj, "XSize"): obj.setExpression('XSize', None) obj.XSize = obj.XSize * scale.x if hasattr(obj, "YSize"): obj.setExpression('YSize', None) obj.YSize = obj.YSize * scale.y if obj.ViewObject and hasattr(obj.ViewObject, "FontSize"): obj.ViewObject.FontSize = obj.ViewObject.FontSize * scale.y if copy: gui_utils.format_object(newobj, obj) newobjlist.append(newobj) if copy and utils.get_param("selectBaseObjects", False): gui_utils.select(objectslist) else: gui_utils.select(newobjlist) if len(newobjlist) == 1: return newobjlist[0] return newobjlist
def execute(self, obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj, "ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply( baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: edges = obj.Base.Shape.Edges if hasattr(obj, "Edges"): if obj.Edges == "Vertical edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(0, 1, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] elif obj.Edges == "Horizontal edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(1, 0, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] elif obj.Edges == "Top Horizontal edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(1, 0, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] edges = sorted(edges, key=lambda x: x.CenterOfMass.z, reverse=True) z = edges[0].CenterOfMass.z edges = [ e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001 ] elif obj.Edges == "Bottom Horizontal edges": rv = obj.Base.Placement.Rotation.multVec( FreeCAD.Vector(1, 0, 0)) edges = [ e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] ] edges = sorted(edges, key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z edges = [ e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001 ] for e in edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj, "BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append( DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage( translate("Arch", "Crossing point not found in profile.") + "\n") basepoint = basepointliste[0] else: basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) angle = bvec.getAngle(axis) if round(angle, Draft.precision()) != 0: if round(angle, Draft.precision()) != round( math.pi, Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate( DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: if hasattr(obj, "Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) s = s.removeSplitter() obj.Shape = s obj.Placement = pl return obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def cut(self,cutplane): "Cuts through the shapes with a given cut plane and builds section faces" if DEBUG: print "\n\n======> Starting cut\n\n" if self.iscut: return if not self.shapes: if DEBUG: print "No objects to make sections" else: fill = (1.0,1.0,1.0,1.0) placement = FreeCAD.Placement(cutplane.Placement) # building boundbox bb = self.shapes[0][0].BoundBox for sh in self.shapes[1:]: bb.add(sh[0].BoundBox) bb.enlarge(1) um = vm = wm = 0 if not bb.isCutPlane(placement.Base,self.wp.axis): if DEBUG: print "No objects are cut by the plane" else: corners = [FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin), FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMin), FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMin), FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMin), FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMax), FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMax), FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMax), FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMax)] for c in corners: dv = c.sub(placement.Base) um1 = DraftVecUtils.project(dv,self.wp.u).Length um = max(um,um1) vm1 = DraftVecUtils.project(dv,self.wp.v).Length vm = max(vm,vm1) wm1 = DraftVecUtils.project(dv,self.wp.axis).Length wm = max(wm,wm1) p1 = FreeCAD.Vector(-um,vm,0) p2 = FreeCAD.Vector(um,vm,0) p3 = FreeCAD.Vector(um,-vm,0) p4 = FreeCAD.Vector(-um,-vm,0) cutface = Part.makePolygon([p1,p2,p3,p4,p1]) cutface = Part.Face(cutface) cutface.Placement = placement cutnormal = DraftVecUtils.scaleTo(self.wp.axis,wm) cutvolume = cutface.extrude(cutnormal) shapes = [] faces = [] sections = [] for sh in self.shapes: for sol in sh[0].Solids: c = sol.cut(cutvolume) shapes.append([c]+sh[1:]) for f in c.Faces: faces.append([f]+sh[1:]) print "iscoplanar:",f.Vertexes[0].Point,f.normalAt(0,0),cutface.Vertexes[0].Point,cutface.normalAt(0,0) if DraftGeomUtils.isCoplanar([f,cutface]): print "COPLANAR" sections.append([f,fill]) self.shapes = shapes self.faces = faces self.sections = sections if DEBUG: print "Built ",len(self.sections)," sections, ", len(self.faces), " faces retained" self.iscut = True self.oriented = False self.trimmed = False self.sorted = False self.joined = False if DEBUG: print "\n\n======> Finished cut\n\n"
def numericRadius(self, rad): """Validate the entry radius in the user interface. This function is called by the toolbar or taskpanel interface when a valid radius has been entered in the input field. """ import DraftGeomUtils plane = App.DraftWorkingPlane if self.step == 1: self.rad = rad if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom2tan1rad( self.tangents[0], self.tangents[1], rad) if self.center: _c = DraftGeomUtils.findClosestCircle(self.center, cir) self.center = _c.Center else: self.center = cir[-1].Center elif self.tangents and self.tanpoints: cir = DraftGeomUtils.circleFrom1tan1pt1rad( self.tangents[0], self.tanpoints[0], rad) if self.center: _c = DraftGeomUtils.findClosestCircle(self.center, cir) self.center = _c.Center else: self.center = cir[-1].Center if self.closedCircle: self.drawArc() else: self.step = 2 self.arctrack.setCenter(self.center) self.ui.labelRadius.setText(translate("draft", "Start angle")) self.ui.radiusValue.setToolTip( translate("draft", "Start angle")) self.linetrack.p1(self.center) self.linetrack.on() self.ui.radiusValue.setText("") self.ui.radiusValue.setFocus() _msg(translate("draft", "Pick start angle")) elif self.step == 2: self.ui.labelRadius.setText(translate("draft", "Aperture angle")) self.ui.radiusValue.setToolTip(translate("draft", "Aperture angle")) self.firstangle = math.radians(rad) if DraftVecUtils.equals(plane.axis, App.Vector(1, 0, 0)): u = App.Vector(0, self.rad, 0) else: u = DraftVecUtils.scaleTo( App.Vector(1, 0, 0).cross(plane.axis), self.rad) urotated = DraftVecUtils.rotate(u, math.radians(rad), plane.axis) self.arctrack.setStartAngle(self.firstangle) self.step = 3 self.ui.radiusValue.setText("") self.ui.radiusValue.setFocus() _msg(translate("draft", "Pick aperture angle")) else: self.updateAngle(rad) self.angle = math.radians(rad) self.step = 4 self.drawArc()
def pos(self, p): self.trans.translation.setValue(DraftVecUtils.tup(p))
def get_dxf(obj, direction=None): """Return a DXF entity from the given object. If direction is given, the object is projected in 2D. """ plane = None result = "" if (obj.isDerivedFrom("Drawing::View") or obj.isDerivedFrom("TechDraw::DrawView")): if obj.Source.isDerivedFrom("App::DocumentObjectGroup"): for o in obj.Source.Group: result += get_dxf(o, obj.Direction) else: result += get_dxf(obj.Source, obj.Direction) return result if direction and isinstance(direction, App.Vector): if direction != App.Vector(0, 0, 0): plane = WorkingPlane.Plane() plane.alignToPointAndAxis(App.Vector(0, 0, 0), direction) if utils.get_type(obj) in ("Dimension", "LinearDimension"): p1 = _get_proj(obj.Start, plane=plane) p2 = _get_proj(obj.End, plane=plane) p3 = _get_proj(obj.Dimline, plane=plane) result += "0\nDIMENSION\n8\n0\n62\n0\n3\nStandard\n70\n1\n" result += "10\n"+str(p3.x)+"\n20\n"+str(p3.y)+"\n30\n"+str(p3.z)+"\n" result += "13\n"+str(p1.x)+"\n23\n"+str(p1.y)+"\n33\n"+str(p1.z)+"\n" result += "14\n"+str(p2.x)+"\n24\n"+str(p2.y)+"\n34\n"+str(p2.z)+"\n" elif utils.get_type(obj) == "Annotation": # Only for App::Annotation p = _get_proj(obj.Position, plane=plane) count = 0 for t in obj.LabeLtext: result += "0\nTEXT\n8\n0\n62\n0\n" result += "10\n" result += str(p.x) + "\n20\n" result += str(p.y + count) + "\n30\n" result += str(p.z) + "\n" result += "40\n1\n" result += "1\n" + str(t) + "\n" result += "7\nSTANDARD\n" count += 1 elif hasattr(obj, 'Shape'): # TODO do this the Draft way, for ex. using polylines and rectangles if not direction: direction = App.Vector(0, 0, -1) if DraftVecUtils.isNull(direction): direction = App.Vector(0, 0, -1) try: d = TechDraw.projectToDXF(obj.Shape, direction) except Exception: # TODO: trap only specific exception. # Impossible to generate DXF from Shape? Which exception is throw? _wrn("get_dxf: " "unable to project '{}' to {}".format(obj.Label, direction)) else: result += d else: _wrn("get_dxf: unsupported object, '{}'".format(obj.Label)) return result
def drawArc(self): """Actually draw the arc object.""" rot, sup, pts, fil = self.getStrings() if self.closedCircle: try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object _base = DraftVecUtils.toString(self.center) _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Circle", "Circle")' _cmd_list = [ 'circle = ' + _cmd, 'circle.Radius = ' + str(self.rad), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + _base, 'circle.Placement = pl', 'Draft.autogroup(circle)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Circle (Part)"), _cmd_list) else: # Insert a Draft circle Gui.addModule("Draft") _base = DraftVecUtils.toString(self.center) _cmd = 'Draft.makeCircle' _cmd += '(' _cmd += 'radius=' + str(self.rad) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl=FreeCAD.Placement()', 'pl.Rotation.Q=' + rot, 'pl.Base=' + _base, 'circle = ' + _cmd, 'Draft.autogroup(circle)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Circle"), _cmd_list) except Exception: _err("Draft: error delaying commit") else: # Not a closed circle, therefore a circular arc sta = math.degrees(self.firstangle) end = math.degrees(self.firstangle + self.angle) if end < sta: sta, end = end, sta while True: if sta > 360: sta = sta - 360 elif end > 360: end = end - 360 else: break try: Gui.addModule("Draft") if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object _base = DraftVecUtils.toString(self.center) _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Circle", "Circle")' _cmd_list = [ 'circle = ' + _cmd, 'circle.Radius = ' + str(self.rad), 'circle.Angle0 = ' + str(sta), 'circle.Angle1 = ' + str(end), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + _base, 'circle.Placement = pl', 'Draft.autogroup(circle)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Arc (Part)"), _cmd_list) else: # Insert a Draft circle _base = DraftVecUtils.toString(self.center) _cmd = 'Draft.makeCircle' _cmd += '(' _cmd += 'radius=' + str(self.rad) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'startangle=' + str(sta) + ', ' _cmd += 'endangle=' + str(end) + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + _base, 'circle = ' + _cmd, 'Draft.autogroup(circle)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Arc"), _cmd_list) except Exception: _err("Draft: error delaying commit") # Finalize full circle or cirular arc self.finish(cont=True)
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. """ import DraftGeomUtils plane = App.DraftWorkingPlane if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) # this is to make sure radius is what you see on screen if self.center and DraftVecUtils.dist(self.point, self.center) > 0: viewdelta = DraftVecUtils.project(self.point.sub(self.center), plane.axis) if not DraftVecUtils.isNull(viewdelta): self.point = self.point.add(viewdelta.negative()) if self.step == 0: # choose center if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): if not self.altdown: self.altdown = True self.ui.switchUi(True) else: if self.altdown: self.altdown = False self.ui.switchUi(False) elif self.step == 1: # choose radius if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom2tan1pt( self.tangents[0], self.tangents[1], self.point) _c = DraftGeomUtils.findClosestCircle(self.point, cir) self.center = _c.Center self.arctrack.setCenter(self.center) elif self.tangents and self.tanpoints: cir = DraftGeomUtils.circleFrom1tan2pt( self.tangents[0], self.tanpoints[0], self.point) _c = DraftGeomUtils.findClosestCircle(self.point, cir) self.center = _c.Center self.arctrack.setCenter(self.center) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): if not self.altdown: self.altdown = True if info: ob = self.doc.getObject(info['Object']) num = int(info['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom3tan( self.tangents[0], self.tangents[1], ed) cl = DraftGeomUtils.findClosestCircle( self.point, cir) self.center = cl.Center self.rad = cl.Radius self.arctrack.setCenter(self.center) else: self.rad = self.center.add( DraftGeomUtils.findDistance( self.center, ed).sub(self.center)).Length else: self.rad = DraftVecUtils.dist(self.point, self.center) else: if self.altdown: self.altdown = False self.rad = DraftVecUtils.dist(self.point, self.center) self.ui.setRadiusValue(self.rad, "Length") self.arctrack.setRadius(self.rad) self.linetrack.p1(self.center) self.linetrack.p2(self.point) self.linetrack.on() elif (self.step == 2): # choose first angle currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 self.linetrack.p2( DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad).add(self.center)) self.ui.setRadiusValue(math.degrees(angle), unit="Angle") self.firstangle = angle else: # choose second angle currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 self.linetrack.p2( DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad).add(self.center)) self.updateAngle(angle) self.ui.setRadiusValue(math.degrees(self.angle), unit="Angle") self.arctrack.setApertureAngle(self.angle) gui_tool_utils.redraw3DView() elif arg["Type"] == "SoMouseButtonEvent": # mouse click if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1": if self.point: if self.step == 0: # choose center if not self.support: gui_tool_utils.getSupport(arg) (self.point, ctrlPoint, info) = gui_tool_utils.getPoint(self, arg) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): snapped = self.view.getObjectInfo( (arg["Position"][0], arg["Position"][1])) if snapped: ob = self.doc.getObject(snapped['Object']) num = int( snapped['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] self.tangents.append(ed) if len(self.tangents) == 2: self.arctrack.on() self.ui.radiusUi() self.step = 1 self.ui.setNextFocus() self.linetrack.on() _msg(translate("draft", "Pick radius")) else: if len(self.tangents) == 1: self.tanpoints.append(self.point) else: self.center = self.point self.node = [self.point] self.arctrack.setCenter(self.center) self.linetrack.p1(self.center) self.linetrack.p2( self.view.getPoint(arg["Position"][0], arg["Position"][1])) self.arctrack.on() self.ui.radiusUi() self.step = 1 self.ui.setNextFocus() self.linetrack.on() _msg(translate("draft", "Pick radius")) if self.planetrack: self.planetrack.set(self.point) elif self.step == 1: # choose radius if self.closedCircle: self.drawArc() else: self.ui.labelRadius.setText( translate("draft", "Start angle")) self.ui.radiusValue.setToolTip( translate("draft", "Start angle")) self.ui.radiusValue.setText( U.Quantity(0, U.Angle).UserString) self.linetrack.p1(self.center) self.linetrack.on() self.step = 2 _msg(translate("draft", "Pick start angle")) elif self.step == 2: # choose first angle self.ui.labelRadius.setText( translate("draft", "Aperture angle")) self.ui.radiusValue.setToolTip( translate("draft", "Aperture angle")) self.step = 3 # scale center->point vector for proper display # u = DraftVecUtils.scaleTo(self.point.sub(self.center), self.rad) obsolete? self.arctrack.setStartAngle(self.firstangle) _msg(translate("draft", "Pick aperture")) else: # choose second angle self.step = 4 self.drawArc()
def findDistance(point, edge, strict=False): """Return a vector from the point to its closest point on the edge. If `strict` is `True`, the vector will be returned only if its endpoint lies on the `edge`. Edge can also be a list of 2 points. """ if isinstance(point, App.Vector): if isinstance(edge, list): segment = edge[1].sub(edge[0]) chord = edge[0].sub(point) norm = segment.cross(chord) perp = segment.cross(norm) dist = DraftVecUtils.project(chord, perp) if not dist: return None newpoint = point.add(dist) if dist.Length == 0: return None if strict: s1 = newpoint.sub(edge[0]) s2 = newpoint.sub(edge[1]) if (s1.Length <= segment.Length and s2.Length <= segment.Length): return dist else: return None else: return dist elif geomType(edge) == "Line": segment = vec(edge) chord = edge.Vertexes[0].Point.sub(point) norm = segment.cross(chord) perp = segment.cross(norm) dist = DraftVecUtils.project(chord, perp) if not dist: return None newpoint = point.add(dist) if (dist.Length == 0): return None if strict: s1 = newpoint.sub(edge.Vertexes[0].Point) s2 = newpoint.sub(edge.Vertexes[-1].Point) if (s1.Length <= segment.Length and s2.Length <= segment.Length): return dist else: return None else: return dist elif geomType(edge) == "Circle": ve1 = edge.Vertexes[0].Point if len(edge.Vertexes) > 1: ve2 = edge.Vertexes[-1].Point else: ve2 = None center = edge.Curve.Center segment = center.sub(point) if segment.Length == 0: return None ratio = (segment.Length - edge.Curve.Radius) / segment.Length dist = segment.multiply(ratio) newpoint = App.Vector.add(point, dist) if dist.Length == 0: return None if strict and ve2: ang1 = DraftVecUtils.angle(ve1.sub(center)) ang2 = DraftVecUtils.angle(ve2.sub(center)) angpt = DraftVecUtils.angle(newpoint.sub(center)) if ((angpt <= ang2 and angpt >= ang1) or (angpt <= ang1 and angpt >= ang2)): return dist else: return None else: return dist elif (geomType(edge) == "BSplineCurve" or geomType(edge) == "BezierCurve"): try: pr = edge.Curve.parameter(point) np = edge.Curve.value(pr) dist = np.sub(point) except Part.OCCError: print( "DraftGeomUtils: Unable to get curve parameter " "for point ", point) return None else: return dist else: print("DraftGeomUtils: Couldn't project point") return None else: print("DraftGeomUtils: Couldn't project point") return None
def execute(self, obj): "creates the panel shape" if self.clone(obj): return import Part, DraftGeomUtils # base tests if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): if not obj.Base.Mesh.isSolid(): return else: if obj.Length.Value: length = obj.Length.Value else: return if obj.Width.Value: width = obj.Width.Value else: return if obj.Thickness.Value: thickness = obj.Thickness.Value else: if not obj.Base: return elif obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Solids: return # creating base shape pl = obj.Placement base = None normal = None if obj.Base: p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0, 0, 1)) normal = normal.multiply(thickness) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif base.Wires: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: base = ArchCommands.makeFace(base.Wires) self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: normal = Vector(0, 0, 1).multiply(thickness) self.ExtrusionVector = normal 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.makePolygon([v1, v2, v3, v4, v1]) base = Part.Face(base) self.BaseProfile = base base = base.extrude(self.ExtrusionVector) if base and (obj.Sheets > 1) and normal and thickness: bases = [base] for i in range(1, obj.Sheets): n = FreeCAD.Vector(normal).normalize().multiply(i * thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) if base and normal and hasattr(obj, "Offset"): if obj.Offset.Value: v = DraftVecUtils.scaleTo(normal, obj.Offset.Value) base.translate(v) # process subshapes base = self.processSubShapes(obj, base, pl) # applying if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( translate("Arch", "Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def tostr(val): return str(round(val, DraftVecUtils.precision()))