示例#1
0
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