summaryrefslogtreecommitdiff
path: root/lib/tartan/utils.py
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2024-06-30 22:49:18 +0200
committerGitHub <noreply@github.com>2024-06-30 22:49:18 +0200
commite52886a64a4e76c3fdc49df95c85655da3c4f7f4 (patch)
tree88a4edee630df0947553ced79fb6eb9dc510ccea /lib/tartan/utils.py
parent7c06dee44ac7951a5db406eb829100a5bc3e5887 (diff)
Various fixes (#3028)
* several thread palette extension fixes * fix svg tartan when original shape is invalid * tartan stroke spaces * style * fix tartan color substituion at pattern start * ripple: do not render too small paths * use less space for params warning headline * fix clone shape path * zip export template fix (typo) * add realistic stitch plan output warning (help tab)
Diffstat (limited to 'lib/tartan/utils.py')
-rw-r--r--lib/tartan/utils.py145
1 files changed, 110 insertions, 35 deletions
diff --git a/lib/tartan/utils.py b/lib/tartan/utils.py
index 4f64fc6f..7949505d 100644
--- a/lib/tartan/utils.py
+++ b/lib/tartan/utils.py
@@ -46,63 +46,138 @@ def stripes_to_shapes(
:returns: a dictionary with shapes grouped by color
"""
+ full_sett = _stripes_to_sett(stripes, symmetry, scale, min_stripe_width)
+
minx, miny, maxx, maxy = dimensions
shapes: defaultdict = defaultdict(list)
- original_stripes = stripes
- if len(original_stripes) == 0:
+ if len(full_sett) == 0:
return shapes
left = minx
top = miny
- add_to_stroke = 0
- add_to_fill = 0
i = -1
while True:
i += 1
- stripes = original_stripes
-
- segments = stripes
- if symmetry and i % 2 != 0 and len(stripes) > 1:
- segments = list(reversed(stripes[1:-1]))
- for stripe in segments:
- width = stripe['width'] * PIXELS_PER_MM * (scale / 100)
+ for stripe in full_sett:
+ width = stripe['width']
right = left + width
bottom = top + width
- if ((top > maxy and weft) or (left > maxx and not weft) or
- (add_to_stroke > maxy and weft) or (add_to_stroke > maxx and not weft)):
+ if (top > maxy and weft) or (left > maxx and not weft):
return _merge_polygons(shapes, outline, intersect_outline)
- if stripe['render'] == 0:
- left = right + add_to_stroke
- top = bottom + add_to_stroke
- add_to_stroke = 0
- continue
- elif stripe['render'] == 2:
- add_to_stroke += width
+ if stripe['color'] is None or not stripe['render']:
+ left = right
+ top = bottom
continue
shape_dimensions = [top, bottom, left, right, minx, miny, maxx, maxy]
- if width <= min_stripe_width * PIXELS_PER_MM:
- add_to_fill = add_to_stroke
- shape_dimensions[0] += add_to_stroke
- shape_dimensions[2] += add_to_stroke
+ if stripe['is_stroke']:
linestrings = _get_linestrings(outline, shape_dimensions, rotation, rotation_center, weft)
shapes[stripe['color']].extend(linestrings)
- add_to_stroke += width
- continue
- add_to_stroke = 0
+ else:
+ polygon = _get_polygon(shape_dimensions, rotation, rotation_center, weft)
+ shapes[stripe['color']].append(polygon)
+ left = right
+ top = bottom
+ return shapes
+
+
+def _stripes_to_sett(
+ stripes: List[dict],
+ symmetry: bool,
+ scale: int,
+ min_stripe_width: float,
+) -> List[dict]:
+ """
+ Builds a full sett for easier conversion into elements
+
+ :param stripes: a list of dictionaries with stripe information
+ :param symmetry: reflective sett (True) / repeating sett (False)
+ :param scale: the scale value (percent) for the pattern
+ :param min_stripe_width: min stripe width before it is rendered as running stitch
+ :returns: a list of dictionaries with stripe information (color, width, is_stroke, render)
+ """
- # add the space of the lines to the following object to avoid bad symmetry
- shape_dimensions[1] += add_to_fill
- shape_dimensions[3] += add_to_fill
+ last_fill_color = _get_last_fill_color(stripes, scale, min_stripe_width, symmetry)
+ first_was_stroke = False
+ last_was_stroke = False
+ add_width = 0
+ sett = []
+ for stripe in stripes:
+ width = stripe['width'] * PIXELS_PER_MM * (scale / 100)
+ is_stroke = width <= min_stripe_width * PIXELS_PER_MM
+ render = stripe['render']
+
+ if render == 0:
+ sett.append({'color': None, 'width': width + add_width, 'is_stroke': False, 'render': False})
+ last_fill_color = None
+ add_width = 0
+ last_was_stroke = False
+ continue
+
+ if render == 2:
+ sett.append({'color': last_fill_color, 'width': width + add_width, 'is_stroke': False, 'render': True})
+ add_width = 0
+ last_was_stroke = False
+ continue
+
+ if is_stroke:
+ if len(sett) == 0:
+ first_was_stroke = True
+ width /= 2
+ sett.append({'color': last_fill_color, 'width': width + add_width, 'is_stroke': False, 'render': True})
+ sett.append({'color': stripe['color'], 'width': 0, 'is_stroke': True, 'render': True})
+ add_width = width
+ last_was_stroke = True
+ else:
+ sett.append({'color': stripe['color'], 'width': width + add_width, 'is_stroke': False, 'render': True})
+ last_fill_color = stripe['color']
+ last_was_stroke = False
+
+ if add_width > 0:
+ sett.append({'color': last_fill_color, 'width': add_width, 'is_stroke': False, 'render': True})
+
+ # For symmetric setts we want to mirror the sett and append to receive a full sett
+ # We do not repeat at pivot points, which means we exclude the first and the last list item from the mirror
+ if symmetry:
+ reversed_sett = list(reversed(sett[1:-1]))
+ if first_was_stroke:
+ reversed_sett = reversed_sett[:-1]
+ if last_was_stroke:
+ reversed_sett = reversed_sett[1:]
+ sett.extend(reversed_sett)
+
+ return sett
+
+
+def _get_last_fill_color(stripes: List[dict], scale: int, min_stripe_width: float, symmetry: bool,) -> List[dict]:
+ '''
+ Returns the first fill color of a pattern to substitute spaces if the pattern starts with strokes or
+ stripes with render mode 2
- polygon = _get_polygon(shape_dimensions, rotation, rotation_center, weft)
- shapes[stripe['color']].append(polygon)
- left = right + add_to_fill
- top = bottom + add_to_fill
- add_to_fill = 0
+ :param stripes: a list of dictionaries with stripe information
+ :param scale: the scale value (percent) for the pattern
+ :param min_stripe_width: min stripe width before it is rendered as running stitch
+ :param symmetry: reflective sett (True) / repeating sett (False)
+ :returns: a list with fill colors or a list with one None item if there are no fills
+ '''
+ fill_colors = []
+ for stripe in stripes:
+ if stripe['render'] == 0:
+ fill_colors.append(None)
+ elif stripe['render'] == 2:
+ continue
+ elif stripe['width'] * (scale / 100) > min_stripe_width:
+ fill_colors.append(stripe['color'])
+ if len(fill_colors) == 0:
+ fill_colors = [None]
+
+ if symmetry:
+ return fill_colors[0]
+ else:
+ return fill_colors[-1]
def _merge_polygons(