def getGlobalCoords(self,point): "returns the global coordinates of the given point, taken relatively to this working plane" vx = Vector(self.u).multiply(point.x) vy = Vector(self.v).multiply(point.y) vz = Vector(self.axis).multiply(point.z) pt = (vx.add(vy)).add(vz) return pt.add(self.position)
def getGlobalRot(self,point): "Same as getGlobalCoords, but discards the WP position" vx = Vector(self.u).multiply(point.x) vy = Vector(self.v).multiply(point.y) vz = Vector(self.axis).multiply(point.z) pt = (vx.add(vy)).add(vz) return pt
def _generate_spiral(self, is_dragging=False): """ Generate a spiral curve """ _key = '' _start = Vector(self.pi_nodes[0].point) _pi = Vector(self.pi_nodes[1].point) _end = Vector(self.pi_nodes[2].point) if not self.curve.get('StartRadius') \ or self.curve['StartRadius'] == math.inf: _key = 'EndRadius' _end = self.curve['End'] #first curve uses the start point. #otherwise, calculate a point halfway in between. if _start.is_end_node: _start = _pi.sub( Vector(_pi.sub(_start)).multiply( _start.distanceToPoint(_pi) / 2.0 ) ) else: _key = 'StartRadius' _start = self.curve['Start'] #last curve uses the end point. #otherwise, calculate a point halfway between. if _start.is_end_node: _end = _pi.add( Vector(_end.sub(_pi)).multiply( _end.distanceToPoint(_pi) / 2.0) ) _curve = { 'PI': _pi, 'Start': _start, 'End': _end, _key: self.curve[_key] } _curve = spiral.solve_unk_length(_curve) #re-render the last known good points if an error occurs if _curve['TanShort'] <= 0.0 or _curve['TanLong'] <= 0.0: _curve = self.curve self.curve = _curve return spiral.get_points(self.curve)
def mirror(point, edge): """Find mirror point relative to an edge.""" normPoint = point.add(findDistance(point, edge, False)) if normPoint: normPoint_point = Vector.sub(point, normPoint) normPoint_refl = normPoint_point.negative() refl = Vector.add(normPoint, normPoint_refl) return refl else: return None
def extend_two_points( p1: FreeCAD.Vector, p2: FreeCAD.Vector, length: float = 4000, ) -> tuple: xs = p1.x xe = p2.x ys = p1.y ye = p2.y delta_x = xe - xs d = p1.distanceToPoint(p2) if delta_x == 0: dx = 0 dy = length new_start_point = p1.add(FreeCAD.Vector(dx, -dy, 0)) new_d = new_start_point.distanceToPoint(p2) if new_d > d: new_end_point = p2.add(FreeCAD.Vector(dx, dy, 0)) else: new_start_point = p1.add(FreeCAD.Vector(dx, dy, 0)) new_end_point = p2.add(FreeCAD.Vector(dx, -dy, 0)) else: equation = get_line_equation(p1, p2) m = (ye - ys) / (xe - xs) dx = length / math.sqrt(1 + m**2) x = p2.x + dx y = eval(equation) dist = math.sqrt((x - p1.x)**2 + (y - p1.y)**2) if dist > d: new_end_point = FreeCAD.Vector(x, y, p2.z) x = p1.x - dx y = eval(equation) new_start_point = FreeCAD.Vector(x, y, p1.z) else: x = p2.x - dx y = eval(equation) new_end_point = FreeCAD.Vector(x, y, p2.z) x = p1.x + dx y = eval(equation) new_start_point = FreeCAD.Vector(x, y, p1.z) return new_start_point, new_end_point
def circleFrom2LinesRadius(edge1, edge2, radius): """circleFrom2LinesRadius(edge,edge,radius)""" int = findIntersection(edge1, edge2, True, True) if not int: return None int = int[0] bis12 = angleBisection(edge1, edge2) bis21 = Part.LineSegment(bis12.Vertexes[0].Point, DraftVecUtils.rotate(vec(bis12), math.pi / 2.0)) ang12 = abs(DraftVecUtils.angle(vec(edge1), vec(edge2))) ang21 = math.pi - ang12 dist12 = radius / math.sin(ang12 * 0.5) dist21 = radius / math.sin(ang21 * 0.5) circles = [] cen = Vector.add(int, vec(bis12).multiply(dist12)) circles.append(Part.Circle(cen, NORM, radius)) cen = Vector.add(int, vec(bis12).multiply(-dist12)) circles.append(Part.Circle(cen, NORM, radius)) cen = Vector.add(int, vec(bis21).multiply(dist21)) circles.append(Part.Circle(cen, NORM, radius)) cen = Vector.add(int, vec(bis21).multiply(-dist21)) circles.append(Part.Circle(cen, NORM, radius)) return circles
def circleFrom2PointsRadius(p1, p2, radius): """circleFrom2PointsRadiust(Vector, Vector, radius)""" if DraftVecUtils.equals(p1, p2): return None p1_p2 = Part.LineSegment(p1, p2).toShape() dist_p1p2 = DraftVecUtils.dist(p1, p1) mid = findMidpoint(p1_p2) if dist_p1p2 == 2 * radius: circle = Part.Circle(mid, NORM, radius) if circle: return [circle] else: return None dir = vec(p1_p2) dir.normalize() perpDir = dir.cross(Vector(0, 0, 1)) perpDir.normalize() dist = math.sqrt(radius**2 - (dist_p1p2 / 2.0)**2) cen1 = Vector.add(mid, Vector(perpDir).multiply(dist)) cen2 = Vector.add(mid, Vector(perpDir).multiply(-dist)) circles = [] if cen1: circles.append(Part.Circle(cen1, NORM, radius)) if cen2: circles.append(Part.Circle(cen2, NORM, radius)) if circles: return circles else: return None
def getGlobalCoords(self, point): """Return the coordinates of the given point, added to the plane. If the `point` was constructed using the plane as origin, return the absolute coordinates from the `point` to the global origin. The `u`, `v`, and `axis` vectors scale the components of `point`, and the result is added to the planes `position`. Parameters ---------- point : Base::Vector3 The external point. Returns ------- Base::Vector3 The coordinates of the point from the absolute origin. See also -------- getLocalCoords, getLocalRot, getGlobalRot Notes ----- The following graphic explains the coordinates. :: g GlobalCoords (1, 11) | | | (n) p point (1, 6) | LocalCoords (1, 1) | ----plane--------c-------- position (0, 5) In the graphic * `p` is an arbitrary point, external to the plane * `c` is the plane's `position` * `g` is the global coordinates of `p` when added to the plane * `n` is the relative coordinates of `p` when referred to the plane """ vx = Vector(self.u).multiply(point.x) vy = Vector(self.v).multiply(point.y) vz = Vector(self.axis).multiply(point.z) pt = (vx.add(vy)).add(vz) return pt.add(self.position)
def solve_unk_length(curve): """ Build the spiral for unknown length and theta """ if not all(curve.get(_k) for _k in ['Start', 'End', 'PI']): return None _is_inbound = True _end_point = curve['End'] _start_point = curve['Start'] _rad = None #a finite start radius means the curve is outbound if curve.get('StartRadius') is not None: _is_inbound = curve['StartRadius'] == math.inf if not _is_inbound: _end_point, _start_point = _start_point, _end_point _rad = curve['StartRadius'] else: _rad = curve['EndRadius'] _long_tan = curve['PI'].sub(_start_point) _short_tan = _end_point.sub(curve['PI']) _chord = _end_point.sub(_start_point) #get the point on the long tangent vector that is the endpoint for Xc _total_vec_x = Vector().projectToLine(_chord, _long_tan) curve['vTotalX'] = _start_point.add(_total_vec_x) curve['vTotalY'] = _end_point.sub(curve['vTotalX']) curve['vTotal'] = _total_vec_x.add(curve['vTotalY']) curve['TanShort'] = _short_tan.Length curve['TanLong'] = _long_tan.Length curve['Direction'] = support.get_rotation(_long_tan, _short_tan) curve['TotalX'] = curve['vTotalX'].Length curve['TotalY'] = curve['vTotalY'].Length curve['Type'] = 'Spiral' curve['Radius'] = _rad curve['Length'] = math.sqrt(curve['TotalX'] * 6.0 * _rad) curve['Theta'] = curve['Length'] / (2 * _rad) return curve
def my_test(): grp2 = [] #base = [0, 0, 0] #base = [1000, 0, 0] #base = [100000, 0, 0] _base = Vector(1000000, 0, 0) #base = [10000000, 0, 0] for i in range(0, 1000): _v = Vector(i * 0.1, i * 10, 0.0) grp1.append(_v) grp2.append(_v.add(_base)) _line1 = Draft.makeWire(grp1, closed=False, face=False) _line2 = Draft.makeWire(grp2, closed=False, face=False) Draft.autogroup(_line) App.ActiveDocument.recompute() Gui.SendMsgToActiveView("ViewFit")
def on_drag(self): """ Ongoing drag ops """ #tracker must be picked for dragging and actively dragged. #Prevents multiple updates in the same mose movement if not self.state.dragging or self != DragState().drag_node: return _drag_line_start = DragState().start _drag_line_end = MouseState().coordinates _mouse_coord = MouseState().coordinates #drag rotation if MouseState().altDown: DragState().rotate(_mouse_coord) _ctr = Vector(DragState().transform.center.getValue()) _offset = Vector(DragState().transform.translation.getValue()) _drag_line_start = _ctr.add(_offset) _mouse_coord = DragState().coordinates #drag translation else: DragState().translate(_mouse_coord) #save the drag state coordinate as the current mouse coordinate DragState().coordinates = _mouse_coord if self.show_drag_line: DragState().update(_drag_line_start, _drag_line_end)
def execute(self, obj): "creates the panel shape" if self.clone(obj): return import Part, DraftGeomUtils # base tests if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): if not obj.Base.Mesh.isSolid(): return else: if obj.Length.Value: length = obj.Length.Value else: return if obj.Width.Value: width = obj.Width.Value else: return if obj.Thickness.Value: thickness = obj.Thickness.Value else: if not obj.Base: return elif obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.Solids: return # creating base shape pl = obj.Placement base = None normal = None if hasattr(obj, "Normal"): if obj.Normal.Length > 0: normal = Vector(obj.Normal) normal.normalize() normal.multiply(thickness) baseprofile = None if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base if not normal: normal = baseprofile.Faces[0].normalAt( 0, 0).multiply(thickness) base = base.extrude(normal) elif base.Wires: fm = False if hasattr(obj, "FaceMaker"): if obj.FaceMaker != "None": try: base = Part.makeFace( base.Wires, "Part::FaceMaker" + str(obj.FaceMaker)) fm = True except: FreeCAD.Console.PrintError( translate("Arch", "Facemaker returned an error") + "\n") return if not fm: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: baseprofile = ArchCommands.makeFace(base.Wires) if not normal: normal = baseprofile.normalAt( 0, 0).multiply(thickness) base = baseprofile.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if not normal: normal = Vector(0, 0, 1).multiply(thickness) l2 = length / 2 or 0.5 w2 = width / 2 or 0.5 v1 = Vector(-l2, -w2, 0) v2 = Vector(l2, -w2, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base = Part.makePolygon([v1, v2, v3, v4, v1]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) if hasattr(obj, "Area"): if baseprofile: obj.Area = baseprofile.Area if hasattr(obj, "WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) p1 = Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z) if obj.WaveType == "Curved": p2 = p1.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) e1 = Part.Arc(p1, p2, p3).toShape() p4 = p3.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) p5 = p4.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) e2 = Part.Arc(p3, p4, p5).toShape() else: if obj.WaveHeight.Value < obj.WaveLength.Value: p2 = p1.add( Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector( obj.WaveLength.Value - 2 * obj.WaveHeight.Value, 0, 0)) p4 = p3.add( Vector(obj.WaveHeight.Value, 0, -obj.WaveHeight.Value)) e1 = Part.makePolygon([p1, p2, p3, p4]) p5 = p4.add( Vector(obj.WaveHeight.Value, 0, -obj.WaveHeight.Value)) p6 = p5.add( Vector( obj.WaveLength.Value - 2 * obj.WaveHeight.Value, 0, 0)) p7 = p6.add( Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value)) e2 = Part.makePolygon([p4, p5, p6, p7]) else: p2 = p1.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) e1 = Part.makePolygon([p1, p2, p3]) p4 = p3.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) p5 = p4.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) e2 = Part.makePolygon([p3, p4, p5]) edges = [e1, e2] for i in range(int(bb.XLength / (obj.WaveLength.Value * 2))): e1 = e1.copy() e1.translate(Vector(obj.WaveLength.Value * 2, 0, 0)) e2 = e2.copy() e2.translate(Vector(obj.WaveLength.Value * 2, 0, 0)) edges.extend([e1, e2]) basewire = Part.Wire(edges) baseface = basewire.extrude(Vector(0, bb.YLength, 0)) base = baseface.extrude(Vector(0, 0, thickness)) rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), normal) base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) if obj.WaveDirection.Value: base.rotate(bb.Center, normal, obj.WaveDirection.Value) n1 = normal.negative().normalize().multiply( obj.WaveHeight.Value * 2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: FreeCAD.Console.PrintError( transpate("Arch", "Error computing shape of ") + obj.Label + "\n") return False if base and (obj.Sheets > 1) and normal and thickness: bases = [base] for i in range(1, obj.Sheets): n = FreeCAD.Vector(normal).normalize().multiply(i * thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) if base and normal and hasattr(obj, "Offset"): if obj.Offset.Value: v = DraftVecUtils.scaleTo(normal, obj.Offset.Value) base.translate(v) # process subshapes base = self.processSubShapes(obj, base, pl) # applying if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( translate("Arch", "Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def makeStraightStairsWithLanding(self,obj,edge): "builds a straight staircase with/without a landing in the middle" if obj.NumberOfSteps < 3: return import Part,DraftGeomUtils v = DraftGeomUtils.vec(edge) landing = 0 if obj.TreadDepthEnforce == 0: if obj.Landings == "At center": if obj.LandingDepth: reslength = edge.Length - obj.LandingDepth.Value else: reslength = edge.Length - obj.Width.Value vLength = DraftVecUtils.scaleTo(v,float(reslength)/(obj.NumberOfSteps-2)) else: reslength = edge.Length vLength = DraftVecUtils.scaleTo(v,float(reslength)/(obj.NumberOfSteps-1)) else: if obj.Landings == "At center": reslength = obj.TreadDepthEnforce * (obj.NumberOfSteps-2) # TODO any use ? else: reslength = obj.TreadDepthEnforce * (obj.NumberOfSteps-1) # TODO any use ? vLength = DraftVecUtils.scaleTo(v,float(obj.TreadDepthEnforce)) vLength = Vector(vLength.x,vLength.y,0) vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),obj.Width.Value) p1 = edge.Vertexes[0].Point if obj.RiserHeightEnforce == 0: if round(v.z,Draft.precision()) != 0: h = v.z else: h = obj.Height.Value hstep = h/obj.NumberOfSteps else: h = obj.RiserHeightEnforce.Value * (obj.NumberOfSteps) hstep = obj.RiserHeightEnforce.Value if obj.Landings == "At center": landing = int(obj.NumberOfSteps/2) else: landing = obj.NumberOfSteps if obj.LastSegment: lastSegmentAbsTop = obj.LastSegment.AbsTop p1 = Vector(p1.x, p1.y,lastSegmentAbsTop.z) # use Last Segment top's z-coordinate obj.AbsTop = p1.add(Vector(0,0,h)) p2 = p1.add(DraftVecUtils.scale(vLength,landing-1).add(Vector(0,0,landing*hstep))) if obj.Landings == "At center": if obj.LandingDepth: p3 = p2.add(DraftVecUtils.scaleTo(vLength,obj.LandingDepth.Value)) else: p3 = p2.add(DraftVecUtils.scaleTo(vLength,obj.Width.Value)) if obj.Flight in ["HalfTurnLeft HalfTurnLeft", "HalfTurnRight"]: if (obj.Align == "Left" and obj.Flight == "HalfTurnLeft") or (obj.Align == "Right" and obj.Flight == "HalfTurnRight"): p3r = p2 elif (obj.Align == "Left" and obj.Flight == "HalfTurnRight"): p3r = self.align(p2,"Right",-2*vWidth) # -ve / opposite direction of "Right" - no "Left" in _Stairs.Align() elif (obj.Align == "Right" and obj.Flight == "HalfTurnLeft"): p3r = self.align(p2,"Right",2*vWidth) elif (obj.Align == "Center" and obj.Flight == "HalfTurnLeft"): p3r = self.align(p2,"Right",vWidth) elif (obj.Align == "Center" and obj.Flight == "HalfTurnRight"): p3r = self.align(p2,"Right",-vWidth) # -ve / opposite direction of "Right" - no "Left" in _Stairs.Align() else: print("Should have a bug here, if see this") p4r = p3r.add(DraftVecUtils.scale(-vLength,obj.NumberOfSteps-(landing+1)).add(Vector(0,0,(obj.NumberOfSteps-landing)*hstep))) else: p4 = p3.add(DraftVecUtils.scale(vLength,obj.NumberOfSteps-(landing+1)).add(Vector(0,0,(obj.NumberOfSteps-landing)*hstep))) self.makeStraightLanding(obj,Part.LineSegment(p2,p3).toShape(), None, True) if obj.Flight in ["HalfTurnLeft", "HalfTurnRight"]: self.makeStraightStairs(obj,Part.LineSegment(p3r,p4r).toShape(),obj.NumberOfSteps-landing) else: self.makeStraightStairs(obj,Part.LineSegment(p3,p4).toShape(),obj.NumberOfSteps-landing) self.makeStraightStairs(obj,Part.LineSegment(p1,p2).toShape(),landing) print (p1, p2) if obj.Landings == "At center" and obj.Flight not in ["HalfTurnLeft", "HalfTurnRight"]: print (p3, p4) elif obj.Landings == "At center" and obj.Flight in ["HalfTurnLeft", "HalfTurnRight"]: print (p3r, p4r)
class rectangleTracker(Tracker): "A Rectangle tracker, used by the rectangle tool" def __init__(self, dotted=False, scolor=None, swidth=None): self.origin = Vector(0, 0, 0) line = coin.SoLineSet() line.numVertices.setValue(5) self.coords = coin.SoCoordinate3() # this is the coordinate self.coords.point.setValues( 0, 50, [[0, 0, 0], [2, 0, 0], [2, 2, 0], [0, 2, 0], [0, 0, 0]]) Tracker.__init__(self, dotted, scolor, swidth, [self.coords, line]) self.u = FreeCAD.DraftWorkingPlane.u self.v = FreeCAD.DraftWorkingPlane.v def setorigin(self, point): "sets the base point of the rectangle" self.coords.point.set1Value(0, point.x, point.y, point.z) self.coords.point.set1Value(4, point.x, point.y, point.z) self.origin = point def update(self, point): "sets the opposite (diagonal) point of the rectangle" diagonal = point.sub(self.origin) inpoint1 = self.origin.add(DraftVecUtils.project(diagonal, self.v)) inpoint2 = self.origin.add(DraftVecUtils.project(diagonal, self.u)) self.coords.point.set1Value(1, inpoint1.x, inpoint1.y, inpoint1.z) self.coords.point.set1Value(2, point.x, point.y, point.z) self.coords.point.set1Value(3, inpoint2.x, inpoint2.y, inpoint2.z) def setPlane(self, u, v=None): '''sets given (u,v) vectors as working plane. You can give only u and v will be deduced automatically given current workplane''' self.u = u if v: self.v = v else: norm = FreeCAD.DraftWorkingPlane.u.cross( FreeCAD.DraftWorkingPlane.v) self.v = self.u.cross(norm) def p1(self, point=None): "sets or gets the base point of the rectangle" if point: self.setorigin(point) else: return Vector(self.coords.point.getValues()[0].getValue()) def p2(self): "gets the second point (on u axis) of the rectangle" return Vector(self.coords.point.getValues()[3].getValue()) def p3(self, point=None): "sets or gets the opposite (diagonal) point of the rectangle" if point: self.update(point) else: return Vector(self.coords.point.getValues()[2].getValue()) def p4(self): "gets the fourth point (on v axis) of the rectangle" return Vector(self.coords.point.getValues()[1].getValue()) def getSize(self): "returns (length,width) of the rectangle" p1 = Vector(self.coords.point.getValues()[0].getValue()) p2 = Vector(self.coords.point.getValues()[2].getValue()) diag = p2.sub(p1) return ((DraftVecUtils.project(diag, self.u)).Length, (DraftVecUtils.project(diag, self.v)).Length) def getNormal(self): "returns the normal of the rectangle" return (self.u.cross(self.v)).normalize()
def get_segments(spiral, deltas, _dtype=Vector): """ Calculate the coordinates of the curve segments bearing - beginning bearing deltas - list of angles to calculate direction - curve direction: -1.0 = ccw, 1.0 = cw start - starting coordinate radius - arc radius """ _vec = None _draw_start = spiral['Start'] _draw_end = spiral['End'] _length = spiral['Length'] _radius = spiral['Radius'] _direction = spiral['Direction'] #reverse only if end radius is infinite / undefined _reverse = spiral.get('EndRadius') is None if not _reverse: _reverse = spiral['EndRadius'] == math.inf #if short tangent leads, we need to calculate from the other end #toward the start of the spiral if _reverse: _draw_start, _draw_end = _draw_end, _draw_start _direction *= -1 _vec = spiral['PI'].sub(_draw_start).normalize() _points = [] _six_rad_len = 6.0 * _radius * _length _two_rad_len_root = math.sqrt(2.0 * _radius * _length) _forty_rad2_len2 = 40.0 * _radius**2 * _length**2 _root_deltas = [math.sqrt(_v) for _v in deltas] for _i, _delta in enumerate(deltas): #calculate length of curve at the current delta _seg_len = _two_rad_len_root * _root_deltas[_i] #calculate positions along curve at delta offset _x = _seg_len**3 / _six_rad_len _y = _seg_len - ((_seg_len**5) / _forty_rad2_len2) #calculate vector coordinates _dy = Vector(_vec).multiply(_y) _dx = Vector(_vec.y, -_vec.x, 0.0).multiply(_direction).multiply(_x) _points.append(_dtype(_dy.add(_dx))) _delta = _draw_end.sub(_points[-1]) if _reverse: _points = _points[::-1] _points = [_v.add(_draw_start) for _v in _points] return _points
def execute(self,obj): "creates the panel shape" if self.clone(obj): return import Part, DraftGeomUtils # base tests if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): if not obj.Base.Mesh.isSolid(): return else: if obj.Length.Value: length = obj.Length.Value else: return if obj.Width.Value: width = obj.Width.Value else: return if obj.Thickness.Value: thickness = obj.Thickness.Value else: if not obj.Base: return elif obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.Solids: return # creating base shape pl = obj.Placement base = None normal = None baseprofile = None if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness) base = base.extrude(normal) elif base.Wires: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: baseprofile = ArchCommands.makeFace(base.Wires) normal = baseprofile.normalAt(0,0).multiply(thickness) base = baseprofile.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: normal = Vector(0,0,1).multiply(thickness) l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) if hasattr(obj,"Area"): if baseprofile: obj.Area = baseprofile.Area if hasattr(obj,"WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) p1 = Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z) if obj.WaveType == "Curved": p2 = p1.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) e1 = Part.Arc(p1,p2,p3).toShape() p4 = p3.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) p5 = p4.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) e2 = Part.Arc(p3,p4,p5).toShape() else: if obj.WaveHeight.Value < obj.WaveLength.Value: p2 = p1.add(Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value-2*obj.WaveHeight.Value,0,0)) p4 = p3.add(Vector(obj.WaveHeight.Value,0,-obj.WaveHeight.Value)) e1 = Part.makePolygon([p1,p2,p3,p4]) p5 = p4.add(Vector(obj.WaveHeight.Value,0,-obj.WaveHeight.Value)) p6 = p5.add(Vector(obj.WaveLength.Value-2*obj.WaveHeight.Value,0,0)) p7 = p6.add(Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)) e2 = Part.makePolygon([p4,p5,p6,p7]) else: p2 = p1.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) e1 = Part.makePolygon([p1,p2,p3]) p4 = p3.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) p5 = p4.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) e2 = Part.makePolygon([p3,p4,p5]) edges = [e1,e2] for i in range(int(bb.XLength/(obj.WaveLength.Value*2))): e1 = e1.copy() e1.translate(Vector(obj.WaveLength.Value*2,0,0)) e2 = e2.copy() e2.translate(Vector(obj.WaveLength.Value*2,0,0)) edges.extend([e1,e2]) basewire = Part.Wire(edges) baseface = basewire.extrude(Vector(0,bb.YLength,0)) base = baseface.extrude(Vector(0,0,thickness)) rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal) base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle)) if obj.WaveDirection.Value: base.rotate(bb.Center,normal,obj.WaveDirection.Value) n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: FreeCAD.Console.PrintError(transpate("Arch","Error computing shape of ")+obj.Label+"\n") return False if base and (obj.Sheets > 1) and normal and thickness: bases = [base] for i in range(1,obj.Sheets): n = FreeCAD.Vector(normal).normalize().multiply(i*thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) if base and normal and hasattr(obj,"Offset"): if obj.Offset.Value: v = DraftVecUtils.scaleTo(normal,obj.Offset.Value) base.translate(v) # process subshapes base = self.processSubShapes(obj,base,pl) # applying if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
class rectangleTracker(Tracker): "A Rectangle tracker, used by the rectangle tool" def __init__(self,dotted=False,scolor=None,swidth=None,face=False): self.origin = Vector(0,0,0) line = coin.SoLineSet() line.numVertices.setValue(5) self.coords = coin.SoCoordinate3() # this is the coordinate self.coords.point.setValues(0,50,[[0,0,0],[2,0,0],[2,2,0],[0,2,0],[0,0,0]]) if face: m1 = coin.SoMaterial() m1.transparency.setValue(0.5) m1.diffuseColor.setValue([0.5,0.5,1.0]) f = coin.SoIndexedFaceSet() f.coordIndex.setValues([0,1,2,3]) Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line,m1,f],name="rectangleTracker") else: Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line],name="rectangleTracker") self.u = FreeCAD.DraftWorkingPlane.u self.v = FreeCAD.DraftWorkingPlane.v def setorigin(self,point): "sets the base point of the rectangle" self.coords.point.set1Value(0,point.x,point.y,point.z) self.coords.point.set1Value(4,point.x,point.y,point.z) self.origin = point def update(self,point): "sets the opposite (diagonal) point of the rectangle" diagonal = point.sub(self.origin) inpoint1 = self.origin.add(DraftVecUtils.project(diagonal,self.v)) inpoint2 = self.origin.add(DraftVecUtils.project(diagonal,self.u)) self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z) self.coords.point.set1Value(2,point.x,point.y,point.z) self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z) def setPlane(self,u,v=None): '''sets given (u,v) vectors as working plane. You can give only u and v will be deduced automatically given current workplane''' self.u = u if v: self.v = v else: norm = FreeCAD.DraftWorkingPlane.u.cross(FreeCAD.DraftWorkingPlane.v) self.v = self.u.cross(norm) def p1(self,point=None): "sets or gets the base point of the rectangle" if point: self.setorigin(point) else: return Vector(self.coords.point.getValues()[0].getValue()) def p2(self): "gets the second point (on u axis) of the rectangle" return Vector(self.coords.point.getValues()[3].getValue()) def p3(self,point=None): "sets or gets the opposite (diagonal) point of the rectangle" if point: self.update(point) else: return Vector(self.coords.point.getValues()[2].getValue()) def p4(self): "gets the fourth point (on v axis) of the rectangle" return Vector(self.coords.point.getValues()[1].getValue()) def getSize(self): "returns (length,width) of the rectangle" p1 = Vector(self.coords.point.getValues()[0].getValue()) p2 = Vector(self.coords.point.getValues()[2].getValue()) diag = p2.sub(p1) return ((DraftVecUtils.project(diag,self.u)).Length,(DraftVecUtils.project(diag,self.v)).Length) def getNormal(self): "returns the normal of the rectangle" return (self.u.cross(self.v)).normalize() def isInside(self,point): "returns True if the given point is inside the rectangle" vp = point.sub(self.p1()) uv = self.p2().sub(self.p1()) vv = self.p4().sub(self.p1()) uvp = DraftVecUtils.project(vp,uv) vvp = DraftVecUtils.project(vp,vv) if uvp.getAngle(uv) < 1: if vvp.getAngle(vv) < 1: if uvp.Length <= uv.Length: if vvp.Length <= vv.Length: return True return False
def makeStraightStairs(self,obj,edge,numberofsteps=None): "builds a simple, straight staircase from a straight edge" # Upgrade obj if it is from an older version of FreeCAD if not(hasattr(obj, "StringerOverlap")): obj.addProperty("App::PropertyLength","StringerOverlap","Structure",QT_TRANSLATE_NOOP("App::Property","The overlap of the stringers above the bottom of the treads")) # general data import Part,DraftGeomUtils if not numberofsteps: numberofsteps = obj.NumberOfSteps # if not numberofsteps - not call by makeStraightStairsWithLanding() # if not 're-base' there (StraightStair is part of StraightStairsWithLanding 'flight'), then 're-base' here (StraightStair is individual 'flight') callByMakeStraightStairsWithLanding = False else: callByMakeStraightStairsWithLanding = True v = DraftGeomUtils.vec(edge) vLength = DraftVecUtils.scaleTo(v,float(edge.Length)/(numberofsteps-1)) vLength = Vector(vLength.x,vLength.y,0) if round(v.z,Draft.precision()) != 0: h = v.z else: h = obj.Height.Value vHeight = Vector(0,0,float(h)/numberofsteps) vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0,0,1)),obj.Width.Value) vBase = edge.Vertexes[0].Point if not callByMakeStraightStairsWithLanding: if obj.LastSegment: print("obj.LastSegment is: " ) print(obj.LastSegment.Name) lastSegmentAbsTop = obj.LastSegment.AbsTop print("lastSegmentAbsTop is: ") print(lastSegmentAbsTop) vBase = Vector(vBase.x, vBase.y,lastSegmentAbsTop.z) # use Last Segment top's z-coordinate obj.AbsTop = vBase.add(Vector(0,0,h)) vNose = DraftVecUtils.scaleTo(vLength,-abs(obj.Nosing.Value)) a = math.atan(vHeight.Length/vLength.Length) # steps for i in range(numberofsteps-1): p1 = vBase.add((Vector(vLength).multiply(i)).add(Vector(vHeight).multiply(i+1))) p1 = self.align(p1,obj.Align,vWidth) p1 = p1.add(vNose).add(Vector(0,0,-abs(obj.TreadThickness.Value))) p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) step = Part.Face(Part.makePolygon([p1,p2,p3,p4,p1])) if obj.TreadThickness.Value: step = step.extrude(Vector(0,0,abs(obj.TreadThickness.Value))) self.steps.append(step) else: self.pseudosteps.append(step) # structure lProfile = [] struct = None if obj.Structure == "Massive": if obj.StructureThickness.Value: # Massive Structure to respect 'align' attribute vBasedAligned = self.align(vBase,obj.Align,vWidth) vBase = vBasedAligned for i in range(numberofsteps-1): if not lProfile: lProfile.append(vBase) last = lProfile[-1] if len(lProfile) == 1: last = last.add(Vector(0,0,-abs(obj.TreadThickness.Value))) lProfile.append(last.add(vHeight)) lProfile.append(lProfile[-1].add(vLength)) resHeight1 = obj.StructureThickness.Value/math.cos(a) lProfile.append(lProfile[-1].add(Vector(0,0,-resHeight1))) resHeight2 = ((numberofsteps-1)*vHeight.Length)-(resHeight1+obj.TreadThickness.Value) resLength = (vLength.Length/vHeight.Length)*resHeight2 h = DraftVecUtils.scaleTo(vLength,-resLength) lProfile.append(lProfile[-1].add(Vector(h.x,h.y,-resHeight2))) lProfile.append(vBase) #print(lProfile) pol = Part.makePolygon(lProfile) struct = Part.Face(pol) evec = vWidth if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) struct.translate(mvec) evec = DraftVecUtils.scaleTo(evec,evec.Length-(2*mvec.Length)) struct = struct.extrude(evec) elif obj.Structure in ["One stringer","Two stringers"]: if obj.StringerWidth.Value and obj.StructureThickness.Value: hyp = math.sqrt(vHeight.Length**2 + vLength.Length**2) l1 = Vector(vLength).multiply(numberofsteps-1) h1 = Vector(vHeight).multiply(numberofsteps-1).add(Vector(0,0,-abs(obj.TreadThickness.Value)+obj.StringerOverlap.Value)) p1 = vBase.add(l1).add(h1) p1 = self.align(p1,obj.Align,vWidth) if obj.StringerOverlap.Value <= float(h)/numberofsteps: lProfile.append(p1) else: p1b = vBase.add(l1).add(Vector(0,0,float(h))) p1a = p1b.add(Vector(vLength).multiply((p1b.z-p1.z)/vHeight.Length)) lProfile.append(p1a) lProfile.append(p1b) h2 = (obj.StructureThickness.Value/vLength.Length)*hyp lProfile.append(p1.add(Vector(0,0,-abs(h2)))) h3 = lProfile[-1].z-vBase.z l3 = (h3/vHeight.Length)*vLength.Length v3 = DraftVecUtils.scaleTo(vLength,-l3) lProfile.append(lProfile[-1].add(Vector(0,0,-abs(h3))).add(v3)) l4 = (obj.StructureThickness.Value/vHeight.Length)*hyp v4 = DraftVecUtils.scaleTo(vLength,-l4) lProfile.append(lProfile[-1].add(v4)) lProfile.append(lProfile[0]) #print(lProfile) pol = Part.makePolygon(lProfile) pol = Part.Face(pol) evec = DraftVecUtils.scaleTo(vWidth,obj.StringerWidth.Value) if obj.Structure == "One stringer": if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) else: mvec = DraftVecUtils.scaleTo(vWidth,(vWidth.Length/2)-obj.StringerWidth.Value/2) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if obj.StructureOffset.Value: mvec = DraftVecUtils.scaleTo(vWidth,obj.StructureOffset.Value) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) else: pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) struct = Part.makeCompound([s1,s2]) if struct: self.structures.append(struct)
class rectangleTracker(Tracker): "A Rectangle tracker, used by the rectangle tool" def __init__(self,dotted=False,scolor=None,swidth=None,face=False): self.origin = Vector(0,0,0) line = coin.SoLineSet() line.numVertices.setValue(5) self.coords = coin.SoCoordinate3() # this is the coordinate self.coords.point.setValues(0,50,[[0,0,0],[2,0,0],[2,2,0],[0,2,0],[0,0,0]]) if face: m1 = coin.SoMaterial() m1.transparency.setValue(0.5) m1.diffuseColor.setValue([0.5,0.5,1.0]) f = coin.SoIndexedFaceSet() f.coordIndex.setValues([0,1,2,3]) Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line,m1,f],name="rectangleTracker") else: Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line],name="rectangleTracker") self.u = FreeCAD.DraftWorkingPlane.u self.v = FreeCAD.DraftWorkingPlane.v def setorigin(self,point): "sets the base point of the rectangle" self.coords.point.set1Value(0,point.x,point.y,point.z) self.coords.point.set1Value(4,point.x,point.y,point.z) self.origin = point def update(self,point): "sets the opposite (diagonal) point of the rectangle" diagonal = point.sub(self.origin) inpoint1 = self.origin.add(DraftVecUtils.project(diagonal,self.v)) inpoint2 = self.origin.add(DraftVecUtils.project(diagonal,self.u)) self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z) self.coords.point.set1Value(2,point.x,point.y,point.z) self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z) def setPlane(self,u,v=None): '''sets given (u,v) vectors as working plane. You can give only u and v will be deduced automatically given current workplane''' self.u = u if v: self.v = v else: norm = FreeCAD.DraftWorkingPlane.u.cross(FreeCAD.DraftWorkingPlane.v) self.v = self.u.cross(norm) def p1(self,point=None): "sets or gets the base point of the rectangle" if point: self.setorigin(point) else: return Vector(self.coords.point.getValues()[0].getValue()) def p2(self): "gets the second point (on u axis) of the rectangle" return Vector(self.coords.point.getValues()[3].getValue()) def p3(self,point=None): "sets or gets the opposite (diagonal) point of the rectangle" if point: self.update(point) else: return Vector(self.coords.point.getValues()[2].getValue()) def p4(self): "gets the fourth point (on v axis) of the rectangle" return Vector(self.coords.point.getValues()[1].getValue()) def getSize(self): "returns (length,width) of the rectangle" p1 = Vector(self.coords.point.getValues()[0].getValue()) p2 = Vector(self.coords.point.getValues()[2].getValue()) diag = p2.sub(p1) return ((DraftVecUtils.project(diag,self.u)).Length,(DraftVecUtils.project(diag,self.v)).Length) def getNormal(self): "returns the normal of the rectangle" return (self.u.cross(self.v)).normalize()
def makeStraightStairsWithLanding(self, obj, edge): "builds a straight staircase with a landing in the middle" if obj.NumberOfSteps < 3: return import Part, DraftGeomUtils v = DraftGeomUtils.vec(edge) if obj.LandingDepth: reslength = edge.Length - obj.LandingDepth.Value else: reslength = edge.Length - obj.Width.Value vLength = DraftVecUtils.scaleTo( v, float(reslength) / (obj.NumberOfSteps - 2)) vLength = Vector(vLength.x, vLength.y, 0) vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0, 0, 1)), obj.Width.Value) p1 = edge.Vertexes[0].Point if round(v.z, Draft.precision()) != 0: h = v.z else: h = obj.Height.Value hstep = h / obj.NumberOfSteps landing = int(obj.NumberOfSteps / 2) if obj.LastSegment: print("obj.LastSegment is: ") print(obj.LastSegment.Name) lastSegmentAbsTop = obj.LastSegment.AbsTop print("lastSegmentAbsTop is: ") print(lastSegmentAbsTop) p1 = Vector( p1.x, p1.y, lastSegmentAbsTop.z) # use Last Segment top's z-coordinate print(p1) obj.AbsTop = p1.add(Vector(0, 0, h)) p2 = p1.add( DraftVecUtils.scale(vLength, landing - 1).add(Vector(0, 0, landing * hstep))) if obj.LandingDepth: p3 = p2.add(DraftVecUtils.scaleTo(vLength, obj.LandingDepth.Value)) else: p3 = p2.add(DraftVecUtils.scaleTo(vLength, obj.Width.Value)) if obj.Flight == "HalfTurnLeft": p3r = p2 p4r = p2.add( DraftVecUtils.scale( -vLength, obj.NumberOfSteps - (landing + 1)).add( Vector(0, 0, (obj.NumberOfSteps - landing) * hstep))) else: p4 = p3.add( DraftVecUtils.scale( vLength, obj.NumberOfSteps - (landing + 1)).add( Vector(0, 0, (obj.NumberOfSteps - landing) * hstep))) self.makeStraightStairs(obj, Part.LineSegment(p1, p2).toShape(), landing) self.makeStraightLanding(obj, Part.LineSegment(p2, p3).toShape(), None, True) if obj.Flight == "HalfTurnLeft": self.makeStraightStairs(obj, Part.LineSegment(p3r, p4r).toShape(), obj.NumberOfSteps - landing) else: self.makeStraightStairs(obj, Part.LineSegment(p3, p4).toShape(), obj.NumberOfSteps - landing)