Beispiel #1
0
    def Activated(self):
        """
        Activated is run when user launch Edit command.
        If something is selected -> call self.proceed()
        If nothing is selected -> self.register_selection_callback()
        """
        if self.running:
            self.finish()
        super(Edit, self).Activated("Edit")
        if not App.ActiveDocument:
            self.finish()

        self.ui = Gui.draftToolBar
        self.view = gui_utils.get_3d_view()

        if Gui.Selection.getSelection():
            self.proceed()
        else:
            self.ui.selectUi()
            App.Console.PrintMessage(
                translate("draft", "Select a Draft object to edit") + "\n")
            self.register_selection_callback()
Beispiel #2
0
def get_normal(shape, tol=-1):
    """Find the normal of a shape or list of points, if possible."""

    # for points
    if isinstance(shape, (list, tuple)):
        if len(shape) <= 2:
            return None
        else:
            poly = Part.makePolygon(shape)
            if is_straight_line(poly, tol):
                return None

            plane = poly.findPlane(tol)
            if plane:
                normal = plane.Axis
                return normal
            else:
                return None

    # for shapes
    if shape.isNull():
        return None

    if is_straight_line(shape, tol):
        return None
    else:
        plane = find_plane(shape, tol)
        if plane:
            normal = plane.Axis
        else:
            return None

    # Check the 3D view to flip the normal if the GUI is available
    if App.GuiUp:
        v_dir = gui_utils.get_3d_view().getViewDirection()
        if normal.getAngle(v_dir) < 0.78:
            normal = normal.negative()

    return normal
Beispiel #3
0
def select_object(arg):
    """Handle the selection of objects depending on buttons pressed.

    This is a scene event handler, to be called from the Draft tools
    when they need to select an object.
    ::
        self.call = self.view.addEventCallback("SoEvent", select_object)

    Parameters
    ----------
    arg: Coin event
        The Coin event received from the 3D view.

        If it is of type Keyboard and the `ESCAPE` key, it runs the `finish`
        method of the active command.

        If it is of type Mouse button and `BUTTON1` press,
        it captures the position of the cursor (x, y)
        and the object below that cursor to add it to the active selection;
        then it runs the `proceed` method of the active command
        to continue with the command's logic.
    """
    if arg["Type"] == "SoKeyboardEvent":
        if arg["Key"] == "ESCAPE":
            App.activeDraftCommand.finish()
            # TODO: this part raises a coin3D warning about scene traversal.
            # It needs to be fixed.
    elif arg["Type"] == "SoMouseButtonEvent":
        if arg["State"] == "DOWN" and arg["Button"] == "BUTTON1":
            cursor = arg["Position"]
            snapped = gui_utils.get_3d_view().getObjectInfo(
                (cursor[0], cursor[1]))
            if snapped:
                obj = App.ActiveDocument.getObject(snapped['Object'])
                Gui.Selection.addSelection(obj)
                App.activeDraftCommand.component = snapped['Component']
                App.activeDraftCommand.proceed()
Beispiel #4
0
    def Activated(self, name="None", noplanesetup=False, is_subtool=False):
        """Execute when the command is called.

        If an active Gui Command exists, it will call the `finish` method
        of it to terminate it.

        If no active Gui Command exists, it will proceed with configuration
        of the tool. The child class that subclasses this class
        then should continue with its own Activated method.

        Parameters
        ----------
        name: str, optional
            It defaults to `'None'`.
            It is the `featureName` of the object, to know what is being run.

        noplanesetup: bool, optional
            It defaults to `False`.
            If it is `False` it will set up the working plane
            by running `App.DraftWorkingPlane.setup()`.

        is_subtool: bool, optional
            It defaults to `False`.
            This is set to `True` when we want to modify an object
            by using the mechanism of a `subtool`, introduced
            through the `Draft_SubelementHighlight` command.
            That is, first we run `Draft_SubelementHighlight`
            then we can use `Draft_Move` while setting `is_subtool` to `True`.
        """
        if App.activeDraftCommand and not is_subtool:
            App.activeDraftCommand.finish()

        # The Part module is first initialized when using any Gui Command
        # for the first time.
        global Part, DraftGeomUtils
        import Part
        import DraftGeomUtils

        self.ui = None
        self.call = None
        self.support = None
        self.point = None
        self.commitList = []
        self.doc = App.ActiveDocument
        if not self.doc:
            self.finish()
            return

        App.activeDraftCommand = self
        self.view = gui_utils.get_3d_view()
        self.ui = Gui.draftToolBar
        self.featureName = name
        self.ui.sourceCmd = self
        self.ui.show()
        if not noplanesetup:
            App.DraftWorkingPlane.setup()
        self.node = []
        self.pos = []
        self.constrain = None
        self.obj = None
        self.extendedCopy = False
        self.planetrack = None
        if utils.get_param("showPlaneTracker", False):
            self.planetrack = trackers.PlaneTracker()
        if hasattr(Gui, "Snapper"):
            Gui.Snapper.setTrackers()

        _msg("{}".format(16 * "-"))
        _msg("GuiCommand: {}".format(self.featureName))
