Exemplo n.º 1
0
def make_text(string, placement=None, screen=False):
    """Create a Text object containing the given list of strings.

    The current color and text height and font specified in preferences
    are used.

    Parameters
    ----------
    string: str, or list of str
        String to display on screen.
        If it is a list, each element in the list represents a new text line.

    placement: Base::Placement, Base::Vector3, or Base::Rotation, optional
        It defaults to `None`.
        If it is provided, it is the placement of the new text.
        The input could be a full placement, just a vector indicating
        the translation, or just a rotation.

    screen: bool, optional
        It defaults to `False`, in which case the text is placed in 3D space
        oriented like any other object, on top of a given plane,
        by the default the XY plane.
        If it is `True`, the text will always face perpendicularly
        to the camera direction, that is, it will be flat on the screen.

    Returns
    -------
    App::FeaturePython
        A scripted object of type `'Text'`.
        This object does not have a `Shape` attribute, as the text is created
        on screen by Coin (pivy).

    None
        If there is a problem it will return `None`.
    """
    _name = "make_text"
    utils.print_header(_name, "Text")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(translate("draft", "No active document. Aborting."))
        return None

    _msg("string: {}".format(string))
    try:
        utils.type_check([(string, (str, list))], name=_name)
    except TypeError:
        _err(
            translate(
                "draft",
                "Wrong input: must be a list of strings or a single string."))
        return None

    if (type(string) is list
            and not all(isinstance(element, str) for element in string)):
        _err(
            translate(
                "draft",
                "Wrong input: must be a list of strings or a single string."))
        return None

    _msg("placement: {}".format(placement))
    if not placement:
        placement = App.Placement()
    try:
        utils.type_check([(placement,
                           (App.Placement, App.Vector, App.Rotation))],
                         name=_name)
    except TypeError:
        _err(
            translate(
                "draft",
                "Wrong input: must be a placement, a vector, or a rotation."))
        return None

    # Convert the vector or rotation to a full placement
    if isinstance(placement, App.Vector):
        placement = App.Placement(placement, App.Rotation())
    elif isinstance(placement, App.Rotation):
        placement = App.Placement(App.Vector(), placement)

    new_obj = doc.addObject("App::FeaturePython", "Text")
    Text(new_obj)
    new_obj.Text = string
    new_obj.Placement = placement

    if App.GuiUp:
        ViewProviderText(new_obj.ViewObject)

        h = utils.get_param("textheight", 2)

        new_obj.ViewObject.DisplayMode = "3D text"
        if screen:
            _msg("screen: {}".format(screen))
            new_obj.ViewObject.DisplayMode = "2D text"
            h = h * 10

        new_obj.ViewObject.FontSize = h
        new_obj.ViewObject.FontName = utils.get_param("textfont", "")
        new_obj.ViewObject.LineSpacing = 1

        gui_utils.format_object(new_obj)
        gui_utils.select(new_obj)

    return new_obj
Exemplo n.º 2
0
def make_bspline(pointslist, closed=False, placement=None, face=None, support=None):
    """make_bspline(pointslist, [closed], [placement])
    
    Creates a B-Spline object from the given list of vectors.
    
    Parameters
    ----------
    pointlist : [Base.Vector]
        List of points to create the polyline.
        Instead of a pointslist, you can also pass a Part Wire.
        TODO: Change the name so!

    closed : bool
        If closed is True or first and last points are identical, 
        the created BSpline will be closed.

    placement : Base.Placement
        If a placement is given, it is used.
    
    face : Bool
        If face is False, the rectangle is shown as a wireframe, 
        otherwise as a face.   

    support : 
        TODO: Describe
    """
    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return
    if not isinstance(pointslist,list):
        nlist = []
        for v in pointslist.Vertexes:
            nlist.append(v.Point)
        pointslist = nlist
    if len(pointslist) < 2:
        _err = "Draft.makeBSpline: not enough points"
        App.Console.PrintError(translate("draft", _err)+"\n")
        return
    if (pointslist[0] == pointslist[-1]):
        if len(pointslist) > 2:
            closed = True
            pointslist.pop()
            _err = "Draft.makeBSpline: Equal endpoints forced Closed"
            App.Console.PrintWarning(translate("Draft", _err) + _err + "\n")
        else:
            # len == 2 and first == last   GIGO
            _err = "Draft.makeBSpline: Invalid pointslist"
            App.Console.PrintError(translate("Draft", _err)+"\n")
            return
    # should have sensible parms from here on
    if placement:
        utils.type_check([(placement,App.Placement)], "make_bspline")
    if len(pointslist) == 2: fname = "Line"
    else: fname = "BSpline"
    obj = App.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
    BSpline(obj)
    obj.Closed = closed
    obj.Points = pointslist
    obj.Support = support
    if face != None:
        obj.MakeFace = face
    if placement: obj.Placement = placement
    if App.GuiUp:
        ViewProviderBSpline(obj.ViewObject)
        gui_utils.format_object(obj)
        gui_utils.select(obj)

    return obj
Exemplo n.º 3
0
def make_dimension(p1, p2, p3=None, p4=None):
    """Create one of three types of dimension objects.

    In all dimensions the p3 parameter defines a point through which
    the dimension line will go through.

    The current line width and color will be used.

    Linear dimension
    ----------------
    - (p1, p2, p3): a simple linear dimension from p1 to p2

    - (object, i1, i2, p3): creates a linked dimension to the provided
      object (edge), measuring the distance between its vertices
      indexed i1 and i2

    Circular dimension
    ------------------
    - (arc, i1, mode, p3): creates a linked dimension to the given arc
      object, i1 is the index of the arc edge that will be measured;
      mode is either "radius" or "diameter".
    """
    if not App.ActiveDocument:
        _err("No active document. Aborting")
        return None

    new_obj = App.ActiveDocument.addObject("App::FeaturePython",
                                           "Dimension")
    LinearDimension(new_obj)

    if App.GuiUp:
        ViewProviderLinearDimension(new_obj.ViewObject)

    if isinstance(p1, App.Vector) and isinstance(p2, App.Vector):
        # Measure a straight distance between p1 and p2
        new_obj.Start = p1
        new_obj.End = p2
        if not p3:
            p3 = p2.sub(p1)
            p3.multiply(0.5)
            p3 = p1.add(p3)

    elif isinstance(p2, int) and isinstance(p3, int):
        # p1 is an object, and measure the distance between vertices p2 and p3
        # of this object
        linked = []
        idx = (p2, p3)
        linked.append((p1, "Vertex" + str(p2 + 1)))
        linked.append((p1, "Vertex" + str(p3 + 1)))
        new_obj.LinkedGeometry = linked
        new_obj.Support = p1

        # p4, and now p3, is the point through which the dimension line
        # will go through
        p3 = p4
        if not p3:
            # When used from the GUI command, this will never run
            # because p4 will always be assinged to a vector,
            # so p3 will never be `None`.
            # Moreover, `new_obj.Base` doesn't exist, and certainly `Shape`
            # doesn't exist, so if this ever runs it will be an error.
            v1 = new_obj.Base.Shape.Vertexes[idx[0]].Point
            v2 = new_obj.Base.Shape.Vertexes[idx[1]].Point
            p3 = v2.sub(v1)
            p3.multiply(0.5)
            p3 = v1.add(p3)

    elif isinstance(p3, str):
        # If the original p3 is a string, we are measuring a circular arc
        # p2 should be an integer number starting from 0
        linked = []
        linked.append((p1, "Edge" + str(p2 + 1)))

        if p3 == "radius":
            # linked.append((p1, "Center"))
            if App.GuiUp:
                new_obj.ViewObject.Override = "R $dim"
            new_obj.Diameter = False
        elif p3 == "diameter":
            # linked.append((p1, "Diameter"))
            if App.GuiUp:
                new_obj.ViewObject.Override = "Ø $dim"
            new_obj.Diameter = True
        new_obj.LinkedGeometry = linked
        new_obj.Support = p1

        # p4, and now p3, is the point through which the dimension line
        # will go through
        p3 = p4
        if not p3:
            p3 = p1.Shape.Edges[p2].Curve.Center.add(App.Vector(1, 0, 0))

    # This p3 is the point through which the dimension line will pass,
    # but this may not be the original p3, it could have been p4
    # depending on the first three parameter values
    new_obj.Dimline = p3

    if hasattr(App, "DraftWorkingPlane"):
        normal = App.DraftWorkingPlane.axis
    else:
        normal = App.Vector(0, 0, 1)

    if App.GuiUp:
        # invert the normal if we are viewing it from the back
        vnorm = gui_utils.get3DView().getViewDirection()

        if vnorm.getAngle(normal) < math.pi/2:
            normal = normal.negative()

    new_obj.Normal = normal

    if App.GuiUp:
        gui_utils.format_object(new_obj)
        gui_utils.select(new_obj)

    return new_obj
