def isPlanar(shape): """Return True if the given shape or list of points is planar.""" n = getNormal(shape) if not n: return False if isinstance(shape, list): if len(shape) <= 3: return True else: for v in shape[3:]: pv = v.sub(shape[0]) rv = DraftVecUtils.project(pv, n) if not DraftVecUtils.isNull(rv): return False else: if len(shape.Vertexes) <= 3: return True for p in shape.Vertexes[1:]: pv = p.Point.sub(shape.Vertexes[0].Point) rv = DraftVecUtils.project(pv, n) if not DraftVecUtils.isNull(rv): return False return True
def polar_placements(base_placement, center, angle, number, axis, axisvector): """Determine the placements where the polar copies will be.""" # print("angle ",angle," num ",num) placements = [base_placement.copy()] if number == 0: return placements spin = App.Placement(App.Vector(), base_placement.Rotation) pl = App.Placement(base_placement.Base, App.Rotation()) center = center.sub(base_placement.Base) if angle == 360: fraction = float(angle)/number else: fraction = float(angle)/(number - 1) center_tuple = DraftVecUtils.tup(center) axis_tuple = DraftVecUtils.tup(axis) for i in range(number - 1): currangle = fraction + (i*fraction) npl = pl.copy() npl.rotate(center_tuple, axis_tuple, currangle) npl = npl.multiply(spin) if axisvector: if not DraftVecUtils.isNull(axisvector): npl.translate(App.Vector(axisvector).multiply(i+1)) placements.append(npl) return placements
def getBase(self,obj,wire,normal,width,height): "returns a full shape from a base wire" import DraftGeomUtils,Part flat = False if hasattr(obj.ViewObject,"DisplayMode"): flat = (obj.ViewObject.DisplayMode == "Flat 2D") dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": dvec = dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) # fixing self-intersections sh.fix(0.1,0,1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh
def getBase(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) 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() 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) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh
def getCircleFromSpline(edge): """Return a circle-based edge from a bspline-based edge.""" if geomType(edge) != "BSplineCurve": return None if len(edge.Vertexes) != 1: return None # get 2 points p1 = edge.Curve.value(0) p2 = edge.Curve.value(math.pi / 2) # get 2 tangents t1 = edge.Curve.tangent(0)[0] t2 = edge.Curve.tangent(math.pi / 2)[0] # get normal n = p1.cross(p2) if DraftVecUtils.isNull(n): return None # get rays r1 = DraftVecUtils.rotate(t1, math.pi / 2, n) r2 = DraftVecUtils.rotate(t2, math.pi / 2, n) # get center (intersection of rays) i = findIntersection(p1, p1.add(r1), p2, p2.add(r2), True, True) if not i: return None c = i[0] r = (p1.sub(c)).Length circle = Part.makeCircle(r, c, n) #print(circle.Curve) return circle
def isClockwise(edge, ref=None): """Return True if a circle-based edge has a clockwise direction.""" if not geomType(edge) == "Circle": return True v1 = edge.Curve.tangent(edge.ParameterRange[0])[0] if DraftVecUtils.isNull(v1): return True # we take an arbitrary other point on the edge that has little chances # to be aligned with the first one v2 = edge.Curve.tangent(edge.ParameterRange[0] + 0.01)[0] n = edge.Curve.Axis # if that axis points "the wrong way" from the reference, we invert it if not ref: ref = FreeCAD.Vector(0, 0, 1) if n.getAngle(ref) > math.pi / 2: n = n.negative() if DraftVecUtils.angle(v1, v2, n) < 0: return False if n.z < 0: return False return True
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 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 handle_mouse_move_event(self, arg): """Handle the mouse when moving.""" plane = App.DraftWorkingPlane for ghost in self.ghosts: ghost.off() self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) # this is to make sure radius is what you see on screen if self.center and DraftVecUtils.dist(self.point, self.center): viewdelta = DraftVecUtils.project(self.point.sub(self.center), plane.axis) if not DraftVecUtils.isNull(viewdelta): self.point = self.point.add(viewdelta.negative()) if self.extendedCopy: if not gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): self.step = 3 self.finish() if self.step == 0: pass elif self.step == 1: currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 self.ui.setRadiusValue(math.degrees(angle), unit="Angle") self.firstangle = angle self.ui.radiusValue.setFocus() self.ui.radiusValue.selectAll() elif self.step == 2: currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 if angle < self.firstangle: sweep = (2 * math.pi - self.firstangle) + angle else: sweep = angle - self.firstangle self.arctrack.setApertureAngle(sweep) for ghost in self.ghosts: ghost.rotate(plane.axis, sweep) ghost.on() self.ui.setRadiusValue(math.degrees(sweep), 'Angle') self.ui.radiusValue.setFocus() self.ui.radiusValue.selectAll() gui_tool_utils.redraw3DView()
def getTextPosition(self, vobj): pos = FreeCAD.Vector() if hasattr(vobj, "TextPosition"): import DraftVecUtils if DraftVecUtils.isNull(vobj.TextPosition): try: pos = vobj.Object.Shape.CenterOfMass z = vobj.Object.Shape.BoundBox.ZMin pos = FreeCAD.Vector(pos.x, pos.y, z) except (AttributeError, RuntimeError): pos = FreeCAD.Vector() else: pos = vobj.TextPosition return pos
def updateData(self, obj, prop): if prop == "Points": if obj.Points: p = obj.Points[-1] if hasattr(self,"coords"): self.coords.translation.setValue((p.x,p.y,p.z)) if len(obj.Points) >= 2: v1 = obj.Points[-2].sub(obj.Points[-1]) if not DraftVecUtils.isNull(v1): v1.normalize() _rot = coin.SbRotation() _rot.setValue(coin.SbVec3f(1, 0, 0), coin.SbVec3f(v1[0], v1[1], v1[2])) self.coords.rotation.setValue(_rot) return
def getTextPosition(self,vobj): pos = FreeCAD.Vector() if hasattr(vobj,"TextPosition"): import DraftVecUtils if DraftVecUtils.isNull(vobj.TextPosition): try: pos = vobj.Object.Shape.CenterOfMass z = vobj.Object.Shape.BoundBox.ZMin pos = FreeCAD.Vector(pos.x,pos.y,z) except: pos = FreeCAD.Vector() else: pos = vobj.TextPosition return pos
def getTextPosition(self,vobj): pos = FreeCAD.Vector() if hasattr(vobj,"TextPosition"): import DraftVecUtils if DraftVecUtils.isNull(vobj.TextPosition): try: pos = vobj.Object.Shape.CenterOfMass z = vobj.Object.Shape.BoundBox.ZMin pos = FreeCAD.Vector(pos.x,pos.y,z) except (AttributeError, RuntimeError): pos = FreeCAD.Vector() else: pos = vobj.TextPosition # placement's displacement will be already added by the coin node pos = vobj.Object.Placement.inverse().multVec(pos) return pos
def getRebarsSpanAxis(rebar): """getRebarsSpanAxis(Rebar): Returns span axis of rebars. """ if (Draft.getType(rebar.Base) == "Wire" or rebar.Base.Shape.ShapeType == "Wire") and len( rebar.Base.Shape.Wires[0].Edges ) != 1: # Draft Wires can have "wrong" placement # This works fine instead for straight rebars i.e. for rebars having # base wire with only one edge axis = DraftGeomUtils.getNormal(rebar.Base.Shape) else: axis = rebar.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) if hasattr(rebar, "Direction"): if not DraftVecUtils.isNull(rebar.Direction): axis = FreeCAD.Vector(rebar.Direction) axis.normalize() return axis
def getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2): if pt1: # first check if we don't already have coincident endpoints if pt1 in [pt3, pt4]: return [pt1] elif (pt2 in [pt3, pt4]): return [pt2] norm1 = pt2.sub(pt1).cross(pt3.sub(pt1)) norm2 = pt2.sub(pt4).cross(pt3.sub(pt4)) if not DraftVecUtils.isNull(norm1): try: norm1.normalize() except Part.OCCError: return [] if not DraftVecUtils.isNull(norm2): try: norm2.normalize() except Part.OCCError: return [] if DraftVecUtils.isNull(norm1.cross(norm2)): vec1 = pt2.sub(pt1) vec2 = pt4.sub(pt3) if DraftVecUtils.isNull(vec1) or DraftVecUtils.isNull(vec2): return [] # One of the lines has zero-length try: vec1.normalize() vec2.normalize() except Part.OCCError: return [] norm3 = vec1.cross(vec2) denom = norm3.x + norm3.y + norm3.z if not DraftVecUtils.isNull(norm3) and denom != 0: k = ((pt3.z - pt1.z) * (vec2.x - vec2.y) + (pt3.y - pt1.y) * (vec2.z - vec2.x) + (pt3.x - pt1.x) * (vec2.y - vec2.z)) / denom vec1.scale(k, k, k) intp = pt1.add(vec1) if infinite1 is False and not isPtOnEdge(intp, edge1): return [] if infinite2 is False and not isPtOnEdge(intp, edge2): return [] return [intp] else: return [] # Lines have same direction else: return [] # Lines aren't on same plane
def areColinear(e1, e2): """Return True if both edges are colinear.""" if not isinstance(e1.Curve, (Part.LineSegment, Part.Line)): return False if not isinstance(e2.Curve, (Part.LineSegment, Part.Line)): return False v1 = vec(e1) v2 = vec(e2) a = round(v1.getAngle(v2), precision()) if (a == 0) or (a == round(math.pi, precision())): v3 = e2.Vertexes[0].Point.sub(e1.Vertexes[0].Point) if DraftVecUtils.isNull(v3): return True else: a2 = round(v1.getAngle(v3), precision()) if (a2 == 0) or (a2 == round(math.pi, precision())): return True return False
def update_arrow(self, obj, vobj): """Update the arrow tip of the line.""" if hasattr(self, "symbol"): if self.arrow.findChild(self.symbol) != -1: self.arrow.removeChild(self.symbol) s = utils.ARROW_TYPES.index(vobj.ArrowType) self.symbol = gui_utils.dim_symbol(s) if vobj.ArrowType == "Circle": # TODO: fix behavior of the 'Circle' marker. # Instead of appearing at the tip of the line # the 'Circle' marker appears displaced and duplicated # a certain distance from the tip, which is the `TargetPoint`. # Somehow the translation is added to the position of the tip # resulting in a wrong value. # So the arrow position is reset; nevertheless, this doesn't # entirely fix the issue. coords2 = coin.SoCoordinate3() coords2.point.setValues([obj.Points[-1]]) self.arrow.addChild(coords2) self.arrowpos.translation.setValue((0, 0, 0)) else: self.arrowpos.translation.setValue(obj.Points[-1]) self.arrow.addChild(self.symbol) v1 = obj.Points[-2].sub(obj.Points[-1]) if not DraftVecUtils.isNull(v1): v1.normalize() v2 = App.Vector(0, 0, 1) if round(v2.getAngle(v1), 4) in [0, round(math.pi, 4)]: v2 = App.Vector(0, 1, 0) v3 = v1.cross(v2).negative() _rot_mat = DraftVecUtils.getPlaneRotation(v1, v3, v2) q = App.Placement(_rot_mat).Rotation.Q self.arrowpos.rotation.setValue((q[0], q[1], q[2], q[3]))
def polarArray(self, spl, center, angle, num, axis, axisvector): #print("angle ",angle," num ",num) spin = App.Placement(App.Vector(), spl.Rotation) pl = App.Placement(spl.Base, App.Rotation()) center = center.sub(spl.Base) base = [spl.copy()] if angle == 360: fraction = float(angle) / num else: if num == 0: return base fraction = float(angle) / (num - 1) ctr = DraftVecUtils.tup(center) axs = DraftVecUtils.tup(axis) for i in range(num - 1): currangle = fraction + (i * fraction) npl = pl.copy() npl.rotate(ctr, axs, currangle) npl = npl.multiply(spin) if axisvector: if not DraftVecUtils.isNull(axisvector): npl.translate(App.Vector(axisvector).multiply(i + 1)) base.append(npl) return base
def get_dxf(obj, direction=None): """Return a DXF entity from the given object. If direction is given, the object is projected in 2D. """ plane = None result = "" if (obj.isDerivedFrom("Drawing::View") or obj.isDerivedFrom("TechDraw::DrawView")): if obj.Source.isDerivedFrom("App::DocumentObjectGroup"): for o in obj.Source.Group: result += get_dxf(o, obj.Direction) else: result += get_dxf(obj.Source, obj.Direction) return result if direction and isinstance(direction, App.Vector): if direction != App.Vector(0, 0, 0): plane = WorkingPlane.Plane() plane.alignToPointAndAxis(App.Vector(0, 0, 0), direction) if utils.get_type(obj) in ("Dimension", "LinearDimension"): p1 = _get_proj(obj.Start, plane=plane) p2 = _get_proj(obj.End, plane=plane) p3 = _get_proj(obj.Dimline, plane=plane) result += "0\nDIMENSION\n8\n0\n62\n0\n3\nStandard\n70\n1\n" result += "10\n" + str(p3.x) + "\n20\n" + str(p3.y) + "\n30\n" + str( p3.z) + "\n" result += "13\n" + str(p1.x) + "\n23\n" + str(p1.y) + "\n33\n" + str( p1.z) + "\n" result += "14\n" + str(p2.x) + "\n24\n" + str(p2.y) + "\n34\n" + str( p2.z) + "\n" elif utils.get_type(obj) == "Annotation": # Only for App::Annotation p = _get_proj(obj.Position, plane=plane) count = 0 for t in obj.LabeLtext: result += "0\nTEXT\n8\n0\n62\n0\n" result += "10\n" result += str(p.x) + "\n20\n" result += str(p.y + count) + "\n30\n" result += str(p.z) + "\n" result += "40\n1\n" result += "1\n" + str(t) + "\n" result += "7\nSTANDARD\n" count += 1 elif hasattr(obj, 'Shape'): # TODO do this the Draft way, for ex. using polylines and rectangles if not direction: direction = App.Vector(0, 0, -1) if DraftVecUtils.isNull(direction): direction = App.Vector(0, 0, -1) try: d = Drawing.projectToDXF(obj.Shape, direction) except Exception: # TODO: trap only specific exception. # Impossible to generate DXF from Shape? Which exception is throw? _wrn("get_dxf: " "unable to project '{}' to {}".format(obj.Label, direction)) else: result += d else: _wrn("get_dxf: unsupported object, '{}'".format(obj.Label)) return result
def 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 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 pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj,"ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: edges = obj.Base.Shape.Edges if hasattr(obj,"Edges"): if obj.Edges == "Vertical edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Top Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z,reverse=True) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] elif obj.Edges == "Bottom Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] for e in edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj,"BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append(DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage(translate("Arch","Crossing point not found in profile.\n")) basepoint = basepointliste[0] else : basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: if hasattr(obj,"Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) s = s.removeSplitter() obj.Shape = s obj.Placement = pl return obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def 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 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 snapToExtensions(self,point,last,constrain,eline): "returns a point snapped to extension or parallel line to last object, if any" if self.isEnabled("extension"): tsnap = self.snapToExtOrtho(last,constrain,eline) if tsnap: if (tsnap[0].sub(point)).Length < self.radius: if self.tracker: self.tracker.setCoords(tsnap[2]) self.tracker.setMarker(self.mk[tsnap[1]]) self.tracker.on() if self.extLine: self.extLine.p2(tsnap[2]) self.extLine.on() self.setCursor(tsnap[1]) return tsnap[2],eline else: tsnap = self.snapToExtPerpendicular(last) if tsnap: if (tsnap[0].sub(point)).Length < self.radius: if self.tracker: self.tracker.setCoords(tsnap[2]) self.tracker.setMarker(self.mk[tsnap[1]]) self.tracker.on() if self.extLine: self.extLine.p2(tsnap[2]) self.extLine.on() self.setCursor(tsnap[1]) return tsnap[2],eline for o in [self.lastObj[1],self.lastObj[0]]: if o: ob = FreeCAD.ActiveDocument.getObject(o) if ob: if ob.isDerivedFrom("Part::Feature"): edges = ob.Shape.Edges if (not self.maxEdges) or (len(edges) <= self.maxEdges): for e in edges: if DraftGeomUtils.geomType(e) == "Line": np = self.getPerpendicular(e,point) if not DraftGeomUtils.isPtOnEdge(np,e): if (np.sub(point)).Length < self.radius: if self.isEnabled('extension'): if np != e.Vertexes[0].Point: if self.tracker: self.tracker.setCoords(np) self.tracker.setMarker(self.mk['extension']) self.tracker.on() if self.extLine: self.extLine.p1(e.Vertexes[0].Point) self.extLine.p2(np) self.extLine.on() self.setCursor('extension') return np,Part.Line(e.Vertexes[0].Point,np).toShape() else: if self.isEnabled('parallel'): if last: ve = DraftGeomUtils.vec(e) if not DraftVecUtils.isNull(ve): de = Part.Line(last,last.add(ve)).toShape() np = self.getPerpendicular(de,point) if (np.sub(point)).Length < self.radius: if self.tracker: self.tracker.setCoords(np) self.tracker.setMarker(self.mk['parallel']) self.tracker.on() self.setCursor('parallel') return np,de return point,eline
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 action(self, arg): """Handle the 3D scene events. This is installed as an EventCallback in the Inventor view. Parameters ---------- arg: dict Dictionary with strings that indicates the type of event received from the 3D view. """ import DraftGeomUtils if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) # this is to make sure radius is what you see on screen if self.center and DraftVecUtils.dist(self.point, self.center) > 0: viewdelta = DraftVecUtils.project(self.point.sub(self.center), App.DraftWorkingPlane.axis) if not DraftVecUtils.isNull(viewdelta): self.point = self.point.add(viewdelta.negative()) if self.step == 0: # choose center if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): if not self.altdown: self.altdown = True self.ui.switchUi(True) else: if self.altdown: self.altdown = False self.ui.switchUi(False) else: # choose radius if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom2tan1pt( self.tangents[0], self.tangents[1], self.point) _c = DraftGeomUtils.findClosestCircle(self.point, cir) self.center = _c.Center self.arctrack.setCenter(self.center) elif self.tangents and self.tanpoints: cir = DraftGeomUtils.circleFrom1tan2pt( self.tangents[0], self.tanpoints[0], self.point) _c = DraftGeomUtils.findClosestCircle(self.point, cir) self.center = _c.Center self.arctrack.setCenter(self.center) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): if not self.altdown: self.altdown = True snapped = self.view.getObjectInfo( (arg["Position"][0], arg["Position"][1])) if snapped: ob = self.doc.getObject(snapped['Object']) num = int(snapped['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] if len(self.tangents) == 2: cir = DraftGeomUtils.circleFrom3tan( self.tangents[0], self.tangents[1], ed) cl = DraftGeomUtils.findClosestCircle( self.point, cir) self.center = cl.Center self.rad = cl.Radius self.arctrack.setCenter(self.center) else: self.rad = self.center.add( DraftGeomUtils.findDistance( self.center, ed).sub(self.center)).Length else: self.rad = DraftVecUtils.dist(self.point, self.center) else: if self.altdown: self.altdown = False self.rad = DraftVecUtils.dist(self.point, self.center) self.ui.setRadiusValue(self.rad, 'Length') self.arctrack.setRadius(self.rad) gui_tool_utils.redraw3DView() elif (arg["Type"] == "SoMouseButtonEvent" and arg["State"] == "DOWN" and arg["Button"] == "BUTTON1"): # mouse click if self.point: if self.step == 0: # choose center if (not self.node) and (not self.support): gui_tool_utils.getSupport(arg) (self.point, ctrlPoint, info) = gui_tool_utils.getPoint(self, arg) if gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): snapped = self.view.getObjectInfo( (arg["Position"][0], arg["Position"][1])) if snapped: ob = self.doc.getObject(snapped['Object']) num = int(snapped['Component'].lstrip('Edge')) - 1 ed = ob.Shape.Edges[num] self.tangents.append(ed) if len(self.tangents) == 2: self.arctrack.on() self.ui.radiusUi() self.step = 1 _msg(translate("draft", "Pick radius")) else: if len(self.tangents) == 1: self.tanpoints.append(self.point) else: self.center = self.point self.node = [self.point] self.arctrack.setCenter(self.center) self.arctrack.on() self.ui.radiusUi() self.step = 1 _msg(translate("draft", "Pick radius")) if self.planetrack: self.planetrack.set(self.point) elif self.step == 1: # choose radius self.drawPolygon()
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 compare(self,face1,face2): "zsorts two faces. Returns 1 if face1 is closer, 2 if face2 is closer, 0 otherwise" #print face1,face2 if not face1: if DEBUG: print "Warning, undefined face!" return 31 elif not face2: if DEBUG: print "Warning, undefined face!" return 32 # theory from # http://www.siggraph.org/education/materials/HyperGraph/scanline/visibility/painter.htm # and practical application http://vrm.ao2.it/ (blender vector renderer) b1 = face1[0].BoundBox b2 = face2[0].BoundBox # test 1: if faces don't overlap, no comparison possible if DEBUG: print "doing test 1" if b1.XMax < b2.XMin: return 0 if b1.XMin > b2.XMax: return 0 if b1.YMax < b2.YMin: return 0 if b1.YMin > b2.YMax: return 0 if DEBUG: print "failed, faces bboxes are not distinct" # test 2: if Z bounds dont overlap, it's easy to know the closest if DEBUG: print "doing test 2" if b1.ZMax < b2.ZMin: return 2 if b2.ZMax < b1.ZMin: return 1 if DEBUG: print "failed, faces Z are not distinct" # test 3: all verts of face1 are in front or behind the plane of face2 if DEBUG: print "doing test 3" norm = face2[0].normalAt(0,0) behind = 0 front = 0 for v in face1[0].Vertexes: dv = v.Point.sub(face2[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv,norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print "front: ",front," behind: ",behind if behind == len(face1[0].Vertexes): return 2 elif front == len(face1[0].Vertexes): return 1 if DEBUG: print "failed, cannot say if face 1 is in front or behind" # test 4: all verts of face2 are in front or behind the plane of face1 if DEBUG: print "doing test 4" norm = face1[0].normalAt(0,0) behind = 0 front = 0 for v in face2[0].Vertexes: dv = v.Point.sub(face1[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv,norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print "front: ",front," behind: ",behind if behind == len(face2[0].Vertexes): return 1 elif front == len(face2[0].Vertexes): return 2 if DEBUG: print "failed, cannot say if face 2 is in front or behind" # test 5: see if faces projections don't overlap, vertexwise if DEBUG: print "doing test 5" if not self.zOverlaps(face1,face2): return 0 elif not self.zOverlaps(face2,face1): return 0 if DEBUG: print "failed, faces are overlapping" if DEBUG: print "Houston, all tests passed, and still no results" return 0
def 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
def findIntersection(edge1, edge2, infinite1=False, infinite2=False, ex1=False, ex2=False, dts=True, findAll=False): """Return a list containing the intersection points of 2 edges. You can also feed 4 points instead of `edge1` and `edge2`. If `dts` is used, `Shape.distToShape()` is used, which can be buggy. """ def getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2): if pt1: # first check if we don't already have coincident endpoints if pt1 in [pt3, pt4]: return [pt1] elif (pt2 in [pt3, pt4]): return [pt2] norm1 = pt2.sub(pt1).cross(pt3.sub(pt1)) norm2 = pt2.sub(pt4).cross(pt3.sub(pt4)) if not DraftVecUtils.isNull(norm1): try: norm1.normalize() except Part.OCCError: return [] if not DraftVecUtils.isNull(norm2): try: norm2.normalize() except Part.OCCError: return [] if DraftVecUtils.isNull(norm1.cross(norm2)): vec1 = pt2.sub(pt1) vec2 = pt4.sub(pt3) if DraftVecUtils.isNull(vec1) or DraftVecUtils.isNull(vec2): return [] # One of the lines has zero-length try: vec1.normalize() vec2.normalize() except Part.OCCError: return [] norm3 = vec1.cross(vec2) denom = norm3.x + norm3.y + norm3.z if not DraftVecUtils.isNull(norm3) and denom != 0: k = ((pt3.z - pt1.z) * (vec2.x - vec2.y) + (pt3.y - pt1.y) * (vec2.z - vec2.x) + (pt3.x - pt1.x) * (vec2.y - vec2.z)) / denom vec1.scale(k, k, k) intp = pt1.add(vec1) if infinite1 is False and not isPtOnEdge(intp, edge1): return [] if infinite2 is False and not isPtOnEdge(intp, edge2): return [] return [intp] else: return [] # Lines have same direction else: return [] # Lines aren't on same plane # First, check bound boxes if (isinstance(edge1, Part.Edge) and isinstance(edge2, Part.Edge) and (not infinite1) and (not infinite2)): if not edge1.BoundBox.intersect(edge2.BoundBox): return [] # bound boxes don't intersect # First, try to use distToShape if possible if (dts and isinstance(edge1, Part.Edge) and isinstance(edge2, Part.Edge) and (not infinite1) and (not infinite2)): dist, pts, geom = edge1.distToShape(edge2) sol = [] if round(dist, precision()) == 0: for p in pts: if p not in sol: sol.append(p[0]) return sol pt1 = None if isinstance(edge1, FreeCAD.Vector) and isinstance(edge2, FreeCAD.Vector): # we got points directly pt1 = edge1 pt2 = edge2 pt3 = infinite1 pt4 = infinite2 infinite1 = ex1 infinite2 = ex2 return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) elif (geomType(edge1) == "Line") and (geomType(edge2) == "Line"): # we have 2 straight lines pt1, pt2, pt3, pt4 = [ edge1.Vertexes[0].Point, edge1.Vertexes[1].Point, edge2.Vertexes[0].Point, edge2.Vertexes[1].Point ] return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) elif ((geomType(edge1) == "Circle") and (geomType(edge2) == "Line") or (geomType(edge1) == "Line") and (geomType(edge2) == "Circle")): # deals with an arc or circle and a line edges = [edge1, edge2] for edge in edges: if geomType(edge) == "Line": line = edge else: arc = edge dirVec = vec(line) dirVec.normalize() pt1 = line.Vertexes[0].Point pt2 = line.Vertexes[1].Point pt3 = arc.Vertexes[0].Point pt4 = arc.Vertexes[-1].Point center = arc.Curve.Center int = [] # first check for coincident endpoints if DraftVecUtils.equals(pt1, pt3) or DraftVecUtils.equals(pt1, pt4): if findAll: int.append(pt1) else: return [pt1] elif pt2 in [pt3, pt4]: if findAll: int.append(pt2) else: return [pt2] if DraftVecUtils.isNull( pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)): # Line and Arc are on same plane dOnLine = center.sub(pt1).dot(dirVec) onLine = FreeCAD.Vector(dirVec) onLine.scale(dOnLine, dOnLine, dOnLine) toLine = pt1.sub(center).add(onLine) if toLine.Length < arc.Curve.Radius: dOnLine = (arc.Curve.Radius**2 - toLine.Length**2)**(0.5) onLine = FreeCAD.Vector(dirVec) onLine.scale(dOnLine, dOnLine, dOnLine) int += [center.add(toLine).add(onLine)] onLine = FreeCAD.Vector(dirVec) onLine.scale(-dOnLine, -dOnLine, -dOnLine) int += [center.add(toLine).add(onLine)] elif round(toLine.Length - arc.Curve.Radius, precision()) == 0: int = [center.add(toLine)] else: return [] else: # Line isn't on Arc's plane if dirVec.dot(arc.Curve.Axis) != 0: toPlane = FreeCAD.Vector(arc.Curve.Axis) toPlane.normalize() d = pt1.dot(toPlane) if not d: return [] dToPlane = center.sub(pt1).dot(toPlane) toPlane = FreeCAD.Vector(pt1) toPlane.scale(dToPlane / d, dToPlane / d, dToPlane / d) ptOnPlane = toPlane.add(pt1) if round( ptOnPlane.sub(center).Length - arc.Curve.Radius, precision()) == 0: int = [ptOnPlane] else: return [] else: return [] if infinite1 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge1): del int[i] if infinite2 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge2): del int[i] return int elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Circle"): # deals with 2 arcs or circles cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center rad1, rad2 = edge1.Curve.Radius, edge2.Curve.Radius axis1, axis2 = edge1.Curve.Axis, edge2.Curve.Axis c2c = cent2.sub(cent1) if cent1.sub(cent2).Length == 0: # circles are concentric return [] if DraftVecUtils.isNull(axis1.cross(axis2)): if round(c2c.dot(axis1), precision()) == 0: # circles are on same plane dc2c = c2c.Length if not DraftVecUtils.isNull(c2c): c2c.normalize() if (round(rad1 + rad2 - dc2c, precision()) < 0 or round(rad1 - dc2c - rad2, precision()) > 0 or round(rad2 - dc2c - rad1, precision()) > 0): return [] else: norm = c2c.cross(axis1) if not DraftVecUtils.isNull(norm): norm.normalize() if DraftVecUtils.isNull(norm): x = 0 else: x = (dc2c**2 + rad1**2 - rad2**2) / (2 * dc2c) y = abs(rad1**2 - x**2)**(0.5) c2c.scale(x, x, x) if round(y, precision()) != 0: norm.scale(y, y, y) int = [cent1.add(c2c).add(norm)] int += [cent1.add(c2c).sub(norm)] else: int = [cent1.add(c2c)] else: return [] # circles are on parallel planes else: # circles aren't on same plane axis1.normalize() axis2.normalize() U = axis1.cross(axis2) V = axis1.cross(U) dToPlane = c2c.dot(axis2) d = V.add(cent1).dot(axis2) V.scale(dToPlane / d, dToPlane / d, dToPlane / d) PtOn2Planes = V.add(cent1) planeIntersectionVector = U.add(PtOn2Planes) intTemp = findIntersection(planeIntersectionVector, edge1, True, True) int = [] for pt in intTemp: if round(pt.sub(cent2).Length - rad2, precision()) == 0: int += [pt] if infinite1 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge1): del int[i] if infinite2 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge2): del int[i] return int else: print("DraftGeomUtils: Unsupported curve type: " "(" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")") return []
def execute(self,obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj,"ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj,"BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append(DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage(translate("Arch","Crossing point not found in profile.\n")) basepoint = basepointliste[0] else : basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
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 getReinforcementDrawingSVGData( structure, rebars_list, view_direction, rebars_stroke_width, rebars_color_style, structure_stroke_width, structure_fill_style, ): """getReinforcementDrawingSVGData(Structure, RebarsList, ViewDirection, RebarsStrokeWidth, RebarsFillStyle, StructureStrokeWidth, StructureFillStyle): Generates Reinforcement Drawing View. view_direction is FreeCAD.Vector() or WorkingPlane.plane() corresponding to direction of view point. rebars_color_style can be: - "shape color" to select color of rebar shape - color name or hex value of color structure_fill_style can be: - "shape color" to select color of rebar shape - color name or hex value of color - "none" to not fill structure shape Returns dictionary format: { "svg": reinforcement_drawing_svg, "rebars": visible_rebars, } """ if isinstance(view_direction, FreeCAD.Vector): if not DraftVecUtils.isNull(view_direction): view_plane = getSVGPlaneFromAxis(view_direction) elif isinstance(view_direction, WorkingPlane.Plane): view_plane = view_direction min_x, min_y, max_x, max_y = getDrawingMinMaxXY(structure, rebars_list, view_plane) svg = getSVGRootElement() reinforcement_drawing = ElementTree.Element( "g", attrib={"id": "reinforcement_drawing"}) svg.append(reinforcement_drawing) # Filter rebars created using Reinforcement Workbench stirrups = [] bent_rebars = [] u_rebars = [] l_rebars = [] straight_rebars = [] helical_rebars = [] custom_rebars = [] for rebar in rebars_list: if not hasattr(rebar, "RebarShape"): custom_rebars.append(rebar) elif rebar.RebarShape == "Stirrup": stirrups.append(rebar) elif rebar.RebarShape == "BentShapeRebar": bent_rebars.append(rebar) elif rebar.RebarShape == "UShapeRebar": u_rebars.append(rebar) elif rebar.RebarShape == "LShapeRebar": l_rebars.append(rebar) elif rebar.RebarShape == "StraightRebar": straight_rebars.append(rebar) elif rebar.RebarShape == "HelicalRebar": helical_rebars.append(rebar) else: custom_rebars.append(rebar) rebars_svg = ElementTree.Element("g", attrib={"id": "Rebars"}) reinforcement_drawing.append(rebars_svg) visible_rebars = [] stirrups_svg = ElementTree.Element("g", attrib={"id": "Stirrup"}) rebars_svg.append(stirrups_svg) for rebar in stirrups: rebar_data = getStirrupSVGData( rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style, ) if rebar_data["visibility"]: stirrups_svg.append(rebar_data["svg"]) visible_rebars.append(rebar) bent_rebars_svg = ElementTree.Element("g", attrib={"id": "BentShapeRebar"}) rebars_svg.append(bent_rebars_svg) for rebar in bent_rebars: rebar_data = getUShapeRebarSVGData( rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style, ) if rebar_data["visibility"]: bent_rebars_svg.append(rebar_data["svg"]) visible_rebars.append(rebar) u_rebars_svg = ElementTree.Element("g", attrib={"id": "UShapeRebar"}) rebars_svg.append(u_rebars_svg) for rebar in u_rebars: rebar_data = getUShapeRebarSVGData( rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style, ) if rebar_data["visibility"]: u_rebars_svg.append(rebar_data["svg"]) visible_rebars.append(rebar) l_rebars_svg = ElementTree.Element("g", attrib={"id": "LShapeRebar"}) rebars_svg.append(l_rebars_svg) for rebar in l_rebars: rebar_data = getUShapeRebarSVGData( rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style, ) if rebar_data["visibility"]: l_rebars_svg.append(rebar_data["svg"]) visible_rebars.append(rebar) straight_rebars_svg = ElementTree.Element("g", attrib={"id": "StraightRebar"}) rebars_svg.append(straight_rebars_svg) for rebar in straight_rebars: rebar_data = getStraightRebarSVGData( rebar, view_plane, rebars_svg, rebars_stroke_width, rebars_color_style, ) if rebar_data["visibility"]: straight_rebars_svg.append(rebar_data["svg"]) visible_rebars.append(rebar) helical_rebars_svg = ElementTree.Element("g", attrib={"id": "HelicalRebar"}) rebars_svg.append(helical_rebars_svg) # SVG is generated for all helical rebars, because all helical rebars in # circular column are assumed to be visible, not overlapped by any other # rebar type (it makes sense for me). Please create an issue on github # repository if you think its wrong assumption for rebar in helical_rebars: rebars_color = getRebarColor(rebar, rebars_color_style) rebars_color = getcolor(rebars_color) rebar_svg_draft = Draft.get_svg( rebar, direction=view_plane, linewidth=rebars_stroke_width, fillstyle="none", color=rebars_color, ) if rebar_svg_draft: helical_rebars_svg.append(ElementTree.fromstring(rebar_svg_draft)) visible_rebars.append(rebar) custom_rebars_svg = ElementTree.Element("g", attrib={"id": "CustomRebar"}) rebars_svg.append(custom_rebars_svg) for rebar in custom_rebars: rebars_color = getRebarColor(rebar, rebars_color_style) rebars_color = getcolor(rebars_color) rebar_svg_draft = Draft.get_svg( rebar, direction=view_plane, linewidth=rebars_stroke_width, fillstyle="none", color=rebars_color, ) if rebar_svg_draft: custom_rebars_svg.append(ElementTree.fromstring(rebar_svg_draft)) # Create Structure SVG _structure_svg = '<g id="structure">{}</g>'.format( Draft.get_svg( structure, direction=view_plane, linewidth=structure_stroke_width, fillstyle=structure_fill_style, )) # Fix structure transparency (useful in console mode where # obj.ViewObject.Transparency is not available OR in gui mode if # structure transparency is ~0) if structure_fill_style != "none": if _structure_svg.find("fill-opacity") == -1: _structure_svg = _structure_svg.replace(";fill:", ";fill-opacity:0.2;fill:") else: import re _structure_svg = re.sub('(fill-opacity:)([^:]+)(;|")', r"\1 0.2\3", _structure_svg) structure_svg = ElementTree.fromstring(_structure_svg) reinforcement_drawing.append(structure_svg) reinforcement_drawing.set( "transform", "translate({}, {})".format(round(-min_x), round(-min_y)), ) svg_width = round(max_x - min_x) svg_height = round(max_y - min_y) svg.set("width", "{}mm".format(svg_width)) svg.set("height", "{}mm".format(svg_height)) svg.set("viewBox", "0 0 {} {}".format(svg_width, svg_height)) return {"svg": svg, "rebars": visible_rebars}
def execute(self, obj): if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) angle = bvec.getAngle(axis) if round(angle, Draft.precision()) != 0: if round(angle, Draft.precision()) != round( math.pi, Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate( DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def compare(self, face1, face2): "zsorts two faces. Returns 1 if face1 is closer, 2 if face2 is closer, 0 otherwise" #print(face1,face2) if not face1: if DEBUG: print("Warning, undefined face!") return 31 elif not face2: if DEBUG: print("Warning, undefined face!") return 32 # theory from # http://www.siggraph.org/education/materials/HyperGraph/scanline/visibility/painter.htm # and practical application http://vrm.ao2.it/ (blender vector renderer) b1 = face1[0].BoundBox b2 = face2[0].BoundBox # test 1: if faces don't overlap, no comparison possible if DEBUG: print("doing test 1") if b1.XMax < b2.XMin: return 0 if b1.XMin > b2.XMax: return 0 if b1.YMax < b2.YMin: return 0 if b1.YMin > b2.YMax: return 0 if DEBUG: print("failed, faces bboxes are not distinct") # test 2: if Z bounds don't overlap, it's easy to know the closest if DEBUG: print("doing test 2") if b1.ZMax < b2.ZMin: return 2 if b2.ZMax < b1.ZMin: return 1 if DEBUG: print("failed, faces Z are not distinct") # test 3: all verts of face1 are in front or behind the plane of face2 if DEBUG: print("doing test 3") norm = face2[0].normalAt(0, 0) behind = 0 front = 0 for v in face1[0].Vertexes: dv = v.Point.sub(face2[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv, norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print("front: ", front, " behind: ", behind) if behind == len(face1[0].Vertexes): return 2 elif front == len(face1[0].Vertexes): return 1 if DEBUG: print("failed, cannot say if face 1 is in front or behind") # test 4: all verts of face2 are in front or behind the plane of face1 if DEBUG: print("doing test 4") norm = face1[0].normalAt(0, 0) behind = 0 front = 0 for v in face2[0].Vertexes: dv = v.Point.sub(face1[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv, norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print("front: ", front, " behind: ", behind) if behind == len(face2[0].Vertexes): return 1 elif front == len(face2[0].Vertexes): return 2 if DEBUG: print("failed, cannot say if face 2 is in front or behind") # test 5: see if faces projections don't overlap, vertexwise if DEBUG: print("doing test 5") if not self.zOverlaps(face1, face2): return 0 elif not self.zOverlaps(face2, face1): return 0 if DEBUG: print("failed, faces are overlapping") if DEBUG: print("Houston, all tests passed, and still no results") return 0
def 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 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 doStretch(self): """Do the actual stretching once the points are selected.""" commitops = [] if self.displacement: if self.displacement.Length > 0: _doc = "FreeCAD.ActiveDocument." # print("displacement: ", self.displacement) # TODO: break this section into individual functions # depending on the type of object (wire, curve, sketch, # rectangle, etc.) that is selected, and use variables # with common strings to avoid repeating # the same information every time, for example, the `_doc` # variable. # This is necessary to reduce the number of indentation levels # and make the code easier to read. for ops in self.ops: tp = utils.getType(ops[0]) _rot = ops[0].Placement.Rotation localdisp = _rot.inverted().multVec(self.displacement) if tp in ["Wire", "BSpline", "BezCurve"]: pts = [] for i in range(len(ops[1])): if ops[1][i] is False: pts.append(ops[0].Points[i]) else: pts.append(ops[0].Points[i].add(localdisp)) pts = str(pts).replace("Vector ", "FreeCAD.Vector") _cmd = _doc + ops[0].Name + ".Points=" + pts commitops.append(_cmd) elif tp in ["Sketch"]: baseverts = [ ops[0].Shape.Vertexes[i].Point for i in range(len(ops[1])) if ops[1][i] ] for i in range(ops[0].GeometryCount): j = 0 while True: try: p = ops[0].getPoint(i, j) except ValueError: break else: p = ops[0].Placement.multVec(p) r = None for bv in baseverts: if DraftVecUtils.isNull(p.sub(bv)): _cmd = _doc _cmd += ops[0].Name _cmd += ".movePoint" _cmd += "(" _cmd += str(i) + ", " _cmd += str(j) + ", " _cmd += "FreeCAD." + str( localdisp) + ", " _cmd += "True" _cmd += ")" commitops.append(_cmd) r = bv break if r: baseverts.remove(r) j += 1 elif tp in ["Rectangle"]: p1 = App.Vector(0, 0, 0) p2 = App.Vector(ops[0].Length.Value, 0, 0) p3 = App.Vector(ops[0].Length.Value, ops[0].Height.Value, 0) p4 = App.Vector(0, ops[0].Height.Value, 0) if ops[1] == [False, True, True, False]: optype = 1 elif ops[1] == [False, False, True, True]: optype = 2 elif ops[1] == [True, False, False, True]: optype = 3 elif ops[1] == [True, True, False, False]: optype = 4 else: optype = 0 # print("length:", ops[0].Length, # "height:", ops[0].Height, # " - ", ops[1], # " - ", self.displacement) done = False if optype > 0: v1 = ops[0].Placement.multVec(p2).sub( ops[0].Placement.multVec(p1)) a1 = round(self.displacement.getAngle(v1), 4) v2 = ops[0].Placement.multVec(p4).sub( ops[0].Placement.multVec(p1)) a2 = round(self.displacement.getAngle(v2), 4) # check if the displacement is along one # of the rectangle directions if a1 == 0: # 0 degrees if optype == 1: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value + self.displacement.Length else: d = ops[ 0].Length.Value - self.displacement.Length _cmd = _doc _cmd += ops[0].Name + ".Length=" + str(d) commitops.append(_cmd) done = True elif optype == 3: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value - self.displacement.Length else: d = ops[ 0].Length.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Length=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True elif a1 == 3.1416: # pi radians, 180 degrees if optype == 1: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value - self.displacement.Length else: d = ops[ 0].Length.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Length=" + str(d) commitops.append(_cmd) done = True elif optype == 3: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value + self.displacement.Length else: d = ops[ 0].Length.Value - self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Length=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True elif a2 == 0: # 0 degrees if optype == 2: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value + self.displacement.Length else: d = ops[ 0].Height.Value - self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) commitops.append(_cmd) done = True elif optype == 4: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value - self.displacement.Length else: d = ops[ 0].Height.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True elif a2 == 3.1416: # pi radians, 180 degrees if optype == 2: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value - self.displacement.Length else: d = ops[ 0].Height.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) commitops.append(_cmd) done = True elif optype == 4: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value + self.displacement.Length else: d = ops[ 0].Height.Value - self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True if not done: # otherwise create a wire copy and stretch it instead _msg( translate("draft", "Turning one Rectangle into a Wire")) pts = [] vts = ops[0].Shape.Vertexes for i in range(4): if ops[1][i] == False: pts.append(vts[i].Point) else: pts.append(vts[i].Point.add( self.displacement)) pts = str(pts).replace("Vector ", "FreeCAD.Vector") _cmd = "Draft.make_wire" _cmd += "(" + pts + ", closed=True, " _cmd += "face=" + str(ops[0].MakeFace) _cmd += ")" _format = "Draft.formatObject" _format += "(w, " _format += _doc + ops[0].Name _format += ")" _hide = _doc + ops[0].Name + ".ViewObject.hide()" commitops.append("w = " + _cmd) commitops.append(_format) commitops.append(_hide) else: _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_pl) if commitops: commitops.append("FreeCAD.ActiveDocument.recompute()") Gui.addModule("Draft") self.commit(translate("draft", "Stretch"), commitops) self.finish()
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 execute(self,obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if hasattr(obj,"ProfilePlacement"): if not obj.ProfilePlacement.isNull(): baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: edges = obj.Base.Shape.Edges if hasattr(obj,"Edges"): if obj.Edges == "Vertical edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] elif obj.Edges == "Top Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z,reverse=True) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] elif obj.Edges == "Bottom Horizontal edges": rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) edges = [e for e in edges if round(rv.getAngle(e.tangentAt(e.FirstParameter)),4) in [0,3.1416]] edges = sorted(edges,key=lambda x: x.CenterOfMass.z) z = edges[0].CenterOfMass.z edges = [e for e in edges if abs(e.CenterOfMass.z-z) < 0.00001] for e in edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base if hasattr(obj,"BasePoint"): edges = Part.__sortEdges__(profile.Edges) basepointliste = [profile.CenterOfMass] for edge in edges: basepointliste.append(DraftGeomUtils.findMidpoint(edge)) basepointliste.append(edge.Vertexes[-1].Point) try: basepoint = basepointliste[obj.BasePoint] except IndexError: FreeCAD.Console.PrintMessage(translate("Arch","Crossing point not found in profile.")+"\n") basepoint = basepointliste[0] else : basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: if hasattr(obj,"Fuse"): if obj.Fuse: if len(shapes) > 1: s = shapes[0].multiFuse(shapes[1:]) s = s.removeSplitter() obj.Shape = s obj.Placement = pl return obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self,obj): if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) angle = bvec.getAngle(axis) if round(angle,Draft.precision()) != 0: if round(angle,Draft.precision()) != round(math.pi,Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) 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