Beispiel #5
0
def make_sketch(objects_list,
                autoconstraints=False,
                addTo=None,
                delete=False,
                name="Sketch",
                radiusPrecision=-1,
                tol=1e-3):
    """makeSketch(objects_list,[autoconstraints],[addTo],[delete],
                  [name],[radiusPrecision],[tol])

    Makes a Sketch objects_list with the given Draft objects.

    Parameters
    ----------
    objects_list: 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 individual
        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.

    tol(1e-3): Tolerance used to check if the shapes are planar and coplanar.
        Consider change to tol=-1 for a more accurate analysis.
    """

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

    import Part
    from Sketcher import Constraint
    import Sketcher

    start_point = 1
    end_point = 2
    middle_point = 3
    deletable = None

    if App.GuiUp:
        v_dir = gui_utils.get_3d_view().getViewDirection()
    else:
        v_dir = App.Base.Vector(0, 0, -1)

    # lists to accumulate shapes with defined normal and undefined normal
    shape_norm_yes = list()
    shape_norm_no = list()

    if not isinstance(objects_list, (list, tuple)):
        objects_list = [objects_list]

    for obj in objects_list:
        if isinstance(obj, Part.Shape):
            shape = obj
        elif not hasattr(obj, 'Shape'):
            App.Console.PrintError(translate("draft", "No shape found") + "\n")
            return None
        else:
            shape = obj.Shape

        if not DraftGeomUtils.is_planar(shape, tol):
            App.Console.PrintError(
                translate("draft", "All Shapes must be planar") + "\n")
            return None

        if DraftGeomUtils.get_normal(shape, tol):
            shape_norm_yes.append(shape)
        else:
            shape_norm_no.append(shape)

    shapes_list = shape_norm_yes + shape_norm_no

    # test if all shapes are coplanar
    if len(shape_norm_yes) >= 1:
        for shape in shapes_list[1:]:
            if not DraftGeomUtils.are_coplanar(shapes_list[0], shape, tol):
                App.Console.PrintError(
                    translate("draft", "All Shapes must be coplanar") + "\n")
                return None
        # define sketch normal
        normal = DraftGeomUtils.get_normal(shapes_list[0], tol)

    else:
        # suppose all geometries are straight lines or points
        points = [
            vertex.Point for shape in shapes_list for vertex in shape.Vertexes
        ]
        if len(points) >= 2:
            poly = Part.makePolygon(points)
            if not DraftGeomUtils.is_planar(poly, tol):
                App.Console.PrintError(
                    translate("draft", "All Shapes must be coplanar") + "\n")
                return None
            normal = DraftGeomUtils.get_normal(poly, tol)
            if not normal:
                # all points aligned
                poly_dir = poly.Edges[0].Curve.Direction
                normal = (v_dir - v_dir.dot(poly_dir) * poly_dir).normalize()
                normal = normal.negative()
        else:
            # only one point
            normal = v_dir.negative()

    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)

    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 obj in objects_list:
        ok = False
        tp = utils.get_type(obj)
        if tp in ["Circle", "Ellipse"]:
            if obj.Shape.Edges:
                edge = obj.Shape.Edges[0]
                if len(edge.Vertexes) == 1:
                    newedge = DraftGeomUtils.orientEdge(edge, normal)
                    nobj.addGeometry(newedge)
                else:
                    # make new ArcOfCircle
                    circle = DraftGeomUtils.orientEdge(edge, normal)
                    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 obj.FilletRadius.Value == 0:
                for edge in obj.Shape.Edges:
                    nobj.addGeometry(DraftGeomUtils.orientEdge(edge, normal))
                # TODO: the previous implementation for autoconstraints fails in front
                # and side view. So the autoconstraints for wires is used. This need
                # more checking
                if autoconstraints:
                    last = nobj.GeometryCount
                    segs = list(range(last - len(obj.Shape.Edges), last - 1))
                    for seg in segs:
                        constraints.append(
                            Constraint("Coincident", seg, end_point, seg + 1,
                                       start_point))
                        if DraftGeomUtils.isAligned(nobj.Geometry[seg], "x"):
                            constraints.append(Constraint("Vertical", seg))
                        elif DraftGeomUtils.isAligned(nobj.Geometry[seg], "y"):
                            constraints.append(Constraint("Horizontal", seg))
                    constraints.append(
                        Constraint("Coincident", last - 1, end_point, segs[0],
                                   start_point))
                ok = True
                # 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,end_point,last-2,start_point))
                #         constraints.append(Constraint("Coincident",last-2,end_point,last-1,start_point))
                #         constraints.append(Constraint("Coincident",last-1,end_point,last,start_point))
                #         constraints.append(Constraint("Coincident",last,end_point,last-3,start_point))
                #     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:
                    for edge in obj.Shape.Edges:
                        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, end_point,
                                           seg + 1, start_point))
                            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, end_point,
                                           segs[0], start_point))
                    ok = True

        elif tp == "BSpline":
            if obj.Shape.Edges:
                edge = DraftGeomUtils.orientEdge(obj.Shape.Edges[0], normal)
                nobj.addGeometry(edge)
                nobj.exposeInternalGeometry(nobj.GeometryCount - 1)
                ok = True

        elif tp == "BezCurve":
            if obj.Shape.Edges:
                for piece in obj.Shape.Edges:
                    bez = piece.Curve
                    bsp = bez.toBSpline(bez.FirstParameter,
                                        bez.LastParameter).toShape()
                    edge = DraftGeomUtils.orientEdge(bsp.Edges[0], normal)
                    nobj.addGeometry(edge)
                    nobj.exposeInternalGeometry(nobj.GeometryCount - 1)
                ok = True
                # TODO: set coincident constraint for vertexes in multi-edge bezier curve

        elif tp == "Point":
            shape = obj.Shape.copy()
            if angle:
                shape.rotate(App.Base.Vector(0, 0, 0), axis, -1 * angle)
            point = Part.Point(shape.Point)
            nobj.addGeometry(point)
            ok = True

        elif tp == 'Shape' or hasattr(obj, 'Shape'):
            shape = obj if tp == 'Shape' else obj.Shape
            if not shape.Wires:
                for e in shape.Edges:
                    # unconnected edges
                    newedge = convertBezier(e)
                    nobj.addGeometry(
                        DraftGeomUtils.orientEdge(newedge,
                                                  normal,
                                                  make_arc=True))
                    addRadiusConstraint(newedge)

            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,
                                                      normal,
                                                      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, end_point, seg2,
                                           start_point))
                            continue
                        end2 = g2.value(g2.LastParameter)
                        start1 = g.value(g.FirstParameter)
                        if DraftVecUtils.equals(end2, start1):
                            constraints.append(
                                Constraint("Coincident", seg, start_point,
                                           seg2, end_point))
                        elif DraftVecUtils.equals(start1, start2):
                            constraints.append(
                                Constraint("Coincident", seg, start_point,
                                           seg2, start_point))
                        elif DraftVecUtils.equals(end1, end2):
                            constraints.append(
                                Constraint("Coincident", seg, end_point, seg2,
                                           end_point))
            else:
                for wire in shape.Wires:
                    for edge in wire.OrderedEdges:
                        newedge = convertBezier(edge)
                        nobj.addGeometry(
                            DraftGeomUtils.orientEdge(newedge,
                                                      normal,
                                                      make_arc=True))
            ok = True
        gui_utils.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")

    nobj.Placement.Rotation = rotation
    nobj.addConstraint(constraints)

    return nobj