diff --git a/src/Mod/Draft/draftguitools/gui_patharray.py b/src/Mod/Draft/draftguitools/gui_patharray.py index 858cc98142..119b5ddc94 100644 --- a/src/Mod/Draft/draftguitools/gui_patharray.py +++ b/src/Mod/Draft/draftguitools/gui_patharray.py @@ -112,6 +112,8 @@ class PathArray(gui_base_original.Modifier): tan_vector = App.Vector(1, 0, 0) force_vertical = False vertical_vector = App.Vector(0, 0, 1) + start_offset = 0.0 + end_offset = 0.0 use_link = self.use_link _edge_list_str = list() @@ -134,6 +136,8 @@ class PathArray(gui_base_original.Modifier): _cmd += "tan_vector=" + DraftVecUtils.toString(tan_vector) + ", " _cmd += "force_vertical=" + str(force_vertical) + ", " _cmd += "vertical_vector=" + vertical_vector_str + ", " + _cmd += "start_offset=" + str(start_offset) + ", " + _cmd += "end_offset=" + str(end_offset) + ", " _cmd += "use_link=" + str(use_link) _cmd += ")" diff --git a/src/Mod/Draft/draftmake/make_patharray.py b/src/Mod/Draft/draftmake/make_patharray.py index 962028a161..793d3a6bb0 100644 --- a/src/Mod/Draft/draftmake/make_patharray.py +++ b/src/Mod/Draft/draftmake/make_patharray.py @@ -57,6 +57,7 @@ def make_path_array(base_object, path_object, count=4, tan_vector=App.Vector(1, 0, 0), force_vertical=False, vertical_vector=App.Vector(0, 0, 1), + start_offset=0.0, end_offset=0.0, use_link=True): """Make a Draft PathArray object. @@ -140,6 +141,14 @@ def make_path_array(base_object, path_object, count=4, It will force this vector to be the vertical direction when `force_vertical` is `True`. + start_offset: float, optional + It defaults to 0.0. + It is the length from the start of the path to the first copy. + + end_offset: float, optional + It defaults to 0.0. + It is the length from the end of the path to the last copy. + use_link: bool, optional It defaults to `True`, in which case the copies are `App::Link` elements. Otherwise, the copies are shape copies which makes @@ -266,6 +275,24 @@ def make_path_array(base_object, path_object, count=4, _err(translate("draft","Wrong input: must be a vector.")) return None + _msg("start_offset: {}".format(start_offset)) + try: + utils.type_check([(start_offset, (int, float))], + name=_name) + except TypeError: + _err(translate("draft","Wrong input: must be a number.")) + return None + start_offset = float(start_offset) + + _msg("end_offset: {}".format(end_offset)) + try: + utils.type_check([(end_offset, (int, float))], + name=_name) + except TypeError: + _err(translate("draft","Wrong input: must be a number.")) + return None + end_offset = float(end_offset) + use_link = bool(use_link) _msg("use_link: {}".format(use_link)) @@ -288,6 +315,8 @@ def make_path_array(base_object, path_object, count=4, new_obj.TangentVector = tan_vector new_obj.ForceVertical = force_vertical new_obj.VerticalVector = vertical_vector + new_obj.StartOffset = start_offset + new_obj.EndOffset = end_offset if App.GuiUp: if use_link: diff --git a/src/Mod/Draft/draftobjects/patharray.py b/src/Mod/Draft/draftobjects/patharray.py index 851aa0b43c..d23a75477b 100644 --- a/src/Mod/Draft/draftobjects/patharray.py +++ b/src/Mod/Draft/draftobjects/patharray.py @@ -124,6 +124,14 @@ class PathArray(DraftLink): It defaults to `False`. If it is `True`, and `AlignMode` is `'Original'` or `'Tangent'`, it will use the vector in `VerticalVector` as the `Z` axis. + + StartOffset: float + It defaults to 0.0. + It is the length from the start of the path to the first copy. + + EndOffset: float + It defaults to 0.0. + It is the length from the end of the path to the last copy. """ def __init__(self, obj): @@ -255,6 +263,22 @@ class PathArray(DraftLink): obj.AlignMode = ['Original', 'Frenet', 'Tangent'] obj.AlignMode = 'Original' + if "StartOffset" not in properties: + _tip = QT_TRANSLATE_NOOP("App::Property","Length from the start of the path to the first copy.") + obj.addProperty("App::PropertyLength", + "StartOffset", + "Alignment", + _tip) + obj.StartOffset = 0.0 + + if "EndOffset" not in properties: + _tip = QT_TRANSLATE_NOOP("App::Property","Length from the end of the path to the last copy.") + obj.addProperty("App::PropertyLength", + "EndOffset", + "Alignment", + _tip) + obj.EndOffset = 0.0 + # The Align property must be attached after other align properties # so that onChanged works properly if "Align" not in properties: @@ -304,7 +328,9 @@ class PathArray(DraftLink): obj.ExtraTranslation, obj.Align, obj.AlignMode, obj.ForceVertical, - obj.VerticalVector) + obj.VerticalVector, + obj.StartOffset.Value, + obj.EndOffset.Value) self.buildShape(obj, array_placement, copy_placements) self.props_changed_clear() @@ -410,7 +436,8 @@ _PathArray = PathArray def placements_on_path(shapeRotation, pathwire, count, xlate, align, mode="Original", forceNormal=False, - normalOverride=None): + normalOverride=None, + startOffset=0.0, endOffset=0.0): """Calculate the placements of a shape along a given path. Copies will be distributed evenly. @@ -434,9 +461,22 @@ def placements_on_path(shapeRotation, pathwire, count, xlate, align, cdist += e.Length ends.append(cdist) - step = cdist / (count if DraftGeomUtils.isReallyClosed(pathwire) else count - 1) + if startOffset > (cdist - 1e-6): + _wrn(translate("draft", "Start Offset too large for path length. Using zero instead.")) + start = 0 + else: + start = startOffset + + if endOffset > (cdist - start - 1e-6): + _wrn(translate("draft", "End Offset too large for path length minus Start Offset. Using zero instead.")) + end = 0 + else: + end = endOffset + + cdist = cdist - start - end + step = cdist / (count if (DraftGeomUtils.isReallyClosed(pathwire) and not (start or end)) else count - 1) remains = 0 - travel = 0 + travel = start placements = [] for i in range(0, count):