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.svgicons/Draft_PathArray.svgicons/Draft_PathLinkArray.svg
+ icons/Draft_PathTwistedArray.svg
+ icons/Draft_PathTwistedLinkArray.svgicons/Draft_PlaneProxy.svgicons/Draft_Point.svgicons/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 @@
+
+
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 @@
+
+
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