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 align(self,basepoint,align,width,widthvec): "moves a given basepoint according to the alignment" if align == "Center": basepoint = basepoint.add(DraftVecUtils.scaleTo(widthvec,-width/2)) elif align == "Right": basepoint = basepoint.add(DraftVecUtils.scaleTo(widthvec,-width)) return basepoint
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 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 update(self, line=None, normal=None): import WorkingPlane, DraftGeomUtils if not normal: normal = FreeCAD.DraftWorkingPlane.axis if line: if isinstance(line, list): bp = line[0] lvec = line[1].sub(line[0]) else: lvec = DraftGeomUtils.vec(line.Shape.Edges[0]) bp = line.Shape.Edges[0].Vertexes[0].Point elif self.baseline: lvec = DraftGeomUtils.vec(self.baseline.Shape.Edges[0]) bp = self.baseline.Shape.Edges[0].Vertexes[0].Point else: return right = lvec.cross(normal) self.cube.width.setValue(lvec.Length) p = WorkingPlane.getPlacementFromPoints([bp, bp.add(lvec), bp.add(right)]) if p: self.trans.rotation.setValue(p.Rotation.Q) bp = bp.add(lvec.multiply(0.5)) bp = bp.add(DraftVecUtils.scaleTo(normal, self.cube.depth.getValue() / 2)) self.pos(bp)
def getSubVolume(self,obj,extension=10000): "returns a volume to be subtracted" if hasattr(self,"baseface"): if self.baseface: norm = self.baseface.normalAt(0,0) norm = DraftVecUtils.scaleTo(norm,extension) return self.baseface.extrude(norm) return None
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.multiply(width) if hasattr(obj, "Offset"): if obj.Offset: dvec2 = DraftVecUtils.scaleTo(dvec, obj.Offset) 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(width) dvec = dvec.negative() if hasattr(obj, "Offset"): if obj.Offset: dvec2 = DraftVecUtils.scaleTo(dvec, obj.Offset) 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(width / 2) w1 = DraftGeomUtils.offsetWire(wire, dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire, dvec) sh = DraftGeomUtils.bind(w1, w2) # fixing self-intersections sh.fix(0.1, 0, 1) self.BaseProfile = sh if height and (not flat): self.ExtrusionVector = Vector(normal).multiply(height) sh = sh.extrude(self.ExtrusionVector) return sh
def CalculatePlacement(baramount, barnumber, size, axis, rotation, offsetstart, offsetend): """ CalculatePlacement([baramount, barnumber, size, axis, rotation, offsetstart, offsetend]): Calculate the placement of the bar from given values.""" if baramount == 1: interval = offsetstart else: interval = size - (offsetstart + offsetend) interval = interval / (baramount - 1) bardistance = (interval * (barnumber - 1)) + offsetstart barplacement = DraftVecUtils.scaleTo(axis, bardistance) placement = FreeCAD.Placement(barplacement, rotation) return placement
def CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, offsetend): """ CustomSpacingPlacement(spacinglist, barnumber, axis, rotation, offsetstart, offsetend): Calculate placement of the bar from custom spacing list.""" if barnumber == 1: bardistance = offsetstart else: bardistance = sum(spacinglist[0:barnumber]) bardistance = bardistance - spacinglist[0] / 2 bardistance = bardistance - spacinglist[barnumber - 1] / 2 bardistance = bardistance + offsetstart barplacement = DraftVecUtils.scaleTo(axis, bardistance) placement = FreeCAD.Placement(barplacement, rotation) return placement
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 = direction.negative() 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 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 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 projectToVector(shape, vector): """projectToVector(shape,vector): projects the given shape on the given vector""" projpoints = [] minl = 10000000000 maxl = -10000000000 for v in shape.Vertexes: p = DraftVecUtils.project(v.Point, vector) projpoints.append(p) l = p.Length if p.getAngle(vector) > 1: l = -l if l > maxl: maxl = l if l < minl: minl = l return DraftVecUtils.scaleTo(vector, maxl - minl)
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 makeStairsTread(self,basepoint,depthvec,widthvec,nosing=0,thickness=0): "returns the shape of a single tread" import Part if thickness: basepoint = basepoint.add(Vector(0,0,-abs(thickness))) if nosing: nosevec = DraftVecUtils.scaleTo(DraftVecUtils.neg(depthvec),nosing) else: nosevec = Vector(0,0,0) p1 = basepoint.add(nosevec) p2 = p1.add(DraftVecUtils.neg(nosevec)).add(depthvec) p3 = p2.add(widthvec) p4 = p3.add(DraftVecUtils.neg(depthvec)).add(nosevec) step = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) if thickness: step = step.extrude(Vector(0,0,abs(thickness))) return step
def update(self, point, info): "this function is called by the Snapper when the mouse is moved" if FreeCADGui.Control.activeDialog(): 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 = dv.negative() self.tracker.update([b.add(dv), point.add(dv)]) if self.Length: self.Length.setText(self.FORMAT % bv.Length)
def projectToVector(shape,vector): '''projectToVector(shape,vector): projects the given shape on the given vector''' projpoints = [] minl = 10000000000 maxl = -10000000000 for v in shape.Vertexes: p = DraftVecUtils.project(v.Point,vector) projpoints.append(p) l = p.Length if p.getAngle(vector) > 1: l = -l if l > maxl: maxl = l if l < minl: minl = l return DraftVecUtils.scaleTo(vector,maxl-minl)
def update(self,point,info): "this function is called by the Snapper when the mouse is moved" if FreeCADGui.Control.activeDialog(): 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 = dv.negative() self.tracker.update([b.add(dv),point.add(dv)]) if self.Length: self.Length.setText(FreeCAD.Units.Quantity(bv.Length,FreeCAD.Units.Length).UserString)
def getCenterPoint(self, x, y, z): if not self.taskd.form.checkCenter.isChecked(): return FreeCAD.Vector() v = FreeCAD.Vector(x, y, z) cam1 = FreeCAD.Vector(FreeCADGui.ActiveDocument.ActiveView. getCameraNode().position.getValue().getValue()) cam2 = FreeCADGui.ActiveDocument.ActiveView.getViewDirection() vcam1 = DraftVecUtils.project(cam1, v) a = vcam1.getAngle(cam2) if a < 0.0001: return FreeCAD.Vector() d = vcam1.Length L = d / math.cos(a) vcam2 = DraftVecUtils.scaleTo(cam2, L) cp = cam1.add(vcam2) return cp
def shp_cyl(r, h, normal=VZ, pos=V0): # we have to bring the active document doc = FreeCAD.ActiveDocument cir = Part.makeCircle( r, # Radius pos, # Position normal) # direction #print "circle: %", cir_out.Curve wire_cir = Part.Wire(cir) face_cir = Part.Face(wire_cir) dir_extrus = DraftVecUtils.scaleTo(normal, h) shpcyl = face_cir.extrude(dir_extrus) return shpcyl
def snapToPerpendicular(self, shape, last): "returns a list of perpendicular snap locations" snaps = [] if self.isEnabled("perpendicular"): if last: if isinstance(shape, Part.Edge): if DraftGeomUtils.geomType(shape) == "Line": np = self.getPerpendicular(shape, last) elif DraftGeomUtils.geomType(shape) == "Circle": dv = last.sub(shape.Curve.Center) dv = DraftVecUtils.scaleTo(dv, shape.Curve.Radius) np = (shape.Curve.Center).add(dv) elif DraftGeomUtils.geomType(shape) == "BSplineCurve": pr = shape.Curve.parameter(last) np = shape.Curve.value(pr) else: return snaps snaps.append([np, 'perpendicular', np]) return snaps
def snapToPerpendicular(self,shape,last): "returns a list of perpendicular snap locations" snaps = [] if self.isEnabled("perpendicular"): if last: if isinstance(shape,Part.Edge): if DraftGeomUtils.geomType(shape) == "Line": np = self.getPerpendicular(shape,last) elif DraftGeomUtils.geomType(shape) == "Circle": dv = last.sub(shape.Curve.Center) dv = DraftVecUtils.scaleTo(dv,shape.Curve.Radius) np = (shape.Curve.Center).add(dv) elif DraftGeomUtils.geomType(shape) == "BSplineCurve": pr = shape.Curve.parameter(last) np = shape.Curve.value(pr) else: return snaps snaps.append([np,'perpendicular',np]) return snaps
def getSubVolume(self, base, width, plac=None): "returns a subvolume from a base object" import Part max_length = 0 f = None for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w if f: f = Part.Face(f) n = f.normalAt(0, 0) v1 = DraftVecUtils.scaleTo(n, width) f.translate(v1) v2 = DraftVecUtils.neg(v1) v2 = DraftVecUtils.scale(v1, -2) f = f.extrude(v2) if plac: f.Placement = plac return f return None
def addCylPos(r, h, name, normal=VZ, pos=V0): # we have to bring the active document doc = FreeCAD.ActiveDocument cir = Part.makeCircle( r, # Radius pos, # Position normal) # direction #print "circle: %", cir_out.Curve wire_cir = Part.Wire(cir) face_cir = Part.Face(wire_cir) dir_extrus = DraftVecUtils.scaleTo(normal, h) shp_cyl = face_cir.extrude(dir_extrus) cyl = doc.addObject("Part::Feature", name) cyl.Shape = shp_cyl return cyl
def getSubVolume(self,base,width,plac=None): "returns a subvolume from a base object" import Part max_length = 0 f = None for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w if f: f = Part.Face(f) n = f.normalAt(0,0) v1 = DraftVecUtils.scaleTo(n,width) f.translate(v1) v2 = DraftVecUtils.neg(v1) v2 = DraftVecUtils.scale(v1,-2) f = f.extrude(v2) if plac: f.Placement = plac return f return None
def onChanged(self, obj, prop): if prop == "Start": pts = obj.Points invpl = App.Placement(obj.Placement).inverse() realfpstart = invpl.multVec(obj.Start) if pts: if pts[0] != realfpstart: pts[0] = realfpstart obj.Points = pts elif prop == "End": pts = obj.Points invpl = App.Placement(obj.Placement).inverse() realfpend = invpl.multVec(obj.End) if len(pts) > 1: if pts[-1] != realfpend: pts[-1] = realfpend obj.Points = pts elif prop == "Length": if (len(obj.Points) == 2 and obj.Length.Value > 1e-7 and obj.Shape and (not obj.Shape.isNull()) and obj.Length.Value != obj.Shape.Length): v = obj.Points[-1].sub(obj.Points[0]) v = DraftVecUtils.scaleTo(v, obj.Length.Value) obj.Points = [obj.Points[0], obj.Points[0].add(v)] elif prop == "Placement": pl = App.Placement(obj.Placement) if len(obj.Points) >= 2: displayfpstart = pl.multVec(obj.Points[0]) displayfpend = pl.multVec(obj.Points[-1]) if obj.Start != displayfpstart: obj.Start = displayfpstart if obj.End != displayfpend: obj.End = displayfpend
def getSubVolume(self,base,width,plac=None): "returns a subvolume from a base object" import Part,DraftVecUtils # finding biggest wire in the base shape max_length = 0 f = None for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w if f: f = Part.Face(f) n = f.normalAt(0,0) v1 = DraftVecUtils.scaleTo(n,width*1.1) # we extrude a little more to avoid face-on-face f.translate(v1) v2 = DraftVecUtils.neg(v1) v2 = DraftVecUtils.scale(v1,-2) f = f.extrude(v2) if plac: f.Placement = plac return f return None
def update(self,line=None,normal=None): import WorkingPlane, DraftGeomUtils if not normal: normal = FreeCAD.DraftWorkingPlane.axis if line: if isinstance(line,list): bp = line[0] lvec = line[1].sub(line[0]) else: lvec = DraftGeomUtils.vec(line.Shape.Edges[0]) bp = line.Shape.Edges[0].Vertexes[0].Point elif self.baseline: lvec = DraftGeomUtils.vec(self.baseline.Shape.Edges[0]) bp = self.baseline.Shape.Edges[0].Vertexes[0].Point else: return right = lvec.cross(normal) self.cube.width.setValue(lvec.Length) p = WorkingPlane.getPlacementFromPoints([bp,bp.add(lvec),bp.add(right)]) self.trans.rotation.setValue(p.Rotation.Q) bp = bp.add(DraftVecUtils.scale(lvec,0.5)) bp = bp.add(DraftVecUtils.scaleTo(normal,self.cube.depth.getValue()/2)) self.pos(bp)
def CalculatePlacement(baramount, barnumber, bardiameter, size, axis, rotation, offsetstart, offsetend, RebarShape=""): """ CalculatePlacement([baramount, barnumber, bardiameter, size, axis, rotation, offsetstart, offsetend, RebarShape]): Calculate the placement of the bar from given values.""" if baramount == 1: interval = offsetstart else: if RebarShape == "Stirrup": interval = size - (offsetstart + offsetend + bardiameter) else: interval = size - (offsetstart + offsetend) interval = interval / (baramount - 1) bardistance = (interval * (barnumber - 1)) + offsetstart barplacement = DraftVecUtils.scaleTo(axis, bardistance) placement = FreeCAD.Placement(barplacement, rotation) return placement
def makeStraightStairs(self,obj,edge,numberofsteps=None): "builds a simple, straight staircase from a straight edge" # general data import Part,DraftGeomUtils if not numberofsteps: numberofsteps = obj.NumberOfSteps v = DraftGeomUtils.vec(edge) vLength = DraftVecUtils.scaleTo(v,float(edge.Length)/(numberofsteps-1)) vLength = Vector(vLength.x,vLength.y,0) if round(v.z,Draft.precision()) != 0: h = v.z else: h = obj.Height.Value vHeight = Vector(0,0,float(h)/numberofsteps) vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),obj.Width.Value) vBase = edge.Vertexes[0].Point vNose = DraftVecUtils.scaleTo(vLength,-abs(obj.Nosing.Value)) a = math.atan(vHeight.Length/vLength.Length) #print "stair data:",vLength.Length,":",vHeight.Length # steps for i in range(numberofsteps-1): p1 = vBase.add((Vector(vLength).multiply(i)).add(Vector(vHeight).multiply(i+1))) p1 = self.align(p1,obj.Align,vWidth) p1 = p1.add(vNose).add(Vector(0,0,-abs(obj.TreadThickness.Value))) p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) step = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) if obj.TreadThickness.Value: step = step.extrude(Vector(0,0,abs(obj.TreadThickness.Value))) self.steps.append(step) else: self.pseudosteps.append(step) # structure lProfile = [] struct = None if obj.Structure == "Massive": if obj.StructureThickness.Value: for i in range(numberofsteps-1): if not lProfile: lProfile.append(vBase) last = lProfile[-1] if len(lProfile) == 1: last = last.add(Vector(0,0,-abs(obj.TreadThickness.Value))) lProfile.append(last.add(vHeight)) lProfile.append(lProfile[-1].add(vLength)) resHeight1 = obj.StructureThickness.Value/math.cos(a) lProfile.append(lProfile[-1].add(Vector(0,0,-resHeight1))) resHeight2 = ((numberofsteps-1)*vHeight.Length)-(resHeight1+obj.TreadThickness.Value) resLength = (vLength.Length/vHeight.Length)*resHeight2 h = DraftVecUtils.scaleTo(vLength,-resLength) lProfile.append(lProfile[-1].add(Vector(h.x,h.y,-resHeight2))) lProfile.append(vBase) #print lProfile pol = Part.makePolygon(lProfile) struct = Part.Face(pol) evec = vWidth if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) struct.translate(mvec) evec = DraftVecUtils.scaleTo(evec,evec.Length-(2*mvec.Length)) struct = struct.extrude(evec) elif obj.Structure in ["One stringer","Two stringers"]: if obj.StringerWidth.Value and obj.StructureThickness.Value: hyp = math.sqrt(vHeight.Length**2 + vLength.Length**2) l1 = Vector(vLength).multiply(numberofsteps-1) h1 = Vector(vHeight).multiply(numberofsteps-1).add(Vector(0,0,-abs(obj.TreadThickness.Value))) p1 = vBase.add(l1).add(h1) p1 = self.align(p1,obj.Align,vWidth) lProfile.append(p1) h2 = (obj.StructureThickness.Value/vLength.Length)*hyp lProfile.append(lProfile[-1].add(Vector(0,0,-abs(h2)))) h3 = lProfile[-1].z-vBase.z l3 = (h3/vHeight.Length)*vLength.Length v3 = DraftVecUtils.scaleTo(vLength,-l3) lProfile.append(lProfile[-1].add(Vector(0,0,-abs(h3))).add(v3)) l4 = (obj.StructureThickness.Value/vHeight.Length)*hyp v4 = DraftVecUtils.scaleTo(vLength,-l4) lProfile.append(lProfile[-1].add(v4)) lProfile.append(lProfile[0]) #print lProfile pol = Part.makePolygon(lProfile) pol = Part.Face(pol) evec = DraftVecUtils.scaleTo(vWidth,obj.StringerWidth.Value) if obj.Structure == "One stringer": if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) else: mvec = DraftVecUtils.scaleTo(vWidth,(vWidth.Length/2)-obj.StringerWidth.Value/2) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) else: pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) struct = Part.makeCompound([s1,s2]) if struct: self.structures.append(struct)
def makeStraightLanding(self,obj,edge,numberofsteps=None): "builds a landing from a straight edge" # general data if not numberofsteps: numberofsteps = obj.NumberOfSteps import Part,DraftGeomUtils v = DraftGeomUtils.vec(edge) vLength = Vector(v.x,v.y,0) vWidth = vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),obj.Width.Value) vBase = edge.Vertexes[0].Point vNose = DraftVecUtils.scaleTo(vLength,-abs(obj.Nosing.Value)) h = obj.Height.Value l = obj.Length.Value if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): l = obj.Base.Shape.Length if obj.Base.Shape.BoundBox.ZLength: h = obj.Base.Shape.BoundBox.ZLength fLength = float(l-obj.Width.Value)/(numberofsteps-2) fHeight = float(h)/numberofsteps a = math.atan(fHeight/fLength) print "landing data:",fLength,":",fHeight # step p1 = self.align(vBase,obj.Align,vWidth) p1 = p1.add(vNose).add(Vector(0,0,-abs(obj.TreadThickness.Value))) p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) step = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) if obj.TreadThickness.Value: step = step.extrude(Vector(0,0,abs(obj.TreadThickness.Value))) self.steps.append(step) else: self.pseudosteps.append(step) # structure lProfile = [] struct = None p7 = None p1 = p1.add(DraftVecUtils.neg(vNose)) p2 = p1.add(Vector(0,0,-fHeight)).add(Vector(0,0,-obj.StructureThickness.Value/math.cos(a))) resheight = p1.sub(p2).Length - obj.StructureThickness.Value reslength = resheight / math.tan(a) p3 = p2.add(DraftVecUtils.scaleTo(vLength,reslength)).add(Vector(0,0,resheight)) p6 = p1.add(vLength) if obj.TreadThickness.Value: p7 = p6.add(Vector(0,0,obj.TreadThickness.Value)) reslength = fLength + (obj.StructureThickness.Value/math.sin(a)-(fHeight-obj.TreadThickness.Value)/math.tan(a)) if p7: p5 = p7.add(DraftVecUtils.scaleTo(vLength,reslength)) else: p5 = p6.add(DraftVecUtils.scaleTo(vLength,reslength)) resheight = obj.StructureThickness.Value + obj.TreadThickness.Value reslength = resheight/math.tan(a) p4 = p5.add(DraftVecUtils.scaleTo(vLength,-reslength)).add(Vector(0,0,-resheight)) if obj.Structure == "Massive": if obj.StructureThickness.Value: if p7: struct = Part.Face(Part.makePolygon([p1,p2,p3,p4,p5,p7,p6,p1])) else: struct = Part.Face(Part.makePolygon([p1,p2,p3,p4,p5,p6,p1])) evec = vWidth if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) struct.translate(mvec) evec = DraftVecUtils.scaleTo(evec,evec.Length-(2*mvec.Length)) struct = struct.extrude(evec) elif obj.Structure in ["One stringer","Two stringers"]: if obj.StringerWidth.Value and obj.StructureThickness.Value: p1b = p1.add(Vector(0,0,-fHeight)) reslength = fHeight/math.tan(a) p1c = p1.add(DraftVecUtils.scaleTo(vLength,reslength)) p5b = None p5c = None if obj.TreadThickness.Value: reslength = obj.StructureThickness.Value/math.sin(a) p5b = p5.add(DraftVecUtils.scaleTo(vLength,-reslength)) reslength = obj.TreadThickness.Value/math.tan(a) p5c = p5b.add(DraftVecUtils.scaleTo(vLength,-reslength)).add(Vector(0,0,-obj.TreadThickness.Value)) pol = Part.Face(Part.makePolygon([p1c,p1b,p2,p3,p4,p5,p5b,p5c,p1c])) else: pol = Part.Face(Part.makePolygon([p1c,p1b,p2,p3,p4,p5,p1c])) evec = DraftVecUtils.scaleTo(vWidth,obj.StringerWidth.Value) if obj.Structure == "One stringer": if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) else: mvec = DraftVecUtils.scaleTo(vWidth,(vWidth.Length/2)-obj.StringerWidth.Value/2) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) else: pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) struct = Part.makeCompound([s1,s2]) if struct: self.structures.append(struct)
def __init__(self, size, length, shaft_l, circle_r, circle_h, name="nemamotor", chmf=1, rshaft_l=0, bolt_depth=3, bolt_out=2, container=1, normal=VZ, pos=V0): doc = FreeCAD.ActiveDocument self.base_place = (0, 0, 0) self.size = size self.width = kcomp.NEMA_W[size] self.length = length self.shaft_l = shaft_l self.circle_r = circle_r self.circle_h = circle_h self.chmf = chmf self.rshaft_l = rshaft_l self.bolt_depth = bolt_depth self.bolt_out = bolt_out self.container = container nnormal = DraftVecUtils.scaleTo(normal, 1) self.normal = nnormal self.pos = pos nemabolt_d = kcomp.NEMA_BOLT_D[size] self.nemabolt_d = nemabolt_d mtol = kcomp.TOL - 0.1 lnormal = DraftVecUtils.scaleTo(nnormal, length) neg_lnormal = DraftVecUtils.neg(lnormal) # motor shape v1 = FreeCAD.Vector(self.width / 2. - chmf, self.width / 2., 0) v2 = FreeCAD.Vector(self.width / 2., self.width / 2. - chmf, 0) motorwire = fcfun.wire_sim_xy([v1, v2]) # motor wire normal is VZ # DraftVecUtils doesnt work as well # rot = DraftVecUtils.getRotation(VZ, nnormal) # the order matter VZ, nnormal. It seems it doent matter VZ or VZN # this is valid: #rot = DraftGeomUtils.getRotation(VZ,nnormal) #print rot rot = FreeCAD.Rotation(VZ, nnormal) print rot motorwire.Placement.Rotation = rot motorwire.Placement.Base = pos motorface = Part.Face(motorwire) shp_motorbox = motorface.extrude(neg_lnormal) # shaft shape if rshaft_l == 0: # no rear shaft shp_shaft = fcfun.shp_cyl(r=kcomp.NEMA_SHAFT_D[size] / 2., h=shaft_l, normal=nnormal, pos=pos) else: rshaft_posend = DraftVecUtils.scaleTo(neg_lnormal, rshaft_l + length) shp_shaft = fcfun.shp_cyl(r=kcomp.NEMA_SHAFT_D[size] / 2., h=shaft_l + rshaft_l + length, normal=nnormal, pos=pos + rshaft_posend) shp_motorshaft = shp_motorbox.fuse(shp_shaft) # Bolt holes # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA # There is something wrong with the position of these bolts # bhole00_pos = FreeCAD.Vector(-kcomp.NEMA_BOLT_SEP[size]/2, # -kcomp.NEMA_BOLT_SEP[size]/2, # -bolt_depth) + pos # bhole01_pos = FreeCAD.Vector(-kcomp.NEMA_BOLT_SEP[size]/2, # kcomp.NEMA_BOLT_SEP[size]/2, # -bolt_depth) + pos # bhole10_pos = FreeCAD.Vector( kcomp.NEMA_BOLT_SEP[size]/2, # -kcomp.NEMA_BOLT_SEP[size]/2, # -bolt_depth) + pos # bhole11_pos = FreeCAD.Vector( kcomp.NEMA_BOLT_SEP[size]/2, # kcomp.NEMA_BOLT_SEP[size]/2, # -bolt_depth) + pos # bhole00_posrot = DraftVecUtils.rotate(bhole00_pos, rot.Angle, rot.Axis) # bhole01_posrot = DraftVecUtils.rotate(bhole01_pos, rot.Angle, rot.Axis) # bhole10_posrot = DraftVecUtils.rotate(bhole10_pos, rot.Angle, rot.Axis) # bhole11_posrot = DraftVecUtils.rotate(bhole11_pos, rot.Angle, rot.Axis) # shp_bolt00 = fcfun.shp_cyl ( # r=kcomp.NEMA_BOLT_D[size]/2.+kcomp.TOL/2., # h=bolt_depth + shaft_l, # normal = nnormal, # pos= bhole00_posrot) # shp_bolt01 = fcfun.shp_cyl ( # r=kcomp.NEMA_BOLT_D[size]/2.+kcomp.TOL/2., # h=bolt_depth + shaft_l, # normal = nnormal, # pos= bhole01_posrot) # shp_bolt10 = fcfun.shp_cyl ( # r=kcomp.NEMA_BOLT_D[size]/2.+kcomp.TOL/2., # h=bolt_depth + shaft_l, # normal = nnormal, # pos= bhole10_posrot) # shp_bolt11 = fcfun.shp_cyl ( # r=kcomp.NEMA_BOLT_D[size]/2.+kcomp.TOL/2., # h=bolt_depth + shaft_l, # normal = nnormal, # pos= bhole11_posrot) # shp_bolts = shp_bolt00.multiFuse([shp_bolt01, shp_bolt10, shp_bolt11]) # list of shapes to make a fusion of the container shp_contfuselist = [] # shp_contfuselist.append(shp_bolts) b2hole00_pos = FreeCAD.Vector(-kcomp.NEMA_BOLT_SEP[size] / 2, -kcomp.NEMA_BOLT_SEP[size] / 2, -bolt_depth) b2hole01_pos = FreeCAD.Vector(-kcomp.NEMA_BOLT_SEP[size] / 2, kcomp.NEMA_BOLT_SEP[size] / 2, -bolt_depth) b2hole10_pos = FreeCAD.Vector(kcomp.NEMA_BOLT_SEP[size] / 2, -kcomp.NEMA_BOLT_SEP[size] / 2, -bolt_depth) b2hole11_pos = FreeCAD.Vector(kcomp.NEMA_BOLT_SEP[size] / 2, kcomp.NEMA_BOLT_SEP[size] / 2, -bolt_depth) b2hole00 = addBolt(r_shank=nemabolt_d / 2. + mtol / 2., l_bolt=bolt_out + bolt_depth, r_head=kcomp.D912_HEAD_D[nemabolt_d] / 2. + mtol / 2., l_head=kcomp.D912_HEAD_L[nemabolt_d] + mtol, hex_head=0, extra=1, support=1, headdown=0, name="b2hole00") b2hole01 = Draft.clone(b2hole00) b2hole01.Label = "b2hole01" b2hole10 = Draft.clone(b2hole00) b2hole10.Label = "b2hole10" b2hole11 = Draft.clone(b2hole00) b2hole11.Label = "b2hole11" b2hole00.ViewObject.Visibility = False b2hole01.ViewObject.Visibility = False b2hole10.ViewObject.Visibility = False b2hole11.ViewObject.Visibility = False b2hole00.Placement.Base = b2hole00_pos b2hole01.Placement.Base = b2hole01_pos b2hole10.Placement.Base = b2hole10_pos b2hole11.Placement.Base = b2hole11_pos # it doesnt work if dont recompute here! probably the clones doc.recompute() b2holes_list = [b2hole00, b2hole01, b2hole10, b2hole11] # not an efficient way, either use shapes or fco, but not both shp_b2holes = b2hole00.Shape.multiFuse( [b2hole01.Shape, b2hole10.Shape, b2hole11.Shape]) #Part.show(shp_b2holes) b2holes = doc.addObject("Part::MultiFuse", "b2holes") b2holes.Shapes = b2holes_list b2holes.ViewObject.Visibility = False shp_b2holes.Placement.Base = pos shp_b2holes.Placement.Rotation = rot shp_contfuselist.append(shp_b2holes) # Circle on the base of the shaft if circle_r == 0: calcircle_r = kcomp.NEMA_BOLT_SEP[size] / 2. else: calcircle_r = circle_r if circle_h != 0: shp_circle = fcfun.shp_cyl( r=calcircle_r, h=circle_h + 1, #supperposition for union normal=nnormal, #supperposition for union pos=pos - nnormal) # fmotor: fused motor shp_fmotor = shp_motorshaft.fuse(shp_circle) else: shp_fmotor = shp_motorshaft #fmotor = doc.addObject("Part::Feature", "fmotor") #fmotor.Shape = shp_fmotor #shp_motor = shp_fmotor.cut(shp_bolts) shp_motor = shp_fmotor.cut(shp_b2holes) #Part.show(shp_bolts) # container if container == 1: # 2*TOL to make sure it fits v1 = FreeCAD.Vector(self.width / 2. - chmf / 2. + 2 * kcomp.TOL, self.width / 2. + 2 * kcomp.TOL, 0) v2 = FreeCAD.Vector(self.width / 2. + 2 * kcomp.TOL, self.width / 2. - chmf / 2. + 2 * kcomp.TOL, 0) cont_motorwire = fcfun.wire_sim_xy([v1, v2]) cont_motorwire.Placement.Rotation = rot cont_motorwire.Placement.Base = pos cont_motorface = Part.Face(cont_motorwire) shp_contmotor_box = cont_motorface.extrude(neg_lnormal) # the container is much wider than the shaft if rshaft_l == 0: # no rear shaft shp_contshaft = fcfun.shp_cyl(r=calcircle_r + kcomp.TOL, h=shaft_l + 1, normal=nnormal, pos=pos - nnormal) else: shp_contshaft = fcfun.shp_cyl(r=calcircle_r + kcomp.TOL, h=shaft_l + rshaft_l + length, normal=nnormal, pos=pos + rshaft_posend) shp_contfuselist.append(shp_contshaft) shp_contmotor = shp_contmotor_box.multiFuse(shp_contfuselist) else: shp_contmotor = shp_motor # we put the same shape doc.recompute() #fco_motor = doc.addObject("Part::Cut", name) #fco_motor.Base = fmotor #fco_motor.Tool = b2holes fco_motor = doc.addObject("Part::Feature", name) fco_motor.Shape = shp_motor self.fco = fco_motor self.shp_cont = shp_contmotor #Part.show(shp_contmotor) doc.recompute()
def execute(self, obj): if self.clone(obj): return if not obj.Base and ((not obj.Shape) or (not obj.Shape.isNull())): # let pass without error if we already have a shape return if not obj.Base: FreeCAD.Console.PrintError( "No Base, return without a rebar shape for {}.\n".format( obj.Name)) return if not hasattr(obj.Base, "Shape") or ( not obj.Base.Shape) or obj.Base.Shape.isNull(): FreeCAD.Console.PrintError( "No Shape in Base, return without a rebar shape for {}.\n". format(obj.Name)) return if obj.Base.Shape.Faces: FreeCAD.Console.PrintError( "Faces in Shape of Base, return without a rebar shape for {}.\n" .format(obj.Name)) return if not obj.Base.Shape.Edges: FreeCAD.Console.PrintError( "No Edges 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: FreeCAD.Console.PrintError( "No Amount, return without a rebar shape for {}.\n".format( obj.Name)) return 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 import Part # corner cases: # compound from more Wires # compound without Wires but with multiple Edges # Does they make sense? If yes handle them. # Does it makes sense to handle Shapes with Faces or even Solids? if not obj.Base.Shape.Wires and len(obj.Base.Shape.Edges) == 1: wire = Part.Wire(obj.Base.Shape.Edges[0]) else: wire = obj.Base.Shape.Wires[0] if hasattr(obj, "Rounding"): #print(obj.Rounding) if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value from DraftGeomUtils import filletWire wire = 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 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 hasattr(obj, "RebarShape"): barplacement = CalculatePlacement( obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.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 hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) for i in range(obj.Amount): if hasattr(obj, "RebarShape"): barplacement = CalculatePlacement(obj.Amount, i + 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.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 hasattr(obj, "RebarShape") and obj.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 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 if not obj.Diameter.Value: return if not obj.Amount: return 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 father.isDerivedFrom("Part::Feature"): 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 #print(axis) #print(size) spacinglist = None if hasattr(obj, "CustomSpacing"): if obj.CustomSpacing: spacinglist = strprocessOfCustomSpacing(obj.CustomSpacing) influenceArea = sum( spacinglist) - spacinglist[0] / 2 - spacinglist[-1] / 2 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 = [] if father: rot = father.Placement.Rotation else: rot = FreeCAD.Rotation() if obj.Amount == 1: barplacement = CalculatePlacement(obj.Amount, 1, 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 interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) for i in range(obj.Amount): barplacement = CalculatePlacement(obj.Amount, i + 1, 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[:] = [] 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): return FreeCAD.Console.PrintError( "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 updateData(self, obj, prop): """called when the base object is changed""" import DraftGui if prop in ["Start", "End", "Dimline", "Direction"]: if obj.Start == obj.End: return if not hasattr(self, "node"): return import Part, DraftGeomUtils from pivy import coin # calculate the 4 points self.p1 = obj.Start self.p4 = obj.End base = None if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): v2 = self.p1.sub(obj.Dimline) v3 = self.p4.sub(obj.Dimline) v2 = DraftVecUtils.project(v2, obj.Direction) v3 = DraftVecUtils.project(v3, obj.Direction) self.p2 = obj.Dimline.add(v2) self.p3 = obj.Dimline.add(v3) if DraftVecUtils.equals(self.p2, self.p3): base = None proj = None else: base = Part.LineSegment(self.p2, self.p3).toShape() proj = DraftGeomUtils.findDistance(self.p1, base) if proj: proj = proj.negative() if not base: if DraftVecUtils.equals(self.p1, self.p4): base = None proj = None else: base = Part.LineSegment(self.p1, self.p4).toShape() proj = DraftGeomUtils.findDistance(obj.Dimline, base) if proj: self.p2 = self.p1.add(proj.negative()) self.p3 = self.p4.add(proj.negative()) else: self.p2 = self.p1 self.p3 = self.p4 if proj: if hasattr(obj.ViewObject, "ExtLines") and hasattr( obj.ViewObject, "ScaleMultiplier"): dmax = obj.ViewObject.ExtLines.Value * obj.ViewObject.ScaleMultiplier if dmax and (proj.Length > dmax): if (dmax > 0): self.p1 = self.p2.add( DraftVecUtils.scaleTo(proj, dmax)) self.p4 = self.p3.add( DraftVecUtils.scaleTo(proj, dmax)) else: rest = proj.Length + dmax self.p1 = self.p2.add( DraftVecUtils.scaleTo(proj, rest)) self.p4 = self.p3.add( DraftVecUtils.scaleTo(proj, rest)) else: proj = (self.p3.sub(self.p2)).cross(App.Vector(0, 0, 1)) # calculate the arrows positions self.trans1.translation.setValue((self.p2.x, self.p2.y, self.p2.z)) self.coord1.point.setValue((self.p2.x, self.p2.y, self.p2.z)) self.trans2.translation.setValue((self.p3.x, self.p3.y, self.p3.z)) self.coord2.point.setValue((self.p3.x, self.p3.y, self.p3.z)) # calculate dimension and extension lines overshoots positions self.transDimOvershoot1.translation.setValue( (self.p2.x, self.p2.y, self.p2.z)) self.transDimOvershoot2.translation.setValue( (self.p3.x, self.p3.y, self.p3.z)) self.transExtOvershoot1.translation.setValue( (self.p2.x, self.p2.y, self.p2.z)) self.transExtOvershoot2.translation.setValue( (self.p3.x, self.p3.y, self.p3.z)) # calculate the text position and orientation if hasattr(obj, "Normal"): if DraftVecUtils.isNull(obj.Normal): if proj: norm = (self.p3.sub(self.p2).cross(proj)).negative() else: norm = App.Vector(0, 0, 1) else: norm = App.Vector(obj.Normal) else: if proj: norm = (self.p3.sub(self.p2).cross(proj)).negative() else: norm = App.Vector(0, 0, 1) if not DraftVecUtils.isNull(norm): norm.normalize() u = self.p3.sub(self.p2) u.normalize() v1 = norm.cross(u) rot1 = App.Placement(DraftVecUtils.getPlaneRotation( u, v1, norm)).Rotation.Q self.transDimOvershoot1.rotation.setValue( (rot1[0], rot1[1], rot1[2], rot1[3])) self.transDimOvershoot2.rotation.setValue( (rot1[0], rot1[1], rot1[2], rot1[3])) if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: u = u.negative() v2 = norm.cross(u) rot2 = App.Placement(DraftVecUtils.getPlaneRotation( u, v2, norm)).Rotation.Q self.trans1.rotation.setValue((rot2[0], rot2[1], rot2[2], rot2[3])) self.trans2.rotation.setValue((rot2[0], rot2[1], rot2[2], rot2[3])) if self.p1 != self.p2: u3 = self.p1.sub(self.p2) u3.normalize() v3 = norm.cross(u3) rot3 = App.Placement( DraftVecUtils.getPlaneRotation(u3, v3, norm)).Rotation.Q self.transExtOvershoot1.rotation.setValue( (rot3[0], rot3[1], rot3[2], rot3[3])) self.transExtOvershoot2.rotation.setValue( (rot3[0], rot3[1], rot3[2], rot3[3])) if hasattr(obj.ViewObject, "TextSpacing") and hasattr( obj.ViewObject, "ScaleMultiplier"): ts = obj.ViewObject.TextSpacing.Value * obj.ViewObject.ScaleMultiplier offset = DraftVecUtils.scaleTo(v1, ts) else: offset = DraftVecUtils.scaleTo(v1, 0.05) rott = rot1 if hasattr(obj.ViewObject, "FlipText"): if obj.ViewObject.FlipText: rott = App.Rotation(*rott).multiply(App.Rotation( norm, 180)).Q offset = offset.negative() # setting text try: m = obj.ViewObject.DisplayMode except: # swallow all exceptions here since it always fails on first run (Displaymode enum no set yet) m = ["2D", "3D"][utils.get_param("dimstyle", 0)] if m == "3D": offset = offset.negative() self.tbase = (self.p2.add( (self.p3.sub(self.p2).multiply(0.5)))).add(offset) if hasattr(obj.ViewObject, "TextPosition"): if not DraftVecUtils.isNull(obj.ViewObject.TextPosition): self.tbase = obj.ViewObject.TextPosition self.textpos.translation.setValue( [self.tbase.x, self.tbase.y, self.tbase.z]) self.textpos.rotation = coin.SbRotation(rott[0], rott[1], rott[2], rott[3]) su = True if hasattr(obj.ViewObject, "ShowUnit"): su = obj.ViewObject.ShowUnit # set text value l = self.p3.sub(self.p2).Length unit = None if hasattr(obj.ViewObject, "UnitOverride"): unit = obj.ViewObject.UnitOverride # special representation if "Building US" scheme if App.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt( "UserSchema", 0) == 5: s = App.Units.Quantity(l, App.Units.Length).UserString self.string = s.replace("' ", "'- ") self.string = s.replace("+", " ") elif hasattr(obj.ViewObject, "Decimals"): self.string = DraftGui.displayExternal(l, obj.ViewObject.Decimals, 'Length', su, unit) else: self.string = DraftGui.displayExternal(l, None, 'Length', su, unit) if hasattr(obj.ViewObject, "Override"): if obj.ViewObject.Override: self.string = obj.ViewObject.Override.replace("$dim",\ self.string) self.text.string = self.text3d.string = utils.string_encode_coin( self.string) # set the lines if m == "3D": # calculate the spacing of the text textsize = (len(self.string) * obj.ViewObject.FontSize.Value) / 4.0 spacing = ((self.p3.sub(self.p2)).Length / 2.0) - textsize self.p2a = self.p2.add( DraftVecUtils.scaleTo(self.p3.sub(self.p2), spacing)) self.p2b = self.p3.add( DraftVecUtils.scaleTo(self.p2.sub(self.p3), spacing)) self.coords.point.setValues( [[self.p1.x, self.p1.y, self.p1.z], [self.p2.x, self.p2.y, self.p2.z], [self.p2a.x, self.p2a.y, self.p2a.z], [self.p2b.x, self.p2b.y, self.p2b.z], [self.p3.x, self.p3.y, self.p3.z], [self.p4.x, self.p4.y, self.p4.z]]) #self.line.numVertices.setValues([3,3]) self.line.coordIndex.setValues(0, 7, (0, 1, 2, -1, 3, 4, 5)) else: self.coords.point.setValues([[self.p1.x, self.p1.y, self.p1.z], [self.p2.x, self.p2.y, self.p2.z], [self.p3.x, self.p3.y, self.p3.z], [self.p4.x, self.p4.y, self.p4.z]]) #self.line.numVertices.setValue(4) self.line.coordIndex.setValues(0, 4, (0, 1, 2, 3))
def offsetWire(wire, dvec, bind=False, occ=False, widthList=None, offsetMode=None, alignList=[], normal=None, basewireOffset=0): """Offset the wire along the given vector. Parameters ---------- wire as a list of edges (use the list directly), or previously as a wire or a face (Draft Wire with MakeFace True or False supported). The vector will be applied at the first vertex of the wire. If bind is True (and the shape is open), the original wire and the offsetted one are bound by 2 edges, forming a face. If widthList is provided (values only, not lengths - i.e. no unit), each value will be used to offset each corresponding edge in the wire. The 1st value overrides 'dvec' for 1st segment of wire; if a value is zero, value of 'widthList[0]' will follow; if widthList[0]' == 0, but dvec still provided, dvec will be followed offsetMode="BasewireMode" or None If alignList is provided, each value will be used to offset each corresponding edge in the wire with corresponding index. 'basewireOffset' corresponds to 'offset' in ArchWall which offset the basewire before creating the wall outline OffsetWire() is now aware of width and align per edge Primarily for use with ArchWall based on Sketch object To Do ----- `dvec` vector to offset is now derived (and can be ignored) in this function if widthList and alignList are provided - 'dvec' to be obsolete in future? """ if isinstance(wire, Part.Wire) or isinstance(wire, Part.Face): # Seems has repeatedly sortEdges, remark out here # edges = Part.__sortEdges__(wire.Edges) edges = wire.Edges elif isinstance(wire, list): if isinstance(wire[0], Part.Edge): edges = wire.copy() # How to avoid __sortEdges__ again? # Make getNormal directly tackle edges? wire = Part.Wire(Part.__sortEdges__(edges)) else: print("Either Part.Wire or Part.Edges should be provided, " "returning None") return None # For sketch with a number of wires, getNormal() may result # in different direction for each wire. # The 'normal' parameter, if provided e.g. by ArchWall, # allows normal over different wires e.g. in a Sketch be consistent # (over different calls of this function) if normal: norm = normal else: norm = getNormal(wire) # norm = Vector(0, 0, 1) closed = isReallyClosed(wire) nedges = [] if occ: length = abs(dvec.Length) if not length: return None if wire.Wires: wire = wire.Wires[0] else: wire = Part.Wire(edges) try: off = wire.makeOffset(length) except Part.OCCError: return None else: return off # vec of first edge depends on its geometry e = edges[0] # Make a copy of alignList - to avoid changes in this function # become starting input of next call of this function? # https://www.dataquest.io/blog/tutorial-functions-modify-lists-dictionaries-python/ # alignListC = alignList.copy() # Only Python 3 alignListC = list(alignList) # Python 2 and 3 # Check the direction / offset of starting edge firstDir = None try: if alignListC[0] == 'Left': firstDir = 1 firstAlign = 'Left' elif alignListC[0] == 'Right': firstDir = -1 firstAlign = 'Right' elif alignListC[0] == 'Center': firstDir = 1 firstAlign = 'Center' except IndexError: # Should no longer happen for ArchWall # as aligns are 'filled in' by ArchWall pass # If not provided by alignListC checked above, check the direction # of offset in dvec (not 'align'). # TODO Should check if dvec is provided or not # ('legacy/backward-compatible' mode) if not firstDir: # need to test against Part.Circle, not Part.ArcOfCircle if isinstance(e.Curve, Part.Circle): v0 = e.Vertexes[0].Point.sub(e.Curve.Center) else: v0 = vec(e).cross(norm) # check against dvec provided for the offset direction # would not know if dvec is vector of width (Left/Right Align) # or width/2 (Center Align) dvec0 = DraftVecUtils.scaleTo(v0, dvec.Length) if DraftVecUtils.equals(dvec0, dvec): # "Left Offset" (Left Align or 'left offset' in Centre Align) firstDir = 1 firstAlign = 'Left' alignListC.append('Left') elif DraftVecUtils.equals(dvec0, dvec.negative()): # "Right Offset" (Right Align or 'right offset' in Centre Align) firstDir = -1 firstAlign = 'Right' alignListC.append('Right') else: print(" something wrong with firstDir ") firstAlign = 'Left' alignListC.append('Left') for i in range(len(edges)): # make a copy so it do not reverse the self.baseWires edges # pointed to by _Wall.getExtrusionData()? curredge = edges[i].copy() # record first edge's Orientation, Dir, Align and set Delta if i == 0: # TODO Could be edge.Orientation in fact # "Forward" or "Reversed" firstOrientation = curredge.Vertexes[0].Orientation curOrientation = firstOrientation curDir = firstDir curAlign = firstAlign delta = dvec # record current edge's Orientation, and set Delta if i != 0: # else: # TODO Should also calculate 1st edge direction above if isinstance(curredge.Curve, Part.Circle): delta = curredge.Vertexes[0].Point.sub(curredge.Curve.Center) else: delta = vec(curredge).cross(norm) # TODO Could be edge.Orientation in fact curOrientation = curredge.Vertexes[0].Orientation # Consider individual edge width if widthList: # ArchWall should now always provide widthList try: if widthList[i] > 0: delta = DraftVecUtils.scaleTo(delta, widthList[i]) elif dvec: delta = DraftVecUtils.scaleTo(delta, dvec.Length) else: # just hardcoded default value as ArchWall would provide # if dvec is not provided either delta = DraftVecUtils.scaleTo(delta, 200) except Part.OCCError: if dvec: delta = DraftVecUtils.scaleTo(delta, dvec.Length) else: # just hardcoded default value as ArchWall would provide # if dvec is not provided either delta = DraftVecUtils.scaleTo(delta, 200) else: delta = DraftVecUtils.scaleTo(delta, dvec.Length) # Consider individual edge Align direction # - ArchWall should now always provide alignList if i == 0: if alignListC[0] == 'Center': delta = DraftVecUtils.scaleTo(delta, delta.Length/2) # No need to do anything for 'Left' and 'Right' as original dvec # have set both the direction and amount of offset correct # elif alignListC[i] == 'Left': #elif alignListC[i] == 'Right': if i != 0: try: if alignListC[i] == 'Left': curDir = 1 curAlign = 'Left' elif alignListC[i] == 'Right': curDir = -1 curAlign = 'Right' delta = delta.negative() elif alignListC[i] == 'Center': curDir = 1 curAlign = 'Center' delta = DraftVecUtils.scaleTo(delta, delta.Length/2) except IndexError: curDir = firstDir curAlign = firstAlign if firstAlign == 'Right': delta = delta.negative() elif firstAlign == 'Center': delta = DraftVecUtils.scaleTo(delta, delta.Length/2) # Consider whether generating the 'offset wire' or the 'base wire' if offsetMode is None: # Consider if curOrientation and/or curDir match their # firstOrientation/firstDir - to determine whether # and how to offset the current edge # This is a xor if (curOrientation == firstOrientation) != (curDir == firstDir): if curAlign in ['Left', 'Right']: # ArchWall has an Offset properties for user to offset # the basewire before creating the base profile of wall # (not applicable to 'Center' align) if basewireOffset: delta = DraftVecUtils.scaleTo(delta, basewireOffset) nedge = offset(curredge,delta,trim=True) else: nedge = curredge elif curAlign == 'Center': delta = delta.negative() nedge = offset(curredge, delta, trim=True) else: # if curAlign in ['Left', 'Right']: # elif curAlign == 'Center': # Both conditions same result. # ArchWall has an Offset properties for user to offset # the basewire before creating the base profile of wall # (not applicable to 'Center' align) if basewireOffset: if curAlign in ['Left', 'Right']: delta = DraftVecUtils.scaleTo(delta, delta.Length + basewireOffset) #else: # elif curAlign == 'Center': #pass # no need to add basewireOffset nedge = offset(curredge, delta, trim=True) # TODO arc always in counter-clockwise directinon # ... ( not necessarily 'reversed') if curOrientation == "Reversed": # need to test against Part.Circle, not Part.ArcOfCircle if not isinstance(curredge.Curve, Part.Circle): # if not arc/circle, assume straight line, reverse it nedge = Part.Edge(nedge.Vertexes[1], nedge.Vertexes[0]) else: # if arc/circle # Part.ArcOfCircle(edge.Curve, # edge.FirstParameter, edge.LastParameter, # edge.Curve.Axis.z > 0) midParameter = nedge.FirstParameter + (nedge.LastParameter - nedge.FirstParameter)/2 midOfArc = nedge.valueAt(midParameter) nedge = Part.ArcOfCircle(nedge.Vertexes[1].Point, midOfArc, nedge.Vertexes[0].Point).toShape() # TODO any better solution than to calculate midpoint # of arc to reverse? elif offsetMode in ["BasewireMode"]: if (not (curOrientation == firstOrientation) != (curDir == firstDir)): if curAlign in ['Left', 'Right']: # ArchWall has an Offset properties for user to offset # the basewire before creating the base profile of wall # (not applicable to 'Center' align) if basewireOffset: delta = DraftVecUtils.scaleTo(delta, basewireOffset) nedge = offset(curredge, delta, trim=True) else: nedge = curredge elif curAlign == 'Center': delta = delta.negative() nedge = offset(curredge, delta, trim=True) else: if curAlign in ['Left', 'Right']: # ArchWall has an Offset properties for user to offset # the basewire before creating the base profile of wall # (not applicable to 'Center' align) if basewireOffset: delta = DraftVecUtils.scaleTo(delta, delta.Length + basewireOffset) nedge = offset(curredge, delta, trim=True) elif curAlign == 'Center': nedge = offset(curredge, delta, trim=True) if curOrientation == "Reversed": # need to test against Part.Circle, not Part.ArcOfCircle if not isinstance(curredge.Curve, Part.Circle): # if not arc/circle, assume straight line, reverse it nedge = Part.Edge(nedge.Vertexes[1], nedge.Vertexes[0]) else: # if arc/circle # Part.ArcOfCircle(edge.Curve, # edge.FirstParameter, # edge.LastParameter, # edge.Curve.Axis.z > 0) midParameter = nedge.FirstParameter + (nedge.LastParameter - nedge.FirstParameter)/2 midOfArc = nedge.valueAt(midParameter) nedge = Part.ArcOfCircle(nedge.Vertexes[1].Point, midOfArc, nedge.Vertexes[0].Point).toShape() # TODO any better solution than to calculate midpoint # of arc to reverse? else: print(" something wrong ") return None if not nedge: return None nedges.append(nedge) if len(edges) > 1: nedges = connect(nedges, closed) else: nedges = Part.Wire(nedges[0]) if bind and not closed: e1 = Part.LineSegment(edges[0].Vertexes[0].Point, nedges[0].Vertexes[0].Point).toShape() e2 = Part.LineSegment(edges[-1].Vertexes[-1].Point, nedges[-1].Vertexes[-1].Point).toShape() alledges = edges.extend(nedges) alledges = alledges.extend([e1, e2]) w = Part.Wire(alledges) return w else: return nedges
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" import Part,DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self,obj) if data: if not isinstance(data[0],list): # multifuses not considered here return data length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value if not height: for p in obj.InList: if Draft.getType(p) in ["Floor","BuildingPart"]: if p.Height.Value: height = p.Height.Value if not height: return None if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None self.basewires = None # build wall layers layers = [] if hasattr(obj,"Material"): if obj.Material: if hasattr(obj.Material,"Materials"): varwidth = 0 restwidth = width - sum(obj.Material.Thicknesses) if restwidth > 0: varwidth = [t for t in obj.Material.Thicknesses if t == 0] if varwidth: varwidth = restwidth/len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) elif varwidth: layers.append(varwidth) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None elif obj.Face > 0: if len(obj.Base.Shape.Faces) >= obj.Face: face = obj.Base.Shape.Faces[obj.Face-1] # this wall is based on a specific face of its base object if obj.Normal != Vector(0,0,0): normal = face.normalAt(0,0) if normal.getAngle(Vector(0,0,1)) > math.pi/4: normal.multiply(width) base = face.extrude(normal) if obj.Align == "Center": base.translate(normal.negative().multiply(0.5)) elif obj.Align == "Right": base.translate(normal.negative()) else: normal.multiply(height) base = face.extrude(normal) base,placement = self.rebase(base) return (base,normal,placement) elif obj.Base.Shape.Faces: if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: base,placement = self.rebase(obj.Base.Shape) elif len(obj.Base.Shape.Edges) == 1: self.basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # self.basewires = obj.Base.Shape.Wires self.basewires = [] for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) if self.basewires and width: if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 baseface = None for i,wire in enumerate(self.basewires): e = wire.Edges[0] if isinstance(e.Curve,Part.Circle): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.negative() off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": if layers: off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w1 = DraftGeomUtils.offsetWire(wire,d1) layeroffset += layers[i] off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w2 = DraftGeomUtils.offsetWire(wire,d1) else: dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) if sh: sh.fix(0.1,0,1) # fixes self-intersecting wires f = Part.Face(sh) if baseface: if layers: baseface.append(f) else: baseface = baseface.fuse(f) # baseface = baseface.removeSplitter() s = DraftGeomUtils.removeSplitter(baseface) if s: baseface = s else: if layers: baseface = [f] else: baseface = f if baseface: base,placement = self.rebase(baseface) else: if layers: totalwidth = sum(layers) offset = 0 base = [] for l in layers: l2 = length/2 or 0.5 w1 = -totalwidth/2 + offset w2 = w1 + l v1 = Vector(-l2,w1,0) v2 = Vector(l2,w1,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base.append(Part.Face(Part.makePolygon([v1,v2,v3,v4,v1]))) offset += l else: l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) placement = FreeCAD.Placement() if base and placement: extrusion = normal.multiply(height) if placement.Rotation.Angle > 0: extrusion = placement.inverse().Rotation.multVec(extrusion) return (base,extrusion,placement) return None
def 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.Shape.Solids: return # creating base shape pl = obj.Placement base = None normal = None if hasattr(obj, "Normal"): if obj.Normal.Length > 0: normal = Vector(obj.Normal) normal.normalize() normal.multiply(thickness) baseprofile = None if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base if not normal: normal = baseprofile.Faces[0].normalAt( 0, 0).multiply(thickness) base = base.extrude(normal) elif base.Wires: fm = False if hasattr(obj, "FaceMaker"): if obj.FaceMaker != "None": try: base = Part.makeFace( base.Wires, "Part::FaceMaker" + str(obj.FaceMaker)) fm = True except: FreeCAD.Console.PrintError( translate("Arch", "Facemaker returned an error") + "\n") return if not fm: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: baseprofile = ArchCommands.makeFace(base.Wires) if not normal: normal = baseprofile.normalAt( 0, 0).multiply(thickness) base = baseprofile.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: if not normal: normal = Vector(0, 0, 1).multiply(thickness) 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]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) if hasattr(obj, "Area"): if baseprofile: obj.Area = baseprofile.Area if hasattr(obj, "WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) p1 = Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z) if obj.WaveType == "Curved": p2 = p1.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) e1 = Part.Arc(p1, p2, p3).toShape() p4 = p3.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) p5 = p4.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) e2 = Part.Arc(p3, p4, p5).toShape() else: if obj.WaveHeight.Value < obj.WaveLength.Value: p2 = p1.add( Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector( obj.WaveLength.Value - 2 * obj.WaveHeight.Value, 0, 0)) p4 = p3.add( Vector(obj.WaveHeight.Value, 0, -obj.WaveHeight.Value)) e1 = Part.makePolygon([p1, p2, p3, p4]) p5 = p4.add( Vector(obj.WaveHeight.Value, 0, -obj.WaveHeight.Value)) p6 = p5.add( Vector( obj.WaveLength.Value - 2 * obj.WaveHeight.Value, 0, 0)) p7 = p6.add( Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value)) e2 = Part.makePolygon([p4, p5, p6, p7]) else: p2 = p1.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) e1 = Part.makePolygon([p1, p2, p3]) p4 = p3.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) p5 = p4.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) e2 = Part.makePolygon([p3, p4, p5]) edges = [e1, e2] for i in range(int(bb.XLength / (obj.WaveLength.Value * 2))): e1 = e1.copy() e1.translate(Vector(obj.WaveLength.Value * 2, 0, 0)) e2 = e2.copy() e2.translate(Vector(obj.WaveLength.Value * 2, 0, 0)) edges.extend([e1, e2]) basewire = Part.Wire(edges) baseface = basewire.extrude(Vector(0, bb.YLength, 0)) base = baseface.extrude(Vector(0, 0, thickness)) rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), normal) base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) if obj.WaveDirection.Value: base.rotate(bb.Center, normal, obj.WaveDirection.Value) n1 = normal.negative().normalize().multiply( obj.WaveHeight.Value * 2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: FreeCAD.Console.PrintError( transpate("Arch", "Error computing shape of ") + obj.Label + "\n") return False 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 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: 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) else: 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 getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direction=None,linestyle=None,color=None,linespacing=None,techdraw=False,rotation=0): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' # if this is a group, gather all the svg views of its children if hasattr(obj,"isDerivedFrom"): if obj.isDerivedFrom("App::DocumentObjectGroup"): svg = "" for child in obj.Group: svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) return svg pathdata = [] svg = "" linewidth = float(linewidth)/scale fontsize = (float(fontsize)/scale)/2 if linespacing: linespacing = float(linespacing)/scale else: linespacing = 0.5 #print obj.Label," line spacing ",linespacing,"scale ",scale pointratio = .75 # the number of times the dots are smaller than the arrow size plane = None if direction: if isinstance(direction,FreeCAD.Vector): if direction != Vector(0,0,0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(Vector(0,0,0),direction.negative().negative(),0) elif isinstance(direction,WorkingPlane.plane): plane = direction stroke = "#000000" if color: if "#" in color: stroke = color else: stroke = getrgb(color) elif gui: if hasattr(obj,"ViewObject"): if hasattr(obj.ViewObject,"LineColor"): stroke = getrgb(obj.ViewObject.LineColor) def getPath(edges=[],wires=[],pathname=None): import Part,DraftGeomUtils svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] for w in wires: w1=w.copy() w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs=() #skipped for the first edge for edgeindex,e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' else: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG(e,drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = (getProj(c.value((c.LastParameter-\ c.FirstParameter)/2.0), plane), \ getProj(vs[-1].Point, plane)) else: endpoints = (getProj(vs[-1].Point), plane) # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -=180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' else: bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline=bspline.approximateBSpline(0.05,50, 3,'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree>3: #should not happen raise AssertionError elif bezierseg.Degree==1: edata +='L ' elif bezierseg.Degree==2: edata +='Q ' elif bezierseg.Degree==3: edata +='C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) +' '+ str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg def getCircle(edge): cen = getProj(edge.Curve.Center, plane) rad = edge.Curve.Radius if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis if round(edge.Curve.Axis.getAngle(drawing_plane_normal),2) == 0: # perpendicular projection: circle svg = '<circle cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" r="' + str(rad)+'" ' else: # any other projection: ellipse svg = '<path d="' svg += getDiscretized(edge, plane) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getEllipse(edge): cen = getProj(edge.Curve.Center, plane) mir = edge.Curve.MinorRadius mar = edge.Curve.MajorRadius svg = '<ellipse cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" rx="' + str(mar) svg += '" ry="' + str(mir)+'" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getArrow(arrowtype,point,arrowsize,color,linewidth,angle=0): svg = "" if gui: if not obj.ViewObject: return svg if obj.ViewObject.ArrowType == "Circle": svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y) svg += '" r="'+str(arrowsize)+'" ' svg += 'fill="none" stroke="'+ color + '" ' svg += 'style="stroke-width:'+ str(linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Dot": svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y) svg += '" r="'+str(arrowsize)+'" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Arrow": svg += '<path transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick": svg += '<path transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick-2": svg += '<line transform="rotate('+str(math.degrees(angle)+45) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="'+ color +'" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:'+ str(linewidth) +'" ' svg += 'x1="-'+ str(arrowsize*2) +'" y1="0" ' svg += 'x2="' + str(arrowsize*2) +'" y2="0" />\n' else: print("getSVG: arrow type not implemented") return svg def getOvershoot(point,shootsize,color,linewidth,angle=0): svg = '<line transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="'+ color +'" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:'+ str(linewidth) +'" ' svg += 'x1="0" y1="0" ' svg += 'x2="'+ str(shootsize*-1) +'" y2="0" />\n' return svg def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): if isinstance(angle,FreeCAD.Rotation): if not plane: angle = angle.Angle else: if plane.axis.getAngle(angle.Axis) < 0.001: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis)-math.pi) < 0.001: return "" # text is perpendicular to view, so it shouldn't appear else: angle = 0 #TODO maybe there is something better to do here? if not isinstance(text,list): text = text.split("\n") if align.lower() == "center": anchor = "middle" elif align.lower() == "left": anchor = "start" else: anchor = "end" if techdraw: svg = "" for i in range(len(text)): t = text[i] if sys.version_info.major < 3 and (not isinstance(t,unicode)): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") svg += '<text fill="' + color +'" font-size="' + str(fontsize) + '" ' svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';' svg += 'font-family:'+ fontname +'" ' svg += 'transform="rotate('+str(math.degrees(angle)) svg += ','+ str(base.x) + ',' + str(base.y-linespacing*i) + ') ' svg += 'translate(' + str(base.x) + ',' + str(base.y-linespacing*i) + ') ' svg += 'scale(1,-1)" ' #svg += '" freecad:skip="1"' svg += '>\n' + t + '</text>\n' else: svg = '<text fill="' svg += color +'" font-size="' svg += str(fontsize) + '" ' svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';' svg += 'font-family:'+ fontname +'" ' svg += 'transform="rotate('+str(math.degrees(angle)) svg += ','+ str(base.x) + ',' + str(base.y) + ') ' if flip: svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')' else: svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')' #svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') ' if flip: svg += ' scale(1,-1) ' else: svg += ' scale(1,1) ' svg += '" freecad:skip="1"' svg += '>\n' if len(text) == 1: try: svg += text[0] except: svg += text[0].decode("utf8") else: for i in range(len(text)): if i == 0: svg += '<tspan>' else: svg += '<tspan x="0" dy="'+str(linespacing)+'">' try: svg += text[i] except: svg += text[i].decode("utf8") svg += '</tspan>\n' svg += '</text>\n' return svg if not obj: pass elif isinstance(obj,Part.Shape): if "#" in fillstyle: fill = fillstyle elif fillstyle == "shape color": fill = "#888888" else: fill = 'url(#'+fillstyle+')' lstyle = getLineStyle(linestyle, scale) svg += getPath(obj.Edges,pathname="") elif getType(obj) == "Dimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"p1"): prx = obj.ViewObject.Proxy ts = (len(prx.string)*obj.ViewObject.FontSize.Value)/4.0 rm = ((prx.p3.sub(prx.p2)).Length/2.0)-ts p2a = getProj(prx.p2.add(DraftVecUtils.scaleTo(prx.p3.sub(prx.p2),rm)), plane) p2b = getProj(prx.p3.add(DraftVecUtils.scaleTo(prx.p2.sub(prx.p3),rm)), plane) p1 = getProj(prx.p1, plane) p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) p4 = getProj(prx.p4, plane) tbase = getProj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() rv = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(1,0,0)) angle = -DraftVecUtils.angle(getProj(rv, plane)) #angle = -DraftVecUtils.angle(p3.sub(p2)) # drawing lines svg = '<path ' if obj.ViewObject.DisplayMode == "2D": tangle = angle if tangle > math.pi/2: tangle = tangle-math.pi #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle+math.pi #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) if rotation != 0: #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string if abs(tangle+math.radians(rotation)) < 0.0001: tangle += math.pi tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase.add(Vector(0,-2.0/scale,0)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p2a.x)+' '+str(p2a.y)+' ' svg += 'M '+str(p2b.x)+' '+str(p2b.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="'+str(p1.x)+' '+str(p1.y)+'" ' svg += 'freecad:basepoint2="'+str(p4.x)+' '+str(p4.y)+'" ' svg += 'freecad:dimpoint="'+str(p2.x)+' '+str(p2.y)+'"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(obj.ViewObject,"DimOvershoot") and obj.ViewObject.DimOvershoot.Value: shootsize = obj.ViewObject.DimOvershoot.Value/pointratio svg += getOvershoot(p2,shootsize,stroke,linewidth,angle) svg += getOvershoot(p3,shootsize,stroke,linewidth,angle+math.pi) if hasattr(obj.ViewObject,"ExtOvershoot") and obj.ViewObject.ExtOvershoot.Value: shootsize = obj.ViewObject.ExtOvershoot.Value/pointratio shootangle = -DraftVecUtils.angle(p1.sub(p2)) svg += getOvershoot(p2,shootsize,stroke,linewidth,shootangle) svg += getOvershoot(p3,shootsize,stroke,linewidth,shootangle) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): arrowsize = obj.ViewObject.ArrowSize.Value/pointratio if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle = angle+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle+math.pi) # drawing text svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "AngularDimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"circle"): prx = obj.ViewObject.Proxy # drawing arc fill= "none" lstyle = getLineStyle(linestyle, scale) if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: if hasattr(prx,"circle1"): svg += getPath([prx.circle1]) svg += getPath([prx.circle2]) else: svg += getPath([prx.circle]) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio arrowlength = 4*obj.ViewObject.ArrowSize.Value u1 = getProj((prx.circle.valueAt(prx.circle.FirstParameter+arrowlength)).sub(prx.circle.valueAt(prx.circle.FirstParameter)), plane) u2 = getProj((prx.circle.valueAt(prx.circle.LastParameter-arrowlength)).sub(prx.circle.valueAt(prx.circle.LastParameter)), plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1+math.pi angle2 = angle2+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle1) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": t = prx.circle.tangentAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0) t = getProj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi/2) or (tangle > math.pi/2): tangle = tangle + math.pi tbase = getProj(prx.circle.valueAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0), plane) tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2.0/scale,0),tangle)) #print(tbase) else: tangle = 0 tbase = getProj(prx.tbase, plane) svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property def format_point(coords, action='L'): return "{action}{x},{y}".format( x=coords.x, y=coords.y, action=action ) # Draw multisegment line proj_points = list(map(lambda x: getProj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.format( stroke=stroke, linewidth=linewidth, directions=path_dir_str ) svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) angle = -DraftVecUtils.angle(getProj(last_segment, plane)) + math.pi svg += getArrow( arrowtype=obj.ViewObject.ArrowType, point=proj_points[-1], arrowsize=obj.ViewObject.ArrowSize.Value/pointratio, color=stroke, linewidth=linewidth, angle=angle ) # print text if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: fontname = obj.ViewObject.TextFont position = getProj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += getText(stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif getType(obj) in ["Annotation","DraftText"]: "returns an svg representation of a document annotation" if gui: if not obj.ViewObject: print ("export of texts to SVG is only available in GUI mode") else: n = obj.ViewObject.FontName if getType(obj) == "Annotation": p = getProj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText p = getProj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += getText(stroke,fontsize,n,r,p,t,linespacing,j) elif getType(obj) == "Axis": "returns the SVG representation of an Arch Axis system" if gui: if not obj.ViewObject: print ("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject lorig = getLineStyle(linestyle, scale) fill = 'none' rad = vobj.BubbleSize.Value/2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += getPath([e]) lstyle = "none" pos = ["Start"] if hasattr(vobj,"BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start","End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad,rad,rad)) svg += getCircle(Part.makeCircle(rad,center)) if hasattr(vobj.Proxy,"bubbletexts"): if len (vobj.Proxy.bubbletexts) >= n: svg += '<text fill="' + stroke + '" ' svg += 'font-size="' + str(rad) + '" ' svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="translate(' + str(center.x+rad/4.0) + ',' + str(center.y-rad/3.0) + ') ' svg += 'scale(1,-1)"> ' svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '</tspan>\n' svg += '</text>\n' n += 1 elif getType(obj) == "Pipe": fill = stroke lstyle = getLineStyle(linestyle, scale) if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve,Part.Circle): svg += getCircle(f.Edges[0]) elif getType(obj) == "Rebar": fill = "none" lstyle = getLineStyle(linestyle, scale) if obj.Proxy: if not hasattr(obj.Proxy,"wires"): obj.Proxy.execute(obj) if hasattr(obj.Proxy,"wires"): svg += getPath(wires=obj.Proxy.wires) elif getType(obj) == "PipeConnector": pass elif getType(obj) == "Space": "returns an SVG fragment for the text of a space" if gui: if not obj.ViewObject: print ("export of spaces to SVG is only available in GUI mode") else: c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = obj.ViewObject.Proxy.text1.string.getValues() t2 = obj.ViewObject.Proxy.text2.string.getValues() scale = obj.ViewObject.FirstLine.Value/obj.ViewObject.FontSize.Value f1 = fontsize*scale p2 = FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation.getValue().getValue()) lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation.getValue().getValue()) p1 = p2.add(lspc) j = obj.ViewObject.TextAlign svg += getText(c,f1,n,a,getProj(p1, plane),t1,linespacing,j,flip=True) if t2: ofs = FreeCAD.Vector(0,lspc.Length,0) if a: ofs = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),-rotation).multVec(ofs) svg += getText(c,fontsize,n,a,getProj(p1, plane).add(ofs),t2,linespacing,j,flip=True) elif obj.isDerivedFrom('Part::Feature'): if obj.Shape.isNull(): return '' # setting fill if obj.Shape.Faces: if gui: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None if (m != "Wireframe"): if fillstyle == "shape color": fill = getrgb(obj.ViewObject.ShapeColor,testbw=False) fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = 'url(#'+fillstyle+')' svg += getPattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' lstyle = getLineStyle(linestyle, scale) if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i,f in enumerate(obj.Shape.Faces): svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) else: for i,w in enumerate(obj.Shape.Wires): svg += getPath(w.Edges,pathname='%s_w%04d' % \ (obj.Name,i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i,e in enumerate(obj.Shape.Edges): if (DraftGeomUtils.findEdge(e,wiredEdges) == None): svg += getPath([e],pathname='%s_nwe%04d' % \ (obj.Name,i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve,Part.Circle): svg = getCircle(obj.Shape.Edges[0]) else: svg = getPath(obj.Shape.Edges) if FreeCAD.GuiUp: if hasattr(obj.ViewObject,"EndArrow") and hasattr(obj.ViewObject,"ArrowType") and (len(obj.Shape.Vertexes) > 1): if obj.ViewObject.EndArrow: p1 = getProj(obj.Shape.Vertexes[-2].Point, plane) p2 = getProj(obj.Shape.Vertexes[-1].Point, plane) angle = -DraftVecUtils.angle(p2.sub(p1)) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">'+svg+'</g>' return svg
def execute(self,obj): "creates the panel shape" 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 execute(self, obj): if self.clone(obj): return 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] 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)) size = (ArchCommands.projectToVector(father.Shape.copy(), axis)).Length if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) #.normalize() # don't normalize so the vector can also be used to determine the distance size = axis.Length #print axis #print size if (obj.OffsetStart.Value + obj.OffsetEnd.Value) > size: return # all tests ok! 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) except Part.OCCError: print "Arch: error sweeping rebar profile along the base sketch" return # building final shape shapes = [] if obj.Amount == 1: offset = DraftVecUtils.scaleTo(axis, size / 2) bar.translate(offset) shapes.append(bar) if hasattr(obj, "Spacing"): obj.Spacing = 0 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: bar.translate(baseoffset) shapes.append(bar) else: bar = bar.copy() bar.translate(vinterval) shapes.append(bar) if hasattr(obj, "Spacing"): obj.Spacing = interval if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def getSVG(obj, scale=1, linewidth=0.35, fontsize=12, fillstyle="shape color", direction=None, linestyle=None, color=None, linespacing=None, techdraw=False, rotation=0, fillSpaces=False, override=True): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' import Part, DraftGeomUtils # if this is a group, gather all the svg views of its children if hasattr(obj, "isDerivedFrom"): if obj.isDerivedFrom("App::DocumentObjectGroup") or getType( obj) == "Layer": svg = "" for child in obj.Group: svg += getSVG(child, scale, linewidth, fontsize, fillstyle, direction, linestyle, color, linespacing, techdraw, rotation, fillSpaces, override) return svg pathdata = [] svg = "" linewidth = float(linewidth) / scale if not override: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "LineWidth"): if hasattr(obj.ViewObject.LineWidth, "Value"): lw = obj.ViewObject.LineWidth.Value else: lw = obj.ViewObject.LineWidth linewidth = lw * linewidth fontsize = (float(fontsize) / scale) / 2 if linespacing: linespacing = float(linespacing) / scale else: linespacing = 0.5 #print obj.Label," line spacing ",linespacing,"scale ",scale pointratio = .75 # the number of times the dots are smaller than the arrow size plane = None if direction: if isinstance(direction, FreeCAD.Vector): if direction != Vector(0, 0, 0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(Vector(0, 0, 0), direction.negative().negative(), 0) elif isinstance(direction, WorkingPlane.plane): plane = direction stroke = "#000000" if color and override: if "#" in color: stroke = color else: stroke = getrgb(color) elif gui: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "LineColor"): stroke = getrgb(obj.ViewObject.LineColor) elif hasattr(obj.ViewObject, "TextColor"): stroke = getrgb(obj.ViewObject.TextColor) lstyle = "none" if override: lstyle = getLineStyle(linestyle, scale) else: if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "DrawStyle"): lstyle = getLineStyle(obj.ViewObject.DrawStyle, scale) def getPath(edges=[], wires=[], pathname=None): svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] first = True for w in wires: w1 = w.copy() if first: first = False else: # invert further wires to create holes w1 = DraftGeomUtils.invert(w1) w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs = () #skipped for the first edge for edgeindex, e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point - previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M ' + str(v.x) + ' ' + str(v.y) + ' ' else: if (vs[0].Point - previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD, "DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0, 0, 1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal), 2) in [0, 3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG( e, drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[ 1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes ) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = [ getProj( c.value((c.LastParameter - c.FirstParameter) / 2.0), plane), getProj(vs[-1].Point, plane) ] else: endpoints = [getProj(vs[-1].Point, plane)] # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -= 180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt( e.FirstParameter + (e.LastParameter - e.FirstParameter) / 10) flag_sweep = (DraftVecUtils.angle( t1, t2, drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' ' else: bspline = e.Curve.toBSpline(e.FirstParameter, e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline = bspline.approximateBSpline( 0.05, 50, 3, 'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree > 3: #should not happen raise AssertionError elif bezierseg.Degree == 1: edata += 'L ' elif bezierseg.Degree == 2: edata += 'Q ' elif bezierseg.Degree == 3: edata += 'C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) + ' ' + str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg def getCircle(edge): cen = getProj(edge.Curve.Center, plane) rad = edge.Curve.Radius if hasattr(FreeCAD, "DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0, 0, 1) if plane: drawing_plane_normal = plane.axis if round(edge.Curve.Axis.getAngle(drawing_plane_normal), 2) in [0, 3.14]: # perpendicular projection: circle svg = '<circle cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" r="' + str(rad) + '" ' else: # any other projection: ellipse svg = '<path d="' svg += getDiscretized(edge, plane) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getEllipse(edge): cen = getProj(edge.Curve.Center, plane) mir = edge.Curve.MinorRadius mar = edge.Curve.MajorRadius svg = '<ellipse cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" rx="' + str(mar) svg += '" ry="' + str(mir) + '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getArrow(arrowtype, point, arrowsize, color, linewidth, angle=0): svg = "" if gui: if not obj.ViewObject: return svg if obj.ViewObject.ArrowType == "Circle": svg += '<circle cx="' + str(point.x) + '" cy="' + str(point.y) svg += '" r="' + str(arrowsize) + '" ' svg += 'fill="none" stroke="' + color + '" ' svg += 'style="stroke-width:' + str( linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Dot": svg += '<circle cx="' + str(point.x) + '" cy="' + str(point.y) svg += '" r="' + str(arrowsize) + '" ' svg += 'fill="' + color + '" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Arrow": svg += '<path transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale(' + str(arrowsize) + ',' + str( arrowsize) + ')" freecad:skip="1" ' svg += 'fill="' + color + '" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick": svg += '<path transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale(' + str(arrowsize) + ',' + str( arrowsize) + ')" freecad:skip="1" ' svg += 'fill="' + color + '" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick-2": svg += '<line transform="rotate(' + str( math.degrees(angle) + 45) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="' + color + '" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:' + str(linewidth) + '" ' svg += 'x1="-' + str(arrowsize * 2) + '" y1="0" ' svg += 'x2="' + str(arrowsize * 2) + '" y2="0" />\n' else: print("getSVG: arrow type not implemented") return svg def getOvershoot(point, shootsize, color, linewidth, angle=0): svg = '<line transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="' + color + '" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:' + str(linewidth) + '" ' svg += 'x1="0" y1="0" ' svg += 'x2="' + str(shootsize * -1) + '" y2="0" />\n' return svg def getText(tcolor, fontsize, fontname, angle, base, text, linespacing=0.5, align="center", flip=True): if isinstance(angle, FreeCAD.Rotation): if not plane: angle = angle.Angle else: if plane.axis.getAngle(angle.Axis) < 0.001: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis) - math.pi) < 0.001: if abs(angle.Angle) > 0.1: angle = -angle.Angle else: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis) - math.pi / 2) < 0.001: return "" # text is perpendicular to view, so it shouldn't appear else: angle = 0 #TODO maybe there is something better to do here? if not isinstance(text, list): text = text.split("\n") if align.lower() == "center": anchor = "middle" elif align.lower() == "left": anchor = "start" else: anchor = "end" if techdraw: svg = "" for i in range(len(text)): t = text[i].replace("&", "&").replace("<", "<").replace( ">", ">") if six.PY2 and not isinstance(t, six.text_type): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") svg += '<text stroke-width="0" stroke="' + tcolor + '" fill="' + tcolor + '" font-size="' + str( fontsize) + '" ' svg += 'style="text-anchor:' + anchor + ';text-align:' + align.lower( ) + ';' svg += 'font-family:' + fontname + '" ' svg += 'transform="rotate(' + str(math.degrees(angle)) svg += ',' + str( base.x) + ',' + str(base.y - linespacing * i) + ') ' svg += 'translate(' + str( base.x) + ',' + str(base.y - linespacing * i) + ') ' svg += 'scale(1,-1)" ' #svg += '" freecad:skip="1"' svg += '>\n' + t + '</text>\n' else: svg = '<text stroke-width="0" stroke="' + tcolor + '" fill="' svg += tcolor + '" font-size="' svg += str(fontsize) + '" ' svg += 'style="text-anchor:' + anchor + ';text-align:' + align.lower( ) + ';' svg += 'font-family:' + fontname + '" ' svg += 'transform="rotate(' + str(math.degrees(angle)) svg += ',' + str(base.x) + ',' + str(base.y) + ') ' if flip: svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')' else: svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')' #svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') ' if flip: svg += ' scale(1,-1) ' else: svg += ' scale(1,1) ' svg += '" freecad:skip="1"' svg += '>\n' if len(text) == 1: try: svg += text[0].replace("&", "&").replace( "<", "<").replace(">", ">") except: svg += text[0].decode("utf8").replace( "&", "&").replace("<", "<").replace(">", ">") else: for i in range(len(text)): if i == 0: svg += '<tspan>' else: svg += '<tspan x="0" dy="' + str(linespacing) + '">' try: svg += text[i].replace("&", "&").replace( "<", "<").replace(">", ">") except: svg += text[i].decode("utf8").replace( "&", "&").replace("<", "<").replace(">", ">") svg += '</tspan>\n' svg += '</text>\n' return svg if not obj: pass elif isinstance(obj, Part.Shape): if "#" in fillstyle: fill = fillstyle elif fillstyle == "shape color": fill = "#888888" else: fill = 'url(#' + fillstyle + ')' svg += getPath(obj.Edges, pathname="") elif getType(obj) in ["Dimension", "LinearDimension"]: if gui: if not obj.ViewObject: print( "export of dimensions to SVG is only available in GUI mode" ) elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy, "p1"): prx = obj.ViewObject.Proxy ts = (len(prx.string) * obj.ViewObject.FontSize.Value) / 4.0 rm = ((prx.p3.sub(prx.p2)).Length / 2.0) - ts p2a = getProj( prx.p2.add( DraftVecUtils.scaleTo(prx.p3.sub(prx.p2), rm)), plane) p2b = getProj( prx.p3.add( DraftVecUtils.scaleTo(prx.p2.sub(prx.p3), rm)), plane) p1 = getProj(prx.p1, plane) p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) p4 = getProj(prx.p4, plane) tbase = getProj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() rv = FreeCAD.Rotation(r[0], r[1], r[2], r[3]).multVec( FreeCAD.Vector(1, 0, 0)) angle = -DraftVecUtils.angle(getProj(rv, plane)) #angle = -DraftVecUtils.angle(p3.sub(p2)) svg = '' nolines = False if hasattr(obj.ViewObject, "ShowLine"): if not obj.ViewObject.ShowLine: nolines = True # drawing lines if not nolines: svg += '<path ' if obj.ViewObject.DisplayMode == "2D": tangle = angle if tangle > math.pi / 2: tangle = tangle - math.pi #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle+math.pi #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) if rotation != 0: #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string if abs(tangle + math.radians(rotation)) < 0.0001: tangle += math.pi tbase = tbase.add( DraftVecUtils.rotate( Vector(0, 2 / scale, 0), tangle)) if not nolines: svg += 'd="M ' + str(p1.x) + ' ' + str(p1.y) + ' ' svg += 'L ' + str(p2.x) + ' ' + str(p2.y) + ' ' svg += 'L ' + str(p3.x) + ' ' + str(p3.y) + ' ' svg += 'L ' + str(p4.x) + ' ' + str(p4.y) + '" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase.add(Vector(0, -2.0 / scale, 0)) if not nolines: svg += 'd="M ' + str(p1.x) + ' ' + str(p1.y) + ' ' svg += 'L ' + str(p2.x) + ' ' + str(p2.y) + ' ' svg += 'L ' + str(p2a.x) + ' ' + str(p2a.y) + ' ' svg += 'M ' + str(p2b.x) + ' ' + str(p2b.y) + ' ' svg += 'L ' + str(p3.x) + ' ' + str(p3.y) + ' ' svg += 'L ' + str(p4.x) + ' ' + str(p4.y) + '" ' if not nolines: svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="' + str(p1.x) + ' ' + str( p1.y) + '" ' svg += 'freecad:basepoint2="' + str(p4.x) + ' ' + str( p4.y) + '" ' svg += 'freecad:dimpoint="' + str(p2.x) + ' ' + str( p2.y) + '"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(obj.ViewObject, "DimOvershoot" ) and obj.ViewObject.DimOvershoot.Value: shootsize = obj.ViewObject.DimOvershoot.Value / pointratio svg += getOvershoot(p2, shootsize, stroke, linewidth, angle) svg += getOvershoot(p3, shootsize, stroke, linewidth, angle + math.pi) if hasattr(obj.ViewObject, "ExtOvershoot" ) and obj.ViewObject.ExtOvershoot.Value: shootsize = obj.ViewObject.ExtOvershoot.Value / pointratio shootangle = -DraftVecUtils.angle(p1.sub(p2)) svg += getOvershoot(p2, shootsize, stroke, linewidth, shootangle) svg += getOvershoot(p3, shootsize, stroke, linewidth, shootangle) # drawing arrows if hasattr(obj.ViewObject, "ArrowType"): arrowsize = obj.ViewObject.ArrowSize.Value / pointratio if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: angle = angle + math.pi svg += getArrow(obj.ViewObject.ArrowType, p2, arrowsize, stroke, linewidth, angle) svg += getArrow(obj.ViewObject.ArrowType, p3, arrowsize, stroke, linewidth, angle + math.pi) # drawing text svg += getText(stroke, fontsize, obj.ViewObject.FontName, tangle, tbase, prx.string) elif getType(obj) == "AngularDimension": if gui: if not obj.ViewObject: print( "export of dimensions to SVG is only available in GUI mode" ) elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy, "circle"): prx = obj.ViewObject.Proxy # drawing arc fill = "none" if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: if hasattr(prx, "circle1"): svg += getPath([prx.circle1]) svg += getPath([prx.circle2]) else: svg += getPath([prx.circle]) # drawing arrows if hasattr(obj.ViewObject, "ArrowType"): p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value / pointratio arrowlength = 4 * obj.ViewObject.ArrowSize.Value u1 = getProj( (prx.circle.valueAt(prx.circle.FirstParameter + arrowlength) ).sub( prx.circle.valueAt( prx.circle.FirstParameter)), plane) u2 = getProj( (prx.circle.valueAt(prx.circle.LastParameter - arrowlength) ).sub(prx.circle.valueAt( prx.circle.LastParameter)), plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1 + math.pi angle2 = angle2 + math.pi svg += getArrow(obj.ViewObject.ArrowType, p2, arrowsize, stroke, linewidth, angle1) svg += getArrow(obj.ViewObject.ArrowType, p3, arrowsize, stroke, linewidth, angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": t = prx.circle.tangentAt(prx.circle.FirstParameter + (prx.circle.LastParameter - prx.circle.FirstParameter) / 2.0) t = getProj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi / 2) or (tangle > math.pi / 2): tangle = tangle + math.pi tbase = getProj( prx.circle.valueAt(prx.circle.FirstParameter + (prx.circle.LastParameter - prx.circle.FirstParameter) / 2.0), plane) tbase = tbase.add( DraftVecUtils.rotate(Vector(0, 2.0 / scale, 0), tangle)) #print(tbase) else: tangle = 0 tbase = getProj(prx.tbase, plane) svg += getText(stroke, fontsize, obj.ViewObject.FontName, tangle, tbase, prx.string) elif getType(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property def format_point(coords, action='L'): return "{action}{x},{y}".format(x=coords.x, y=coords.y, action=action) # Draw multisegment line proj_points = list(map(lambda x: getProj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.format( stroke=stroke, linewidth=linewidth, directions=path_dir_str) svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) angle = -DraftVecUtils.angle(getProj(last_segment, plane)) + math.pi svg += getArrow(arrowtype=obj.ViewObject.ArrowType, point=proj_points[-1], arrowsize=obj.ViewObject.ArrowSize.Value / pointratio, color=stroke, linewidth=linewidth, angle=angle) # print text if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: fontname = obj.ViewObject.TextFont position = getProj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += getText(stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif getType(obj) in ["Annotation", "DraftText"]: "returns an svg representation of a document annotation" if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: n = obj.ViewObject.FontName if getType(obj) == "Annotation": p = getProj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText p = getProj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += getText(stroke, fontsize, n, r, p, t, linespacing, j) elif getType(obj) == "Axis": "returns the SVG representation of an Arch Axis system" if gui: if not obj.ViewObject: print("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject lorig = lstyle fill = 'none' rad = vobj.BubbleSize.Value / 2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += getPath([e]) lstyle = "none" pos = ["Start"] if hasattr(vobj, "BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start", "End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad, rad, rad)) svg += getCircle(Part.makeCircle(rad, center)) if hasattr(vobj.Proxy, "bubbletexts"): if len(vobj.Proxy.bubbletexts) >= n: svg += '<text fill="' + stroke + '" ' svg += 'font-size="' + str(rad) + '" ' svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="translate(' + str( center.x + rad / 4.0) + ',' + str(center.y - rad / 3.0) + ') ' svg += 'scale(1,-1)"> ' svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[ n].string.getValues()[0] + '</tspan>\n' svg += '</text>\n' n += 1 lstyle = lorig elif getType(obj) == "Pipe": fill = stroke if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve, Part.Circle): svg += getCircle(f.Edges[0]) elif getType(obj) == "Rebar": fill = "none" if obj.Proxy: if not hasattr(obj.Proxy, "wires"): obj.Proxy.execute(obj) if hasattr(obj.Proxy, "wires"): svg += getPath(wires=obj.Proxy.wires) elif getType(obj) == "PipeConnector": pass elif getType(obj) == "Space": "returns an SVG fragment for the text of a space" if gui: if not obj.ViewObject: print("export of spaces to SVG is only available in GUI mode") else: if fillSpaces: if hasattr(obj, "Proxy"): if not hasattr(obj.Proxy, "face"): obj.Proxy.getArea(obj, notouch=True) if hasattr(obj.Proxy, "face"): # setting fill if gui: fill = getrgb(obj.ViewObject.ShapeColor, testbw=False) fill_opacity = 1 - ( obj.ViewObject.Transparency / 100.0) else: fill = "#888888" svg += getPath(wires=[obj.Proxy.face.OuterWire]) c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = obj.ViewObject.Proxy.text1.string.getValues() t2 = obj.ViewObject.Proxy.text2.string.getValues() scale = obj.ViewObject.FirstLine.Value / obj.ViewObject.FontSize.Value f1 = fontsize * scale p2 = obj.Placement.multVec( FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation. getValue().getValue())) lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation. getValue().getValue()) p1 = p2.add(lspc) j = obj.ViewObject.TextAlign t3 = getText(c, f1, n, a, getProj(p1, plane), t1, linespacing, j, flip=True) svg += t3 if t2: ofs = FreeCAD.Vector(0, -lspc.Length, 0) if a: ofs = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -rotation).multVec(ofs) t4 = getText(c, fontsize, n, a, getProj(p1, plane).add(ofs), t2, linespacing, j, flip=True) svg += t4 elif obj.isDerivedFrom('Part::Feature'): if obj.Shape.isNull(): return '' # setting fill if obj.Shape.Faces: if gui: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None if (m != "Wireframe"): if fillstyle == "shape color": fill = getrgb(obj.ViewObject.ShapeColor, testbw=False) fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = 'url(#' + fillstyle + ')' svg += getPattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i, f in enumerate(obj.Shape.Faces): # place outer wire first wires = [f.OuterWire] wires.extend([ w for w in f.Wires if w.hashCode() != f.OuterWire.hashCode() ]) svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) else: for i, w in enumerate(obj.Shape.Wires): svg += getPath(w.Edges,pathname='%s_w%04d' % \ (obj.Name,i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i, e in enumerate(obj.Shape.Edges): if (DraftGeomUtils.findEdge(e, wiredEdges) is None): svg += getPath([e],pathname='%s_nwe%04d' % \ (obj.Name,i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve, Part.Circle): svg = getCircle(obj.Shape.Edges[0]) else: svg = getPath(obj.Shape.Edges) if FreeCAD.GuiUp: if hasattr(obj.ViewObject, "EndArrow") and hasattr( obj.ViewObject, "ArrowType") and (len(obj.Shape.Vertexes) > 1): if obj.ViewObject.EndArrow: p1 = getProj(obj.Shape.Vertexes[-1].Point, plane) p2 = getProj(obj.Shape.Vertexes[-2].Point, plane) angle = -DraftVecUtils.angle(p2.sub(p1)) arrowsize = obj.ViewObject.ArrowSize.Value / pointratio svg += getArrow(obj.ViewObject.ArrowType, p1, arrowsize, stroke, linewidth, angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">\n ' + svg + '</g>\n' return svg
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 execute(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] 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(obj) if not bpoint: return 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) #.normalize() # don't normalize so the vector can also be used to determine the distance size = axis.Length #print axis #print size if (obj.OffsetStart.Value + obj.OffsetEnd.Value) > size: return # all tests ok! 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) except: print "Arch: error sweeping rebar profile along the base sketch" return # building final shape shapes = [] if obj.Amount == 1: offset = DraftVecUtils.scaleTo(axis,size/2) bar.translate(offset) shapes.append(bar) if hasattr(obj,"Spacing"): obj.Spacing = 0 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: bar.translate(baseoffset) shapes.append(bar) else: bar = bar.copy() bar.translate(vinterval) shapes.append(bar) if hasattr(obj,"Spacing"): obj.Spacing = interval if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self, obj): "creates the panel shape" 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 getProfiles(self, obj, noplacement=False): "Returns the base profile(s) of this component, if applicable" wires = [] if Draft.getType(obj) == "Precast": return wires n, l, w, h = self.getDefaultValues(obj) if obj.Base: if obj.Base.isDerivedFrom("Part::Extrusion"): if obj.Base.Base: base = obj.Base.Base.Shape.copy() #if noplacement: # base.Placement = FreeCAD.Placement() return [base] elif 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( Part.__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( Part.__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 execute(self, obj): import Part plm = obj.Placement if obj.Base and (not obj.Tool): if obj.Base.isDerivedFrom("Sketcher::SketchObject"): shape = obj.Base.Shape.copy() if obj.Base.Shape.isClosed(): if hasattr(obj, "MakeFace"): if obj.MakeFace: shape = Part.Face(shape) else: shape = Part.Face(shape) obj.Shape = shape elif obj.Base and obj.Tool: if hasattr(obj.Base, 'Shape') and hasattr(obj.Tool, 'Shape'): if (not obj.Base.Shape.isNull()) and ( not obj.Tool.Shape.isNull()): sh1 = obj.Base.Shape.copy() sh2 = obj.Tool.Shape.copy() shape = sh1.fuse(sh2) if DraftGeomUtils.isCoplanar(shape.Faces): shape = DraftGeomUtils.concatenate(shape) obj.Shape = shape p = [] for v in shape.Vertexes: p.append(v.Point) if obj.Points != p: obj.Points = p elif obj.Points: if obj.Points[0] == obj.Points[-1]: if not obj.Closed: obj.Closed = True obj.Points.pop() if obj.Closed and (len(obj.Points) > 2): pts = obj.Points if hasattr(obj, "Subdivisions"): if obj.Subdivisions > 0: npts = [] for i in range(len(pts)): p1 = pts[i] npts.append(pts[i]) if i == len(pts) - 1: p2 = pts[0] else: p2 = pts[i + 1] v = p2.sub(p1) v = DraftVecUtils.scaleTo( v, v.Length / (obj.Subdivisions + 1)) for j in range(obj.Subdivisions): npts.append( p1.add(App.Vector(v).multiply(j + 1))) pts = npts shape = Part.makePolygon(pts + [pts[0]]) if "ChamferSize" in obj.PropertiesList: if obj.ChamferSize.Value != 0: w = DraftGeomUtils.filletWire(shape, obj.ChamferSize.Value, chamfer=True) if w: shape = w if "FilletRadius" in obj.PropertiesList: if obj.FilletRadius.Value != 0: w = DraftGeomUtils.filletWire(shape, obj.FilletRadius.Value) if w: shape = w try: if hasattr(obj, "MakeFace"): if obj.MakeFace: shape = Part.Face(shape) else: shape = Part.Face(shape) except Part.OCCError: pass else: edges = [] pts = obj.Points[1:] lp = obj.Points[0] for p in pts: if not DraftVecUtils.equals(lp, p): if hasattr(obj, "Subdivisions"): if obj.Subdivisions > 0: npts = [] v = p.sub(lp) v = DraftVecUtils.scaleTo( v, v.Length / (obj.Subdivisions + 1)) edges.append( Part.LineSegment(lp, lp.add(v)).toShape()) lv = lp.add(v) for j in range(obj.Subdivisions): edges.append( Part.LineSegment(lv, lv.add(v)).toShape()) lv = lv.add(v) else: edges.append(Part.LineSegment(lp, p).toShape()) else: edges.append(Part.LineSegment(lp, p).toShape()) lp = p try: shape = Part.Wire(edges) except Part.OCCError: print("Error wiring edges") shape = None if "ChamferSize" in obj.PropertiesList: if obj.ChamferSize.Value != 0: w = DraftGeomUtils.filletWire(shape, obj.ChamferSize.Value, chamfer=True) if w: shape = w if "FilletRadius" in obj.PropertiesList: if obj.FilletRadius.Value != 0: w = DraftGeomUtils.filletWire(shape, obj.FilletRadius.Value) if w: shape = w if shape: obj.Shape = shape if hasattr(obj, "Area") and hasattr(shape, "Area"): obj.Area = shape.Area if hasattr(obj, "Length"): obj.Length = shape.Length obj.Placement = plm obj.positionBySupport() self.onChanged(obj, "Placement")
def updateData(self, obj, prop): if hasattr(self, "arc"): from pivy import coin import Part, DraftGeomUtils import DraftGui arcsegs = 24 # calculate the arc data if DraftVecUtils.isNull(obj.Normal): norm = App.Vector(0, 0, 1) else: norm = obj.Normal radius = (obj.Dimline.sub(obj.Center)).Length self.circle = Part.makeCircle(radius, obj.Center, norm, obj.FirstAngle.Value, obj.LastAngle.Value) self.p2 = self.circle.Vertexes[0].Point self.p3 = self.circle.Vertexes[-1].Point mp = DraftGeomUtils.findMidpoint(self.circle.Edges[0]) ray = mp.sub(obj.Center) # set text value if obj.LastAngle.Value > obj.FirstAngle.Value: a = obj.LastAngle.Value - obj.FirstAngle.Value else: a = (360 - obj.FirstAngle.Value) + obj.LastAngle.Value su = True if hasattr(obj.ViewObject, "ShowUnit"): su = obj.ViewObject.ShowUnit if hasattr(obj.ViewObject, "Decimals"): self.string = DraftGui.displayExternal(a, obj.ViewObject.Decimals, 'Angle', su) else: self.string = DraftGui.displayExternal(a, None, 'Angle', su) if obj.ViewObject.Override: self.string = obj.ViewObject.Override.replace("$dim",\ self.string) self.text.string = self.text3d.string = utils.string_encode_coin( self.string) # check display mode try: m = obj.ViewObject.DisplayMode except: # swallow all exceptions here since it always fails on first run (Displaymode enum no set yet) m = ["2D", "3D"][utils.get_param("dimstyle", 0)] # set the arc if m == "3D": # calculate the spacing of the text spacing = (len(self.string) * obj.ViewObject.FontSize.Value) / 8.0 pts1 = [] cut = None pts2 = [] for i in range(arcsegs + 1): p = self.circle.valueAt(self.circle.FirstParameter + ( (self.circle.LastParameter - self.circle.FirstParameter) / arcsegs) * i) if (p.sub(mp)).Length <= spacing: if cut is None: cut = i else: if cut is None: pts1.append([p.x, p.y, p.z]) else: pts2.append([p.x, p.y, p.z]) self.coords.point.setValues(pts1 + pts2) i1 = len(pts1) i2 = i1 + len(pts2) self.arc.coordIndex.setValues( 0, len(pts1) + len(pts2) + 1, list(range(len(pts1))) + [-1] + list(range(i1, i2))) if (len(pts1) >= 3) and (len(pts2) >= 3): self.circle1 = Part.Arc( App.Vector(pts1[0][0], pts1[0][1], pts1[0][2]), App.Vector(pts1[1][0], pts1[1][1], pts1[1][2]), App.Vector(pts1[-1][0], pts1[-1][1], pts1[-1][2])).toShape() self.circle2 = Part.Arc( App.Vector(pts2[0][0], pts2[0][1], pts2[0][2]), App.Vector(pts2[1][0], pts2[1][1], pts2[1][2]), App.Vector(pts2[-1][0], pts2[-1][1], pts2[-1][2])).toShape() else: pts = [] for i in range(arcsegs + 1): p = self.circle.valueAt(self.circle.FirstParameter + ( (self.circle.LastParameter - self.circle.FirstParameter) / arcsegs) * i) pts.append([p.x, p.y, p.z]) self.coords.point.setValues(pts) self.arc.coordIndex.setValues(0, arcsegs + 1, list(range(arcsegs + 1))) # set the arrow coords and rotation self.trans1.translation.setValue((self.p2.x, self.p2.y, self.p2.z)) self.coord1.point.setValue((self.p2.x, self.p2.y, self.p2.z)) self.trans2.translation.setValue((self.p3.x, self.p3.y, self.p3.z)) self.coord2.point.setValue((self.p3.x, self.p3.y, self.p3.z)) # calculate small chords to make arrows look better arrowlength = 4 * obj.ViewObject.ArrowSize.Value u1 = (self.circle.valueAt(self.circle.FirstParameter + arrowlength) ).sub(self.circle.valueAt( self.circle.FirstParameter)).normalize() u2 = (self.circle.valueAt(self.circle.LastParameter)).sub( self.circle.valueAt(self.circle.LastParameter - arrowlength)).normalize() if hasattr(obj.ViewObject, "FlipArrows"): if obj.ViewObject.FlipArrows: u1 = u1.negative() u2 = u2.negative() w2 = self.circle.Curve.Axis w1 = w2.negative() v1 = w1.cross(u1) v2 = w2.cross(u2) q1 = App.Placement(DraftVecUtils.getPlaneRotation(u1, v1, w1)).Rotation.Q q2 = App.Placement(DraftVecUtils.getPlaneRotation(u2, v2, w2)).Rotation.Q self.trans1.rotation.setValue((q1[0], q1[1], q1[2], q1[3])) self.trans2.rotation.setValue((q2[0], q2[1], q2[2], q2[3])) # setting text pos & rot self.tbase = mp if hasattr(obj.ViewObject, "TextPosition"): if not DraftVecUtils.isNull(obj.ViewObject.TextPosition): self.tbase = obj.ViewObject.TextPosition u3 = ray.cross(norm).normalize() v3 = norm.cross(u3) r = App.Placement(DraftVecUtils.getPlaneRotation(u3, v3, norm)).Rotation offset = r.multVec(App.Vector(0, 1, 0)) if hasattr(obj.ViewObject, "TextSpacing"): offset = DraftVecUtils.scaleTo( offset, obj.ViewObject.TextSpacing.Value) else: offset = DraftVecUtils.scaleTo(offset, 0.05) if m == "3D": offset = offset.negative() self.tbase = self.tbase.add(offset) q = r.Q self.textpos.translation.setValue( [self.tbase.x, self.tbase.y, self.tbase.z]) self.textpos.rotation = coin.SbRotation(q[0], q[1], q[2], q[3]) # set the angle property if round(obj.Angle, utils.precision()) != round( a, utils.precision()): obj.Angle = a
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" import Part,DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self,obj) if data: if not isinstance(data[0],list): # multifuses not considered here return data length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value if not height: for p in obj.InList: if Draft.getType(p) in ["Floor","BuildingPart"]: if p.Height.Value: height = p.Height.Value if not height: return None if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None self.basewires = None # build wall layers layers = [] if hasattr(obj,"Material"): if obj.Material: if hasattr(obj.Material,"Materials"): varwidth = 0 restwidth = width - sum(obj.Material.Thicknesses) if restwidth > 0: varwidth = [t for t in obj.Material.Thicknesses if t == 0] if varwidth: varwidth = restwidth/len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) elif varwidth: layers.append(varwidth) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None elif obj.Face > 0: if len(obj.Base.Shape.Faces) >= obj.Face: face = obj.Base.Shape.Faces[obj.Face-1] # this wall is based on a specific face of its base object normal = face.normalAt(0,0) if normal.getAngle(Vector(0,0,1)) > math.pi/4: normal.multiply(width) base = face.extrude(normal) if obj.Align == "Center": base.translate(normal.negative().multiply(0.5)) elif obj.Align == "Right": base.translate(normal.negative()) else: normal.multiply(height) base = face.extrude(normal) base,placement = self.rebase(base) return (base,normal,placement) elif obj.Base.Shape.Faces: if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: base,placement = self.rebase(obj.Base.Shape) elif len(obj.Base.Shape.Edges) == 1: self.basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # self.basewires = obj.Base.Shape.Wires self.basewires = [] for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) if self.basewires and width: if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 baseface = None for i,wire in enumerate(self.basewires): e = wire.Edges[0] if isinstance(e.Curve,Part.Circle): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.negative() off = obj.Offset.Value if layers: off = off+layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": if layers: off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w1 = DraftGeomUtils.offsetWire(wire,d1) layeroffset += layers[i] off = width/2-layeroffset d1 = Vector(dvec).multiply(off) w2 = DraftGeomUtils.offsetWire(wire,d1) else: dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) if sh: sh.fix(0.1,0,1) # fixes self-intersecting wires f = Part.Face(sh) if baseface: if layers: baseface.append(f) else: baseface = baseface.fuse(f) # baseface = baseface.removeSplitter() s = DraftGeomUtils.removeSplitter(baseface) if s: baseface = s else: if layers: baseface = [f] else: baseface = f if baseface: base,placement = self.rebase(baseface) else: if layers: totalwidth = sum(layers) offset = 0 base = [] for l in layers: l2 = length/2 or 0.5 w1 = -totalwidth/2 + offset w2 = w1 + l v1 = Vector(-l2,w1,0) v2 = Vector(l2,w1,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base.append(Part.Face(Part.makePolygon([v1,v2,v3,v4,v1]))) offset += l else: l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) placement = FreeCAD.Placement() if base and placement: extrusion = normal.multiply(height) return (base,extrusion,placement) return None
def getRebarData(self, obj): #if not obj.Host: # return #if Draft.getType(obj.Host) != "Structure": # return #if not obj.Host.Shape: # return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = None if obj.Host: father = obj.Host wire = obj.Base.Shape.Wires[0] axis = None if Draft.getType( obj.Base) == "Wire": # Draft Wires can have "wrong" placement import DraftGeomUtils axis = DraftGeomUtils.getNormal(obj.Base.Shape) if not axis: axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector( 0, 0, -1)) size = 0 if father: size = (ArchCommands.projectToVector(father.Shape.copy(), axis)).Length if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value if hasattr(obj, "Rounding"): if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire, radius) wires = [] if obj.Amount == 1: if size and father: offset = DraftVecUtils.scaleTo(axis, size / 2) else: offset = FreeCAD.Vector() wire.translate(offset) wires.append(wire) else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) vinterval = DraftVecUtils.scaleTo(axis, interval) for i in range(obj.Amount): if i == 0: if baseoffset: wire.translate(baseoffset) wires.append(wire) else: wire = wire.copy() wire.translate(vinterval) wires.append(wire) return [wires, obj.Diameter.Value / 2]
def 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 if not obj.Diameter.Value: return if not obj.Amount: return 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 father.isDerivedFrom("Part::Feature"): 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 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: barplacement = CalculatePlacement(obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.ViewObject.RebarShape) 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.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): barplacement = CalculatePlacement(obj.Amount, i+1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.ViewObject.RebarShape) 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 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.Shape.Solids: return # creating base shape pl = obj.Placement base = None normal = None baseprofile = None if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness) base = base.extrude(normal) elif base.Wires: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: baseprofile = ArchCommands.makeFace(base.Wires) normal = baseprofile.normalAt(0,0).multiply(thickness) base = baseprofile.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) 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]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) if hasattr(obj,"Area"): if baseprofile: obj.Area = baseprofile.Area if hasattr(obj,"WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) p1 = Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z) if obj.WaveType == "Curved": p2 = p1.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) e1 = Part.Arc(p1,p2,p3).toShape() p4 = p3.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) p5 = p4.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) e2 = Part.Arc(p3,p4,p5).toShape() else: if obj.WaveHeight.Value < obj.WaveLength.Value: p2 = p1.add(Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value-2*obj.WaveHeight.Value,0,0)) p4 = p3.add(Vector(obj.WaveHeight.Value,0,-obj.WaveHeight.Value)) e1 = Part.makePolygon([p1,p2,p3,p4]) p5 = p4.add(Vector(obj.WaveHeight.Value,0,-obj.WaveHeight.Value)) p6 = p5.add(Vector(obj.WaveLength.Value-2*obj.WaveHeight.Value,0,0)) p7 = p6.add(Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)) e2 = Part.makePolygon([p4,p5,p6,p7]) else: p2 = p1.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) e1 = Part.makePolygon([p1,p2,p3]) p4 = p3.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) p5 = p4.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) e2 = Part.makePolygon([p3,p4,p5]) edges = [e1,e2] for i in range(int(bb.XLength/(obj.WaveLength.Value*2))): e1 = e1.copy() e1.translate(Vector(obj.WaveLength.Value*2,0,0)) e2 = e2.copy() e2.translate(Vector(obj.WaveLength.Value*2,0,0)) edges.extend([e1,e2]) basewire = Part.Wire(edges) baseface = basewire.extrude(Vector(0,bb.YLength,0)) base = baseface.extrude(Vector(0,0,thickness)) rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal) base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle)) if obj.WaveDirection.Value: base.rotate(bb.Center,normal,obj.WaveDirection.Value) n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: FreeCAD.Console.PrintError(transpate("Arch","Error computing shape of ")+obj.Label+"\n") return False 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 _svg_dimension(obj, plane, scale, linewidth, fontsize, stroke, pointratio, techdraw, rotation): """Return the SVG representation of a linear dimension.""" if not App.GuiUp: _wrn("'{}': SVG can only be generated " "in GUI mode".format(obj.Label)) return "" if not hasattr(obj.ViewObject, "Proxy") or not obj.ViewObject.Proxy: _err("'{}': doesn't have Proxy, " "SVG cannot be generated".format(obj.Label)) return "" vobj = obj.ViewObject prx = vobj.Proxy if not hasattr(prx, "p1"): _err("'{}': doesn't have points, " "SVG cannot be generated".format(obj.Label)) return "" ts = len(prx.string) * vobj.FontSize.Value / 4.0 rm = (prx.p3 - prx.p2).Length / 2.0 - ts _diff32 = prx.p3 - prx.p2 _diff23 = prx.p2 - prx.p3 _v32 = DraftVecUtils.scaleTo(_diff32, rm) _v23 = DraftVecUtils.scaleTo(_diff23, rm) p2a = get_proj(prx.p2 + _v32, plane) p2b = get_proj(prx.p3 + _v23, plane) p1 = get_proj(prx.p1, plane) p2 = get_proj(prx.p2, plane) p3 = get_proj(prx.p3, plane) p4 = get_proj(prx.p4, plane) tbase = get_proj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() _rv = App.Rotation(r[0], r[1], r[2], r[3]) rv = _rv.multVec(App.Vector(1, 0, 0)) angle = -DraftVecUtils.angle(get_proj(rv, plane)) # angle = -DraftVecUtils.angle(p3.sub(p2)) svg = '' nolines = False if hasattr(vobj, "ShowLine"): if not vobj.ShowLine: nolines = True # drawing lines if not nolines: svg += '<path ' if vobj.DisplayMode == "2D": tangle = angle if tangle > math.pi / 2: tangle = tangle - math.pi # elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle + math.pi if rotation != 0: # print("dim: tangle:", tangle, # " rot: ", rotation, # " text: ", prx.string) if abs(tangle + math.radians(rotation)) < 0.0001: tangle += math.pi _v = App.Vector(0, 2.0 / scale, 0) _rot = DraftVecUtils.rotate(_v, tangle) tbase = tbase + _rot if not nolines: svg += 'd="M ' + str(p1.x) + ' ' + str(p1.y) + ' ' svg += 'L ' + str(p2.x) + ' ' + str(p2.y) + ' ' svg += 'L ' + str(p3.x) + ' ' + str(p3.y) + ' ' svg += 'L ' + str(p4.x) + ' ' + str(p4.y) + '" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase + App.Vector(0, -2.0 / scale, 0) if not nolines: svg += 'd="M ' + str(p1.x) + ' ' + str(p1.y) + ' ' svg += 'L ' + str(p2.x) + ' ' + str(p2.y) + ' ' svg += 'L ' + str(p2a.x) + ' ' + str(p2a.y) + ' ' svg += 'M ' + str(p2b.x) + ' ' + str(p2b.y) + ' ' svg += 'L ' + str(p3.x) + ' ' + str(p3.y) + ' ' svg += 'L ' + str(p4.x) + ' ' + str(p4.y) + '" ' if not nolines: svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="' + str(p1.x) + ' ' + str(p1.y) + '" ' svg += 'freecad:basepoint2="' + str(p4.x) + ' ' + str(p4.y) + '" ' svg += 'freecad:dimpoint="' + str(p2.x) + ' ' + str(p2.y) + '"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(vobj, "DimOvershoot") and vobj.DimOvershoot.Value: shootsize = vobj.DimOvershoot.Value / pointratio svg += get_overshoot(p2, shootsize, stroke, linewidth, angle) svg += get_overshoot(p3, shootsize, stroke, linewidth, angle + math.pi) if hasattr(vobj, "ExtOvershoot") and vobj.ExtOvershoot.Value: shootsize = vobj.ExtOvershoot.Value / pointratio shootangle = -DraftVecUtils.angle(p1 - p2) svg += get_overshoot(p2, shootsize, stroke, linewidth, shootangle) svg += get_overshoot(p3, shootsize, stroke, linewidth, shootangle) # drawing arrows if hasattr(vobj, "ArrowType"): arrowsize = vobj.ArrowSize.Value / pointratio if hasattr(vobj, "FlipArrows"): if vobj.FlipArrows: angle = angle + math.pi svg += get_arrow(obj, vobj.ArrowType, p2, arrowsize, stroke, linewidth, angle) svg += get_arrow(obj, vobj.ArrowType, p3, arrowsize, stroke, linewidth, angle + math.pi) # drawing text svg += svgtext.get_text(plane, techdraw, stroke, fontsize, vobj.FontName, tangle, tbase, prx.string) return svg
def getCutVolume(cutplane, shapes): """getCutVolume(cutplane,shapes): returns a cut face and a cut volume from the given shapes and the given cutting plane""" if not shapes: return None, None, None 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 action(self, arg): """Handle the 3D scene events. This is installed as an EventCallback in the Inventor view. Parameters ---------- arg: dict Dictionary with strings that indicates the type of event received from the 3D view. """ if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection import DraftGeomUtils shift = gui_tool_utils.hasMod(arg, gui_tool_utils.MODCONSTRAIN) if self.arcmode or self.point2: gui_tool_utils.setMod(arg, gui_tool_utils.MODCONSTRAIN, False) (self.point, ctrlPoint, self.info) = gui_tool_utils.getPoint( self, arg, noTracker=(len(self.node) > 0)) if (gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT) or self.selectmode) and (len(self.node) < 3): self.dimtrack.off() if not self.altdown: self.altdown = True self.ui.switchUi(True) if hasattr(Gui, "Snapper"): Gui.Snapper.setSelectMode(True) snapped = self.view.getObjectInfo( (arg["Position"][0], arg["Position"][1])) if snapped: ob = self.doc.getObject(snapped['Object']) if "Edge" in snapped['Component']: num = int(snapped['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] v1 = ed.Vertexes[0].Point v2 = ed.Vertexes[-1].Point self.dimtrack.update([v1, v2, self.cont]) else: if self.node and (len(self.edges) < 2): self.dimtrack.on() if len(self.edges) == 2: # angular dimension self.dimtrack.off() r = self.point.sub(self.center) self.arctrack.setRadius(r.Length) a = self.arctrack.getAngle(self.point) pair = DraftGeomUtils.getBoundaryAngles(a, self.pts) if not (pair[0] < a < pair[1]): self.angledata = [ 4 * math.pi - pair[0], 2 * math.pi - pair[1] ] else: self.angledata = [ 2 * math.pi - pair[0], 2 * math.pi - pair[1] ] self.arctrack.setStartAngle(self.angledata[0]) self.arctrack.setEndAngle(self.angledata[1]) if self.altdown: self.altdown = False self.ui.switchUi(False) if hasattr(Gui, "Snapper"): Gui.Snapper.setSelectMode(False) if self.dir: _p = DraftVecUtils.project(self.point.sub(self.node[0]), self.dir) self.point = self.node[0].add(_p) if len(self.node) == 2: if self.arcmode and self.edges: cen = self.edges[0].Curve.Center rad = self.edges[0].Curve.Radius baseray = self.point.sub(cen) v2 = DraftVecUtils.scaleTo(baseray, rad) v1 = v2.negative() if shift: self.node = [cen, cen.add(v2)] self.arcmode = "radius" else: self.node = [cen.add(v1), cen.add(v2)] self.arcmode = "diameter" self.dimtrack.update(self.node) # Draw constraint tracker line. if shift and (not self.arcmode): if len(self.node) == 2: if not self.point2: self.point2 = self.node[1] else: self.node[1] = self.point2 if not self.force: _p = self.point.sub(self.node[0]) a = abs(_p.getAngle(App.DraftWorkingPlane.u)) if (a > math.pi / 4) and (a <= 0.75 * math.pi): self.force = 1 else: self.force = 2 if self.force == 1: self.node[1] = App.Vector(self.node[0].x, self.node[1].y, self.node[0].z) elif self.force == 2: self.node[1] = App.Vector(self.node[1].x, self.node[0].y, self.node[0].z) else: self.force = None if self.point2 and (len(self.node) > 1): self.node[1] = self.point2 self.point2 = None # update the dimline if self.node and not self.arcmode: self.dimtrack.update(self.node + [self.point] + [self.cont]) gui_tool_utils.redraw3DView() elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): import DraftGeomUtils if self.point: self.ui.redraw() if (not self.node) and (not self.support): gui_tool_utils.getSupport(arg) if (gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT) or self.selectmode) and (len(self.node) < 3): # print("snapped: ",self.info) if self.info: ob = self.doc.getObject(self.info['Object']) if 'Edge' in self.info['Component']: num = int( self.info['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] v1 = ed.Vertexes[0].Point v2 = ed.Vertexes[-1].Point i1 = i2 = None for i in range(len(ob.Shape.Vertexes)): if v1 == ob.Shape.Vertexes[i].Point: i1 = i if v2 == ob.Shape.Vertexes[i].Point: i2 = i if (i1 is not None) and (i2 is not None): self.indices.append(num) if not self.edges: # nothing snapped yet, we treat it # as a normal edge-snapped dimension self.node = [v1, v2] self.link = [ob, i1, i2] self.edges.append(ed) if DraftGeomUtils.geomType( ed) == "Circle": # snapped edge is an arc self.arcmode = "diameter" self.link = [ob, num] else: # there is already a snapped edge, # so we start angular dimension self.edges.append(ed) # self.node now has the 4 endpoints self.node.extend([v1, v2]) c = DraftGeomUtils.findIntersection( self.node[0], self.node[1], self.node[2], self.node[3], True, True) if c: # print("centers:",c) self.center = c[0] self.arctrack.setCenter( self.center) self.arctrack.on() for e in self.edges: for v in e.Vertexes: self.pts.append( self.arctrack.getAngle( v.Point)) self.link = [self.link[0], ob] else: _msg( translate( "draft", "Edges don't intersect!")) self.finish() return self.dimtrack.on() else: self.node.append(self.point) self.selectmode = False # print("node", self.node) self.dimtrack.update(self.node) if len(self.node) == 2: self.point2 = self.node[1] if len(self.node) == 1: self.dimtrack.on() if self.planetrack: self.planetrack.set(self.node[0]) elif len(self.node) == 2 and self.cont: self.node.append(self.cont) self.createObject() if not self.cont: self.finish() elif len(self.node) == 3: # for unlinked arc mode: # if self.arcmode: # v = self.node[1].sub(self.node[0]) # v.multiply(0.5) # cen = self.node[0].add(v) # self.node = [self.node[0], self.node[1], cen] self.createObject() if not self.cont: self.finish() elif self.angledata: self.node.append(self.point) self.createObject() self.finish()
def __init__(self, fc_fro_ax, fc_top_ax, base_h = 2, base_l = 0, base_w = 0, bolt_d = 3, bolt_csunk = 0, ref = 1, pos = V0, extra=1, wfco = 1, intol = 0, name = 'belt_clamp' ): doc = FreeCAD.ActiveDocument self.name = name # if more tolerance is needed in the center cb_in_w = CB_IW + intol self.cb_in_w = cb_in_w cb_wall_w = CB_W - intol/2 self.cb_wall_w = cb_wall_w # normalize and get the other base vector nfro_ax = DraftVecUtils.scaleTo(fc_fro_ax,1) nfro_ax_n = nfro_ax.negative() ntop_ax = DraftVecUtils.scaleTo(fc_top_ax,1) ntop_ax_n = ntop_ax.negative() nsid_ax = nfro_ax.cross(ntop_ax) clamponly_l = CB_L + CS + 2 * CCYL_R clamponly_w = max((cb_in_w+2*cb_wall_w), (2*CCYL_R)) bolt2end = 0 # they will change in case they are used clamp2end = 0 if base_h == 0 and bolt_d == 0: # No base base = 0 base_l = 0 else: base = 1 if bolt_d > 0 : d_bolt = kcomp.D912[bolt_d] bolt_shank_r = d_bolt['shank_r_tol'] bolt_head_r = d_bolt['head_r_tol'] bolt_head_l = d_bolt['head_l'] # there are bolt holes, calculate the extra length needed # from the end to the clamp / cylinder bolt2end = fcfun.get_bolt_end_sep(bolt_d,hasnut=0) # bolt space on one side clamp2end = 2 * bolt2end base_min_len = clamponly_l + 2 * clamp2end if base_l < base_min_len: logger.debug("base_l too short, taking min len %s", base_min_len) base_l = base_min_len else: clamp2end = (base_l - clamponly_l) /2. bolt2end = clamp2end / 2. if bolt_csunk > 0: # there are countersunk holes for the bolts if base_h == 0: # minimum height: base_h = bolt_csunk + bolt_head_l else: # check if there is enough base height if base_h < bolt_csunk + bolt_head_l: base_h = bolt_csunk + bolt_head_l logger.debug("base_h too short,taking height %s", base_h) else: if base_h < self.MIN_BASE_H: base_h = self.MIN_BASE_H logger.debug("taking base height %s", base_h) else: # No bolt bolt2end = 0 if base_l < clamponly_l: if base_l > 0: logger.debug("base_l too short, Taking min len %s", clamponly_l) base_l = clamponly_l clamp2end = 0 else: #base larger than clamp clamp2end = (base_l - clamponly_l) /2. if base_w == 0: base_w = clamponly_w elif base_w < clamponly_w: logger.debug("base_w too short, Taking min width %s", clamponly_w) base_w = clamponly_w ### cencyl2froclamp = CB_L+CS+CCYL_R #vectors to references: # from the center of the cylinder to the front clamp vec_1to2 = DraftVecUtils.scale(nfro_ax,cencyl2froclamp) vec_2to1 = vec_1to2.negative() # from the front clamp to the front bolt vec_2to3 = DraftVecUtils.scale(nfro_ax, bolt2end) vec_3to2 = vec_2to3.negative() # Since not always there is a bolt, from the clamp vec_2to4 = DraftVecUtils.scale(nfro_ax, clamp2end) vec_4to2 = vec_2to4.negative() # from the center of the cylinder to the back bolt vec_5to1 = DraftVecUtils.scale(nfro_ax,CCYL_R) + vec_2to3 vec_1to5 = vec_5to1.negative() # from the back bolt to the back end vec_6to1 = DraftVecUtils.scale(nfro_ax,CCYL_R) + vec_2to4 vec_1to6 = vec_6to1.negative() # default values vec_tocencyl = V0 vec_tofrontclamp = V0 vec_tofrontbolt = V0 vec_tofrontbase = V0 vec_tobackbolt = V0 vec_tobackbase = V0 if ref==1: #ref on the center of the cylinder vec_tocencyl = V0 vec_tofrontclamp = vec_1to2 vec_tofrontbolt = vec_1to2 + vec_2to3 vec_tofrontbase = vec_1to2 + vec_2to4 vec_tobackbolt = vec_1to5 vec_tobackbase = vec_1to6 elif ref==2: #ref on the front of the clamps vec_tocencyl = vec_2to1 vec_tofrontclamp = V0 vec_tofrontbolt = vec_2to3 vec_tofrontbase = vec_2to4 vec_tobackbolt = ve_2to1 + vec_1to5 vec_tobackbase = ve_2to1 + vec_1to6 elif ref == 3: if clamp2end == 0 or bolt2end == 0: logger.error('reference on the bolts, there are no bolts') else: vec_tocencyl = vec_3to2 + vec_2to1 vec_tofrontclamp = vec_3to2 vec_tofrontbolt = V0 vec_tofrontbase = vec_2to3 #same as 3 to 4 vec_tobackbolt = vec_3to2 + vec_2to1 + vec_1to5 vec_tobackbase = vec_3to2 + vec_2to1 + vec_1to6 elif ref == 4: if clamp2end == 0: logger.debug('reference at the end, same as the clamps') vec_tocencyl = vec_4to2 + vec_2to1 vec_tofrontclamp = vec_4to2 vec_tofrontbolt = vec_3to2 # same as 4 to 3 vec_tofrontbase = V0 vec_tobackbolt = vec_4to2 + vec_2to1 + vec_1to5 vec_tobackbase = vec_4to2 + vec_2to1 + vec_1to6 elif ref == 5: if clamp2end == 0 or bolt2end == 0: logger.error('reference on the bolts, there are no bolts') else: vec_tocencyl = vec_5to1 vec_tofrontclamp = vec_5to1 + vec_1to2 vec_tofrontbolt = vec_5to1 + vec_1to2 + vec_2to3 vec_tofrontbase = vec_5to1 + vec_1to2 + vec_2to4 vec_tobackbolt = V0 vec_tobackbase = vec_3to2 #5to6: same as 3to2 elif ref == 6: if clamp2end == 0: logger.debug('reference at the end, same as the clamps') vec_tocencyl = vec_6to1 vec_tofrontclamp = vec_6to1 + vec_1to2 vec_tofrontbolt = vec_6to1 + vec_1to2 + vec_2to3 vec_tofrontbase = vec_6to1 + vec_1to2 + vec_2to4 vec_tobackbolt = vec_2to3 # same as 6 to 5 vec_tobackbase = V0 else: logger.error('reference out of range') if extra == 0: extra_pos = V0 else: extra_pos = DraftVecUtils.scale(ntop_ax, -extra) pos_extra = pos + extra_pos base_top_add = DraftVecUtils.scale(ntop_ax, base_h + extra) # total height of the clamp, including the base clamp_tot_h = C_H + base_h + extra # position of the clamp cylinder: clampcyl_pos = pos_extra + vec_tocencyl shp_cyl = fcfun.shp_cyl(CCYL_R, clamp_tot_h, ntop_ax, clampcyl_pos) # position of the clamp blocks, without going to the side axis clampblock_pos = pos_extra + vec_tofrontclamp clampblock_side_add = DraftVecUtils.scale(nsid_ax, (cb_in_w + cb_wall_w)/2.) clampblock_1_pos = clampblock_pos + clampblock_side_add clampblock_2_pos = clampblock_pos - clampblock_side_add shp_clampblock_1 = fcfun.shp_box_dir(box_w = cb_wall_w, box_d = CB_L, box_h = clamp_tot_h, fc_axis_h = ntop_ax, fc_axis_d = nfro_ax_n, cw=1, cd=0, ch=0, pos = clampblock_1_pos) shp_clampblock_2 = fcfun.shp_box_dir(box_w = cb_wall_w, box_d = CB_L, box_h = clamp_tot_h, fc_axis_h = ntop_ax, fc_axis_d = nfro_ax_n, cw=1, cd=0, ch=0, pos = clampblock_2_pos) shp_clamp = shp_cyl.multiFuse([shp_clampblock_1, shp_clampblock_2]) #position of the base, we will take it on the point 4 and make it not # centered if base == 1: base_pos = pos_extra + vec_tofrontbase shp_base = fcfun.shp_box_dir(box_w = base_w, box_d = base_l, box_h = base_h + extra, fc_axis_h = ntop_ax, fc_axis_d = nfro_ax_n, cw=1, cd=0, ch=0, pos = base_pos) if base_l > clamponly_l: # chamfer shp_base = fcfun.shp_filletchamfer_dir (shp_base, fc_axis=ntop_ax, fillet=1, radius= 2) # shape of the bolt holes, if there are if bolt_d > 0: pos_bolt_front = pos_extra + vec_tofrontbolt + base_top_add pos_bolt_back = pos_extra + vec_tobackbolt + base_top_add if bolt_csunk > 0 : shp_bolt_front = fcfun.shp_bolt_dir( r_shank = bolt_shank_r, l_bolt = base_h + extra, r_head = bolt_head_r, l_head = bolt_head_l, support=0, fc_normal = ntop_ax_n, pos=pos_bolt_front) shp_bolt_back = fcfun.shp_bolt_dir( r_shank = bolt_shank_r, l_bolt = base_h + extra, r_head = bolt_head_r, l_head = bolt_head_l, support=0, fc_normal = ntop_ax_n, pos=pos_bolt_back) else: # no head, just a cylinder: shp_bolt_front = fcfun.shp_cylcenxtr ( r = bolt_shank_r, h = base_h + extra, normal = ntop_ax_n, ch = 0, xtr_top=1, xtr_bot=1, pos = pos_bolt_front) shp_bolt_back = fcfun.shp_cylcenxtr ( r = bolt_shank_r, h = base_h + extra, normal = ntop_ax_n, ch = 0, xtr_top=1, xtr_bot=1, pos = pos_bolt_back) # fuse the bolts: shp_bolts = shp_bolt_front.fuse(shp_bolt_back) shp_base = shp_base.cut(shp_bolts) shp_clamp = shp_base.fuse(shp_clamp) doc.recompute() shp_clamp = shp_clamp.removeSplitter() self.shp = shp_clamp self.wfco = wfco if wfco == 1: # a freeCAD object is created fco_clamp = doc.addObject("Part::Feature", name ) fco_clamp.Shape = shp_clamp self.fco = fco_clamp
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 obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None basewires = None 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 obj.Base.Shape.Wires: basewires = obj.Base.Shape.Wires elif len(obj.Base.Shape.Edges) == 1: basewires = [Part.Wire(obj.Base.Shape.Edges)] if basewires and width: baseface = None 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(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": dvec.multiply(width) if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) 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.multiply(width) dvec = dvec.negative() if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) 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": 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: baseface = baseface.fuse(f) else: baseface = f if baseface: base,placement = self.rebase(baseface) 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