Exemplo n.º 4
0
def make_angular_dimension(center=App.Vector(0, 0, 0),
                           angles=[0, 90],
                           dim_line=App.Vector(10, 10, 0), normal=None):
    """Create an angular dimension from the given center and angles.

    Parameters
    ----------
    center: Base::Vector3, optional
        It defaults to the origin `Vector(0, 0, 0)`.
        Center of the dimension line, which is a circular arc.

    angles: list of two floats, optional
        It defaults to `[0, 90]`.
        It is a list of two angles, given in degrees, that determine
        the apperture of the dimension line, that is, of the circular arc.
        It is drawn counter-clockwise.
        ::
            angles = [0 90]
            angles = [330 60]  # the arc crosses the X axis
            angles = [-30 60]  # same angle

    dim_line: Base::Vector3, optional
        It defaults to `Vector(10, 10, 0)`.
        This is a point through which the extension of the dimension line
        will pass. This defines the radius of the dimension line,
        the circular arc.

    normal: Base::Vector3, optional
        It defaults to `None`, in which case the `normal` is taken
        from the currently active `App.DraftWorkingPlane.axis`.

        If the working plane is not available, then the `normal`
        defaults to +Z or `Vector(0, 0, 1)`.

    Returns
    -------
    App::FeaturePython
        A scripted object of type `'AngularDimension'`.
        This object does not have a `Shape` attribute, as the text and lines
        are created on screen by Coin (pivy).

    None
        If there is a problem it will return `None`.
    """
    _name = "make_angular_dimension"
    utils.print_header(_name, "Angular dimension")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(_tr("No active document. Aborting."))
        return None

    _msg("center: {}".format(center))
    try:
        utils.type_check([(center, App.Vector)], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a vector."))
        return None

    _msg("angles: {}".format(angles))
    try:
        utils.type_check([(angles, (tuple, list))], name=_name)

        if len(angles) != 2:
            _err(_tr("Wrong input: must be a list with two angles."))
            return None

        ang1, ang2 = angles
        utils.type_check([(ang1, (int, float)),
                          (ang2, (int, float))], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a list with two angles."))
        return None

    # If the angle is larger than 360 degrees, make sure
    # it is smaller than 360
    for n in range(len(angles)):
        if angles[n] > 360:
            angles[n] = angles[n] - 360

    _msg("dim_line: {}".format(dim_line))
    try:
        utils.type_check([(dim_line, App.Vector)], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a vector."))
        return None

    _msg("normal: {}".format(normal))
    if normal:
        try:
            utils.type_check([(dim_line, App.Vector)], name=_name)
        except TypeError:
            _err(_tr("Wrong input: must be a vector."))
            return None

    if not normal:
        if hasattr(App, "DraftWorkingPlane"):
            normal = App.DraftWorkingPlane.axis
        else:
            normal = App.Vector(0, 0, 1)

    new_obj = App.ActiveDocument.addObject("App::FeaturePython",
                                           "Dimension")
    AngularDimension(new_obj)

    new_obj.Center = center
    new_obj.FirstAngle = angles[0]
    new_obj.LastAngle = angles[1]
    new_obj.Dimline = dim_line

    if App.GuiUp:
        ViewProviderAngularDimension(new_obj.ViewObject)

        # Invert the normal if we are viewing it from the back.
        # This is determined by the angle between the current
        # 3D view and the provided normal being below 90 degrees
        vnorm = gui_utils.get3DView().getViewDirection()
        if vnorm.getAngle(normal) < math.pi/2:
            normal = normal.negative()

    new_obj.Normal = normal

    if App.GuiUp:
        gui_utils.format_object(new_obj)
        gui_utils.select(new_obj)

    return new_obj
Exemplo n.º 5
0
def make_bezcurve(pointslist,
                  closed=False,
                  placement=None,
                  face=None,
                  support=None,
                  degree=None):
    """make_bezcurve(pointslist, [closed], [placement])

    Creates a Bezier Curve object from the given list of vectors.

    Parameters
    ----------
    pointlist : [Base.Vector]
        List of points to create the polyline.
        Instead of a pointslist, you can also pass a Part Wire.
        TODO: Change the name so!

    closed : bool
        If closed is True or first and last points are identical,
        the created BSpline will be closed.

    placement : Base.Placement
        If a placement is given, it is used.

    face : Bool
        If face is False, the rectangle is shown as a wireframe,
        otherwise as a face.

    support :
        TODO: Describe

    degree : int
        Degree of the BezCurve
    """
    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return
    if not isinstance(pointslist, list):
        nlist = []
        for v in pointslist.Vertexes:
            nlist.append(v.Point)
        pointslist = nlist
    if placement:
        utils.type_check([(placement, App.Placement)], "make_bezcurve")
    if len(pointslist) == 2: fname = "Line"
    else: fname = "BezCurve"
    obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", fname)
    BezCurve(obj)
    obj.Points = pointslist
    if degree:
        obj.Degree = degree
    else:
        import Part
        obj.Degree = min((len(pointslist) - (1 * (not closed))),
                         Part.BezierCurve().MaxDegree)
    obj.Closed = closed
    obj.Support = support
    if face is not None:
        obj.MakeFace = face
    obj.Proxy.resetcontinuity(obj)
    if placement: obj.Placement = placement
    if App.GuiUp:
        ViewProviderBezCurve(obj.ViewObject)
        #        if not face: obj.ViewObject.DisplayMode = "Wireframe"
        #        obj.ViewObject.DisplayMode = "Wireframe"
        gui_utils.format_object(obj)
        gui_utils.select(obj)

    return obj
Exemplo n.º 6
0
def make_label(target_point=App.Vector(0, 0, 0),
               placement=App.Vector(30, 30, 0),
               target_object=None,
               subelements=None,
               label_type="Custom",
               custom_text="Label",
               direction="Horizontal",
               distance=-10,
               points=None):
    """Create a Label object containing different types of information.

    The current color and text height and font specified in preferences
    are used.

    Parameters
    ----------
    target_point: Base::Vector3, optional
        It defaults to the origin `App.Vector(0, 0, 0)`.
        This is the point which is pointed to by the label's leader line.
        This point can be adorned with a marker like an arrow or circle.

    placement: Base::Placement, Base::Vector3, or Base::Rotation, optional
        It defaults to `App.Vector(30, 30, 0)`.
        If it is provided, it defines the base point of the textual
        label.
        The input could be a full placement, just a vector indicating
        the translation, or just a rotation.

    target_object: Part::Feature or str, optional
        It defaults to `None`.
        If it exists it should be an object which will be used to provide
        information to the label, as long as `label_type` is different
        from `'Custom'`.

        If it is a string, it must be the `Label` of that object.
        Since a `Label` is not guaranteed to be unique in a document,
        it will use the first object found with this `Label`.

    subelements: str, optional
        It defaults to `None`.
        If `subelements` is provided, `target_object` should be provided
        as well, otherwise it is ignored.

        It should be a string indicating a subelement name, either
        `'VertexN'`, `'EdgeN'`, or `'FaceN'` which should exist
        within `target_object`.
        In this case `'N'` is an integer that indicates the specific number
        of vertex, edge, or face in `target_object`.

        Both `target_object` and `subelements` are used to link the label
        to a particular object, or to the particular vertex, edge, or face,
        and get information from them.
        ::
            make_label(..., target_object=App.ActiveDocument.Box)
            make_label(..., target_object="My box", subelements="Face3")

        These two parameters can be can be obtained from the `Gui::Selection`
        module.
        ::
            sel_object = Gui.Selection.getSelectionEx()[0]
            target_object = sel_object.Object
            subelements = sel_object.SubElementNames[0]

    label_type: str, optional
        It defaults to `'Custom'`.
        It can be `'Custom'`, `'Name'`, `'Label'`, `'Position'`,
        `'Length'`, `'Area'`, `'Volume'`, `'Tag'`, or `'Material'`.
        It indicates the type of information that will be shown in the label.

        Only `'Custom'` allows you to manually set the text
        by defining `custom_text`. The other types take their information
        from the object included in `target`.

        - `'Position'` will show the base position of the target object,
          or of the indicated `'VertexN'` in `target`.
        - `'Length'` will show the `Length` of the target object's `Shape`,
          or of the indicated `'EdgeN'` in `target`.
        - `'Area'` will show the `Area` of the target object's `Shape`,
          or of the indicated `'FaceN'` in `target`.

    custom_text: str, optional
        It defaults to `'Label'`.
        It is the text that will be displayed by the label when
        `label_type` is `'Custom'`.

    direction: str, optional
        It defaults to `'Horizontal'`.
        It can be `'Horizontal'`, `'Vertical'`, or `'Custom'`.
        It indicates the direction of the straight segment of the leader line
        that ends up next to the textual label.

        If `'Custom'` is selected, the leader line can be manually drawn
        by specifying the value of `points`.
        Normally, the leader line has only three points, but with `'Custom'`
        you can specify as many points as needed.

    distance: int, float, Base::Quantity, optional
        It defaults to -10.
        It indicates the length of the horizontal or vertical segment
        of the leader line.

        The leader line is composed of two segments, the first segment is
        inclined, while the second segment is either horizontal or vertical
        depending on the value of `direction`.
        ::
            T
            |
            |
            o------- L text

        The `oL` segment's length is defined by `distance`
        while the `oT` segment is automatically calculated depending
        on the values of `placement` (L) and `distance` (o).

        This `distance` is oriented, meaning that if it is positive
        the segment will be to the right and above of the textual
        label, depending on if `direction` is `'Horizontal'` or `'Vertical'`,
        respectively.
        If it is negative, the segment will be to the left
        and below of the text.

    points: list of Base::Vector3, optional
        It defaults to `None`.
        It is a list of vectors defining the shape of the leader line;
        the list must have at least two points.
        This argument must be used together with `direction='Custom'`
        to display this custom leader.

        However, notice that if the Label's `StraightDirection` property
        is later changed to `'Horizontal'` or `'Vertical'`,
        the custom point list will be overwritten with a new,
        automatically calculated three-point list.

        For the object to use custom points, `StraightDirection`
        must remain `'Custom'`, and then the `Points` property
        can be overwritten by a suitable list of points.

    Returns
    -------
    App::FeaturePython
        A scripted object of type `'Label'`.
        This object does not have a `Shape` attribute, as the text and lines
        are created on screen by Coin (pivy).

    None
        If there is a problem it will return `None`.
    """
    _name = "make_label"
    utils.print_header(_name, "Label")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(translate("draft", "No active document. Aborting."))
        return None

    _msg("target_point: {}".format(target_point))
    if not target_point:
        target_point = App.Vector(0, 0, 0)
    try:
        utils.type_check([(target_point, App.Vector)], name=_name)
    except TypeError:
        _err(translate("draft", "Wrong input: must be a vector."))
        return None

    _msg("placement: {}".format(placement))
    if not placement:
        placement = App.Placement()
    try:
        utils.type_check([(placement,
                           (App.Placement, App.Vector, App.Rotation))],
                         name=_name)
    except TypeError:
        _err(
            translate(
                "draft",
                "Wrong input: must be a placement, a vector, or a rotation."))
        return None

    # Convert the vector or rotation to a full placement
    if isinstance(placement, App.Vector):
        placement = App.Placement(placement, App.Rotation())
    elif isinstance(placement, App.Rotation):
        placement = App.Placement(App.Vector(), placement)

    if isinstance(target_object, str):
        target_object_str = target_object

    if target_object:
        if isinstance(target_object, (list, tuple)):
            _msg("target_object: {}".format(target_object))
            _err(translate("draft", "Wrong input: object must not be a list."))
            return None

        found, target_object = utils.find_object(target_object, doc)
        if not found:
            _msg("target_object: {}".format(target_object_str))
            _err(translate("draft", "Wrong input: object not in document."))
            return None

        _msg("target_object: {}".format(target_object.Label))

    if target_object and subelements:
        _msg("subelements: {}".format(subelements))
        try:
            # Make a list
            if isinstance(subelements, str):
                subelements = [subelements]

            utils.type_check([(subelements, (list, tuple, str))], name=_name)
        except TypeError:
            _err(
                translate(
                    "draft",
                    "Wrong input: must be a list or tuple of strings, or a single string."
                ))
            return None

        # The subelements list is used to build a special list
        # called a LinkSub, which includes the target_object
        # and the subelements.
        # Single: (target_object, "Edge1")
        # Multiple: (target_object, ("Edge1", "Edge2"))
        for sub in subelements:
            _sub = target_object.getSubObject(sub)
            if not _sub:
                _err("subelement: {}".format(sub))
                _err(
                    translate("draft",
                              "Wrong input: subelement not in object."))
                return None

    _msg("label_type: {}".format(label_type))
    if not label_type:
        label_type = "Custom"
    try:
        utils.type_check([(label_type, str)], name=_name)
    except TypeError:
        _err(
            translate(
                "draft",
                "Wrong input: must be a string, 'Custom', 'Name', 'Label', 'Position', 'Length', 'Area', 'Volume', 'Tag', or 'Material'."
            ))
        return None

    if label_type not in ("Custom", "Name", "Label", "Position", "Length",
                          "Area", "Volume", "Tag", "Material"):
        _err(
            translate(
                "draft",
                "Wrong input: must be a string, 'Custom', 'Name', 'Label', 'Position', 'Length', 'Area', 'Volume', 'Tag', or 'Material'."
            ))
        return None

    _msg("custom_text: {}".format(custom_text))
    if not custom_text:
        custom_text = "Label"
    try:
        utils.type_check([(custom_text, str)], name=_name)
    except TypeError:
        _err(translate("draft", "Wrong input: must be a string."))
        return None

    _msg("direction: {}".format(direction))
    if not direction:
        direction = "Horizontal"
    try:
        utils.type_check([(direction, str)], name=_name)
    except TypeError:
        _err(
            translate(
                "draft",
                "Wrong input: must be a string, 'Horizontal', 'Vertical', or 'Custom'."
            ))
        return None

    if direction not in ("Horizontal", "Vertical", "Custom"):
        _err(
            translate(
                "draft",
                "Wrong input: must be a string, 'Horizontal', 'Vertical', or 'Custom'."
            ))
        return None

    _msg("distance: {}".format(distance))
    if not distance:
        distance = 1
    try:
        utils.type_check([(distance, (int, float))], name=_name)
    except TypeError:
        _err(translate("draft", "Wrong input: must be a number."))
        return None

    if points:
        _msg("points: {}".format(points))

        _err_msg = translate(
            "draft", "Wrong input: must be a list of at least two vectors.")
        try:
            utils.type_check([(points, (tuple, list))], name=_name)
        except TypeError:
            _err(_err_msg)
            return None

        if len(points) < 2:
            _err(_err_msg)
            return None

        if not all(isinstance(p, App.Vector) for p in points):
            _err(_err_msg)
            return None

    new_obj = doc.addObject("App::FeaturePython", "dLabel")
    Label(new_obj)

    new_obj.TargetPoint = target_point
    new_obj.Placement = placement
    if target_object:
        if subelements:
            new_obj.Target = [target_object, subelements]
        else:
            new_obj.Target = [target_object, []]

    new_obj.LabelType = label_type
    new_obj.CustomText = custom_text

    new_obj.StraightDirection = direction
    new_obj.StraightDistance = distance
    if points:
        if direction != "Custom":
            _wrn(
                translate("draft",
                          "Direction is not 'Custom'; points won't be used."))
        new_obj.Points = points

    if App.GuiUp:
        ViewProviderLabel(new_obj.ViewObject)
        h = utils.get_param("textheight", 0.20)
        new_obj.ViewObject.TextSize = h

        gui_utils.format_object(new_obj)
        gui_utils.select(new_obj)

    return new_obj
Exemplo n.º 7
0
def make_clone(obj, delta=None, forcedraft=False):
    """clone(obj,[delta,forcedraft])
    
    Makes a clone of the given object(s). 
    The clone is an exact, linked copy of the given object. If the original
    object changes, the final object changes too. 
    
    Parameters
    ----------
    obj : 

    delta : Base.Vector
        Delta Vector to move the clone from the original position. 

    forcedraft : bool
        If forcedraft is True, the resulting object is a Draft clone
        even if the input object is an Arch object.
    
    """

    prefix = utils.get_param("ClonePrefix", "")

    cl = None

    if prefix:
        prefix = prefix.strip() + " "

    if not isinstance(obj, list):
        obj = [obj]

    if (len(obj) == 1) and obj[0].isDerivedFrom("Part::Part2DObject"):
        cl = App.ActiveDocument.addObject("Part::Part2DObjectPython",
                                          "Clone2D")
        cl.Label = prefix + obj[0].Label + " (2D)"

    elif (len(obj) == 1) and (hasattr(obj[0], "CloneOf") or (utils.get_type(
            obj[0]) == "BuildingPart")) and (not forcedraft):
        # arch objects can be clones
        import Arch
        if utils.get_type(obj[0]) == "BuildingPart":
            cl = Arch.makeComponent()
        else:
            try:
                clonefunc = getattr(Arch, "make" + obj[0].Proxy.Type)
            except Exception:
                pass  # not a standard Arch object... Fall back to Draft mode
            else:
                cl = clonefunc()
        if cl:
            base = utils.get_clone_base(obj[0])
            cl.Label = prefix + base.Label
            cl.CloneOf = base
            if hasattr(cl, "Material") and hasattr(obj[0], "Material"):
                cl.Material = obj[0].Material
            if utils.get_type(obj[0]) != "BuildingPart":
                cl.Placement = obj[0].Placement
            try:
                cl.Role = base.Role
                cl.Description = base.Description
                cl.Tag = base.Tag
            except Exception:
                pass
            if App.GuiUp:
                gui_utils.format_object(cl, base)
                cl.ViewObject.DiffuseColor = base.ViewObject.DiffuseColor
                if utils.get_type(obj[0]) in ["Window", "BuildingPart"]:
                    ToDo.delay(Arch.recolorize, cl)
            gui_utils.select(cl)
            return cl
    # fall back to Draft clone mode
    if not cl:
        cl = App.ActiveDocument.addObject("Part::FeaturePython", "Clone")
        cl.addExtension("Part::AttachExtensionPython")
        cl.Label = prefix + obj[0].Label
    Clone(cl)
    if App.GuiUp:
        ViewProviderClone(cl.ViewObject)
    cl.Objects = obj
    if delta:
        cl.Placement.move(delta)
    elif (len(obj) == 1) and hasattr(obj[0], "Placement"):
        cl.Placement = obj[0].Placement
    gui_utils.format_object(cl, obj[0])
    if hasattr(cl, "LongName") and hasattr(obj[0], "LongName"):
        cl.LongName = obj[0].LongName
    if App.GuiUp and (len(obj) > 1):
        cl.ViewObject.Proxy.resetColors(cl.ViewObject)
    gui_utils.select(cl)
    return cl
Exemplo n.º 8
0
def make_sketch(objectslist,
                autoconstraints=False,
                addTo=None,
                delete=False,
                name="Sketch",
                radiusPrecision=-1):
    """makeSketch(objectslist,[autoconstraints],[addTo],[delete],[name],[radiusPrecision])

    Makes a Sketch objectslist with the given Draft objects.

    Parameters
    ----------
    objectlist: can be single or list of objects of Draft type objects,
        Part::Feature, Part.Shape, or mix of them.

    autoconstraints(False): if True, constraints will be automatically added to
        wire nodes, rectangles and circles.

    addTo(None) : if set to an existing sketch, geometry will be added to it
        instead of creating a new one.

    delete(False): if True, the original object will be deleted.
        If set to a string 'all' the object and all its linked object will be
        deleted

    name('Sketch'): the name for the new sketch object

    radiusPrecision(-1): If <0, disable radius constraint. If =0, add indiviaul
        radius constraint. If >0, the radius will be rounded according to this
        precision, and 'Equal' constraint will be added to curve with equal
        radius within precision.
    """

    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return

    import Part
    from Sketcher import Constraint
    import Sketcher

    StartPoint = 1
    EndPoint = 2
    MiddlePoint = 3
    deletable = None

    if not isinstance(objectslist, (list, tuple)):
        objectslist = [objectslist]
    for obj in objectslist:
        if isinstance(obj, Part.Shape):
            shape = obj
        elif not hasattr(obj, 'Shape'):
            App.Console.PrintError(translate("draft", "not shape found"))
            return None
        else:
            shape = obj.Shape
        if not DraftGeomUtils.isPlanar(shape):
            App.Console.PrintError(
                translate("draft", "All Shapes must be co-planar"))
            return None
    if addTo:
        nobj = addTo
    else:
        nobj = App.ActiveDocument.addObject("Sketcher::SketchObject", name)
        deletable = nobj
        if App.GuiUp:
            nobj.ViewObject.Autoconstraints = False

    # Collect constraints and add in one go to improve performance
    constraints = []
    radiuses = {}

    def addRadiusConstraint(edge):
        try:
            if radiusPrecision < 0:
                return
            if radiusPrecision == 0:
                constraints.append(
                    Constraint('Radius', nobj.GeometryCount - 1,
                               edge.Curve.Radius))
                return
            r = round(edge.Curve.Radius, radiusPrecision)
            constraints.append(
                Constraint('Equal', radiuses[r], nobj.GeometryCount - 1))
        except KeyError:
            radiuses[r] = nobj.GeometryCount - 1
            constraints.append(Constraint('Radius', nobj.GeometryCount - 1, r))
        except AttributeError:
            pass

    def convertBezier(edge):
        if DraftGeomUtils.geomType(edge) == "BezierCurve":
            return (edge.Curve.toBSpline(edge.FirstParameter,
                                         edge.LastParameter).toShape())
        else:
            return (edge)

    rotation = None
    for obj in objectslist:
        ok = False
        tp = utils.get_type(obj)
        if tp in ["Circle", "Ellipse"]:
            if obj.Shape.Edges:
                if rotation is None:
                    rotation = obj.Placement.Rotation
                edge = obj.Shape.Edges[0]
                if len(edge.Vertexes) == 1:
                    newEdge = DraftGeomUtils.orientEdge(edge)
                    nobj.addGeometry(newEdge)
                else:
                    # make new ArcOfCircle
                    circle = DraftGeomUtils.orientEdge(edge)
                    angle = edge.Placement.Rotation.Angle
                    axis = edge.Placement.Rotation.Axis
                    circle.Center = DraftVecUtils.rotate(
                        edge.Curve.Center, -angle, axis)
                    first = math.radians(obj.FirstAngle)
                    last = math.radians(obj.LastAngle)
                    arc = Part.ArcOfCircle(circle, first, last)
                    nobj.addGeometry(arc)
                addRadiusConstraint(edge)
                ok = True
        elif tp == "Rectangle":
            if rotation is None:
                rotation = obj.Placement.Rotation
            if obj.FilletRadius.Value == 0:
                for edge in obj.Shape.Edges:
                    nobj.addGeometry(DraftGeomUtils.orientEdge(edge))
                if autoconstraints:
                    last = nobj.GeometryCount - 1
                    segs = [last - 3, last - 2, last - 1, last]
                    if obj.Placement.Rotation.Q == (0, 0, 0, 1):
                        constraints.append(
                            Constraint("Coincident", last - 3, EndPoint,
                                       last - 2, StartPoint))
                        constraints.append(
                            Constraint("Coincident", last - 2, EndPoint,
                                       last - 1, StartPoint))
                        constraints.append(
                            Constraint("Coincident", last - 1, EndPoint, last,
                                       StartPoint))
                        constraints.append(
                            Constraint("Coincident", last, EndPoint, last - 3,
                                       StartPoint))
                    constraints.append(Constraint("Horizontal", last - 3))
                    constraints.append(Constraint("Vertical", last - 2))
                    constraints.append(Constraint("Horizontal", last - 1))
                    constraints.append(Constraint("Vertical", last))
                ok = True
        elif tp in ["Wire", "Polygon"]:
            if obj.FilletRadius.Value == 0:
                closed = False
                if tp == "Polygon":
                    closed = True
                elif hasattr(obj, "Closed"):
                    closed = obj.Closed

                if obj.Shape.Edges:
                    if (len(obj.Shape.Vertexes) < 3):
                        e = obj.Shape.Edges[0]
                        nobj.addGeometry(
                            Part.LineSegment(e.Curve, e.FirstParameter,
                                             e.LastParameter))
                    else:
                        # Use the first three points to make a working plane. We've already
                        # checked to make sure everything is coplanar
                        plane = Part.Plane(
                            *[i.Point for i in obj.Shape.Vertexes[:3]])
                        normal = plane.Axis
                        if rotation is None:
                            axis = App.Vector(0, 0, 1).cross(normal)
                            angle = DraftVecUtils.angle(
                                normal, App.Vector(0, 0, 1)) * App.Units.Radian
                            rotation = App.Rotation(axis, angle)
                        for edge in obj.Shape.Edges:
                            # edge.rotate(App.Vector(0,0,0), rotAxis, rotAngle)
                            edge = DraftGeomUtils.orientEdge(edge, normal)
                            nobj.addGeometry(edge)
                        if autoconstraints:
                            last = nobj.GeometryCount
                            segs = list(
                                range(last - len(obj.Shape.Edges), last - 1))
                            for seg in segs:
                                constraints.append(
                                    Constraint("Coincident", seg, EndPoint,
                                               seg + 1, StartPoint))
                                if DraftGeomUtils.isAligned(
                                        nobj.Geometry[seg], "x"):
                                    constraints.append(
                                        Constraint("Vertical", seg))
                                elif DraftGeomUtils.isAligned(
                                        nobj.Geometry[seg], "y"):
                                    constraints.append(
                                        Constraint("Horizontal", seg))
                            if closed:
                                constraints.append(
                                    Constraint("Coincident", last - 1,
                                               EndPoint, segs[0], StartPoint))
                    ok = True
        elif tp == "BSpline":
            if obj.Shape.Edges:
                nobj.addGeometry(obj.Shape.Edges[0].Curve)
                nobj.exposeInternalGeometry(nobj.GeometryCount - 1)
                ok = True
        elif tp == "BezCurve":
            if obj.Shape.Edges:
                bez = obj.Shape.Edges[0].Curve
                bsp = bez.toBSpline(bez.FirstParameter, bez.LastParameter)
                nobj.addGeometry(bsp)
                nobj.exposeInternalGeometry(nobj.GeometryCount - 1)
                ok = True
        elif tp == 'Shape' or hasattr(obj, 'Shape'):
            shape = obj if tp == 'Shape' else obj.Shape

            if not DraftGeomUtils.isPlanar(shape):
                App.Console.PrintError(
                    translate(
                        "draft",
                        "The given object is not planar and cannot be converted into a sketch."
                    ))
                return None
            if rotation is None:
                #rotation = obj.Placement.Rotation
                norm = DraftGeomUtils.getNormal(shape)
                if norm:
                    rotation = App.Rotation(App.Vector(0, 0, 1), norm)
                else:
                    App.Console.PrintWarning(
                        translate(
                            "draft",
                            "Unable to guess the normal direction of this object"
                        ))
                    rotation = App.Rotation()
                    norm = obj.Placement.Rotation.Axis
            if not shape.Wires:
                for e in shape.Edges:
                    # unconnected edges
                    newedge = convertBezier(e)
                    nobj.addGeometry(
                        DraftGeomUtils.orientEdge(newedge, norm,
                                                  make_arc=True))
                    addRadiusConstraint(newedge)

            # if not addTo:
            # nobj.Placement.Rotation = DraftGeomUtils.calculatePlacement(shape).Rotation

            if autoconstraints:
                for wire in shape.Wires:
                    last_count = nobj.GeometryCount
                    edges = wire.OrderedEdges
                    for edge in edges:
                        newedge = convertBezier(edge)
                        nobj.addGeometry(
                            DraftGeomUtils.orientEdge(newedge,
                                                      norm,
                                                      make_arc=True))
                        addRadiusConstraint(newedge)
                    for i, g in enumerate(nobj.Geometry[last_count:]):
                        if edges[i].Closed:
                            continue
                        seg = last_count + i

                        if DraftGeomUtils.isAligned(g, "x"):
                            constraints.append(Constraint("Vertical", seg))
                        elif DraftGeomUtils.isAligned(g, "y"):
                            constraints.append(Constraint("Horizontal", seg))

                        if seg == nobj.GeometryCount - 1:
                            if not wire.isClosed():
                                break
                            g2 = nobj.Geometry[last_count]
                            seg2 = last_count
                        else:
                            seg2 = seg + 1
                            g2 = nobj.Geometry[seg2]

                        end1 = g.value(g.LastParameter)
                        start2 = g2.value(g2.FirstParameter)
                        if DraftVecUtils.equals(end1, start2):
                            constraints.append(
                                Constraint("Coincident", seg, EndPoint, seg2,
                                           StartPoint))
                            continue
                        end2 = g2.value(g2.LastParameter)
                        start1 = g.value(g.FirstParameter)
                        if DraftVecUtils.equals(end2, start1):
                            constraints.append(
                                Constraint("Coincident", seg, StartPoint, seg2,
                                           EndPoint))
                        elif DraftVecUtils.equals(start1, start2):
                            constraints.append(
                                Constraint("Coincident", seg, StartPoint, seg2,
                                           StartPoint))
                        elif DraftVecUtils.equals(end1, end2):
                            constraints.append(
                                Constraint("Coincident", seg, EndPoint, seg2,
                                           EndPoint))
            else:
                for wire in shape.Wires:
                    for edge in wire.OrderedEdges:
                        newedge = convertBezier(edge)
                        nobj.addGeometry(
                            DraftGeomUtils.orientEdge(newedge,
                                                      norm,
                                                      make_arc=True))
            ok = True
        format_object(nobj, obj)
        if ok and delete and hasattr(obj, 'Shape'):
            doc = obj.Document

            def delObj(obj):
                if obj.InList:
                    App.Console.PrintWarning(
                        translate(
                            "draft", "Cannot delete object {} with dependency".
                            format(obj.Label)) + "\n")
                else:
                    doc.removeObject(obj.Name)

            try:
                if delete == 'all':
                    objs = [obj]
                    while objs:
                        obj = objs[0]
                        objs = objs[1:] + obj.OutList
                        delObj(obj)
                else:
                    delObj(obj)
            except Exception as ex:
                App.Console.PrintWarning(
                    translate(
                        "draft", "Failed to delete object {}: {}".format(
                            obj.Label, ex)) + "\n")
    if rotation:
        nobj.Placement.Rotation = rotation
    else:
        print("-----error!!! rotation is still None...")
    nobj.addConstraint(constraints)

    return nobj
Exemplo n.º 9
0
def scale(objectslist,
          scale=App.Vector(1, 1, 1),
          center=App.Vector(0, 0, 0),
          copy=False):
    """scale(objects, scale, [center], copy)
    
    Scales the objects contained in objects (that can be a list of objects or 
    an object) of the given  around given center. 

    Parameters
    ----------
    objectlist : list

    scale : Base.Vector
        Scale factors defined by a given vector (in X, Y, Z directions).

    objectlist : Base.Vector
        Center of the scale operation.

    copy : bool
        If copy is True, the actual objects are not scaled, but copies
        are created instead. 

    Return
    ----------
    The objects (or their copies) are returned.
    """
    if not isinstance(objectslist, list):
        objectslist = [objectslist]
    newobjlist = []
    for obj in objectslist:
        if copy:
            newobj = make_copy.make_copy(obj)
        else:
            newobj = obj
        if hasattr(obj, 'Shape'):
            scaled_shape = obj.Shape.copy()
            m = App.Matrix()
            m.move(center.negative())
            m.scale(scale.x, scale.y, scale.z)
            m.move(center)
            scaled_shape = scaled_shape.transformGeometry(m)
        if utils.get_type(obj) == "Rectangle":
            p = []
            for v in scaled_shape.Vertexes:
                p.append(v.Point)
            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 < 0: l = -nb.Length
            else: l = nb.Length
            if obj.Height < 0: h = -nh.Length
            else: h = nh.Length
            newobj.Length = l
            newobj.Height = h
            tr = p[0].sub(obj.Shape.Vertexes[0].Point)  # unused?
            newobj.Placement = pl
        elif utils.get_type(obj) == "Wire" or utils.get_type(obj) == "BSpline":
            for index, point in enumerate(newobj.Points):
                scale_vertex(newobj, index, scale, center)
        elif hasattr(obj, 'Shape'):
            newobj.Shape = scaled_shape
        elif hasattr(obj, "Position"):
            d = obj.Position.sub(center)
            newobj.Position = center.add(
                App.Vector(d.x * scale.x, d.y * scale.y, d.z * scale.z))
        elif hasattr(obj, "Placement"):
            d = obj.Placement.Base.sub(center)
            newobj.Placement.Base = center.add(
                App.Vector(d.x * scale.x, d.y * scale.y, d.z * scale.z))
            if hasattr(obj, "Height"):
                obj.setExpression('Height', None)
                obj.Height = obj.Height * scale.y
            if hasattr(obj, "Width"):
                obj.setExpression('Width', None)
                obj.Width = obj.Width * scale.x
            if hasattr(obj, "XSize"):
                obj.setExpression('XSize', None)
                obj.XSize = obj.XSize * scale.x
            if hasattr(obj, "YSize"):
                obj.setExpression('YSize', None)
                obj.YSize = obj.YSize * scale.y
        if obj.ViewObject and hasattr(obj.ViewObject, "FontSize"):
            obj.ViewObject.FontSize = obj.ViewObject.FontSize * scale.y

        if copy:
            gui_utils.format_object(newobj, obj)
        newobjlist.append(newobj)
    if copy and utils.get_param("selectBaseObjects", False):
        gui_utils.select(objectslist)
    else:
        gui_utils.select(newobjlist)
    if len(newobjlist) == 1: return newobjlist[0]
    return newobjlist
Exemplo n.º 10
0
def make_fillet(objs, radius=100, chamfer=False, delete=False):
    """Create a fillet between two lines or Part.Edges.

    Parameters
    ----------
    objs: list
        List of two objects of type wire, or edges.

    radius: float, optional
        It defaults to 100. The curvature of the fillet.

    chamfer: bool, optional
        It defaults to `False`. If it is `True` it no longer produces
        a rounded fillet but a chamfer (straight edge)
        with the value of the `radius`.

    delete: bool, optional
        It defaults to `False`. If it is `True` it will delete
        the pair of objects that are used to create the fillet.
        Otherwise, the original objects will still be there.

    Returns
    -------
    Part::Part2DObjectPython
        The object of Proxy type `'Fillet'`.
        It returns `None` if it fails producing the object.
    """
    _name = "make_fillet"
    utils.print_header(_name, "Fillet")

    if len(objs) != 2:
        _err(translate("draft", "Two elements are needed."))
        return None

    e1, e2 = _extract_edges(objs)

    edges = DraftGeomUtils.fillet([e1, e2], radius, chamfer)
    if len(edges) < 3:
        _err(
            translate("draft", "Radius is too large") +
            ", r={}".format(radius))
        return None

    lengths = [edges[0].Length, edges[1].Length, edges[2].Length]
    _msg(
        translate("draft", "Segment") + " 1, " +
        translate("draft", "length:") + " {}".format(lengths[0]))
    _msg(
        translate("draft", "Segment") + " 2, " +
        translate("draft", "length:") + " {}".format(lengths[1]))
    _msg(
        translate("draft", "Segment") + " 3, " +
        translate("draft", "length:") + " {}".format(lengths[2]))

    try:
        wire = Part.Wire(edges)
    except Part.OCCError:
        return None

    _doc = App.activeDocument()
    obj = _doc.addObject("Part::Part2DObjectPython", "Fillet")
    fillet.Fillet(obj)
    obj.Shape = wire
    obj.Length = wire.Length
    obj.Start = wire.Vertexes[0].Point
    obj.End = wire.Vertexes[-1].Point
    obj.FilletRadius = radius

    if delete:
        _doc.removeObject(objs[0].Name)
        _doc.removeObject(objs[1].Name)
        _msg(translate("draft", "Removed original objects."))

    if App.GuiUp:
        view_fillet.ViewProviderFillet(obj.ViewObject)
        gui_utils.format_object(obj)
        gui_utils.select(obj)
        gui_utils.autogroup(obj)

    return obj
Exemplo n.º 11
0
def mirror(objlist, p1, p2):
    """Create a mirror object from the provided list and line.

    It creates a `Part::Mirroring` object from the given `objlist` using
    a plane that is defined by the two given points `p1` and `p2`,
    and either

    - the Draft working plane normal, or
    - the negative normal provided by the camera direction
      if the working plane normal does not exist and the graphical interface
      is available.

    If neither of these two is available, it uses as normal the +Z vector.

    Parameters
    ----------
    objlist: single object or a list of objects
        A single object or a list of objects.

    p1: Base::Vector3
        Point 1 of the mirror plane. It is also used as the `Placement.Base`
        of the resulting object.

    p2: Base::Vector3
        Point 1 of the mirror plane.

    Returns
    -------
    None
        If the operation fails.

    list
        List of `Part::Mirroring` objects, or a single one
        depending on the input `objlist`.

    To Do
    -----
    Implement a mirror tool specific to the workbench that does not
    just use `Part::Mirroring`. It should create a derived object,
    that is, it should work similar to `Draft.offset`.
    """
    utils.print_header('mirror', "Create mirror")

    if not objlist:
        _err(_tr("No object given"))
        return

    if p1 == p2:
        _err(_tr("The two points are coincident"))
        return

    if not isinstance(objlist, list):
        objlist = [objlist]

    if hasattr(App, "DraftWorkingPlane"):
        norm = App.DraftWorkingPlane.getNormal()
    elif App.GuiUp:
        norm = Gui.ActiveDocument.ActiveView.getViewDirection().negative()
    else:
        norm = App.Vector(0, 0, 1)

    pnorm = p2.sub(p1).cross(norm).normalize()

    result = []

    for obj in objlist:
        mir = App.ActiveDocument.addObject("Part::Mirroring", "mirror")
        mir.Label = obj.Label + _tr(" (mirrored)")
        mir.Source = obj
        mir.Base = p1
        mir.Normal = pnorm
        gui_utils.format_object(mir, obj)
        result.append(mir)

    if len(result) == 1:
        result = result[0]
        gui_utils.select(result)

    return result
Exemplo n.º 12
0
def make_circle(radius,
                placement=None,
                face=None,
                startangle=None,
                endangle=None,
                support=None):
    """make_circle(radius, [placement, face, startangle, endangle])
    or make_circle(edge,[face]):

    Creates a circle object with given parameters.

    Parameters
    ----------
    radius : the radius of the circle.

    placement :
        If placement is given, it is used.

    face : Bool
        If face is False, the circle is shown as a wireframe,
        otherwise as a face.

    startangle : start angle of the arc (in degrees)

    endangle : end angle of the arc (in degrees)
        if startangle and endangle are equal, a circle is created,
        if they are different an arc is created

    edge : edge.Curve must be a 'Part.Circle'
        the circle is created from the given edge

    support :
        TODO: Describe
    """

    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return

    if placement:
        utils.type_check([(placement, App.Placement)], "make_circle")

    if startangle != endangle:
        _name = "Arc"
    else:
        _name = "Circle"

    obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", _name)

    Circle(obj)

    if face is not None:
        obj.MakeFace = face

    if isinstance(radius, Part.Edge):
        edge = radius
        if DraftGeomUtils.geomType(edge) == "Circle":
            obj.Radius = edge.Curve.Radius
            placement = App.Placement(edge.Placement)
            delta = edge.Curve.Center.sub(placement.Base)
            placement.move(delta)
            # Rotation of the edge
            rotOk = App.Rotation(edge.Curve.XAxis, edge.Curve.YAxis,
                                 edge.Curve.Axis, "ZXY")
            placement.Rotation = rotOk
            if len(edge.Vertexes) > 1:
                v0 = edge.Curve.XAxis
                v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center)
                v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center)
                # Angle between edge.Curve.XAxis and the vector from center to start of arc
                a0 = math.degrees(App.Vector.getAngle(v0, v1))
                # Angle between edge.Curve.XAxis and the vector from center to end of arc
                a1 = math.degrees(App.Vector.getAngle(v0, v2))
                obj.FirstAngle = a0
                obj.LastAngle = a1
    else:
        obj.Radius = radius
        if (startangle is not None) and (endangle is not None):
            if startangle == -0: startangle = 0
            obj.FirstAngle = startangle
            obj.LastAngle = endangle

    obj.Support = support

    if placement:
        obj.Placement = placement

    if App.GuiUp:
        ViewProviderDraft(obj.ViewObject)
        gui_utils.format_object(obj)
        gui_utils.select(obj)

    return obj
Exemplo n.º 13
0
def make_wire(pointslist,
              closed=False,
              placement=None,
              face=None,
              support=None,
              bs2wire=False):
    """make_wire(pointslist, [closed], [placement])

    Creates a Wire object from the given list of vectors.  If face is
    true (and wire is closed), the wire will appear filled. Instead of
    a pointslist, you can also pass a Part Wire.

    Parameters
    ----------
    pointslist : [Base.Vector]
        List of points to create the polyline

    closed : bool
        If closed is True or first and last points are identical,
        the created polyline will be closed.

    placement : Base.Placement
        If a placement is given, it is used.

    face : Bool
        If face is False, the rectangle is shown as a wireframe,
        otherwise as a face.

    support :
        TODO: Describe

    bs2wire : bool
        TODO: Describe
    """
    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return None

    import Part

    if isinstance(pointslist, (list, tuple)):
        for pnt in pointslist:
            if not isinstance(pnt, App.Vector):
                App.Console.PrintError(
                    "Items must be Base.Vector objects, not {}\n".format(
                        type(pnt)))
                return None

    elif isinstance(pointslist, Part.Wire):
        for edge in pointslist.Edges:
            if not DraftGeomUtils.is_straight_line(edge):
                App.Console.PrintError("All edges must be straight lines\n")
                return None
        closed = pointslist.isClosed()
        pointslist = [v.Point for v in pointslist.OrderedVertexes]

    else:
        App.Console.PrintError("Can't make Draft Wire from {}\n".format(
            type(pointslist)))
        return None

    if len(pointslist) == 0:
        App.Console.PrintWarning("Draft Wire created with empty point list\n")

    if placement:
        utils.type_check([(placement, App.Placement)], "make_wire")
        ipl = placement.inverse()
        if not bs2wire:
            pointslist = [ipl.multVec(p) for p in pointslist]

    if len(pointslist) == 2:
        fname = "Line"
    else:
        fname = "Wire"

    obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", fname)
    Wire(obj)
    obj.Points = pointslist
    obj.Closed = closed
    obj.Support = support

    if face is not None:
        obj.MakeFace = face

    if placement:
        obj.Placement = placement

    if App.GuiUp:
        ViewProviderWire(obj.ViewObject)
        gui_utils.format_object(obj)
        gui_utils.select(obj)

    return obj
Exemplo n.º 14
0
def make_array(base_object,
               arg1, arg2, arg3,
               arg4=None, arg5=None, arg6=None,
               use_link=True):
    """Create a Draft Array of the given object.

    Rectangular array
    -----------------
    make_array(object, xvector, yvector, xnum, ynum)
    make_array(object, xvector, yvector, zvector, xnum, ynum, znum)

    xnum of iterations in the x direction
    at xvector distance between iterations, same for y direction
    with yvector and ynum, same for z direction with zvector and znum.

    Polar array
    -----------
    make_array(object, center, totalangle, totalnum) for polar array, or

    center is a vector, totalangle is the angle to cover (in degrees)
    and totalnum is the number of objects, including the original.

    Circular array
    --------------
    make_array(object, rdistance, tdistance, axis, center, ncircles, symmetry)

    In case of a circular array, rdistance is the distance of the
    circles, tdistance is the distance within circles, axis the rotation-axis,
    center the center of rotation, ncircles the number of circles
    and symmetry the number of symmetry-axis of the distribution.

    To Do
    -----
    The `Array` class currently handles three types of arrays,
    orthogonal, polar, and circular. In the future, probably they should be
    split in separate classes so that they are easier to manage.
    """
    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(translate("draft","No active document. Aborting."))
        return None

    if use_link:
        # The Array class must be called in this special way
        # to make it a LinkArray
        new_obj = doc.addObject("Part::FeaturePython", "Array",
                                Array(None), None, True)
    else:
        new_obj = doc.addObject("Part::FeaturePython", "Array")
        Array(new_obj)

    new_obj.Base = base_object
    if arg6:
        if isinstance(arg1, (int, float, App.Units.Quantity)):
            new_obj.ArrayType = "circular"
            new_obj.RadialDistance = arg1
            new_obj.TangentialDistance = arg2
            new_obj.Axis = arg3
            new_obj.Center = arg4
            new_obj.NumberCircles = arg5
            new_obj.Symmetry = arg6
        else:
            new_obj.ArrayType = "ortho"
            new_obj.IntervalX = arg1
            new_obj.IntervalY = arg2
            new_obj.IntervalZ = arg3
            new_obj.NumberX = arg4
            new_obj.NumberY = arg5
            new_obj.NumberZ = arg6
    elif arg4:
        new_obj.ArrayType = "ortho"
        new_obj.IntervalX = arg1
        new_obj.IntervalY = arg2
        new_obj.NumberX = arg3
        new_obj.NumberY = arg4
    else:
        new_obj.ArrayType = "polar"
        new_obj.Center = arg1
        new_obj.Angle = arg2
        new_obj.NumberPolar = arg3

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(new_obj.ViewObject)
        else:
            ViewProviderDraftArray(new_obj.ViewObject)
            gui_utils.format_object(new_obj, new_obj.Base)

            if hasattr(new_obj.Base.ViewObject, "DiffuseColor"):
                if len(new_obj.Base.ViewObject.DiffuseColor) > 1:
                    new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject)

        new_obj.Base.ViewObject.hide()
        gui_utils.select(new_obj)

    return new_obj
