summaryrefslogtreecommitdiffstats
path: root/share/extensions/fractalize.py
blob: dca72de38e7d9e4a1ab55990aae9ff4388ebd456 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
# coding=utf-8
#
# Copyright (C) 2005 Carsten Goetze c.goetze@tu-bs.de
#
# 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 math
import random
import inkex
from inkex.paths import Move, Line


def calculate_subdivision(smoothness, x1, y1, x2, y2):
    # Calculate the vector from (x1,y1) to (x2,y2)
    x3 = x2 - x1
    y3 = y2 - y1
    # Calculate the point half-way between the two points
    hx = x1 + x3 / 2
    hy = y1 + y3 / 2
    # Calculate normalized vector perpendicular to the vector (x3,y3)
    length = math.sqrt(x3 * x3 + y3 * y3)
    if length != 0:
        nx = -y3 / length
        ny = x3 / length
    else:
        nx = 1
        ny = 0
    # Scale perpendicular vector by random factor """
    r = random.uniform(-length / (1 + smoothness), length / (1 + smoothness))
    nx = nx * r
    ny = ny * r
    # add scaled perpendicular vector to the half-way point to get the final displaced subdivision point
    x = hx + nx
    y = hy + ny
    return (x, y)


class Fractalize(inkex.EffectExtension):
    def add_arguments(self, pars):
        pars.add_argument(
            "-s", "--subdivs", type=int, default=6, help="Number of subdivisons"
        )
        pars.add_argument(
            "-f",
            "--smooth",
            type=float,
            default=4.0,
            help="Smoothness of the subdivision",
        )

    def effect(self):
        for node in self.svg.selection.filter(inkex.PathElement):
            path = node.path.to_absolute()
            result = []
            for cmd_proxy in path.proxy_iterator():  # type: inkex.Path.PathCommandProxy
                prev = cmd_proxy.previous_end_point
                end = cmd_proxy.end_point
                if cmd_proxy.letter == "M":
                    result.append(Move(*cmd_proxy.args))
                else:
                    for seg in self.fractalize(
                        (prev.x, prev.y, end.x, end.y),
                        self.options.subdivs,
                        self.options.smooth,
                    ):
                        result.append(Line(*seg))
                    result.append(Line(end.x, end.y))

            node.path = result

    def fractalize(self, coords, subdivs, smooth):
        """recursively subdivide the segments left and right of the subdivision"""
        subdiv_point = calculate_subdivision(smooth, *coords)

        if subdivs:
            # recursively subdivide the segment left of the subdivision point
            for left_seg in self.fractalize(
                coords[:2] + subdiv_point[-2:], subdivs - 1, smooth
            ):
                yield left_seg

            yield subdiv_point

            # recursively subdivide the segment right of the subdivision point
            for right_seg in self.fractalize(
                subdiv_point[-2:] + coords[-2:], subdivs - 1, smooth
            ):
                yield right_seg


if __name__ == "__main__":
    Fractalize().run()