diff options
Diffstat (limited to 'share/extensions/path_envelope.py')
-rwxr-xr-x | share/extensions/path_envelope.py | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/share/extensions/path_envelope.py b/share/extensions/path_envelope.py new file mode 100755 index 0000000..7f92da7 --- /dev/null +++ b/share/extensions/path_envelope.py @@ -0,0 +1,99 @@ +# coding=utf-8 +# +# Copyright (C) 2005 Aaron Spike, aaron@ekips.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +import inkex +from inkex.transforms import DirectedLineSegment +from inkex.localization import inkex_gettext as _ + +class Envelope(inkex.EffectExtension): + """Distort a path/group of paths to a second path""" + def effect(self): + if len(self.svg.selection) != 2: + raise inkex.AbortExtension(_("You must select two objects only.")) + + obj, envelope = self.svg.selection.values() + + if isinstance(obj, (inkex.PathElement, inkex.Group)): + if isinstance(envelope, inkex.PathElement): + # Get bounding box plus any extra composed transform of parents. + bbox = obj.bounding_box(obj.getparent().composed_transform()) + + # distill trafo into four node points + path = envelope.path.transform(envelope.composed_transform()).to_superpath() + if len(path[0]) < 4: + raise inkex.AbortExtension(_("Selected path is too short. Must be four or more nodes.")) + + trafo = [[(csp[1][0], csp[1][1]) for csp in subs] for subs in path][0][:4] + + #vectors pointing away from the trafo origin + tbox = [ + DirectedLineSegment(trafo[0], trafo[1]), + DirectedLineSegment(trafo[1], trafo[2]), + DirectedLineSegment(trafo[3], trafo[2]), + DirectedLineSegment(trafo[0], trafo[3]), + ] + else: + if isinstance(envelope, inkex.Group): + raise inkex.AbortExtension(_("The second selected object is a group, not a" + " path.\nTry using Object->Ungroup.")) + raise inkex.AbortExtension(_("The second selected object is not a path.\nTry using" + " the procedure Path->Object to Path.")) + else: + raise inkex.AbortExtension(_("The first selected object is neither a path nor a group.\nTry using" + " the procedure Path->Object to Path.")) + + self.process_object(obj, tbox, bbox) + + def process_object(self, obj, tbox, bbox): + if isinstance(obj, inkex.PathElement): + self.process_path(obj, tbox, bbox) + elif isinstance(obj, inkex.Group): + self.process_group(obj, tbox, bbox) + + def process_group(self, group, tbox, bbox): + """Go through all groups to process all paths inside them""" + for node in group: + self.process_object(node, tbox, bbox) + + def process_path(self, element, tbox, bbox): + # Get out path's absolute and root coordinates, so obj and envelope + # are always in the same coordinate system. + points = element.path.to_absolute().transform(element.composed_transform()).to_superpath() + + for subs in points: + for csp in subs: + csp[0] = self.transform_point(tbox, bbox, *csp[0]) + csp[1] = self.transform_point(tbox, bbox, *csp[1]) + csp[2] = self.transform_point(tbox, bbox, *csp[2]) + + # Put the modified path back, undo the root transformation + element.path = points.to_path().transform(-element.composed_transform()) + + @staticmethod + def transform_point(tbox, bbox, x, y): + """Transform algorithm thanks to Jose Hevia (freon)""" + vector = DirectedLineSegment((bbox.left, bbox.top), (x, y)) + xratio = vector.dx / bbox.width + yratio = vector.dy / bbox.height + horz = DirectedLineSegment(tbox[0].point_at_ratio(xratio), tbox[2].point_at_ratio(xratio)) + vert = DirectedLineSegment(tbox[3].point_at_ratio(yratio), tbox[1].point_at_ratio(yratio)) + return vert.intersect(horz) + +if __name__ == '__main__': + Envelope().run() |