diff options
Diffstat (limited to 'lib/elements')
| -rw-r--r-- | lib/elements/__init__.py | 22 | ||||
| -rw-r--r-- | lib/elements/auto_fill.py | 28 | ||||
| -rw-r--r-- | lib/elements/clone.py | 75 | ||||
| -rw-r--r-- | lib/elements/element.py | 56 | ||||
| -rw-r--r-- | lib/elements/fill.py | 6 | ||||
| -rw-r--r-- | lib/elements/image.py | 14 | ||||
| -rw-r--r-- | lib/elements/polyline.py | 7 | ||||
| -rw-r--r-- | lib/elements/satin_column.py | 47 | ||||
| -rw-r--r-- | lib/elements/stroke.py | 10 | ||||
| -rw-r--r-- | lib/elements/svg_objects.py | 71 | ||||
| -rw-r--r-- | lib/elements/text.py | 14 |
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 [] |
