Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[0.20] Draft: allow items to be skipped in draft array #4430

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
129 changes: 107 additions & 22 deletions src/Mod/Draft/draftobjects/array.py
Expand Up @@ -34,6 +34,8 @@
## \addtogroup draftobjects
# @{
import math
import json
import re
from PySide.QtCore import QT_TRANSLATE_NOOP

import FreeCAD as App
Expand Down Expand Up @@ -112,6 +114,15 @@ def set_general_properties(self, obj):
_tip)
obj.Fuse = False

if "Skip" not in properties:
_tip = QT_TRANSLATE_NOOP("App::Property",
"List of items in pattern to skip")
obj.addProperty("App::PropertyString",
"Skip",
"Objects",
_tip)
obj.Skip = "[]"

def set_ortho_properties(self, obj):
"""Set orthogonal properties only if they don't exist."""
properties = obj.PropertiesList
Expand Down Expand Up @@ -399,25 +410,29 @@ def execute(self, obj):
"reference.")
raise TypeError(_info)

skip_list = parse_skip_list(obj.Skip)

if obj.ArrayType == "ortho":
pls = rect_placements(obj.Base.Placement,
obj.IntervalX,
obj.IntervalY,
obj.IntervalZ,
obj.NumberX,
obj.NumberY,
obj.NumberZ)
obj.NumberZ,
skip_list)
elif obj.ArrayType == "polar":
av = obj.IntervalAxis if hasattr(obj, "IntervalAxis") else None
pls = polar_placements(obj.Base.Placement,
center, obj.Angle.Value,
obj.NumberPolar, axis, av)
obj.NumberPolar, axis, av, skip_list)
elif obj.ArrayType == "circular":
pls = circ_placements(obj.Base.Placement,
obj.RadialDistance,
obj.TangentialDistance,
axis, center,
obj.NumberCircles, obj.Symmetry)
obj.NumberCircles, obj.Symmetry,
skip_list)

return super(Array, self).buildShape(obj, pl, pls)

Expand All @@ -428,42 +443,47 @@ def execute(self, obj):

def rect_placements(base_placement,
xvector, yvector, zvector,
xnum, ynum, znum):
xnum, ynum, znum, skip_list):
"""Determine the placements where the rectangular copies will be."""
pl = base_placement
placements = [pl.copy()]

count = 0
for xcount in range(xnum):
currentxvector = App.Vector(xvector).multiply(xcount)
if xcount != 0:
npl = pl.copy()
npl.translate(currentxvector)
placements.append(npl)
if count not in skip_list:
npl = pl.copy()
npl.translate(currentxvector)
placements.append(npl)

for ycount in range(ynum):
currentyvector = App.Vector(currentxvector)
_y_shift = App.Vector(yvector).multiply(ycount)
currentyvector = currentyvector.add(_y_shift)
if ycount != 0:
npl = pl.copy()
npl.translate(currentyvector)
placements.append(npl)
if count not in skip_list:
npl = pl.copy()
npl.translate(currentyvector)
placements.append(npl)

for zcount in range(znum):
currentzvector = App.Vector(currentyvector)
_z_shift = App.Vector(zvector).multiply(zcount)
currentzvector = currentzvector.add(_z_shift)
if zcount != 0:
npl = pl.copy()
npl.translate(currentzvector)
placements.append(npl)
if count not in skip_list:
npl = pl.copy()
npl.translate(currentzvector)
placements.append(npl)
print("count: ", count)
count += 1

return placements


def polar_placements(base_placement,
center, angle,
number, axis, axisvector):
number, axis, axisvector, skip_list):
"""Determine the placements where the polar copies will be."""
# print("angle ",angle," num ",num)
placements = [base_placement.copy()]
Expand All @@ -484,6 +504,8 @@ def polar_placements(base_placement,
axis_tuple = DraftVecUtils.tup(axis)

for i in range(number - 1):
if i in skip_list:
continue
currangle = fraction + (i*fraction)
npl = pl.copy()
npl.rotate(center_tuple, axis_tuple, currangle)
Expand All @@ -510,6 +532,7 @@ def circ_placements(base_placement,
direction = axis.cross(App.Vector(lead)).normalize()
placements = [base_placement.copy()]

count = 0
for xcount in range(1, circle_number):
rc = xcount * r_distance
c = 2 * rc * math.pi
Expand All @@ -520,14 +543,76 @@ def circ_placements(base_placement,

angle = 360.0/n
for ycount in range(0, n):
npl = base_placement.copy()
trans = App.Vector(direction).multiply(rc)
npl.translate(trans)
npl.rotate(npl.Rotation.inverted().multVec(center-trans),
axis,
ycount * angle)
placements.append(npl)
if count not in skip_list:
npl = base_placement.copy()
trans = App.Vector(direction).multiply(rc)
npl.translate(trans)
npl.rotate(npl.Rotation.inverted().multVec(center-trans),
axis,
ycount * angle)
placements.append(npl)
count += 1

return placements


def parse_skip_list(skip_expression):
"""
Parses a skip expression (as found in obj.Skip) as a list of integers.

The reason for parsing this ourselves instead of using PropertyIntegerList
is so that we can handle failure silently, and support more advanced indexing.

Syntax:
========

Skip expressions are expanded in a python like way

Example 1: skip_expression = 1:5
Will be expanded to [1, 2, 3, 4]

Example 2: skip_expression = [2:5, 9]
Will be expanded to [2, 3, 4, 9]

Example 3: skip_expression = [1, 3:7:2, 9]
Will be expanded to [1, 3, 5, 9]
"""

matches = re.finditer(r"(?P<A>-?\d+):(?P<B>-?\d*)(:(?P<C>-?\d+))*", skip_expression)

skip_list = []

if matches:
for m in matches:
d = m.groupdict()
d = {k:None if d[k] is None else int(d[k]) for k in d}

# some input checking
if d['A'] < 0 or d['B'] < 0 or (d['C'] is not None and d['C'] < 0):
App.Console.PrintError("Draft-Array parse Skip expression: Negative index not supported yet. -> skipping pattern\n")
else:
if d['C']:
skip_list.extend(range(d['A'], d['B'], d['C']))
else:
skip_list.extend(range(d['A'], d['B']))

skip_expression = skip_expression.replace(m.group(), '-0.1') # hack: we use a float as a marker in order have valid json, but not a valid index.

try:
skip_list_raw = json.loads(skip_expression)
except json.decoder.JSONDecodeError:
return skip_list

if not isinstance(skip_list_raw, list):
return skip_list

for s in skip_list_raw:
if isinstance(s, int):
if s < 0:
App.Console.PrintError("Draft-Array-Skip parse Skip expression: Negative index not supported yet. -> skipping pattern")
continue
skip_list.append(s)

return skip_list

## @}