summaryrefslogtreecommitdiff
path: root/lib/elements
diff options
context:
space:
mode:
Diffstat (limited to 'lib/elements')
-rw-r--r--lib/elements/__init__.py22
-rw-r--r--lib/elements/auto_fill.py28
-rw-r--r--lib/elements/clone.py75
-rw-r--r--lib/elements/element.py56
-rw-r--r--lib/elements/fill.py6
-rw-r--r--lib/elements/image.py14
-rw-r--r--lib/elements/polyline.py7
-rw-r--r--lib/elements/satin_column.py47
-rw-r--r--lib/elements/stroke.py10
-rw-r--r--lib/elements/svg_objects.py71
-rw-r--r--lib/elements/text.py14
11 files changed, 128 insertions, 222 deletions
diff --git a/lib/elements/__init__.py b/lib/elements/__init__.py
index 92ef94a3..d53b2314 100644
--- a/lib/elements/__init__.py
+++ b/lib/elements/__init__.py
@@ -1,11 +1,11 @@
-from auto_fill import AutoFill
-from clone import Clone
-from element import EmbroideryElement
-from empty_d_object import EmptyDObject
-from fill import Fill
-from image import ImageObject
-from polyline import Polyline
-from satin_column import SatinColumn
-from stroke import Stroke
-from text import TextObject
-from utils import node_to_elements, nodes_to_elements
+from .auto_fill import AutoFill
+from .clone import Clone
+from .element import EmbroideryElement
+from .empty_d_object import EmptyDObject
+from .fill import Fill
+from .image import ImageObject
+from .polyline import Polyline
+from .satin_column import SatinColumn
+from .stroke import Stroke
+from .text import TextObject
+from .utils import node_to_elements, nodes_to_elements
diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py
index b574c8bf..31da7e63 100644
--- a/lib/elements/auto_fill.py
+++ b/lib/elements/auto_fill.py
@@ -6,10 +6,9 @@ from shapely import geometry as shgeo
from ..i18n import _
from ..stitches import auto_fill
-from ..utils import cache
+from ..utils import cache, version
from .element import Patch, param
from .fill import Fill
-
from .validation import ValidationWarning
@@ -20,6 +19,18 @@ class SmallShapeWarning(ValidationWarning):
"the outline instead.")
+class ExpandWarning(ValidationWarning):
+ name = _("Expand")
+ description = _("The expand parameter for this fill object cannot be applied. "
+ "Ink/Stitch will ignore it and will use original size instead.")
+
+
+class UnderlayInsetWarning(ValidationWarning):
+ name = _("Inset")
+ description = _("The underlay inset parameter for this fill object cannot be applied. "
+ "Ink/Stitch will ignore it and will use the original size instead.")
+
+
class AutoFill(Fill):
element_name = _("AutoFill")
@@ -157,9 +168,13 @@ class AutoFill(Fill):
def underlay_underpath(self):
return self.get_boolean_param('underlay_underpath', True)
- def shrink_or_grow_shape(self, amount):
+ def shrink_or_grow_shape(self, amount, validate=False):
if amount:
shape = self.shape.buffer(amount)
+ # changing the size can empty the shape
+ # in this case we want to use the original shape rather than returning an error
+ if shape.is_empty and not validate:
+ return self.shape
if not isinstance(shape, shgeo.MultiPolygon):
shape = shgeo.MultiPolygon([shape])
return shape
@@ -235,6 +250,7 @@ class AutoFill(Fill):
# L10N this message is followed by a URL: https://github.com/inkstitch/inkstitch/issues/new
message += _("If you'd like to help us make Ink/Stitch better, please paste this whole message into a new issue at: ")
message += "https://github.com/inkstitch/inkstitch/issues/new\n\n"
+ message += version.get_inkstitch_version() + "\n\n"
message += traceback.format_exc()
self.fatal(message)
@@ -245,5 +261,11 @@ class AutoFill(Fill):
if self.shape.area < 20:
yield SmallShapeWarning(self.shape.centroid)
+ if self.shrink_or_grow_shape(self.expand, True).is_empty:
+ yield ExpandWarning(self.shape.centroid)
+
+ if self.shrink_or_grow_shape(-self.fill_underlay_inset, True).is_empty:
+ yield UnderlayInsetWarning(self.shape.centroid)
+
for warning in super(AutoFill, self).validation_warnings():
yield warning
diff --git a/lib/elements/clone.py b/lib/elements/clone.py
index b8046d2d..fd770bd7 100644
--- a/lib/elements/clone.py
+++ b/lib/elements/clone.py
@@ -1,16 +1,13 @@
-from copy import copy
from math import atan, degrees
-from simpletransform import (applyTransformToNode, applyTransformToPoint,
- computeBBox, parseTransform)
+import inkex
from ..commands import is_command, is_command_symbol
from ..i18n import _
from ..svg.path import get_node_transform
from ..svg.svg import find_elements
-from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS, SVG_GROUP_TAG,
- SVG_LINK_TAG, SVG_POLYLINE_TAG, SVG_USE_TAG,
- XLINK_HREF)
+from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS,
+ SVG_POLYLINE_TAG, SVG_USE_TAG, XLINK_HREF)
from ..utils import cache
from .auto_fill import AutoFill
from .element import EmbroideryElement, param
@@ -74,16 +71,16 @@ class Clone(EmbroideryElement):
if node.tag == SVG_POLYLINE_TAG:
return [Polyline(node)]
- elif element.get_boolean_param("satin_column") and element.get_style("stroke"):
+ elif element.get_boolean_param("satin_column") and self.get_clone_style("stroke", self.node):
return [SatinColumn(node)]
else:
elements = []
- if element.get_style("fill", "black") and not element.get_style("fill-opacity", 1) == "0":
+ if element.get_style("fill", "black") and not element.get_style("stroke", 1) == "0":
if element.get_boolean_param("auto_fill", True):
elements.append(AutoFill(node))
else:
elements.append(Fill(node))
- if element.get_style("stroke"):
+ if element.get_style("stroke", self.node) is not None:
if not is_command(element.node):
elements.append(Stroke(node))
if element.get_boolean_param("stroke_first", False):
@@ -98,32 +95,8 @@ class Clone(EmbroideryElement):
if source_node.tag not in EMBROIDERABLE_TAGS:
return []
- clone = copy(source_node)
-
- # set id
- clone_id = 'clone__%s__%s' % (self.node.get('id', ''), clone.get('id', ''))
- clone.set('id', clone_id)
-
- # apply transform
- transform = get_node_transform(self.node)
- applyTransformToNode(transform, clone)
-
- # apply style
- stroke_style = self.get_clone_style('stroke', self.node)
- if not stroke_style:
- stroke_style = self.get_clone_style('stroke', source_node)
- fill_style = self.node.get('fill')
- if not fill_style:
- fill_style = self.get_clone_style('fill', source_node, "#000000")
- fill_opacity = self.node.get('fill-opacity')
- if not fill_opacity:
- fill_opacity = self.get_clone_style('fill-opacity', source_node, "1")
- style = "fill:%s;fill-opacity:%s;" % (fill_style, fill_opacity)
- if stroke_style:
- style += "stroke:%s;" % stroke_style
- clone.set('style', style)
-
- # set fill angle. Use either
+ self.node.style = source_node.composed_style()
+
# a. a custom set fill angle
# b. calculated rotation for the cloned fill element to look exactly as it's source
param = INKSTITCH_ATTRIBS['angle']
@@ -131,48 +104,32 @@ class Clone(EmbroideryElement):
angle = self.clone_fill_angle
else:
# clone angle
- clone_mat = parseTransform(clone.get('transform', ''))
+ clone_mat = self.node.composed_transform()
clone_angle = degrees(atan(-clone_mat[1][0]/clone_mat[1][1]))
# source node angle
- source_mat = parseTransform(source_node.get('transform', ''))
+ source_mat = source_node.composed_transform()
source_angle = degrees(atan(-source_mat[1][0]/source_mat[1][1]))
# source node fill angle
source_fill_angle = source_node.get(param, 0)
angle = clone_angle + float(source_fill_angle) - source_angle
- clone.set(param, str(angle))
+ self.node.set(param, str(angle))
- elements = self.clone_to_element(clone)
+ elements = self.clone_to_element(self.node)
for element in elements:
patches.extend(element.to_patches(last_patch))
return patches
- def _get_clone_style_raw(self, style_name, node):
- style = self.parse_style()
- style = style.get(style_name) or self.node.get(style_name)
- parent = self.node.getparent()
- # style not found, get inherited style elements
- while not style and parent is not None:
- if parent.tag not in [SVG_GROUP_TAG, SVG_LINK_TAG]:
- parent = parent.getparent()
- continue
- style = self.parse_style(parent)
- style = style.get(style_name) or parent.get(style_name)
- parent = parent.getparent()
- return style
-
def get_clone_style(self, style_name, node, default=None):
- style = self._get_clone_style_raw(style_name, node) or default
+ style = inkex.styles.AttrFallbackStyle(node).get(style_name) or default
return style
def center(self, source_node):
- xmin, xmax, ymin, ymax = computeBBox([source_node])
- point = [(xmax-((xmax-xmin)/2)), (ymax-((ymax-ymin)/2))]
- transform = get_node_transform(self.node)
- applyTransformToPoint(transform, point)
- return point
+ transform = get_node_transform(self.node.getparent())
+ center = self.node.bounding_box(transform).center
+ return center
def validation_warnings(self):
source_node = get_clone_source(self.node)
diff --git a/lib/elements/element.py b/lib/elements/element.py
index 5d2934cd..2ced143b 100644
--- a/lib/elements/element.py
+++ b/lib/elements/element.py
@@ -1,18 +1,16 @@
import sys
from copy import deepcopy
-import cubicsuperpath
-import simpletransform
+import inkex
import tinycss2
-from cspsubdiv import cspsubdiv
+from inkex import bezier
-from .svg_objects import circle_to_path, ellipse_to_path, rect_to_path
from ..commands import find_commands
from ..i18n import _
from ..svg import (PIXELS_PER_MM, apply_transforms, convert_length,
get_node_transform)
-from ..svg.tags import (EMBROIDERABLE_TAGS, INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_CIRCLE_TAG, SVG_ELLIPSE_TAG, SVG_GROUP_TAG, SVG_LINK_TAG,
- SVG_OBJECT_TAGS, SVG_RECT_TAG)
+from ..svg.tags import (EMBROIDERABLE_TAGS, INKSCAPE_LABEL, INKSTITCH_ATTRIBS,
+ SVG_GROUP_TAG, SVG_LINK_TAG, SVG_USE_TAG)
from ..utils import Point, cache
@@ -155,22 +153,29 @@ class EmbroideryElement(object):
def parse_style(self, node=None):
if node is None:
node = self.node
+ element_style = node.get("style", "")
+ if element_style is None:
+ return None
declarations = tinycss2.parse_declaration_list(node.get("style", ""))
style = {declaration.lower_name: declaration.value[0].serialize() for declaration in declarations}
return style
@cache
def _get_style_raw(self, style_name):
- if self.node.tag not in [SVG_GROUP_TAG, SVG_LINK_TAG] and self.node.tag not in EMBROIDERABLE_TAGS:
+ if self.node is None:
+ return None
+ if self.node.tag not in [SVG_GROUP_TAG, SVG_LINK_TAG, SVG_USE_TAG] and self.node.tag not in EMBROIDERABLE_TAGS:
return None
style = self.parse_style()
- style = style.get(style_name) or self.node.get(style_name)
+ if style:
+ style = style.get(style_name) or self.node.get(style_name)
parent = self.node.getparent()
# style not found, get inherited style elements
while not style and parent is not None:
style = self.parse_style(parent)
- style = style.get(style_name) or parent.get(style_name)
+ if style:
+ style = style.get(style_name) or parent.get(style_name)
parent = parent.getparent()
return style
@@ -196,23 +201,23 @@ class EmbroideryElement(object):
# Of course, transforms may also involve rotation, skewing, and translation.
# All except translation can affect how wide the stroke appears on the screen.
- node_transform = get_node_transform(self.node)
+ node_transform = inkex.transforms.Transform(get_node_transform(self.node))
# First, figure out the translation component of the transform. Using a zero
# vector completely cancels out the rotation, scale, and skew components.
zero = [0, 0]
- simpletransform.applyTransformToPoint(node_transform, zero)
+ zero = inkex.Transform.apply_to_point(node_transform, zero)
translate = Point(*zero)
# Next, see how the transform affects unit vectors in the X and Y axes. We
# need to subtract off the translation or it will affect the magnitude of
# the resulting vector, which we don't want.
unit_x = [1, 0]
- simpletransform.applyTransformToPoint(node_transform, unit_x)
+ unit_x = inkex.Transform.apply_to_point(node_transform, unit_x)
sx = (Point(*unit_x) - translate).length()
unit_y = [0, 1]
- simpletransform.applyTransformToPoint(node_transform, unit_y)
+ unit_y = inkex.Transform.apply_to_point(node_transform, unit_y)
sy = (Point(*unit_y) - translate).length()
# Take the average as a best guess.
@@ -223,11 +228,7 @@ class EmbroideryElement(object):
@property
@cache
def stroke_width(self):
- width = self.get_style("stroke-width", None)
-
- if width is None:
- return 1.0
-
+ width = self.get_style("stroke-width", "1.0")
width = convert_length(width)
return width * self.stroke_scale
@@ -271,20 +272,15 @@ class EmbroideryElement(object):
# In a path, each element in the 3-tuple is itself a tuple of (x, y).
# Tuples all the way down. Hasn't anyone heard of using classes?
- if self.node.tag in SVG_OBJECT_TAGS:
- if self.node.tag == SVG_RECT_TAG:
- d = rect_to_path(self.node)
- elif self.node.tag == SVG_ELLIPSE_TAG:
- d = ellipse_to_path(self.node)
- elif self.node.tag == SVG_CIRCLE_TAG:
- d = circle_to_path(self.node)
+ if getattr(self.node, "get_path", None):
+ d = self.node.get_path()
else:
d = self.node.get("d", "")
if not d:
self.fatal(_("Object %(id)s has an empty 'd' attribute. Please delete this object from your document.") % dict(id=self.node.get("id")))
- return cubicsuperpath.parsePath(d)
+ return inkex.paths.Path(d).to_superpath()
@cache
def parse_path(self):
@@ -315,7 +311,7 @@ class EmbroideryElement(object):
return commands[0]
elif len(commands) > 1:
raise ValueError(_("%(id)s has more than one command of type '%(command)s' linked to it") %
- dict(id=self.node.get(id), command=command))
+ dict(id=self.node.get('id'), command=command))
else:
return None
@@ -326,13 +322,13 @@ class EmbroideryElement(object):
"""approximate a path containing beziers with a series of points"""
path = deepcopy(path)
- cspsubdiv(path, 0.1)
+ bezier.cspsubdiv(path, 0.1)
return [self.strip_control_points(subpath) for subpath in path]
def flatten_subpath(self, subpath):
path = [deepcopy(subpath)]
- cspsubdiv(path, 0.1)
+ bezier.cspsubdiv(path, 0.1)
return self.strip_control_points(path[0])
@@ -373,7 +369,7 @@ class EmbroideryElement(object):
# L10N used when showing an error message to the user such as
# "Some Path (path1234): error: satin column: One or more of the rungs doesn't intersect both rails."
error_msg = "%s: %s %s" % (name, _("error:"), message)
- print >> sys.stderr, "%s" % (error_msg.encode("UTF-8"))
+ inkex.errormsg(error_msg)
sys.exit(1)
def validation_errors(self):
diff --git a/lib/elements/fill.py b/lib/elements/fill.py
index 2e94847a..1f4c7b1e 100644
--- a/lib/elements/fill.py
+++ b/lib/elements/fill.py
@@ -136,6 +136,12 @@ class Fill(EmbroideryElement):
# biggest path.
paths = self.paths
paths.sort(key=lambda point_list: shgeo.Polygon(point_list).area, reverse=True)
+ # Very small holes will cause a shape to be rendered as an outline only
+ # they are too small to be rendered and only confuse the auto_fill algorithm.
+ # So let's ignore them
+ if shgeo.Polygon(paths[0]).area > 5 and shgeo.Polygon(paths[-1]).area < 5:
+ paths = [path for path in paths if shgeo.Polygon(path).area > 3]
+
polygon = shgeo.MultiPolygon([(paths[0], paths[1:])])
# There is a great number of "crossing border" errors on fill shapes
diff --git a/lib/elements/image.py b/lib/elements/image.py
index ec8d1765..160898a5 100644
--- a/lib/elements/image.py
+++ b/lib/elements/image.py
@@ -1,7 +1,5 @@
-from simpletransform import applyTransformToPoint
-
from ..i18n import _
-from ..svg import get_node_transform
+from ..svg.path import get_node_transform
from .element import EmbroideryElement
from .validation import ObjectTypeWarning
@@ -19,13 +17,9 @@ class ImageTypeWarning(ObjectTypeWarning):
class ImageObject(EmbroideryElement):
def center(self):
- point = [float(self.node.get('x', 0)), float(self.node.get('y', 0))]
- point = [(point[0]+(float(self.node.get('width', 0))/2)), (point[1]+(float(self.node.get('height', 0))/2))]
-
- transform = get_node_transform(self.node)
- applyTransformToPoint(transform, point)
-
- return point
+ transform = get_node_transform(self.node.getparent())
+ center = self.node.bounding_box(transform).center
+ return center
def validation_warnings(self):
yield ImageTypeWarning(self.center())
diff --git a/lib/elements/polyline.py b/lib/elements/polyline.py
index 2d008d35..da1e807d 100644
--- a/lib/elements/polyline.py
+++ b/lib/elements/polyline.py
@@ -1,3 +1,4 @@
+from inkex import Path
from shapely import geometry as shgeo
from ..i18n import _
@@ -36,7 +37,7 @@ class Polyline(EmbroideryElement):
@property
@param('polyline', _('Manual stitch along path'), type='toggle', inverse=True)
- def satin_column(self):
+ def polyline(self):
return self.get_boolean_param("polyline")
@property
@@ -61,8 +62,8 @@ class Polyline(EmbroideryElement):
# svg transforms that is in our superclass, we'll convert the polyline
# to a degenerate cubic superpath in which the bezier handles are on
# the segment endpoints.
-
- path = [[[point[:], point[:], point[:]] for point in self.points]]
+ path = self.node.get_path()
+ path = Path(path).to_superpath()
return path
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py
index d0a3f8ff..fbadd92f 100644
--- a/lib/elements/satin_column.py
+++ b/lib/elements/satin_column.py
@@ -1,14 +1,15 @@
from copy import deepcopy
-from itertools import chain, izip
+from itertools import chain
-import cubicsuperpath
-from shapely import affinity as shaffinity, geometry as shgeo
+from inkex import paths
+from shapely import affinity as shaffinity
+from shapely import geometry as shgeo
-from .element import EmbroideryElement, Patch, param
-from .validation import ValidationError
from ..i18n import _
from ..svg import line_strings_to_csp, point_lists_to_csp
from ..utils import Point, cache, collapse_duplicate_point, cut
+from .element import EmbroideryElement, Patch, param
+from .validation import ValidationError
class SatinHasFillError(ValidationError):
@@ -234,7 +235,7 @@ class SatinColumn(EmbroideryElement):
rung_endpoints.append(points)
rungs = []
- for start, end in izip(*rung_endpoints):
+ for start, end in zip(*rung_endpoints):
# Expand the points just a bit to ensure that shapely thinks they
# intersect with the rails even with floating point inaccuracy.
start = Point(*start)
@@ -266,12 +267,12 @@ class SatinColumn(EmbroideryElement):
if num_paths <= 2:
# old-style satin column with no rungs
- return range(num_paths)
+ return list(range(num_paths))
# This takes advantage of the fact that sum() counts True as 1
- intersection_counts = [sum(paths[i].intersects(paths[j]) for j in xrange(num_paths) if i != j)
- for i in xrange(num_paths)]
- paths_not_intersecting_two = [i for i in xrange(num_paths) if intersection_counts[i] != 2]
+ intersection_counts = [sum(paths[i].intersects(paths[j]) for j in range(num_paths) if i != j)
+ for i in range(num_paths)]
+ paths_not_intersecting_two = [i for i in range(num_paths) if intersection_counts[i] != 2]
num_not_intersecting_two = len(paths_not_intersecting_two)
if num_not_intersecting_two == 2:
@@ -292,7 +293,7 @@ class SatinColumn(EmbroideryElement):
# kind of weird thing. Maybe one of the rungs crosses a rail more
# than once. Treat it like the previous case and we'll sort out
# the intersection issues later.
- indices_by_length = sorted(range(num_paths), key=lambda index: paths[index].length, reverse=True)
+ indices_by_length = sorted(list(range(num_paths)), key=lambda index: paths[index].length, reverse=True)
return indices_by_length[:2]
def _cut_rail(self, rail, rung):
@@ -330,7 +331,7 @@ class SatinColumn(EmbroideryElement):
self._cut_rail(rail, rung)
for rail in rails:
- for i in xrange(len(rail)):
+ for i in range(len(rail)):
if rail[i] is not None:
rail[i] = [Point(*coord) for coord in rail[i].coords]
@@ -345,7 +346,7 @@ class SatinColumn(EmbroideryElement):
# zero-length bezier at the star. The user's goal here is to ignore the
# horizontal section of the right rail.
- sections = zip(*rails)
+ sections = list(zip(*rails))
sections = [s for s in sections if s[0] is not None and s[1] is not None]
return sections
@@ -438,13 +439,13 @@ class SatinColumn(EmbroideryElement):
"""
# like in do_satin()
- points = list(chain.from_iterable(izip(*self.plot_points_on_rails(self.zigzag_spacing, 0))))
+ points = list(chain.from_iterable(zip(*self.plot_points_on_rails(self.zigzag_spacing, 0))))
if isinstance(split_point, float):
index_of_closest_stitch = int(round(len(points) * split_point))
else:
split_point = Point(*split_point)
- index_of_closest_stitch = min(range(len(points)), key=lambda index: split_point.distance(points[index]))
+ index_of_closest_stitch = min(list(range(len(points))), key=lambda index: split_point.distance(points[index]))
if index_of_closest_stitch % 2 == 0:
# split point is on the first rail
@@ -517,7 +518,7 @@ class SatinColumn(EmbroideryElement):
def _csp_to_satin(self, csp):
node = deepcopy(self.node)
- d = cubicsuperpath.formatPath(csp)
+ d = paths.CubicSuperPath(csp).to_path()
node.set("d", d)
# we've already applied the transform, so get rid of it
@@ -626,7 +627,9 @@ class SatinColumn(EmbroideryElement):
# Each iteration of this outer loop is one stitch. Keep going
# until we fall off the end of the section.
- old_center = (pos0 + pos1) / 2.0
+ # TODO: is there an other way?
+ # old_center = (pos0 + pos1) / 2.0
+ old_center = shgeo.Point(x/2 for x in (pos0 + pos1))
while to_travel > 0 and index0 < last_index0 and index1 < last_index1:
# In this loop, we inch along each rail a tiny bit per
@@ -653,7 +656,9 @@ class SatinColumn(EmbroideryElement):
pos0, index0 = self.walk(section0, pos0, index0, 0.05)
pos1, index1 = self.walk(section1, pos1, index1, 0.05 * ratio)
- new_center = (pos0 + pos1) / 2.0
+ # TODO: is there a better way?
+ # new_center = (pos0 + pos1) / 2.0
+ new_center = shgeo.Point(x/2 for x in (pos0 + pos1))
to_travel -= new_center.distance(old_center)
old_center = new_center
@@ -705,7 +710,7 @@ class SatinColumn(EmbroideryElement):
# This fancy bit of iterable magic just repeatedly takes a point
# from each side in turn.
- for point in chain.from_iterable(izip(*sides)):
+ for point in chain.from_iterable(zip(*sides)):
patch.add_stitch(point)
return patch
@@ -724,7 +729,7 @@ class SatinColumn(EmbroideryElement):
sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation)
# Like in zigzag_underlay(): take a point from each side in turn.
- for point in chain.from_iterable(izip(*sides)):
+ for point in chain.from_iterable(zip(*sides)):
patch.add_stitch(point)
return patch
@@ -743,7 +748,7 @@ class SatinColumn(EmbroideryElement):
# "left" and "right" here are kind of arbitrary designations meaning
# a point from the first and second rail repectively
- for left, right in izip(*sides):
+ for left, right in zip(*sides):
patch.add_stitch(left)
patch.add_stitch(right)
patch.add_stitch(left)
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 36d1048e..d63a21a9 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -134,9 +134,9 @@ class Stroke(EmbroideryElement):
global warned_about_legacy_running_stitch
if not warned_about_legacy_running_stitch:
warned_about_legacy_running_stitch = True
- print >> sys.stderr, _("Legacy running stitch setting detected!\n\nIt looks like you're using a stroke " +
- "smaller than 0.5 units to indicate a running stitch, which is deprecated. Instead, please set " +
- "your stroke to be dashed to indicate running stitch. Any kind of dash will work.")
+ print(_("Legacy running stitch setting detected!\n\nIt looks like you're using a stroke " +
+ "smaller than 0.5 units to indicate a running stitch, which is deprecated. Instead, please set " +
+ "your stroke to be dashed to indicate running stitch. Any kind of dash will work."), file=sys.stderr)
# still allow the deprecated setting to work in order to support old files
return True
@@ -157,7 +157,7 @@ class Stroke(EmbroideryElement):
offset = stroke_width / 2.0
- for i in xrange(len(patch) - 1):
+ for i in range(len(patch) - 1):
start = patch.stitches[i]
end = patch.stitches[i + 1]
segment_direction = (end - start).unit()
@@ -174,7 +174,7 @@ class Stroke(EmbroideryElement):
repeated_path = []
# go back and forth along the path as specified by self.repeats
- for i in xrange(self.repeats):
+ for i in range(self.repeats):
if i % 2 == 1:
# reverse every other pass
this_path = path[::-1]
diff --git a/lib/elements/svg_objects.py b/lib/elements/svg_objects.py
deleted file mode 100644
index 4760af5f..00000000
--- a/lib/elements/svg_objects.py
+++ /dev/null
@@ -1,71 +0,0 @@
-def rect_to_path(node):
- x = float(node.get('x', '0'))
- y = float(node.get('y', '0'))
- width = float(node.get('width', '0'))
- height = float(node.get('height', '0'))
- rx = 0
- ry = 0
-
- # rounded corners
- # the following rules apply for radius calculations:
- # * if rx or ry is missing it has to take the value of the other one
- # * the radius cannot be bigger than half of the corresponding side
- # (otherwise we receive an invalid path)
- if node.get('rx') or node.get('ry'):
- if node.get('rx'):
- rx = float(node.get('rx', '0'))
- ry = rx
- if node.get('ry'):
- ry = float(node.get('ry', '0'))
- if not ry:
- ry = rx
-
- rx = min(width/2, rx)
- ry = min(height/2, ry)
-
- path = 'M %(startx)f,%(y)f ' \
- 'h %(width)f ' \
- 'q %(rx)f,0 %(rx)f,%(ry)f ' \
- 'v %(height)f ' \
- 'q 0,%(ry)f -%(rx)f,%(ry)f ' \
- 'h -%(width)f ' \
- 'q -%(rx)f,0 -%(rx)f,-%(ry)f ' \
- 'v -%(height)f ' \
- 'q 0,-%(ry)f %(rx)f,-%(ry)f ' \
- 'Z' \
- % dict(startx=x+rx, x=x, y=y, width=width-(2*rx), height=height-(2*ry), rx=rx, ry=ry)
-
- else:
- path = "M %f,%f H %f V %f H %f Z" % (x, y, width+x, height+y, x)
-
- return path
-
-
-def ellipse_to_path(node):
- rx = float(node.get('rx', "0")) or float(node.get('r', "0"))
- ry = float(node.get('ry', "0")) or float(node.get('r', "0"))
- cx = float(node.get('cx'))
- cy = float(node.get('cy'))
-
- path = 'M %(cx_r)f,%(cy)f' \
- 'C %(cx_r)f,%(cy_r)f %(cx)f,%(cy_r)f %(cx)f,%(cy_r)f ' \
- '%(cxr)f,%(cy_r)f %(cxr)f,%(cy)f %(cxr)f,%(cy)f ' \
- '%(cxr)f,%(cyr)f %(cx)f,%(cyr)f %(cx)f,%(cyr)f ' \
- '%(cx_r)f,%(cyr)f %(cx_r)f,%(cy)f %(cx_r)f,%(cy)f ' \
- 'Z' \
- % dict(cx=cx, cx_r=cx-rx, cxr=cx+rx, cy=cy, cyr=cy+ry, cy_r=cy-ry)
-
- return path
-
-
-def circle_to_path(node):
- cx = float(node.get('cx'))
- cy = float(node.get('cy'))
- r = float(node.get('r'))
-
- path = 'M %(xstart)f, %(cy)f ' \
- 'a %(r)f,%(r)f 0 1,0 %(rr)f,0 ' \
- 'a %(r)f,%(r)f 0 1,0 -%(rr)f,0 ' \
- % dict(xstart=(cx-r), cy=cy, r=r, rr=(r*2))
-
- return path
diff --git a/lib/elements/text.py b/lib/elements/text.py
index 2d066bb0..838be96a 100644
--- a/lib/elements/text.py
+++ b/lib/elements/text.py
@@ -1,9 +1,7 @@
-from simpletransform import applyTransformToPoint
-
from ..i18n import _
-from ..svg import get_node_transform
from .element import EmbroideryElement
from .validation import ObjectTypeWarning
+from ..svg.path import get_node_transform
class TextTypeWarning(ObjectTypeWarning):
@@ -17,16 +15,14 @@ class TextTypeWarning(ObjectTypeWarning):
class TextObject(EmbroideryElement):
- def center(self):
- point = [float(self.node.get('x', 0)), float(self.node.get('y', 0))]
-
- transform = get_node_transform(self.node)
- applyTransformToPoint(transform, point)
+ def pointer(self):
+ transform = get_node_transform(self.node.getparent())
+ point = self.node.bounding_box(transform).center
return point
def validation_warnings(self):
- yield TextTypeWarning(self.center())
+ yield TextTypeWarning(self.pointer())
def to_patches(self, last_patch):
return []