diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 6b54a6c819..40f971ccfe 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -88,6 +88,7 @@ SET(Draft_make_functions draftmake/make_copy.py draftmake/make_ellipse.py draftmake/make_facebinder.py + draftmake/make_fillet.py draftmake/make_line.py draftmake/make_polygon.py draftmake/make_point.py @@ -114,6 +115,7 @@ SET(Draft_objects draftobjects/orthoarray.py draftobjects/polararray.py draftobjects/draft_annotation.py + draftobjects/fillet.py draftobjects/label.py draftobjects/dimension.py draftobjects/point.py @@ -138,6 +140,7 @@ SET(Draft_view_providers draftviewproviders/view_orthoarray.py draftviewproviders/view_polararray.py draftviewproviders/view_draft_annotation.py + draftviewproviders/view_fillet.py draftviewproviders/view_label.py draftviewproviders/view_dimension.py draftviewproviders/view_point.py diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 7deeec0dc8..d5353eddb1 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -322,6 +322,11 @@ from draftobjects.wpproxy import WorkingPlaneProxy if FreeCAD.GuiUp: from draftviewproviders.view_wpproxy import ViewProviderWorkingPlaneProxy +from draftmake.make_fillet import make_fillet +from draftobjects.fillet import Fillet +if FreeCAD.GuiUp: + from draftviewproviders.view_fillet import ViewProviderFillet + #--------------------------------------------------------------------------- # Draft annotation objects #--------------------------------------------------------------------------- diff --git a/src/Mod/Draft/draftmake/make_fillet.py b/src/Mod/Draft/draftmake/make_fillet.py new file mode 100644 index 0000000000..d946730356 --- /dev/null +++ b/src/Mod/Draft/draftmake/make_fillet.py @@ -0,0 +1,173 @@ +# *************************************************************************** +# * Copyright (c) 2019 Eliud Cabrera Castillo * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""Provides the code to create Fillet objects. + +This creates a `Part::Part2DObjectPython`, and then assigns the Proxy class +`Fillet`, and the `ViewProviderFillet` for the view provider. +""" +## @package make_fillet +# \ingroup DRAFT +# \brief Provides the code to create Fillet objects. + +import lazy_loader.lazy_loader as lz + +import FreeCAD as App +import Draft_rc +import DraftGeomUtils +import draftutils.utils as utils +import draftutils.gui_utils as gui_utils +import draftobjects.fillet as fillet +from draftutils.messages import _msg, _err +from draftutils.translate import _tr + +if App.GuiUp: + import draftviewproviders.view_fillet as view_fillet + +# Delay import of Part module until first use because it is heavy +Part = lz.LazyLoader("Part", globals(), "Part") + +# The module is used to prevent complaints from code checkers (flake8) +True if Draft_rc.__name__ else False + + +def _print_obj_length(obj, edge, num=1): + if hasattr(obj, "Label"): + name = obj.Label + else: + name = num + + _msg("({0}): {1}; {2} {3}".format(num, name, + _tr("length:"), edge.Length)) + + +def _extract_edges(objs): + """Extract the edges from the list of objects, Draft lines or Part.Edges. + + Parameters + ---------- + objs: list of Draft Lines or Part.Edges + The list of edges from which to create the fillet. + """ + o1, o2 = objs + if hasattr(o1, "PropertiesList"): + if "Proxy" in o1.PropertiesList: + if hasattr(o1.Proxy, "Type"): + if o1.Proxy.Type in ("Wire", "Fillet"): + e1 = o1.Shape.Edges[0] + elif "Shape" in o1.PropertiesList: + if o1.Shape.ShapeType in ("Wire", "Edge"): + e1 = o1.Shape + elif hasattr(o1, "ShapeType"): + if o1.ShapeType in "Edge": + e1 = o1 + + _print_obj_length(o1, e1, num=1) + + if hasattr(o2, "PropertiesList"): + if "Proxy" in o2.PropertiesList: + if hasattr(o2.Proxy, "Type"): + if o2.Proxy.Type in ("Wire", "Fillet"): + e2 = o2.Shape.Edges[0] + elif "Shape" in o2.PropertiesList: + if o2.Shape.ShapeType in ("Wire", "Edge"): + e2 = o2.Shape + elif hasattr(o2, "ShapeType"): + if o2.ShapeType in "Edge": + e2 = o2 + + _print_obj_length(o2, e2, num=2) + + return e1, e2 + + +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(_tr("Two elements are needed.")) + return None + + e1, e2 = _extract_edges(objs) + + edges = DraftGeomUtils.fillet([e1, e2], radius, chamfer) + if len(edges) < 3: + _err(_tr("Radius is too large") + ", r={}".format(radius)) + return None + + lengths = [edges[0].Length, edges[1].Length, edges[2].Length] + _msg(_tr("Segment") + " 1, " + _tr("length:") + " {}".format(lengths[0])) + _msg(_tr("Segment") + " 2, " + _tr("length:") + " {}".format(lengths[1])) + _msg(_tr("Segment") + " 3, " + _tr("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(_tr("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 diff --git a/src/Mod/Draft/draftobjects/fillet.py b/src/Mod/Draft/draftobjects/fillet.py new file mode 100644 index 0000000000..1054298f97 --- /dev/null +++ b/src/Mod/Draft/draftobjects/fillet.py @@ -0,0 +1,123 @@ +# *************************************************************************** +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""Provides the object code for the Fillet object.""" +## @package fillet +# \ingroup DRAFT +# \brief Provides the object code for the Fillet object. + +from PySide.QtCore import QT_TRANSLATE_NOOP + +import FreeCAD as App +import draftobjects.base as base +from draftutils.messages import _msg + + +class Fillet(base.DraftObject): + """Proxy class for the Fillet object.""" + + def __init__(self, obj): + super(Fillet, self).__init__(obj, "Fillet") + self._set_properties(obj) + + def _set_properties(self, obj): + """Set the properties of objects if they don't exist.""" + if not hasattr(obj, "Start"): + _tip = "The start point of this line." + obj.addProperty("App::PropertyVectorDistance", + "Start", + "Draft", + QT_TRANSLATE_NOOP("App::Property", _tip)) + obj.Start = App.Vector(0, 0, 0) + + if not hasattr(obj, "End"): + _tip = "The end point of this line." + obj.addProperty("App::PropertyVectorDistance", + "End", + "Draft", + QT_TRANSLATE_NOOP("App::Property", _tip)) + obj.End = App.Vector(0, 0, 0) + + if not hasattr(obj, "Length"): + _tip = "The length of this line." + obj.addProperty("App::PropertyLength", + "Length", + "Draft", + QT_TRANSLATE_NOOP("App::Property", _tip)) + obj.Length = 0 + + if not hasattr(obj, "FilletRadius"): + _tip = "Radius to use to fillet the corner." + obj.addProperty("App::PropertyLength", + "FilletRadius", + "Draft", + QT_TRANSLATE_NOOP("App::Property", _tip)) + obj.FilletRadius = 0 + + # TODO: these two properties should link two straight lines + # or edges so we can use them to build a fillet from them. + # if not hasattr(obj, "Edge1"): + # _tip = "First line used as reference." + # obj.addProperty("App::PropertyLinkGlobal", + # "Edge1", + # "Draft", + # QT_TRANSLATE_NOOP("App::Property", _tip)) + + # if not hasattr(obj, "Edge2"): + # _tip = "Second line used as reference." + # obj.addProperty("App::PropertyLinkGlobal", + # "Edge2", + # "Draft", + # QT_TRANSLATE_NOOP("App::Property", _tip)) + + # Read only, change to 0 to make it editable. + # The Fillet Radius should be made editable + # when we are able to recalculate the arc of the fillet. + obj.setEditorMode("Start", 1) + obj.setEditorMode("End", 1) + obj.setEditorMode("Length", 1) + obj.setEditorMode("FilletRadius", 1) + # obj.setEditorMode("Edge1", 1) + # obj.setEditorMode("Edge2", 1) + + def execute(self, obj): + """Run when the object is created or recomputed.""" + if hasattr(obj, "Length"): + obj.Length = obj.Shape.Length + if hasattr(obj, "Start"): + obj.Start = obj.Shape.Vertexes[0].Point + if hasattr(obj, "End"): + obj.End = obj.Shape.Vertexes[-1].Point + + def _update_radius(self, obj, radius): + if (hasattr(obj, "Line1") and hasattr(obj, "Line2") + and obj.Line1 and obj.Line2): + _msg("Recalculate the radius with objects.") + + _msg("Update radius currently not implemented: r={}".format(radius)) + + def onChanged(self, obj, prop): + """Change the radius of fillet. NOT IMPLEMENTED. + + This should automatically recalculate the new fillet + based on the new value of `FilletRadius`. + """ + if prop in "FilletRadius": + self._update_radius(obj, obj.FilletRadius) diff --git a/src/Mod/Draft/draftviewproviders/view_fillet.py b/src/Mod/Draft/draftviewproviders/view_fillet.py new file mode 100644 index 0000000000..c638e212e8 --- /dev/null +++ b/src/Mod/Draft/draftviewproviders/view_fillet.py @@ -0,0 +1,38 @@ +# *************************************************************************** +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""Provides the view provider code for Fillet objects. + +At the moment this view provider subclasses the Wire view provider, +and behaves the same as it. In the future this could change +if another behavior is desired. +""" +## @package view_fillet +# \ingroup DRAFT +# \brief Provides the view provider code for Fillet objects. + +from draftviewproviders.view_wire import ViewProviderWire + + +class ViewProviderFillet(ViewProviderWire): + """The view provider for the Fillet object.""" + + def __init__(self, vobj): + super(ViewProviderFillet, self).__init__(vobj)