kopia lustrzana https://github.com/inkstitch/inkstitch
Merge pull request #2284 from inkstitch/kaalleen/various-fixes
commit
1193b4f206
|
@ -352,14 +352,14 @@ class FillStitch(EmbroideryElement):
|
||||||
'Also used for meander and circular fill.'),
|
'Also used for meander and circular fill.'),
|
||||||
unit='mm',
|
unit='mm',
|
||||||
type='float',
|
type='float',
|
||||||
default=1.5,
|
default=2.5,
|
||||||
select_items=[('fill_method', 'auto_fill'),
|
select_items=[('fill_method', 'auto_fill'),
|
||||||
('fill_method', 'guided_fill'),
|
('fill_method', 'guided_fill'),
|
||||||
('fill_method', 'meander_fill'),
|
('fill_method', 'meander_fill'),
|
||||||
('fill_method', 'circular_fill')],
|
('fill_method', 'circular_fill')],
|
||||||
sort_index=31)
|
sort_index=31)
|
||||||
def running_stitch_length(self):
|
def running_stitch_length(self):
|
||||||
return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
|
return max(self.get_float_param("running_stitch_length_mm", 2.5), 0.01)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@param('running_stitch_tolerance_mm',
|
@param('running_stitch_tolerance_mm',
|
||||||
|
@ -607,13 +607,13 @@ class FillStitch(EmbroideryElement):
|
||||||
def validation_errors(self):
|
def validation_errors(self):
|
||||||
if not self.shape_is_valid(self.shape):
|
if not self.shape_is_valid(self.shape):
|
||||||
why = explain_validity(self.shape)
|
why = explain_validity(self.shape)
|
||||||
message, x, y = re.findall(r".+?(?=\[)|-?\d+(?:\.\d+)?", why)
|
message, x, y = re.match(r"(?P<message>.+)\[(?P<x>.+)\s(?P<y>.+)\]", why).groups()
|
||||||
yield InvalidShapeError((x, y))
|
yield InvalidShapeError((x, y))
|
||||||
|
|
||||||
def validation_warnings(self): # noqa: C901
|
def validation_warnings(self): # noqa: C901
|
||||||
if not self.shape_is_valid(self.original_shape):
|
if not self.shape_is_valid(self.original_shape):
|
||||||
why = explain_validity(self.original_shape)
|
why = explain_validity(self.original_shape)
|
||||||
message, x, y = re.findall(r".+?(?=\[)|-?\d+(?:\.\d+)?", why)
|
message, x, y = re.match(r"(?P<message>.+)\[(?P<x>.+)\s(?P<y>.+)\]", why).groups()
|
||||||
if "Hole lies outside shell" in message:
|
if "Hole lies outside shell" in message:
|
||||||
yield UnconnectedWarning((x, y))
|
yield UnconnectedWarning((x, y))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
from math import pi
|
|
||||||
|
|
||||||
from inkex import DirectedLineSegment, Transform
|
|
||||||
from shapely import geometry as shgeo
|
|
||||||
from shapely.affinity import affine_transform, rotate
|
|
||||||
from shapely.ops import split
|
|
||||||
|
|
||||||
from ..svg import PIXELS_PER_MM, get_correction_transform
|
|
||||||
|
|
||||||
|
|
||||||
def gradient_shapes_and_attributes(element, shape):
|
|
||||||
# e.g. url(#linearGradient872) -> linearGradient872
|
|
||||||
color = element.color[5:-1]
|
|
||||||
xpath = f'.//svg:defs/svg:linearGradient[@id="{color}"]'
|
|
||||||
gradient = element.node.getroottree().getroot().findone(xpath)
|
|
||||||
gradient.apply_transform()
|
|
||||||
point1 = (float(gradient.get('x1')), float(gradient.get('y1')))
|
|
||||||
point2 = (float(gradient.get('x2')), float(gradient.get('y2')))
|
|
||||||
# get 90° angle to calculate the splitting angle
|
|
||||||
line = DirectedLineSegment(point1, point2)
|
|
||||||
angle = line.angle - (pi / 2)
|
|
||||||
# Ink/Stitch somehow turns the stitch angle
|
|
||||||
stitch_angle = angle * -1
|
|
||||||
# create bbox polygon to calculate the length necessary to make sure that
|
|
||||||
# the gradient splitter lines will cut the entire design
|
|
||||||
bbox = element.node.bounding_box()
|
|
||||||
bbox_polygon = shgeo.Polygon([(bbox.left, bbox.top), (bbox.right, bbox.top),
|
|
||||||
(bbox.right, bbox.bottom), (bbox.left, bbox.bottom)])
|
|
||||||
# gradient stops
|
|
||||||
offsets = gradient.stop_offsets
|
|
||||||
stop_styles = gradient.stop_styles
|
|
||||||
# now split the shape according to the gradient stops
|
|
||||||
polygons = []
|
|
||||||
colors = []
|
|
||||||
attributes = []
|
|
||||||
previous_color = None
|
|
||||||
end_row_spacing = None
|
|
||||||
for i, offset in enumerate(offsets):
|
|
||||||
shape_rest = []
|
|
||||||
split_point = shgeo.Point(line.point_at_ratio(float(offset)))
|
|
||||||
length = split_point.hausdorff_distance(bbox_polygon)
|
|
||||||
split_line = shgeo.LineString([(split_point.x - length - 2, split_point.y),
|
|
||||||
(split_point.x + length + 2, split_point.y)])
|
|
||||||
split_line = rotate(split_line, angle, origin=split_point, use_radians=True)
|
|
||||||
transform = -Transform(get_correction_transform(element.node))
|
|
||||||
transform = list(transform.to_hexad())
|
|
||||||
split_line = affine_transform(split_line, transform)
|
|
||||||
offset_line = split_line.parallel_offset(1, 'right')
|
|
||||||
polygon = split(shape, split_line)
|
|
||||||
color = stop_styles[i]['stop-color']
|
|
||||||
# does this gradient line split the shape
|
|
||||||
offset_outside_shape = len(polygon.geoms) == 1
|
|
||||||
for poly in polygon.geoms:
|
|
||||||
if isinstance(poly, shgeo.Polygon) and element.shape_is_valid(poly):
|
|
||||||
if poly.intersects(offset_line):
|
|
||||||
if previous_color:
|
|
||||||
polygons.append(poly)
|
|
||||||
colors.append(previous_color)
|
|
||||||
attributes.append({'angle': stitch_angle, 'end_row_spacing': end_row_spacing, 'color': previous_color})
|
|
||||||
polygons.append(poly)
|
|
||||||
attributes.append({'angle': stitch_angle + pi, 'end_row_spacing': end_row_spacing, 'color': color})
|
|
||||||
else:
|
|
||||||
shape_rest.append(poly)
|
|
||||||
shape = shgeo.MultiPolygon(shape_rest)
|
|
||||||
previous_color = color
|
|
||||||
end_row_spacing = element.row_spacing / PIXELS_PER_MM * 2
|
|
||||||
# add left over shape(s)
|
|
||||||
if shape:
|
|
||||||
if offset_outside_shape:
|
|
||||||
for s in shape.geoms:
|
|
||||||
polygons.append(s)
|
|
||||||
attributes.append({'color': stop_styles[-2]['stop-color'], 'angle': stitch_angle, 'end_row_spacing': end_row_spacing})
|
|
||||||
stitch_angle += pi
|
|
||||||
else:
|
|
||||||
end_row_spacing = None
|
|
||||||
for s in shape.geoms:
|
|
||||||
polygons.append(s)
|
|
||||||
attributes.append({'color': stop_styles[-1]['stop-color'], 'angle': stitch_angle, 'end_row_spacing': end_row_spacing})
|
|
||||||
return polygons, attributes
|
|
|
@ -101,10 +101,10 @@ class Stroke(EmbroideryElement):
|
||||||
unit='mm',
|
unit='mm',
|
||||||
type='float',
|
type='float',
|
||||||
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
|
||||||
default=1.5,
|
default=2.5,
|
||||||
sort_index=4)
|
sort_index=4)
|
||||||
def running_stitch_length(self):
|
def running_stitch_length(self):
|
||||||
return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
|
return max(self.get_float_param("running_stitch_length_mm", 2.5), 0.01)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@param('running_stitch_tolerance_mm',
|
@param('running_stitch_tolerance_mm',
|
||||||
|
@ -261,6 +261,27 @@ class Stroke(EmbroideryElement):
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
return self.get_boolean_param("reverse", False)
|
return self.get_boolean_param("reverse", False)
|
||||||
|
|
||||||
|
_reverse_rails_options = [ParamOption('automatic', _('Automatic')),
|
||||||
|
ParamOption('none', _("Don't reverse")),
|
||||||
|
ParamOption('first', _('Reverse first rail')),
|
||||||
|
ParamOption('second', _('Reverse second rail')),
|
||||||
|
ParamOption('both', _('Reverse both rails'))
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@param(
|
||||||
|
'reverse_rails',
|
||||||
|
_('Reverse rails'),
|
||||||
|
tooltip=_('Reverse satin ripple rails. ' +
|
||||||
|
'Default: automatically detect and fix a reversed rail.'),
|
||||||
|
type='combo',
|
||||||
|
options=_reverse_rails_options,
|
||||||
|
default='automatic',
|
||||||
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
|
sort_index=15)
|
||||||
|
def reverse_rails(self):
|
||||||
|
return self.get_param('reverse_rails', 'automatic')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@param('grid_size_mm',
|
@param('grid_size_mm',
|
||||||
_('Grid size'),
|
_('Grid size'),
|
||||||
|
@ -269,7 +290,7 @@ class Stroke(EmbroideryElement):
|
||||||
default=0,
|
default=0,
|
||||||
unit='mm',
|
unit='mm',
|
||||||
select_items=[('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
sort_index=15)
|
sort_index=16)
|
||||||
@cache
|
@cache
|
||||||
def grid_size(self):
|
def grid_size(self):
|
||||||
return abs(self.get_float_param("grid_size_mm", 0))
|
return abs(self.get_float_param("grid_size_mm", 0))
|
||||||
|
@ -283,7 +304,7 @@ class Stroke(EmbroideryElement):
|
||||||
# 0: xy, 1: x, 2: y, 3: none
|
# 0: xy, 1: x, 2: y, 3: none
|
||||||
options=["X Y", "X", "Y", _("None")],
|
options=["X Y", "X", "Y", _("None")],
|
||||||
select_items=[('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
sort_index=16)
|
sort_index=17)
|
||||||
def scale_axis(self):
|
def scale_axis(self):
|
||||||
return self.get_int_param('scale_axis', 0)
|
return self.get_int_param('scale_axis', 0)
|
||||||
|
|
||||||
|
@ -295,7 +316,7 @@ class Stroke(EmbroideryElement):
|
||||||
unit='%',
|
unit='%',
|
||||||
default=100,
|
default=100,
|
||||||
select_items=[('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
sort_index=17)
|
sort_index=18)
|
||||||
def scale_start(self):
|
def scale_start(self):
|
||||||
return self.get_float_param('scale_start', 100.0)
|
return self.get_float_param('scale_start', 100.0)
|
||||||
|
|
||||||
|
@ -307,7 +328,7 @@ class Stroke(EmbroideryElement):
|
||||||
unit='%',
|
unit='%',
|
||||||
default=0.0,
|
default=0.0,
|
||||||
select_items=[('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
sort_index=18)
|
sort_index=19)
|
||||||
def scale_end(self):
|
def scale_end(self):
|
||||||
return self.get_float_param('scale_end', 0.0)
|
return self.get_float_param('scale_end', 0.0)
|
||||||
|
|
||||||
|
@ -318,7 +339,7 @@ class Stroke(EmbroideryElement):
|
||||||
type='boolean',
|
type='boolean',
|
||||||
default=True,
|
default=True,
|
||||||
select_items=[('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
sort_index=19)
|
sort_index=20)
|
||||||
@cache
|
@cache
|
||||||
def rotate_ripples(self):
|
def rotate_ripples(self):
|
||||||
return self.get_boolean_param("rotate_ripples", True)
|
return self.get_boolean_param("rotate_ripples", True)
|
||||||
|
@ -331,7 +352,7 @@ class Stroke(EmbroideryElement):
|
||||||
default=0,
|
default=0,
|
||||||
options=(_("flat"), _("point")),
|
options=(_("flat"), _("point")),
|
||||||
select_items=[('stroke_method', 'ripple_stitch')],
|
select_items=[('stroke_method', 'ripple_stitch')],
|
||||||
sort_index=20)
|
sort_index=21)
|
||||||
@cache
|
@cache
|
||||||
def join_style(self):
|
def join_style(self):
|
||||||
return self.get_int_param('join_style', 0)
|
return self.get_int_param('join_style', 0)
|
||||||
|
|
|
@ -99,6 +99,7 @@ class ConvertToSatin(InkstitchExtension):
|
||||||
path[0] = start.as_tuple()
|
path[0] = start.as_tuple()
|
||||||
|
|
||||||
def remove_duplicate_points(self, path):
|
def remove_duplicate_points(self, path):
|
||||||
|
path = [[round(coord, 4) for coord in point] for point in path]
|
||||||
return [point for point, repeats in groupby(path)]
|
return [point for point, repeats in groupby(path)]
|
||||||
|
|
||||||
def join_style_args(self, element):
|
def join_style_args(self, element):
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
# Copyright (c) 2010 Authors
|
# Copyright (c) 2010 Authors
|
||||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||||
|
|
||||||
from math import degrees
|
from math import degrees, pi
|
||||||
|
|
||||||
from inkex import DirectedLineSegment, PathElement, errormsg
|
from inkex import DirectedLineSegment, PathElement, Transform, errormsg
|
||||||
|
from shapely import geometry as shgeo
|
||||||
|
from shapely.affinity import affine_transform, rotate
|
||||||
from shapely.geometry import Point
|
from shapely.geometry import Point
|
||||||
from shapely.ops import nearest_points
|
from shapely.ops import nearest_points, split
|
||||||
|
|
||||||
from ..commands import add_commands
|
from ..commands import add_commands
|
||||||
from ..elements import FillStitch
|
from ..elements import FillStitch
|
||||||
from ..elements.gradient_fill import gradient_shapes_and_attributes
|
|
||||||
from ..i18n import _
|
from ..i18n import _
|
||||||
from ..svg import get_correction_transform
|
from ..svg import PIXELS_PER_MM, get_correction_transform
|
||||||
from ..svg.tags import INKSTITCH_ATTRIBS
|
from ..svg.tags import INKSTITCH_ATTRIBS
|
||||||
from .commands import CommandsExtension
|
from .commands import CommandsExtension
|
||||||
from .duplicate_params import get_inkstitch_attributes
|
from .duplicate_params import get_inkstitch_attributes
|
||||||
|
@ -28,6 +29,9 @@ class GradientBlocks(CommandsExtension):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
CommandsExtension.__init__(self, *args, **kwargs)
|
CommandsExtension.__init__(self, *args, **kwargs)
|
||||||
|
self.arg_parser.add_argument("--notebook", type=str, default=0.0)
|
||||||
|
self.arg_parser.add_argument("--options", type=str, default=0.0)
|
||||||
|
self.arg_parser.add_argument("--info", type=str, default=0.0)
|
||||||
self.arg_parser.add_argument("-e", "--end-row-spacing", type=float, default=0.0, dest="end_row_spacing")
|
self.arg_parser.add_argument("-e", "--end-row-spacing", type=float, default=0.0, dest="end_row_spacing")
|
||||||
|
|
||||||
def effect(self):
|
def effect(self):
|
||||||
|
@ -53,35 +57,45 @@ class GradientBlocks(CommandsExtension):
|
||||||
fill_shapes.reverse()
|
fill_shapes.reverse()
|
||||||
attributes.reverse()
|
attributes.reverse()
|
||||||
|
|
||||||
|
if self.options.end_row_spacing != 0:
|
||||||
|
end_row_spacing = self.options.end_row_spacing
|
||||||
|
else:
|
||||||
|
end_row_spacing = element.row_spacing / PIXELS_PER_MM * 2
|
||||||
|
end_row_spacing = f'{end_row_spacing: .2f}'
|
||||||
|
|
||||||
previous_color = None
|
previous_color = None
|
||||||
previous_element = None
|
previous_element = None
|
||||||
for i, shape in enumerate(fill_shapes):
|
for i, shape in enumerate(fill_shapes):
|
||||||
color = attributes[i]['color']
|
color = attributes[i]['color']
|
||||||
style['fill'] = color
|
style['fill'] = color
|
||||||
end_row_spacing = attributes[i]['end_row_spacing'] or None
|
is_gradient = attributes[i]['is_gradient']
|
||||||
angle = degrees(attributes[i]['angle'])
|
angle = degrees(attributes[i]['angle'])
|
||||||
|
angle = f'{angle: .2f}'
|
||||||
d = "M " + " ".join([f'{x}, {y}' for x, y in list(shape.exterior.coords)]) + " Z"
|
d = "M " + " ".join([f'{x}, {y}' for x, y in list(shape.exterior.coords)]) + " Z"
|
||||||
block = PathElement(attrib={
|
block = PathElement(attrib={
|
||||||
"id": self.uniqueId("path"),
|
"id": self.uniqueId("path"),
|
||||||
"style": str(style),
|
"style": str(style),
|
||||||
"transform": correction_transform,
|
"transform": correction_transform,
|
||||||
"d": d,
|
"d": d,
|
||||||
INKSTITCH_ATTRIBS['angle']: f'{angle: .2f}'
|
INKSTITCH_ATTRIBS['angle']: angle
|
||||||
})
|
})
|
||||||
# apply parameters from original element
|
# apply parameters from original element
|
||||||
params = get_inkstitch_attributes(element.node)
|
params = get_inkstitch_attributes(element.node)
|
||||||
for attrib in params:
|
for attrib in params:
|
||||||
block.attrib[attrib] = str(element.node.attrib[attrib])
|
block.attrib[attrib] = str(element.node.attrib[attrib])
|
||||||
# set end_row_spacing
|
|
||||||
if end_row_spacing:
|
|
||||||
if self.options.end_row_spacing != 0:
|
|
||||||
end_row_spacing = self.options.end_row_spacing
|
|
||||||
block.set('inkstitch:end_row_spacing_mm', f'{end_row_spacing: .2f}')
|
|
||||||
else:
|
|
||||||
block.pop('inkstitch:end_row_spacing_mm')
|
|
||||||
# disable underlay and underpath
|
# disable underlay and underpath
|
||||||
block.set('inkstitch:fill_underlay', False)
|
block.set('inkstitch:fill_underlay', False)
|
||||||
block.set('inkstitch:underpath', False)
|
block.set('inkstitch:underpath', False)
|
||||||
|
# set end_row_spacing
|
||||||
|
if is_gradient:
|
||||||
|
block.set('inkstitch:end_row_spacing_mm', end_row_spacing)
|
||||||
|
else:
|
||||||
|
block.pop('inkstitch:end_row_spacing_mm')
|
||||||
|
# use underlay to compensate for higher density in the gradient parts
|
||||||
|
block.set('inkstitch:fill_underlay', True)
|
||||||
|
block.set('inkstitch:fill_underlay_angle', angle)
|
||||||
|
block.set('inkstitch:fill_underlay_row_spacing_mm', end_row_spacing)
|
||||||
|
|
||||||
parent.insert(index, block)
|
parent.insert(index, block)
|
||||||
|
|
||||||
if previous_color == color:
|
if previous_color == color:
|
||||||
|
@ -106,6 +120,77 @@ class GradientBlocks(CommandsExtension):
|
||||||
return Point(pos)
|
return Point(pos)
|
||||||
|
|
||||||
|
|
||||||
|
def gradient_shapes_and_attributes(element, shape):
|
||||||
|
# e.g. url(#linearGradient872) -> linearGradient872
|
||||||
|
color = element.color[5:-1]
|
||||||
|
xpath = f'.//svg:defs/svg:linearGradient[@id="{color}"]'
|
||||||
|
gradient = element.node.getroottree().getroot().findone(xpath)
|
||||||
|
gradient.apply_transform()
|
||||||
|
point1 = (float(gradient.get('x1')), float(gradient.get('y1')))
|
||||||
|
point2 = (float(gradient.get('x2')), float(gradient.get('y2')))
|
||||||
|
# get 90° angle to calculate the splitting angle
|
||||||
|
line = DirectedLineSegment(point1, point2)
|
||||||
|
angle = line.angle - (pi / 2)
|
||||||
|
# Ink/Stitch somehow turns the stitch angle
|
||||||
|
stitch_angle = angle * -1
|
||||||
|
# create bbox polygon to calculate the length necessary to make sure that
|
||||||
|
# the gradient splitter lines will cut the entire design
|
||||||
|
bbox = element.node.bounding_box()
|
||||||
|
bbox_polygon = shgeo.Polygon([(bbox.left, bbox.top), (bbox.right, bbox.top),
|
||||||
|
(bbox.right, bbox.bottom), (bbox.left, bbox.bottom)])
|
||||||
|
# gradient stops
|
||||||
|
offsets = gradient.stop_offsets
|
||||||
|
stop_styles = gradient.stop_styles
|
||||||
|
# now split the shape according to the gradient stops
|
||||||
|
polygons = []
|
||||||
|
colors = []
|
||||||
|
attributes = []
|
||||||
|
previous_color = None
|
||||||
|
is_gradient = False
|
||||||
|
for i, offset in enumerate(offsets):
|
||||||
|
shape_rest = []
|
||||||
|
split_point = shgeo.Point(line.point_at_ratio(float(offset)))
|
||||||
|
length = split_point.hausdorff_distance(bbox_polygon)
|
||||||
|
split_line = shgeo.LineString([(split_point.x - length - 2, split_point.y),
|
||||||
|
(split_point.x + length + 2, split_point.y)])
|
||||||
|
split_line = rotate(split_line, angle, origin=split_point, use_radians=True)
|
||||||
|
transform = -Transform(get_correction_transform(element.node))
|
||||||
|
transform = list(transform.to_hexad())
|
||||||
|
split_line = affine_transform(split_line, transform)
|
||||||
|
offset_line = split_line.parallel_offset(1, 'right')
|
||||||
|
polygon = split(shape, split_line)
|
||||||
|
color = stop_styles[i]['stop-color']
|
||||||
|
# does this gradient line split the shape
|
||||||
|
offset_outside_shape = len(polygon.geoms) == 1
|
||||||
|
for poly in polygon.geoms:
|
||||||
|
if isinstance(poly, shgeo.Polygon) and element.shape_is_valid(poly):
|
||||||
|
if poly.intersects(offset_line):
|
||||||
|
if previous_color:
|
||||||
|
polygons.append(poly)
|
||||||
|
colors.append(previous_color)
|
||||||
|
attributes.append({'color': previous_color, 'angle': stitch_angle, 'is_gradient': is_gradient})
|
||||||
|
polygons.append(poly)
|
||||||
|
attributes.append({'color': color, 'angle': stitch_angle + pi, 'is_gradient': is_gradient})
|
||||||
|
else:
|
||||||
|
shape_rest.append(poly)
|
||||||
|
shape = shgeo.MultiPolygon(shape_rest)
|
||||||
|
previous_color = color
|
||||||
|
is_gradient = True
|
||||||
|
# add left over shape(s)
|
||||||
|
if shape:
|
||||||
|
if offset_outside_shape:
|
||||||
|
for s in shape.geoms:
|
||||||
|
polygons.append(s)
|
||||||
|
attributes.append({'color': stop_styles[-2]['stop-color'], 'angle': stitch_angle, 'is_gradient': is_gradient})
|
||||||
|
stitch_angle += pi
|
||||||
|
else:
|
||||||
|
is_gradient = False
|
||||||
|
for s in shape.geoms:
|
||||||
|
polygons.append(s)
|
||||||
|
attributes.append({'color': stop_styles[-1]['stop-color'], 'angle': stitch_angle, 'is_gradient': is_gradient})
|
||||||
|
return polygons, attributes
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
e = GradientBlocks()
|
e = GradientBlocks()
|
||||||
e.effect()
|
e.effect()
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import inkex
|
import inkex
|
||||||
|
|
||||||
from ..elements import SatinColumn, Stroke
|
from ..elements import EmptyDObject, SatinColumn, Stroke
|
||||||
from ..i18n import _
|
from ..i18n import _
|
||||||
from ..svg.tags import ORIGINAL_D, PATH_EFFECT, SODIPODI_NODETYPES
|
from ..svg.tags import ORIGINAL_D, PATH_EFFECT, SODIPODI_NODETYPES
|
||||||
from .base import InkstitchExtension
|
from .base import InkstitchExtension
|
||||||
|
@ -35,7 +35,10 @@ class StrokeToLpeSatin(InkstitchExtension):
|
||||||
|
|
||||||
if not any((isinstance(item, Stroke) or isinstance(item, SatinColumn)) for item in self.elements):
|
if not any((isinstance(item, Stroke) or isinstance(item, SatinColumn)) for item in self.elements):
|
||||||
# L10N: Convert To Satin extension, user selected one or more objects that were not lines.
|
# L10N: Convert To Satin extension, user selected one or more objects that were not lines.
|
||||||
inkex.errormsg(_("Please select at least one stroke to convert to a satin column."))
|
if any(isinstance(item, EmptyDObject) for item in self.elements):
|
||||||
|
inkex.errormsg(_("This element has lost its path information. Please move the element slightly back and forth before you try again."))
|
||||||
|
else:
|
||||||
|
inkex.errormsg(_("Please select at least one stroke to convert to a satin column."))
|
||||||
return
|
return
|
||||||
|
|
||||||
pattern = self.options.pattern
|
pattern = self.options.pattern
|
||||||
|
|
|
@ -14,8 +14,8 @@ class UpdateSvg(InkstitchExtension):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
InkstitchExtension.__init__(self, *args, **kwargs)
|
InkstitchExtension.__init__(self, *args, **kwargs)
|
||||||
# TODO: When there are more legacy versions than only one, this can be transformed in a user input
|
# TODO: When there are more legacy versions than only one, this can be transformed into a user input
|
||||||
# inkstitch_svg_version history: 1 -> v2.3.0
|
# inkstitch_svg_version history: 1 -> v3.0.0, May 2023
|
||||||
self.update_from = 0
|
self.update_from = 0
|
||||||
|
|
||||||
def effect(self):
|
def effect(self):
|
||||||
|
|
|
@ -182,7 +182,10 @@ class Font(object):
|
||||||
return self.name + '*'
|
return self.name + '*'
|
||||||
|
|
||||||
def is_custom_font(self):
|
def is_custom_font(self):
|
||||||
return get_custom_font_dir() in self.path
|
custom_dir = get_custom_font_dir()
|
||||||
|
if not custom_dir:
|
||||||
|
return False
|
||||||
|
return custom_dir in self.path
|
||||||
|
|
||||||
def render_text(self, text, destination_group, variant=None, back_and_forth=True, trim_option=0, use_trim_symbols=False):
|
def render_text(self, text, destination_group, variant=None, back_and_forth=True, trim_option=0, use_trim_symbols=False):
|
||||||
|
|
||||||
|
|
|
@ -103,9 +103,7 @@ def _get_satin_line_count(stroke, pairs):
|
||||||
if shortest_line_len == 0 or length < shortest_line_len:
|
if shortest_line_len == 0 or length < shortest_line_len:
|
||||||
shortest_line_len = length
|
shortest_line_len = length
|
||||||
num_lines = ceil(shortest_line_len / stroke.min_line_dist)
|
num_lines = ceil(shortest_line_len / stroke.min_line_dist)
|
||||||
if stroke.join_style == 1:
|
return _line_count_adjust(stroke, num_lines)
|
||||||
num_lines += 1
|
|
||||||
return num_lines
|
|
||||||
|
|
||||||
|
|
||||||
def _get_target_line_count(stroke, target, outline):
|
def _get_target_line_count(stroke, target, outline):
|
||||||
|
@ -117,7 +115,19 @@ def _get_guided_line_count(stroke, guide_line):
|
||||||
num_lines = stroke.line_count
|
num_lines = stroke.line_count
|
||||||
else:
|
else:
|
||||||
num_lines = ceil(guide_line.length / stroke.min_line_dist)
|
num_lines = ceil(guide_line.length / stroke.min_line_dist)
|
||||||
|
return _line_count_adjust(stroke, num_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _line_count_adjust(stroke, num_lines):
|
||||||
|
if stroke.min_line_dist and stroke.line_count % 2 != num_lines % 2:
|
||||||
|
# We want the line count always to be either even or odd - depending on the line count value.
|
||||||
|
# So that the end point stays the same even if the design is resized. This is necessary to enable
|
||||||
|
# the user to carefully plan the output and and connect the end point to the following object
|
||||||
|
num_lines -= 1
|
||||||
|
# ensure minimum line count
|
||||||
|
num_lines = max(1, num_lines)
|
||||||
if stroke.is_closed or stroke.join_style == 1:
|
if stroke.is_closed or stroke.join_style == 1:
|
||||||
|
# for flat join styles we need to add an other line
|
||||||
num_lines += 1
|
num_lines += 1
|
||||||
return num_lines
|
return num_lines
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ def _update_to_one(element): # noqa: C901
|
||||||
element.remove_param('e_stitch')
|
element.remove_param('e_stitch')
|
||||||
element.set_param('satin_method', 'e_stitch')
|
element.set_param('satin_method', 'e_stitch')
|
||||||
|
|
||||||
if element.get_boolean_param('satin_column', False):
|
if element.get_boolean_param('satin_column', False) or element.get_int_param('stroke_method', 0) == 1:
|
||||||
# reverse_rails defaults to Automatic, but we should never reverse an
|
# reverse_rails defaults to Automatic, but we should never reverse an
|
||||||
# old satin automatically, only new ones
|
# old satin automatically, only new ones
|
||||||
element.set_param('reverse_rails', 'none')
|
element.set_param('reverse_rails', 'none')
|
||||||
|
@ -112,6 +112,9 @@ def _update_to_one(element): # noqa: C901
|
||||||
# default setting for fill_underlay has changed
|
# default setting for fill_underlay has changed
|
||||||
if legacy_attribs and not element.get_param('fill_underlay', ""):
|
if legacy_attribs and not element.get_param('fill_underlay', ""):
|
||||||
element.set_param('fill_underlay', False)
|
element.set_param('fill_underlay', False)
|
||||||
|
# default setting for running stitch length has changed (fills and strokes, not satins)
|
||||||
|
if not element.get_boolean_param('satin_column', False) and element.get_float_param('running_stitch_length_mm', None) is None:
|
||||||
|
element.set_param('running_stitch_length_mm', 1.5)
|
||||||
|
|
||||||
# convert legacy stroke_method
|
# convert legacy stroke_method
|
||||||
if element.get_style("stroke") and not element.node.get('inkscape:connection-start', None):
|
if element.get_style("stroke") and not element.node.get('inkscape:connection-start', None):
|
||||||
|
|
|
@ -11,12 +11,26 @@
|
||||||
</submenu>
|
</submenu>
|
||||||
</effects-menu>
|
</effects-menu>
|
||||||
</effect>
|
</effect>
|
||||||
<param name="end-row-spacing"
|
<param name="notebook" type="notebook">
|
||||||
gui-text="End row spacing"
|
<page name="options" gui-text="Options">
|
||||||
gui-description="Set to zero to use twice the row spacing value"
|
<param name="end-row-spacing"
|
||||||
type="float"
|
gui-text="End row spacing"
|
||||||
min="0" max="100"
|
gui-description="Set to zero to use twice the row spacing value"
|
||||||
indents="1">0.5</param>
|
type="float"
|
||||||
|
min="0" max="100"
|
||||||
|
precision="2"
|
||||||
|
indents="1">0</param>
|
||||||
|
</page>
|
||||||
|
<page name="info" gui-text="Help">
|
||||||
|
<label appearance="header">Converts a fill with a linear color gradient into color blocks with variable row spacing.</label>
|
||||||
|
<spacer />
|
||||||
|
<label>This may add density at the center.</label>
|
||||||
|
<spacer />
|
||||||
|
<label>If necessary adapt the end row spacing value after the conversion with the params dialog.</label>
|
||||||
|
<spacer />
|
||||||
|
<label appearance="url">https://inkstitch.org/docs/fill-tools/#convert-to-gradient-blocks</label>
|
||||||
|
</page>
|
||||||
|
</param>
|
||||||
<script>
|
<script>
|
||||||
{{ command_tag | safe }}
|
{{ command_tag | safe }}
|
||||||
</script>
|
</script>
|
||||||
|
|
Ładowanie…
Reference in New Issue