Exemplo n.º 15
0
def make_wire(pointslist,
              closed=False,
              placement=None,
              face=None,
              support=None,
              bs2wire=False):
    """makeWire(pointslist,[closed],[placement])
    
    Creates a Wire object from the given list of vectors.  If face is
    true (and wire is closed), the wire will appear filled. Instead of
    a pointslist, you can also pass a Part Wire.

    Parameters
    ----------
    pointlist : [Base.Vector]
        List of points to create the polyline

    closed : bool
        If closed is True or first and last points are identical, 
        the created polyline will be closed.

    placement : Base.Placement
        If a placement is given, it is used.
    
    face : Bool
        If face is False, the rectangle is shown as a wireframe, 
        otherwise as a face.   

    support : 
        TODO: Describe
    
    bs2wire : bool
        TODO: Describe
    """
    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return

    import Part

    if not isinstance(pointslist, list):
        e = pointslist.Wires[0].Edges
        pointslist = Part.Wire(Part.__sortEdges__(e))
        nlist = []
        for v in pointslist.Vertexes:
            nlist.append(v.Point)
        if DraftGeomUtils.isReallyClosed(pointslist):
            closed = True
        pointslist = nlist

    if len(pointslist) == 0:
        print("Invalid input points: ", pointslist)
    #print(pointslist)
    #print(closed)

    if placement:
        utils.type_check([(placement, App.Placement)], "make_wire")
        ipl = placement.inverse()
        if not bs2wire:
            pointslist = [ipl.multVec(p) for p in pointslist]

    if len(pointslist) == 2:
        fname = "Line"
    else:
        fname = "Wire"

    obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", fname)
    Wire(obj)
    obj.Points = pointslist
    obj.Closed = closed
    obj.Support = support

    if face != None:
        obj.MakeFace = face

    if placement:
        obj.Placement = placement

    if App.GuiUp:
        ViewProviderWire(obj.ViewObject)
        gui_utils.format_object(obj)
        gui_utils.select(obj)

    return obj
