1#!/usr/local/bin/python3.8
2# coding=utf-8
3#
4# Copyright (C) 2005 Carsten Goetze c.goetze@tu-bs.de
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19#
20import math
21import random
22import inkex
23from inkex.paths import Move, Line
24
25def calculate_subdivision(smoothness, x1, y1, x2, y2):
26    # Calculate the vector from (x1,y1) to (x2,y2)
27    x3 = x2 - x1
28    y3 = y2 - y1
29    # Calculate the point half-way between the two points
30    hx = x1 + x3 / 2
31    hy = y1 + y3 / 2
32    # Calculate normalized vector perpendicular to the vector (x3,y3)
33    length = math.sqrt(x3 * x3 + y3 * y3)
34    if length != 0:
35        nx = -y3 / length
36        ny = x3 / length
37    else:
38        nx = 1
39        ny = 0
40    # Scale perpendicular vector by random factor """
41    r = random.uniform(-length / (1 + smoothness), length / (1 + smoothness))
42    nx = nx * r
43    ny = ny * r
44    # add scaled perpendicular vector to the half-way point to get the final displaced subdivision point
45    x = hx + nx
46    y = hy + ny
47    return (x, y)
48
49
50class Fractalize(inkex.EffectExtension):
51    def add_arguments(self, pars):
52        pars.add_argument("-s", "--subdivs", type=int, default="6",
53                          help="Number of subdivisons")
54        pars.add_argument("-f", "--smooth", type=float, default="4.0",
55                          help="Smoothness of the subdivision")
56
57    def effect(self):
58        for node in self.svg.selection.filter(inkex.PathElement):
59            path = node.path.to_absolute()
60            result = []
61            for cmd_proxy in path.proxy_iterator():  # type: inkex.Path.PathCommandProxy
62                prev = cmd_proxy.previous_end_point
63                end = cmd_proxy.end_point
64                if cmd_proxy.letter == 'M':
65                    result.append(Move(*cmd_proxy.args))
66                else:
67                    for seg in self.fractalize((prev.x, prev.y, end.x, end.y), self.options.subdivs,
68                                               self.options.smooth):
69                        result.append(Line(*seg))
70                    result.append(Line(end.x, end.y))
71
72            node.path = result
73
74    def fractalize(self, coords, subdivs, smooth):
75        """recursively subdivide the segments left and right of the subdivision"""
76        subdiv_point = calculate_subdivision(smooth, *coords)
77
78        if subdivs:
79            # recursively subdivide the segment left of the subdivision point
80            for left_seg in self.fractalize(coords[:2] + subdiv_point[-2:], subdivs - 1, smooth):
81                yield left_seg
82
83            yield subdiv_point
84
85            # recursively subdivide the segment right of the subdivision point
86            for right_seg in self.fractalize(subdiv_point[-2:] + coords[-2:], subdivs - 1, smooth):
87                yield right_seg
88
89if __name__ == '__main__':
90    Fractalize().run()
91