From e477283ffc3b46c3efdc16947d27bab102554421 Mon Sep 17 00:00:00 2001 From: vocx-fc Date: Tue, 25 Aug 2020 14:10:11 -0500 Subject: [PATCH] Draft: new PathTwistedArray object It takes a `Shape` and replicates it around a path, while at the same time adding a rotation to each copy. This can be used to create a twisted "ribcage" from a frame-like object, which can be used in a more complicated `Shape`, for example, a tunnel or bridge object. --- src/Mod/Draft/CMakeLists.txt | 3 + src/Mod/Draft/Draft.py | 3 +- src/Mod/Draft/Resources/Draft.qrc | 2 + .../icons/Draft_PathTwistedArray.svg | 1001 +++++++++++++++ .../icons/Draft_PathTwistedLinkArray.svg | 1074 +++++++++++++++++ src/Mod/Draft/draftgeoutils/geo_arrays.py | 198 +++ src/Mod/Draft/draftguitools/gui_arrays.py | 4 +- .../draftguitools/gui_pathtwistedarray.py | 150 +++ src/Mod/Draft/draftmake/make_patharray.py | 78 ++ src/Mod/Draft/draftobjects/patharray.py | 1 + .../Draft/draftobjects/pathtwistedarray.py | 173 +++ .../Draft/draftviewproviders/view_array.py | 2 + .../draftviewproviders/view_draftlink.py | 5 +- 13 files changed, 2691 insertions(+), 3 deletions(-) create mode 100644 src/Mod/Draft/Resources/icons/Draft_PathTwistedArray.svg create mode 100644 src/Mod/Draft/Resources/icons/Draft_PathTwistedLinkArray.svg create mode 100644 src/Mod/Draft/draftgeoutils/geo_arrays.py create mode 100644 src/Mod/Draft/draftguitools/gui_pathtwistedarray.py create mode 100644 src/Mod/Draft/draftobjects/pathtwistedarray.py diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 24bfbbc108..39d7a09aaa 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -36,6 +36,7 @@ SET (Draft_geoutils draftgeoutils/sort_edges.py draftgeoutils/faces.py draftgeoutils/geometry.py + draftgeoutils/geo_arrays.py draftgeoutils/wires.py draftgeoutils/arcs.py draftgeoutils/fillets.py @@ -154,6 +155,7 @@ SET(Draft_objects draftobjects/layer.py draftobjects/dimension.py draftobjects/patharray.py + draftobjects/pathtwistedarray.py draftobjects/point.py draftobjects/pointarray.py draftobjects/polygon.py @@ -231,6 +233,7 @@ SET(Modifier_tools draftguitools/gui_circulararray.py draftguitools/gui_orthoarray.py draftguitools/gui_patharray.py + draftguitools/gui_pathtwistedarray.py draftguitools/gui_pointarray.py draftguitools/gui_polararray.py draftguitools/gui_clone.py diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 6a2f3fd3b6..0d20c307b3 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -312,7 +312,8 @@ from draftmake.make_circulararray import make_circular_array from draftobjects.patharray import (PathArray, _PathArray) from draftmake.make_patharray import (make_path_array, - makePathArray) + makePathArray, + make_path_twisted_array) from draftobjects.pointarray import (PointArray, _PointArray) diff --git a/src/Mod/Draft/Resources/Draft.qrc b/src/Mod/Draft/Resources/Draft.qrc index 52aaedeff9..1e4727904a 100644 --- a/src/Mod/Draft/Resources/Draft.qrc +++ b/src/Mod/Draft/Resources/Draft.qrc @@ -59,6 +59,8 @@ icons/Draft_Offset.svg icons/Draft_PathArray.svg icons/Draft_PathLinkArray.svg + icons/Draft_PathTwistedArray.svg + icons/Draft_PathTwistedLinkArray.svg icons/Draft_PlaneProxy.svg icons/Draft_Point.svg icons/Draft_PointArray.svg diff --git a/src/Mod/Draft/Resources/icons/Draft_PathTwistedArray.svg b/src/Mod/Draft/Resources/icons/Draft_PathTwistedArray.svg new file mode 100644 index 0000000000..4591f8759f --- /dev/null +++ b/src/Mod/Draft/Resources/icons/Draft_PathTwistedArray.svg @@ -0,0 +1,1001 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Tue Sept 1 00:00:00 2020 -0500 + + + [vocx] + + + + + FreeCAD LGPL2+ + + + + + FreeCAD + + + FreeCAD/src/Mod/Draft/Resources/icons/Draft_PathTwistedArray.svg + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + [agryson] Alexander Gryson + + + + + rectangle + line + path + sequence + repetition + + + Three rectangles arranged following a path, one to the left, one in the middle, and one near the end. The path curves in an approximate spiral pattern. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Draft/Resources/icons/Draft_PathTwistedLinkArray.svg b/src/Mod/Draft/Resources/icons/Draft_PathTwistedLinkArray.svg new file mode 100644 index 0000000000..ce7bfe987e --- /dev/null +++ b/src/Mod/Draft/Resources/icons/Draft_PathTwistedLinkArray.svg @@ -0,0 +1,1074 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Tue Sept 1 00:00:00 2020 -0500 + + + [vocx] + + + + + FreeCAD LGPL2+ + + + + + FreeCAD + + + FreeCAD/src/Mod/Draft/Resources/icons/Draft_PathTwistedLinkArray.svg + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + [agryson] Alexander Gryson + + + + + rectangle + line + path + sequence + repetition + + + Three rectangles arranged following a path, one to the left, one in the middle, and one near the end. The path curves in an approximate spiral pattern. A green arrow indicates the Link variant. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Draft/draftgeoutils/geo_arrays.py b/src/Mod/Draft/draftgeoutils/geo_arrays.py new file mode 100644 index 0000000000..61d1e9647a --- /dev/null +++ b/src/Mod/Draft/draftgeoutils/geo_arrays.py @@ -0,0 +1,198 @@ +# *************************************************************************** +# * Copyright (c) 2020 three_d * +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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 various functions to work with arrays. + +One of the functions is used to create a `twisted array` object. +See `draftobjects/twistedarray.py`. + +This array was developed in order to build a `twisted bridge` object. + +See https://forum.freecadweb.org/viewtopic.php?f=23&t=49617 +""" +## @package geo_arrays +# \ingroup draftgeoutils +# \brief Provides various functions to work with arrays. + +import lazy_loader.lazy_loader as lz + +import FreeCAD as App +from draftutils.messages import _msg + +# Delay import of module until first use because it is heavy +Part = lz.LazyLoader("Part", globals(), "Part") + +## \addtogroup draftgeoutils +# @{ + + +def print_places(places, title="Places"): + """Print a vector with a title.""" + _msg(12*"-") + _msg(title) + for i in places: + _msg("{}".format(i)) + + +def get_init_values(path, count=6): + """Set values needed to create the array.""" + norm = App.Vector(0, 0, 1) + + # Currently this works with a sketch that has a single edge. + # Here we need a more general function to extract all edges from a shape, + # so that the array uses all of them. + edge = path.Shape.Edges[0] + edge_length = edge.Length + + step = edge_length / count + inc = 360/count + + return norm, edge, step, inc + + +def get_n_params(edge, number, step, norm): + """Get the parameters needed in each iteration.""" + parameter = edge.getParameterByLength(number * step) + + v0 = edge.valueAt(parameter) + tan = edge.tangentAt(parameter).normalize() + binorm = tan.cross(norm).normalize() + rot = App.Rotation(binorm, tan, norm) + + return v0, tan, rot + + +def get_twisted_placements(path, count=15, rot_factor=0.25): + """Get the placements of the twisted array elements.""" + (norm, edge, + step, inc) = get_init_values(path, count) + + increment = 0 + places = [] + params = [] + + for number in range(count + 1): + v0, tan, rot = get_n_params(edge, number, step, norm) + + increment += inc + angle = increment * rot_factor + place = App.Placement(v0, tan, angle) + place.Rotation = place.Rotation * rot + places.append(place) + + params.append((v0, tan, angle, rot)) + + return places, params + + +def get_twisted_array_shape(base, path, count=15, rot_factor=0.25): + """Get the twisted array shape as a compound.""" + places, _ = get_twisted_placements(path, + count=count, + rot_factor=rot_factor) + shapes, _ = create_frames(base, places) + + shape = Part.makeCompound(shapes) + return shape + + +def get_twisted_bridge_shape(base, path, count=15, rot_factor=0.25, + width=100, + thickness=10): + """Get the twisted bridge array shape as a compound.""" + compound = list() + + places, _ = get_twisted_placements(path, + count=count, + rot_factor=rot_factor) + # print_places(places) + + shapes, profiles = create_frames(base, places) + compound.extend(shapes) + + tunnel = make_tunnel(path, profiles) + compound.append(tunnel) + + # size = base.Shape.Wires[-1].BoundBox.XLength * 0.9 + # size = int(size) + # thickness = size/12.0 + + walkway = make_walkway(path, width, thickness) + compound.append(walkway) + shape = Part.makeCompound(compound) + + return shape + + +def create_frames(obj, places): + """Create the frames from the placements.""" + len_wires = len(obj.Shape.Wires) + frames = list() + profiles = list() + # _msg("{}: {} wires".format(obj.Label, len_wires)) + # _msg("places: {}".format(len(places))) + + for i in places: + frame = obj.Shape.copy() + frame.Placement = i + frames.append(frame) + profiles.append(frame.Wires[len_wires - 1]) + + return frames, profiles + + +def make_tunnel(path, profiles): + """Create the tunnel shape.""" + edge = path.Shape.Edges[0] + wire = Part.Wire(edge) + sweep = wire.makePipeShell(profiles) + return sweep + + +def make_walkway(path, width=100, thickness=10): + """Construct the walkway of the twisted bridge array.""" + spine = path.Shape.Edges[0] + + half_size = width/2 + offset_height = thickness + + norm1 = App.Vector(0, 0, 1) + v1 = spine.valueAt(1) + tan1 = spine.tangentAt(1).normalize() + binorm1 = tan1.cross(norm1) + + place = App.Placement() + place.Rotation = App.Rotation(binorm1, norm1, tan1) + place.move(v1 - App.Vector(0, 0, 3 * offset_height)) + + plane = Part.makePlane(width, thickness, + App.Vector(-half_size, -2 * offset_height, 0)) + face = Part.Face(plane) + face.Placement = place.multiply(face.Placement) + + wire = Part.Wire(spine) + sweep = wire.makePipe(face) + + return sweep + +## @} diff --git a/src/Mod/Draft/draftguitools/gui_arrays.py b/src/Mod/Draft/draftguitools/gui_arrays.py index 92964db901..57d772ed0f 100644 --- a/src/Mod/Draft/draftguitools/gui_arrays.py +++ b/src/Mod/Draft/draftguitools/gui_arrays.py @@ -36,6 +36,7 @@ import draftguitools.gui_orthoarray import draftguitools.gui_patharray import draftguitools.gui_pointarray import draftguitools.gui_polararray +import draftguitools.gui_pathtwistedarray # The module is used to prevent complaints from code checkers (flake8) bool(Draft_rc.__name__) @@ -54,7 +55,8 @@ class ArrayGroup: return ("Draft_OrthoArray", "Draft_PolarArray", "Draft_CircularArray", "Draft_PathArray", "Draft_PathLinkArray", - "Draft_PointArray") + "Draft_PointArray", + "Draft_PathTwistedArray", "Draft_PathTwistedLinkArray") def GetResources(self): """Set icon, menu and tooltip.""" diff --git a/src/Mod/Draft/draftguitools/gui_pathtwistedarray.py b/src/Mod/Draft/draftguitools/gui_pathtwistedarray.py new file mode 100644 index 0000000000..0275258648 --- /dev/null +++ b/src/Mod/Draft/draftguitools/gui_pathtwistedarray.py @@ -0,0 +1,150 @@ +# *************************************************************************** +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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 GUI tools to create PathTwistedArray objects. + +The copies will be created along a path, like a polyline, B-spline, +or Bezier curve. +""" +## @package gui_pathtwistedarray +# \ingroup draftguitools +# \brief Provides GUI tools to create PathTwistedArray objects. + +## \addtogroup draftguitools +# @{ +from PySide.QtCore import QT_TRANSLATE_NOOP + +import FreeCADGui as Gui +import Draft_rc +import draftguitools.gui_base_original as gui_base_original + +from draftutils.messages import _err +from draftutils.translate import _tr + +# The module is used to prevent complaints from code checkers (flake8) +True if Draft_rc.__name__ else False + + +class PathTwistedArray(gui_base_original.Modifier): + """Gui Command for the Path twisted array tool. + + Parameters + ---------- + use_link: bool, optional + It defaults to `False`. If it is `True`, the created object + will be a `Link array`. + """ + + def __init__(self, use_link=False): + super(PathTwistedArray, self).__init__() + self.use_link = use_link + self.call = None + + def GetResources(self): + """Set icon, menu and tooltip.""" + _menu = "Path twisted array" + _tip = ("Creates copies of the selected object " + "along a selected path, and twists the copies.\n" + "First select the object, and then select the path.\n" + "The path can be a polyline, B-spline, Bezier curve, " + "or even edges from other objects.") + + return {'Pixmap': 'Draft_PathTwistedArray', + 'MenuText': QT_TRANSLATE_NOOP("Draft_PathTwistedArray", _menu), + 'ToolTip': QT_TRANSLATE_NOOP("Draft_PathTwistedArray", _tip)} + + def Activated(self, name=_tr("Path twisted array")): + """Execute when the command is called.""" + super(PathTwistedArray, self).Activated(name=name) + self.name = name + self.proceed() + + def proceed(self): + """Proceed with the command if one object was selected.""" + if self.call: + self.view.removeEventCallback("SoEvent", self.call) + + sel = Gui.Selection.getSelectionEx() + if len(sel) != 2: + _err(_tr("Please select exactly two objects, " + "the base object and the path object, " + "before calling this command.")) + else: + base_object = sel[0].Object + path_object = sel[1].Object + + count = 15 + rot_factor = 0.25 + use_link = self.use_link + + Gui.addModule("Draft") + _cmd = "Draft.make_path_twisted_array" + _cmd += "(" + _cmd += "App.ActiveDocument." + base_object.Name + ", " + _cmd += "App.ActiveDocument." + path_object.Name + ", " + _cmd += "count=" + str(count) + ", " + _cmd += "rot_factor=" + str(rot_factor) + ", " + _cmd += "use_link=" + str(use_link) + _cmd += ")" + + _cmd_list = ["_obj_ = " + _cmd, + "Draft.autogroup(_obj_)", + "App.ActiveDocument.recompute()"] + self.commit(_tr(self.name), _cmd_list) + + # Commit the transaction and execute the commands + # through the parent class + self.finish() + + +Gui.addCommand('Draft_PathTwistedArray', PathTwistedArray()) + + +class PathTwistedLinkArray(PathTwistedArray): + """Gui Command for the PathTwistedLinkArray tool.""" + + def __init__(self): + super(PathTwistedLinkArray, self).__init__(use_link=True) + + def GetResources(self): + """Set icon, menu and tooltip.""" + _menu = "Path twisted Link array" + _tip = ("Like the PathTwistedArray tool, but creates a 'Link array' " + "instead.\n" + "A 'Link array' is more efficient when handling many copies " + "but the 'Fuse' option cannot be used.") + + return {'Pixmap': 'Draft_PathTwistedLinkArray', + 'MenuText': QT_TRANSLATE_NOOP("Draft_PathTwistedLinkArray", + _menu), + 'ToolTip': QT_TRANSLATE_NOOP("Draft_PathTwistedLinkArray", + _tip)} + + def Activated(self): + """Execute when the command is called.""" + super(PathTwistedLinkArray, + self).Activated(name=_tr("Path twisted link array")) + + +Gui.addCommand('Draft_PathTwistedLinkArray', PathTwistedLinkArray()) + +## @} diff --git a/src/Mod/Draft/draftmake/make_patharray.py b/src/Mod/Draft/draftmake/make_patharray.py index 7f6bc28035..e611a435cc 100644 --- a/src/Mod/Draft/draftmake/make_patharray.py +++ b/src/Mod/Draft/draftmake/make_patharray.py @@ -43,6 +43,7 @@ import draftutils.gui_utils as gui_utils from draftutils.messages import _msg, _err from draftutils.translate import _tr from draftobjects.patharray import PathArray +from draftobjects.pathtwistedarray import PathTwistedArray if App.GuiUp: from draftviewproviders.view_array import ViewProviderDraftArray @@ -318,4 +319,81 @@ def makePathArray(baseobject, pathobject, count, align, use_link) + +def make_path_twisted_array(base_object, path_object, + count=15, rot_factor=0.25, + use_link=True): + """Create a Path twisted array.""" + _name = "make_path_twisted_array" + utils.print_header(_name, "Path twisted 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(path_object, str): + path_object_str = path_object + + found, path_object = utils.find_object(path_object, doc) + if not found: + _msg("path_object: {}".format(path_object_str)) + _err(_tr("Wrong input: object not in document.")) + return None + + _msg("path_object: {}".format(path_object.Label)) + + _msg("count: {}".format(count)) + try: + utils.type_check([(count, (int, float))], + name=_name) + except TypeError: + _err(_tr("Wrong input: must be a number.")) + return None + count = int(count) + + use_link = bool(use_link) + _msg("use_link: {}".format(use_link)) + + if use_link: + # The PathTwistedArray class must be called in this special way + # to make it a PathTwistLinkArray + new_obj = doc.addObject("Part::FeaturePython", "PathTwistedArray", + PathTwistedArray(None), None, True) + else: + new_obj = doc.addObject("Part::FeaturePython", "PathTwistedArray") + PathTwistedArray(new_obj) + + new_obj.Base = base_object + new_obj.PathObject = path_object + new_obj.Count = count + new_obj.RotationFactor = rot_factor + + if App.GuiUp: + if use_link: + ViewProviderDraftLink(new_obj.ViewObject) + else: + ViewProviderDraftArray(new_obj.ViewObject) + gui_utils.formatObject(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 + ## @} diff --git a/src/Mod/Draft/draftobjects/patharray.py b/src/Mod/Draft/draftobjects/patharray.py index c1bcbde9ee..493d72ae93 100644 --- a/src/Mod/Draft/draftobjects/patharray.py +++ b/src/Mod/Draft/draftobjects/patharray.py @@ -70,6 +70,7 @@ import lazy_loader.lazy_loader as lz from draftutils.messages import _msg, _wrn, _err from draftutils.translate import _tr +from draftobjects.base import DraftObject from draftobjects.draftlink import DraftLink # Delay import of module until first use because it is heavy diff --git a/src/Mod/Draft/draftobjects/pathtwistedarray.py b/src/Mod/Draft/draftobjects/pathtwistedarray.py new file mode 100644 index 0000000000..605d3a7962 --- /dev/null +++ b/src/Mod/Draft/draftobjects/pathtwistedarray.py @@ -0,0 +1,173 @@ +# *************************************************************************** +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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 PathTwistedArray object. + +The copies will be placed along a path like a polyline, spline, or bezier +curve, and the copies are twisted around the path by a given rotation +parameter. + +This array was developed in order to build a `twisted bridge` object. + +See https://forum.freecadweb.org/viewtopic.php?f=23&t=49617 + +A `twisted bridge` would consist of three parts: + 1. The ribcage composed of a twisted array generated from a frame + and a path. + 2. The `tunnel` object produced by lofting or sweeping the internal twisted + profiles of the ribcage along the path. + 3. The `walkway` object on which the person can stand; it is generated + from the path, and the internal width of the ribcage profile. + +This module builds only the first element, the twisted ribcage. + +The tunnel and walkway are built with the `twisted bridge` +object in the Arch Workbench. +""" +## @package pathtwistedarray +# \ingroup draftobjects +# \brief Provides the object code for the TwistedArray object. + +import draftgeoutils.geo_arrays as geo +from draftutils.translate import _tr + +from draftobjects.draftlink import DraftLink + +## \addtogroup draftobjects +# @{ + + +class PathTwistedArray(DraftLink): + """The PathTwistedArray object. + + This array distributes copies of an object along a path like a polyline, + spline, or bezier curve, and the copies are twisted around the path + by a given rotation parameter. + """ + + def __init__(self, obj): + super(PathTwistedArray, self).__init__(obj, "PathTwistedArray") + + def attach(self, obj): + """Set up the properties when the object is attached.""" + self.set_properties(obj) + super(PathTwistedArray, self).attach(obj) + + def set_properties(self, obj): + """Set properties only if they don't exist.""" + if hasattr(obj, "PropertiesList"): + properties = obj.PropertiesList + else: + properties = [] + + if "Base" not in properties: + _tip = _tr("The base object that will be duplicated.") + obj.addProperty("App::PropertyLink", + "Base", + "Objects", + _tip) + obj.Base = None + + if "PathObject" not in properties: + _tip = _tr("The object along which " + "the copies will be distributed. " + "It must contain 'Edges'.") + obj.addProperty("App::PropertyLink", + "PathObject", + "Objects", + _tip) + obj.PathObject = None + + if "Count" not in properties: + _tip = _tr("Number of copies to create.") + obj.addProperty("App::PropertyInteger", + "Count", + "Objects", + _tip) + obj.Count = 15 + + if "RotationFactor" not in properties: + _tip = _tr("Rotation factor of the twisted array.") + obj.addProperty("App::PropertyFloat", + "RotationFactor", + "Objects", + _tip) + obj.RotationFactor = 0.25 + + if self.use_link and "ExpandArray" not in properties: + _tip = _tr("Show the individual array elements " + "(only for Link arrays)") + obj.addProperty("App::PropertyBool", + "ExpandArray", + "Objects", + _tip) + obj.ExpandArray = False + obj.setPropertyStatus('Shape', 'Transient') + + def linkSetup(self, obj): + """Set up the object as a link object.""" + super(PathTwistedArray, self).linkSetup(obj) + obj.configLinkProperty(ElementCount='Count') + + def onChanged(self, obj, prop): + """Execute when a property is changed.""" + super(PathTwistedArray, self).onChanged(obj, prop) + + def onDocumentRestored(self, obj): + """Execute code when the document is restored. + + Add properties that don't exist. + """ + self.set_properties(obj) + + if self.use_link: + self.linkSetup(obj) + else: + obj.setPropertyStatus('Shape', '-Transient') + + if obj.Shape.isNull(): + if getattr(obj, 'PlacementList', None): + self.buildShape(obj, obj.Placement, obj.PlacementList) + else: + self.execute(obj) + + def execute(self, obj): + """Execute when the object is created or recomputed.""" + if not obj.Base or not obj.PathObject: + return + + # placement of entire PathArray object + array_placement = obj.Placement + + path = obj.PathObject + count = obj.Count + rot_factor = obj.RotationFactor + + copy_placements, _ = geo.get_twisted_placements(path, + count=count, + rot_factor=rot_factor) + + return super(PathTwistedArray, self).buildShape(obj, + array_placement, + copy_placements) + +## @} diff --git a/src/Mod/Draft/draftviewproviders/view_array.py b/src/Mod/Draft/draftviewproviders/view_array.py index ba43728b32..c930768628 100644 --- a/src/Mod/Draft/draftviewproviders/view_array.py +++ b/src/Mod/Draft/draftviewproviders/view_array.py @@ -46,6 +46,8 @@ class ViewProviderDraftArray(ViewProviderDraft): return ":/icons/Draft_CircularArray.svg" elif hasattr(self.Object, "PointList"): return ":/icons/Draft_PointArray.svg" + elif self.Object.Proxy.Type == "PathTwistedArray": + return ":/icons/Draft_PathTwistedArray.svg" else: return ":/icons/Draft_PathArray.svg" diff --git a/src/Mod/Draft/draftviewproviders/view_draftlink.py b/src/Mod/Draft/draftviewproviders/view_draftlink.py index 7287736bdc..bd163487d6 100644 --- a/src/Mod/Draft/draftviewproviders/view_draftlink.py +++ b/src/Mod/Draft/draftviewproviders/view_draftlink.py @@ -1,7 +1,8 @@ # *************************************************************************** # * Copyright (c) 2009, 2010 Yorik van Havre * # * Copyright (c) 2009, 2010 Ken Cline * -# * Copyright (c) 2020 FreeCAD Developers * +# * Copyright (c) 2019 Zheng, Lei (realthunder)* +# * 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) * @@ -57,6 +58,8 @@ class ViewProviderDraftLink: return ":/icons/Draft_CircularLinkArray.svg" elif tp == 'PathArray': return ":/icons/Draft_PathLinkArray.svg" + elif tp == 'PathTwistedArray': + return ":/icons/Draft_PathTwistedLinkArray.svg" def claimChildren(self): obj = self.Object