From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- share/extensions/pathalongpath.py | 222 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100755 share/extensions/pathalongpath.py (limited to 'share/extensions/pathalongpath.py') diff --git a/share/extensions/pathalongpath.py b/share/extensions/pathalongpath.py new file mode 100755 index 0000000..9c4bac5 --- /dev/null +++ b/share/extensions/pathalongpath.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python +# +# Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr +# 2021 Jonathan Neuhauser, jonathan.neuhauser@outlook.com +# +# 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. +# barraud@math.univ-lille1.fr +# +""" +This script deforms an object (the pattern) along other paths (skeletons)... +The first selected object is the pattern +the last selected ones are the skeletons. + +Imagine a straight horizontal line L in the middle of the bounding box of the pattern. +Consider the normal bundle of L: the collection of all the vertical lines meeting L. +Consider this as the initial state of the plane; in particular, think of the pattern +as painted on these lines. + +Now move and bend L to make it fit a skeleton, and see what happens to the normals: +they move and rotate, deforming the pattern. +""" +import copy +import math + +import inkex +from inkex.bezier import tpoint +from inkex.paths import CubicSuperPath +from inkex.localization import inkex_gettext as _ + +import pathmodifier + + +class PathAlongPath(pathmodifier.PathModifier): + """Deform a path along a second path""" + + def add_arguments(self, pars): + pars.add_argument( + "-n", "--noffset", type=float, default=0.0, help="normal offset" + ) + pars.add_argument( + "-t", "--toffset", type=float, default=0.0, help="tangential offset" + ) + pars.add_argument("-k", "--kind", type=str, default="") + pars.add_argument( + "-c", + "--copymode", + default="Single", + help="repeat the path to fit deformer's length", + ) + pars.add_argument("-p", "--space", type=float, default=0.0) + pars.add_argument( + "-v", + "--vertical", + type=inkex.Boolean, + default=False, + help="reference path is vertical", + ) + pars.add_argument( + "-d", + "--duplicate", + type=inkex.Boolean, + default=True, + help="duplicate pattern before deformation", + ) + pars.add_argument("--tab", help="The selected UI-tab when OK was pressed") + + def apply_diffeomorphism(self, bpt, skelcomp, lengths, isclosed, vects=()): + """ + The kernel of this stuff: + bpt is a base point and for v in vectors, v'=v-p is a tangent vector at bpt. + """ + s = bpt[0] - skelcomp[0][0] + i, t = self.lengthtotime(s, lengths, isclosed) + if i == len(skelcomp) - 1: + x, y = tpoint(skelcomp[i - 1], skelcomp[i], 1 + t) + dx = (skelcomp[i][0] - skelcomp[i - 1][0]) / lengths[-1] + dy = (skelcomp[i][1] - skelcomp[i - 1][1]) / lengths[-1] + else: + x, y = tpoint(skelcomp[i], skelcomp[i + 1], t) + dx = (skelcomp[i + 1][0] - skelcomp[i][0]) / lengths[i] + dy = (skelcomp[i + 1][1] - skelcomp[i][1]) / lengths[i] + + vx = 0 + vy = bpt[1] - skelcomp[0][1] + if self.options.wave: + bpt[0] = x + vx * dx + bpt[1] = y + vy + vx * dy + else: + bpt[0] = x + vx * dx - vy * dy + bpt[1] = y + vx * dy + vy * dx + + for v in vects: + vx = v[0] - skelcomp[0][0] - s + vy = v[1] - skelcomp[0][1] + if self.options.wave: + v[0] = x + vx * dx + v[1] = y + vy + vx * dy + else: + v[0] = x + vx * dx - vy * dy + v[1] = y + vx * dy + vy * dx + + def effect(self): + if len(self.options.ids) < 2: + raise inkex.AbortExtension(_("This extension requires two selected paths.")) + + self.options.wave = self.options.kind == "Ribbon" + if self.options.copymode == "Single": + self.options.repeat = False + self.options.stretch = False + elif self.options.copymode == "Repeated": + self.options.repeat = True + self.options.stretch = False + elif self.options.copymode == "Single, stretched": + self.options.repeat = False + self.options.stretch = True + elif self.options.copymode == "Repeated, stretched": + self.options.repeat = True + self.options.stretch = True + + patterns, skels = self.get_patterns_and_skeletons(True, self.options.duplicate) + bboxes = [pattern.bounding_box() for pattern in patterns.values()] + if None in bboxes: # for texts, we can't compute the bounding box + raise inkex.AbortExtension(_("Please convert texts to path first")) + bbox = sum(bboxes, None) + + if self.options.vertical: + # flipxy(bbox)... + bbox = inkex.BoundingBox(-bbox.y, -bbox.x) + + width = bbox.width + delta_x = width + self.options.space + if delta_x < 0.01: + raise inkex.AbortExtension( + _( + "The total length of the pattern is too small\n" + "Please choose a larger object or set 'Space between copies' > 0" + ) + ) + for pattern in patterns.values(): + if isinstance(pattern, inkex.PathElement): + pattern.apply_transform() + pattern.path = self._do_transform( + skels, pattern.path.to_superpath(), delta_x, bbox + ) + + def _do_transform(self, skeletons, p0, dx, bbox): + if self.options.vertical: + self.flipxy(p0) + newp = [] + for skelnode in skeletons.values(): + skelnode.apply_transform() + cur_skeleton = skelnode.path.to_superpath() + if self.options.vertical: + self.flipxy(cur_skeleton) + for comp in cur_skeleton: + path = copy.deepcopy(p0) + skelcomp, lengths = self.linearize(comp) + + skel_closed = all( + [math.isclose(i, j) for i, j in zip(skelcomp[0], skelcomp[-1])] + ) + + length = sum(lengths) + xoffset = skelcomp[0][0] - bbox.x.minimum + self.options.toffset + yoffset = skelcomp[0][1] - bbox.y.center - self.options.noffset + if self.options.repeat: + nb_copies = max(1, int(round((length + self.options.space) / dx))) + width = dx * nb_copies + if not skel_closed: + width -= self.options.space + bbox.x.maximum = bbox.x.minimum + width + new = [] + for sub in path: + for _ in range(nb_copies): + new.append(copy.deepcopy(sub)) + self.offset(sub, dx, 0) + path = new + + for sub in path: + self.offset(sub, xoffset, yoffset) + + if self.options.stretch: + if not bbox.width: + raise inkex.AbortExtension( + _( + "The 'stretch' option requires that the pattern must " + "have non-zero width :\nPlease edit the pattern width." + ) + ) + for sub in path: + self.stretch(sub, length / bbox.width, 1, skelcomp[0]) + + for sub in path: + for ctlpt in sub: + self.apply_diffeomorphism( + ctlpt[1], + skelcomp, + lengths, + skel_closed, + (ctlpt[0], ctlpt[2]), + ) + + if self.options.vertical: + self.flipxy(path) + newp += path + return CubicSuperPath(newp) + + +if __name__ == "__main__": + PathAlongPath().run() -- cgit v1.2.3