def offset(obj, delta, copy=False, bind=False, sym=False, occ=False): """offset(object,delta,[copymode],[bind]) Offset the given wire by applying the given delta Vector to its first vertex. Parameters ---------- obj : delta : Base.Vector or list of Base.Vector If offsetting a BSpline, the delta must not be a Vector but a list of Vectors, one for each node of the spline. copy : bool If copymode is True, another object is created, otherwise the same object gets offsetted. copy : bool If bind is True, and provided the wire is open, the original and the offset wires will be bound by their endpoints, forming a face. sym : bool if sym is True, bind must be true too, and the offset is made on both sides, the total width being the given delta length. """ import Part import DraftGeomUtils newwire = None delete = None if utils.get_type(obj).startswith("Part::") or utils.get_type( obj).startswith("Sketcher::"): copy = True print( "the offset tool is currently unable to offset a non-Draft object directly - Creating a copy" ) def getRect(p, obj): """returns length,height,placement""" pl = obj.Placement.copy() pl.Base = p[0] diag = p[2].sub(p[0]) bb = p[1].sub(p[0]) bh = p[3].sub(p[0]) nb = DraftVecUtils.project(diag, bb) nh = DraftVecUtils.project(diag, bh) if obj.Length.Value < 0: l = -nb.Length else: l = nb.Length if obj.Height.Value < 0: h = -nh.Length else: h = nh.Length return l, h, pl def getRadius(obj, delta): """returns a new radius for a regular polygon""" an = math.pi / obj.FacesNumber nr = DraftVecUtils.rotate(delta, -an) nr.multiply(1 / math.cos(an)) nr = obj.Shape.Vertexes[0].Point.add(nr) nr = nr.sub(obj.Placement.Base) nr = nr.Length if obj.DrawMode == "inscribed": return nr else: return nr * math.cos(math.pi / obj.FacesNumber) newwire = None if utils.get_type(obj) == "Circle": pass elif utils.get_type(obj) == "BSpline": pass else: if sym: d1 = App.Vector(delta).multiply(0.5) d2 = d1.negative() n1 = DraftGeomUtils.offsetWire(obj.Shape, d1) n2 = DraftGeomUtils.offsetWire(obj.Shape, d2) else: if isinstance(delta, float) and (len(obj.Shape.Edges) == 1): # circle c = obj.Shape.Edges[0].Curve nc = Part.Circle(c.Center, c.Axis, delta) if len(obj.Shape.Vertexes) > 1: nc = Part.ArcOfCircle(nc, obj.Shape.Edges[0].FirstParameter, obj.Shape.Edges[0].LastParameter) newwire = Part.Wire(nc.toShape()) p = [] else: newwire = DraftGeomUtils.offsetWire(obj.Shape, delta) if DraftGeomUtils.hasCurves(newwire) and copy: p = [] else: p = DraftGeomUtils.getVerts(newwire) if occ: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape, delta, occ=True) gui_utils.formatObject(newobj, obj) if not copy: delete = obj.Name elif bind: if not DraftGeomUtils.isReallyClosed(obj.Shape): if sym: s1 = n1 s2 = n2 else: s1 = obj.Shape s2 = newwire if s1 and s2: w1 = s1.Edges w2 = s2.Edges w3 = Part.LineSegment(s1.Vertexes[0].Point, s2.Vertexes[0].Point).toShape() w4 = Part.LineSegment(s1.Vertexes[-1].Point, s2.Vertexes[-1].Point).toShape() newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = Part.Face(Part.Wire(w1 + [w3] + w2 + [w4])) else: print("Draft.offset: Unable to bind wires") else: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = Part.Face(obj.Shape.Wires[0]) if not copy: delete = obj.Name elif copy: newobj = None if sym: return None if utils.get_type(obj) == "Wire": if p: newobj = make_wire(p) newobj.Closed = obj.Closed elif newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to duplicate this object") elif utils.get_type(obj) == "Rectangle": if p: length, height, plac = getRect(p, obj) newobj = make_rectangle(length, height, plac) elif newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to duplicate this object") elif utils.get_type(obj) == "Circle": pl = obj.Placement newobj = make_circle(delta) newobj.FirstAngle = obj.FirstAngle newobj.LastAngle = obj.LastAngle newobj.Placement = pl elif utils.get_type(obj) == "Polygon": pl = obj.Placement newobj = make_polygon(obj.FacesNumber) newobj.Radius = getRadius(obj, delta) newobj.DrawMode = obj.DrawMode newobj.Placement = pl elif utils.get_type(obj) == "BSpline": newobj = make_bspline(delta) newobj.Closed = obj.Closed else: # try to offset anyway try: if p: newobj = make_wire(p) newobj.Closed = obj.Shape.isClosed() except Part.OCCError: pass if (not newobj) and newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire if not newobj: print("Draft.offset: Unable to create an offset") if newobj: gui_utils.formatObject(newobj, obj) else: newobj = None if sym: return None if utils.get_type(obj) == "Wire": if obj.Base or obj.Tool: App.Console.PrintWarning("Warning: object history removed\n") obj.Base = None obj.Tool = None obj.Placement = App.Placement( ) # p points are in the global coordinate system obj.Points = p elif utils.get_type(obj) == "BSpline": #print(delta) obj.Points = delta #print("done") elif utils.get_type(obj) == "Rectangle": length, height, plac = getRect(p, obj) obj.Placement = plac obj.Length = length obj.Height = height elif utils.get_type(obj) == "Circle": obj.Radius = delta elif utils.get_type(obj) == "Polygon": obj.Radius = getRadius(obj, delta) elif utils.get_type(obj) == 'Part': print("unsupported object") # TODO newobj = obj if copy and utils.get_param("selectBaseObjects", False): gui_utils.select(newobj) else: gui_utils.select(obj) if delete: App.ActiveDocument.removeObject(delete) return newobj
def getEdgesAngleSVG( edge1: Part.Edge, edge2: Part.Edge, arc_radius: float, view_plane: WorkingPlane.Plane, font_family: str, font_size: Union[float, str], bent_angle_exclude_list: Tuple[float, ...] = (90, 180), stroke_width: Union[float, str] = 0.2, stroke_color: str = "black", ) -> ElementTree.Element: """Returns svg representation for angle between two edges by drawing an arc of given radius and adding angle text svg. It returns empty svg if edges doesn't intersect when extended infinitely. Parameters ---------- edge1: Part.Edge The first edge to get its angle dimension svg with edge2. edge2: The second edge to get its angle dimension svg with edge1. arc_radius: float The radius of dimension arc. view_plane: WorkingPlane.Plane The view plane acting as svg plane. font_family: str The font-family of angle dimension. font_size: float or str The font-size of angle dimension. bent_angle_exclude_list: tuple of float, optional If angle between two edges is present in bent_angle_exclude_list, then empty svg element will be returned. Default is (90, 180) stroke_width: float or str, optional The stroke-width of arc svg. Default is 0.2 stroke_color: str, optional The stroke color of arc svg. Default is "black". Returns ------- ElementTree.Element The generated edges angle dimension svg. """ intersection = DraftGeomUtils.findIntersection(edge1, edge2, True, True) if not intersection: return ElementTree.Element("g") else: intersection = intersection[0] p1 = max( DraftGeomUtils.getVerts(edge1), key=lambda x: x.distanceToPoint(intersection), ) p2 = max( DraftGeomUtils.getVerts(edge2), key=lambda x: x.distanceToPoint(intersection), ) angle = round( math.degrees( abs(DraftVecUtils.angle(p2.sub(intersection), p1.sub(intersection))))) if angle in bent_angle_exclude_list: return ElementTree.Element("g") arc_p1 = intersection.add(arc_radius * p2.sub(intersection).normalize()) arc_p2 = intersection.add(arc_radius * p1.sub(intersection).normalize()) arc_edge = DraftGeomUtils.arcFrom2Pts(arc_p1, arc_p2, intersection) arc_svg = getRoundEdgeSVG(arc_edge, view_plane, stroke_width, stroke_color) arc_mid_point = DraftGeomUtils.findMidpoint(arc_edge) proj_intersection = getProjectionToSVGPlane(intersection, view_plane) proj_mid_point = getProjectionToSVGPlane(arc_mid_point, view_plane) if round(proj_intersection.x) < round(proj_mid_point.x): text_anchor = "start" elif round(proj_intersection.x) > round(proj_mid_point.x): text_anchor = "end" else: text_anchor = "middle" if round(proj_intersection.y) < round(proj_mid_point.y): text_y = proj_mid_point.y + font_size elif round(proj_intersection.y) > round(proj_mid_point.y): text_y = proj_mid_point.y else: text_y = proj_mid_point.y + font_size / 2 angle_text_svg = getSVGTextElement( "{}°".format(angle), proj_mid_point.x, text_y, font_family, font_size, text_anchor, ) bent_angle_svg = ElementTree.Element("g") bent_angle_svg.extend([arc_svg, angle_text_svg]) return bent_angle_svg