Exemplo n.º 16
0
def make_dimension(p1, p2, p3=None, p4=None):
    """makeDimension(p1,p2,[p3]) or makeDimension(object,i1,i2,p3)
    or makeDimension(objlist,indices,p3): Creates a Dimension object with
    the dimension line passign through p3.The current line width and color
    will be used. There are multiple  ways to create a dimension, depending on
    the arguments you pass to it:
    - (p1,p2,p3): creates a standard dimension from p1 to p2
    - (object,i1,i2,p3): creates a linked dimension to the given object,
    measuring the distance between its vertices indexed i1 and i2
    - (object,i1,mode,p3): creates a linked dimension
    to the given object, i1 is the index of the (curved) edge to measure,
    and mode is either "radius" or "diameter".
    """
    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return
    obj = App.ActiveDocument.addObject("App::FeaturePython", "Dimension")
    LinearDimension(obj)
    if App.GuiUp:
        ViewProviderLinearDimension(obj.ViewObject)
    if isinstance(p1, App.Vector) and isinstance(p2, App.Vector):
        obj.Start = p1
        obj.End = p2
        if not p3:
            p3 = p2.sub(p1)
            p3.multiply(0.5)
            p3 = p1.add(p3)
    elif isinstance(p2, int) and isinstance(p3, int):
        l = []
        idx = (p2, p3)
        l.append((p1, "Vertex" + str(p2 + 1)))
        l.append((p1, "Vertex" + str(p3 + 1)))
        obj.LinkedGeometry = l
        obj.Support = p1
        p3 = p4
        if not p3:
            v1 = obj.Base.Shape.Vertexes[idx[0]].Point
            v2 = obj.Base.Shape.Vertexes[idx[1]].Point
            p3 = v2.sub(v1)
            p3.multiply(0.5)
            p3 = v1.add(p3)
    elif isinstance(p3, str):
        l = []
        l.append((p1, "Edge" + str(p2 + 1)))
        if p3 == "radius":
            #l.append((p1,"Center"))
            if App.GuiUp:
                obj.ViewObject.Override = "R $dim"
            obj.Diameter = False
        elif p3 == "diameter":
            #l.append((p1,"Diameter"))
            if App.GuiUp:
                obj.ViewObject.Override = "Ø $dim"
            obj.Diameter = True
        obj.LinkedGeometry = l
        obj.Support = p1
        p3 = p4
        if not p3:
            p3 = p1.Shape.Edges[p2].Curve.Center.add(App.Vector(1, 0, 0))
    obj.Dimline = p3
    if hasattr(App, "DraftWorkingPlane"):
        normal = App.DraftWorkingPlane.axis
    else:
        normal = App.Vector(0, 0, 1)
    if App.GuiUp:
        # invert the normal if we are viewing it from the back
        vnorm = gui_utils.get3DView().getViewDirection()
        if vnorm.getAngle(normal) < math.pi / 2:
            normal = normal.negative()
    obj.Normal = normal

    if App.GuiUp:
        gui_utils.format_object(obj)
        gui_utils.select(obj)

    return obj
Exemplo n.º 17
0
def make_point_array(base_object, point_object, extra=None, use_link=True):
    """Make a Draft PointArray object.

    Distribute copies of a `base_object` in the points
    defined by `point_object`.

    Parameters
    ----------
    base_object: Part::Feature or str
        Any of object that has a `Part::TopoShape` that can be duplicated.
        This means most 2D and 3D objects produced with any workbench.
        If it is a string, it must be the `Label` of that object.
        Since a label is not guaranteed to be unique in a document,
        it will use the first object found with this label.

    point_object: Part::Feature or str
        An object that is a type of container for holding points.
        This object must have one of the following properties `Geometry`,
        `Links`, or `Components`, which themselves must contain objects
        with `X`, `Y`, and `Z` properties.

        This object could be:

        - A `Sketcher::SketchObject`, as it has a `Geometry` property.
          The sketch can contain different elements but it must contain
          at least one `Part::GeomPoint`.

        - A `Part::Compound`, as it has a `Links` property. The compound
          can contain different elements but it must contain at least
          one object that has `X`, `Y`, and `Z` properties,
          like a `Draft Point` or a `Part::Vertex`.

        - A `Draft Block`, as it has a `Components` property. This `Block`
          behaves essentially the same as a `Part::Compound`. It must
          contain at least a point or vertex object.

    extra: Base::Placement, Base::Vector3, or Base::Rotation, optional
        It defaults to `None`.
        If it is provided, it is an additional placement that is applied
        to each copy of the array.
        The input could be a full placement, just a vector indicating
        the additional translation, or just a rotation.

    Returns
    -------
    Part::FeaturePython
        A scripted object of type `'PointArray'`.
        Its `Shape` is a compound of the copies of the original object.

    None
        If there is a problem it will return `None`.
    """
    _name = "make_point_array"
    utils.print_header(_name, "Point array")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(_tr("No active document. Aborting."))
        return None

    if isinstance(base_object, str):
        base_object_str = base_object

    found, base_object = utils.find_object(base_object, doc)
    if not found:
        _msg("base_object: {}".format(base_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("base_object: {}".format(base_object.Label))

    if isinstance(point_object, str):
        point_object_str = point_object

    found, point_object = utils.find_object(point_object, doc)
    if not found:
        _msg("point_object: {}".format(point_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("point_object: {}".format(point_object.Label))
    if (not hasattr(point_object, "Geometry")
            and not hasattr(point_object, "Links")
            and not hasattr(point_object, "Components")):
        _err(
            _tr("Wrong input: point object doesn't have "
                "'Geometry', 'Links', or 'Components'."))
        return None

    _msg("extra: {}".format(extra))
    if not extra:
        extra = App.Placement()
    try:
        utils.type_check([(extra, (App.Placement, App.Vector, App.Rotation))],
                         name=_name)
    except TypeError:
        _err(
            _tr("Wrong input: must be a placement, a vector, "
                "or a rotation."))
        return None

    # Convert the vector or rotation to a full placement
    if isinstance(extra, App.Vector):
        extra = App.Placement(extra, App.Rotation())
    elif isinstance(extra, App.Rotation):
        extra = App.Placement(App.Vector(), extra)

    if use_link:
        # The PointArray class must be called in this special way
        # to make it a LinkArray
        new_obj = doc.addObject("Part::FeaturePython", "PointArray",
                                PointArray(None), None, True)
    else:
        new_obj = doc.addObject("Part::FeaturePython", "PointArray")
        PointArray(new_obj)

    new_obj.Base = base_object
    new_obj.PointObject = point_object
    new_obj.ExtraPlacement = extra

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(new_obj.ViewObject)
        else:
            ViewProviderDraftArray(new_obj.ViewObject)
            gui_utils.format_object(new_obj, new_obj.Base)

            if hasattr(new_obj.Base.ViewObject, "DiffuseColor"):
                if len(new_obj.Base.ViewObject.DiffuseColor) > 1:
                    new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject)

        new_obj.Base.ViewObject.hide()
        gui_utils.select(new_obj)

    return new_obj