Skip to content

Fractal Docs

A module for viewing fractals

Basic usage:

heighway = HeighwayDragon()
heighway.iterate(15) # More than 15 iterations can cause crashing
heighway.plot()
The fractals available are

BinaryTree #

Bases: DragonFractal

Creates Binary Trees Based off of Larry Riddle's Webpage

See Documentation for DragonFractal for implementation details and attached resource for creation Details

Source code in fractal.py
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
class BinaryTree(DragonFractal):
    """
    Creates Binary Trees Based off of
    [Larry Riddle's Webpage](https://larryriddle.agnesscott.org/ifs/pythagorean/symbinarytree.htm )

    See Documentation for [DragonFractal](index.md#fractal.DragonFractal)
    for implementation details
    and attached resource for creation Details
    """
    def __init__(self, B_r: float, theta: float):
        super().__init__(
            S0=[0, 1j],
            func_list=[
                lambda z: B_r * z * cmath.rect(1, theta) + 1j,
                lambda z: B_r * z * cmath.rect(1, -theta) + 1j,
            ],
        )
        self.iterations = 0

    def iterate(self, i: int):
        for _ in range(i):
            S = []
            for func in self.func_list:
                S.extend(list(map(func, self.S)))
                self._plot_list.append(S)
                self.iterations += 1
            self.S = S

    def translate(self, offset: complex, angle: float):
        for j in range(self.iterations + 1):
            # print(j)
            s_trans = [i * cmath.exp(angle * 1j) + offset
                       for i in self._plot_list[j]]
            self._plot_list.append(s_trans)

DragonFractal #

Bases: Fractal

A class used to draw and calculate Fractals that require nans inbetween segments

Based loosely on research by Larry Riddle

Source code in fractal.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
class DragonFractal(Fractal):
    """A class used to draw and calculate Fractals
    that require nans inbetween segments

    Based loosely on research by
    [Larry Riddle](https://larryriddle.agnesscott.org/ifs/ifs.htm)
    """

    def segment(self, points: list[complex]) -> list:
        len_ = len(self._S0)
        lines = []
        for i in range(len(points) - len_ + 1):
            if i % len_ == 0:
                lines.extend(points[i:i + len_])
                lines.append(np.nan)
        return lines

    def plot(self, autoscale=True):
        self._plot_list = [self.segment(s) for s in self._plot_list]
        super().plot(autoscale)

    if gif:
        def save_gif(self, iterations: int, duration=1000):
            self.tile()
            frames = [self._gif_plot()]
            for _ in range(iterations - 1):
                self.iterate(1)
                self._plot_list = [self.segment(s) for s in self._plot_list]
                self.tile()
                frame = self._gif_plot()
                frames.append(frame)
            gif.save(
                frames,
                "{0}_{1}.gif".format(type(self).__name__, iterations),
                duration
            )

Flowsnake #

Bases: DragonFractal

Flowsnake inheriting from :class:~fractal.DragonFractal

Source code in fractal.py
637
638
639
640
641
642
class Flowsnake(DragonFractal):
    """[Flowsnake](https://larryriddle.agnesscott.org/ifs/ksnow/flowsnake.htm) inheriting from :class:`~fractal.DragonFractal`"""
    limits = [-1, 2, -0.4, 1]

    def __init__(self):
        super().__init__(S0i, IFS_function["flowsnake"])

Fractal #

A class used to draw and calculate Fractals.

Fractals can be generated by using one of the premade classes, or by creating a new fractal with arbitrary starting points and functions. Note: The majority of funtion systems don't result in fractals.

Once created, fractals are iterated using (index.md#Fractal.iterate)

Based on research by Larry Riddle

Attributes#

S : list[complex]

Default S0 = (0 + 0j) to (1 + 0j)
the current points of the fractal

func_list : list[functions]

the functions to be applied every iteration

Methods#

iterate(i: int) Applies func_list to the current points, S, and updates S to match

plot() Plots the points S using matplotlib

save_gif(iterations: int, duration: int = 1000) saves a gif at 'name _iterations.gif' with a frame duration of duration milliseconds

Source code in fractal.py
 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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
class Fractal:
    """A class used to draw and calculate Fractals.

    Fractals can be generated by using one of the premade classes, or by
    creating a new fractal with arbitrary starting points and functions.
    Note: The majority of funtion systems don't result in fractals.

    Once created, fractals are iterated using (index.md#Fractal.iterate)

    Based on research by
    [Larry Riddle](https://larryriddle.agnesscott.org/ifs/ifs.htm)

    Attributes
    ----------
    S : list[complex]

        Default S0 = (0 + 0j) to (1 + 0j)
        the current points of the fractal

    func_list : list[functions]

        the functions to be applied every iteration


    Methods
    -------
    iterate(i: int)
        Applies func_list to the current points, S, and updates S to match

    plot()
        Plots the points S using matplotlib

    save_gif(iterations: int, duration: int = 1000)
    saves a gif at '__name__ _iterations.gif' with a frame duration of duration
    milliseconds

    """

    limits = False

    def __init__(self, S0: list[complex], func_list: list[Callable]):
        """
        Parameters
        ----------
        S0 : list[complex]
            The initial points to iterate
        func_list : list[Callable]
            A list of funtions that determines the function system

        Returns
        -------
        Fractal

        """
        self._S0 = S0
        self.S = S0
        self.func_list = func_list
        self._plot_list = [S0]
        self.plot_handle = []

    def iterate(self, i: int) -> None:
        """maps the functions to S, reassigning S

        if used with the gif package
        clears the plot list (ensures proper gif plotting)
        appends S back to plot list

        Parameters
        ----------
        i : int
            The number of iterations to advance from the current state

        Returns
        -------
        None

        """
        self._plot_list.clear()
        for _ in range(i):
            S = []
            for func in self.func_list:
                S.extend(list(map(func, self.S)))
            self.S = S
        self._plot_list.append(S)

    # Rotate and translate (in that order) & create a copy
    def translate(self, offset: complex, angle: float) -> None:
        """Translate and rotate the fractal in the complex plane

        Parameters
        ----------
        offset : complex
            The vector to use as offset
        angle : float
            The angle (in radians) to rotate

        Returns
        -------
        None

        """
        s_trans = [i * cmath.exp(angle * 1j) + offset
                   for i in self._plot_list[0]]
        self._plot_list.append(s_trans)

    def tile(self):
        pass

    def plot(self, autoscale=True):
        """Plots the fractal for human viewing"""
        self.tile()
        fig = plt.figure()
        ax = fig.add_subplot(111)
        self.plot_handle = [
            (ax.plot(
                np.real(s)[:len(s)//len(self.func_list)],
                np.imag(s)[:len(s)//len(self.func_list)], color="tab:blue"),
            ax.plot(
                np.real(s)[len(s)//len(self.func_list):],
                np.imag(s)[len(s)//len(self.func_list):], color="tab:red")) for s in self._plot_list
        ]
        if self.limits and not autoscale:
            ax.set_xlim(self.limits[:2])
            ax.set_ylim(self.limits[2:])
        ax.set_aspect("equal")
        ax.axis("off")
        plt.show()

    # Not intended for Call except through save_gif method
    if gif:

        @gif.frame
        def _gif_plot(self) -> None:
            fig = plt.figure()
            ax = fig.add_subplot(111)
            if self.limits:
                ax.set_xlim(self.limits[:2])
                ax.set_ylim(self.limits[2:])
            else:
                ax.set_aspect("equal")
            for s in self._plot_list:
                ax.plot(np.real(s), np.imag(s), color="tab:blue")

        def save_gif(self, iterations: int, duration: int = 1000) -> None:
            """
            Create a gif with a specific duration in milliseconds

            Parameters
            ----------
            iterations : int
                the number of iterations to include
            duration : int, optional
                The time in milliseconds for the gif to run.
                The default is 1000.

            Returns
            -------
            None

            """
            self.tile()
            frames = [self._gif_plot()]
            for _ in range(iterations - 1):
                self.iterate(1)
                self.tile()
                frame = self._gif_plot()
                frames.append(frame)
            gif.save(
                frames,
                f"{type(self).__name__}_{iterations}.gif",
                duration
            )

__init__(S0, func_list) #

Parameters#

S0 : list[complex] The initial points to iterate func_list : list[Callable] A list of funtions that determines the function system

Returns#

Fractal

Source code in fractal.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def __init__(self, S0: list[complex], func_list: list[Callable]):
    """
    Parameters
    ----------
    S0 : list[complex]
        The initial points to iterate
    func_list : list[Callable]
        A list of funtions that determines the function system

    Returns
    -------
    Fractal

    """
    self._S0 = S0
    self.S = S0
    self.func_list = func_list
    self._plot_list = [S0]
    self.plot_handle = []

iterate(i) #

maps the functions to S, reassigning S

if used with the gif package clears the plot list (ensures proper gif plotting) appends S back to plot list

Parameters#

i : int The number of iterations to advance from the current state

Returns#

None

Source code in fractal.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def iterate(self, i: int) -> None:
    """maps the functions to S, reassigning S

    if used with the gif package
    clears the plot list (ensures proper gif plotting)
    appends S back to plot list

    Parameters
    ----------
    i : int
        The number of iterations to advance from the current state

    Returns
    -------
    None

    """
    self._plot_list.clear()
    for _ in range(i):
        S = []
        for func in self.func_list:
            S.extend(list(map(func, self.S)))
        self.S = S
    self._plot_list.append(S)

plot(autoscale=True) #

Plots the fractal for human viewing

Source code in fractal.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def plot(self, autoscale=True):
    """Plots the fractal for human viewing"""
    self.tile()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    self.plot_handle = [
        (ax.plot(
            np.real(s)[:len(s)//len(self.func_list)],
            np.imag(s)[:len(s)//len(self.func_list)], color="tab:blue"),
        ax.plot(
            np.real(s)[len(s)//len(self.func_list):],
            np.imag(s)[len(s)//len(self.func_list):], color="tab:red")) for s in self._plot_list
    ]
    if self.limits and not autoscale:
        ax.set_xlim(self.limits[:2])
        ax.set_ylim(self.limits[2:])
    ax.set_aspect("equal")
    ax.axis("off")
    plt.show()

save_gif(iterations, duration=1000) #

Create a gif with a specific duration in milliseconds

Parameters#

iterations : int the number of iterations to include duration : int, optional The time in milliseconds for the gif to run. The default is 1000.

Returns#

None

Source code in fractal.py
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def save_gif(self, iterations: int, duration: int = 1000) -> None:
    """
    Create a gif with a specific duration in milliseconds

    Parameters
    ----------
    iterations : int
        the number of iterations to include
    duration : int, optional
        The time in milliseconds for the gif to run.
        The default is 1000.

    Returns
    -------
    None

    """
    self.tile()
    frames = [self._gif_plot()]
    for _ in range(iterations - 1):
        self.iterate(1)
        self.tile()
        frame = self._gif_plot()
        frames.append(frame)
    gif.save(
        frames,
        f"{type(self).__name__}_{iterations}.gif",
        duration
    )

translate(offset, angle) #

Translate and rotate the fractal in the complex plane

Parameters#

offset : complex The vector to use as offset angle : float The angle (in radians) to rotate

Returns#

None

Source code in fractal.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def translate(self, offset: complex, angle: float) -> None:
    """Translate and rotate the fractal in the complex plane

    Parameters
    ----------
    offset : complex
        The vector to use as offset
    angle : float
        The angle (in radians) to rotate

    Returns
    -------
    None

    """
    s_trans = [i * cmath.exp(angle * 1j) + offset
               for i in self._plot_list[0]]
    self._plot_list.append(s_trans)

FudgeFlake #

Bases: Terdragon

Fudgeflake Fractal inheriting from :class:fractal.Terdragon

Source code in fractal.py
540
541
542
543
544
545
546
547
class FudgeFlake(Terdragon):
    """[Fudgeflake](https://larryriddle.agnesscott.org/ifs/heighway/fudgeflake.htm) Fractal inheriting from :class:`fractal.Terdragon`"""

    limits = -0.55, 1.6, -0.4, 1.04

    def tile(self):
        self.translate(0, math.pi / 3)
        self.translate(1, 2 * math.pi / 3)

GoldenDragon #

Bases: DragonFractal

Golden Dragon Fractal inheriting from :class:~fractal.DragonFractal

Source code in fractal.py
522
523
524
525
526
527
528
class GoldenDragon(DragonFractal):
    """[Golden Dragon](https://larryriddle.agnesscott.org/ifs/heighway/goldenDragon.htm) Fractal inheriting from :class:`~fractal.DragonFractal`"""

    limits = (-0.317, 1.16, -0.243, 0.616)

    def __init__(self):
        super().__init__(S0i, IFS_function["golden_dragon"])

GoldenFlake #

Bases: BinaryTree

GoldenFlake

Source code in fractal.py
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
class GoldenFlake(BinaryTree):
    """[GoldenFlake](https://larryriddle.agnesscott.org/ifs/pentagon/Durer.htm)"""
    limits = -1.64, 1.64, -1.09, 1.09

    def __init__(self):
        super().__init__(1 / PHI, 0.8 * math.pi)

    def tile(self):
        for angle in np.linspace(0, 2 * math.pi, 6):
            self.translate(0, angle)

    def iterate(self, i):
        for _ in range(i):
            self._plot_list.clear()
            S = []
            for func in self.func_list:
                S.extend(list(map(func, self.S)))
                self._plot_list.append(S)
                self.iterations += 1
            self.S = S

HeighwayDragon #

Bases: DragonFractal

Heighway Dragon Fractal inheriting from :class:~fractal.DragonFractal

Source code in fractal.py
505
506
507
508
509
510
class HeighwayDragon(DragonFractal):
    """[Heighway Dragon](https://larryriddle.agnesscott.org/ifs/heighway/heighway.htm) Fractal inheriting from :class:`~fractal.DragonFractal`"""
    limits = (-0.407, 1.24, -0.382, 0.714)

    def __init__(self):
        super().__init__(S0i, func_list=IFS_function["dragon"])

KochFlake #

Bases: Fractal

Koch Flake inheriting from :class:~fractal.Fractal

Note: this is constructed as a koch curve, then tiled.

Source code in fractal.py
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
class KochFlake(Fractal):
    """
    [Koch Flake](https://larryriddle.agnesscott.org/ifs/kcurve/kcurve.htm) inheriting from :class:`~fractal.Fractal`

    Note: this is constructed as a koch curve, then tiled.
    """

    limits = -0.5, 1.5, -0.924, 0.346

    def __init__(self):
        super().__init__(S0i, func_list=IFS_function["koch_flake"])

    def tile(self):
        translations = [
            (cmath.rect(-1, 2 * math.pi / 3), 2 * math.pi / 3),
            (1, -2 * math.pi / 3),
        ]
        for off, theta in translations:
            self.translate(off, theta)

LevyC #

Bases: Fractal

Levy C Curve inheriting from :class:~fractal.Fractal

Source code in fractal.py
550
551
552
553
554
555
556
class LevyC(Fractal):
    """[Levy C Curve](https://larryriddle.agnesscott.org/ifs/levy/levy.htm) inheriting from :class:`~fractal.Fractal`"""

    limits = -0.6, 1.6, -1.06, 0.308

    def __init__(self):
        super().__init__(S0=[0, 1], func_list=IFS_function["levy_c"])

LevyTapestryInside #

Bases: LevyC

Levy Tapestry inheriting from :class:~fractal.LevyC

Source code in fractal.py
568
569
570
571
572
573
574
575
576
class LevyTapestryInside(LevyC):
    """[Levy Tapestry](https://larryriddle.agnesscott.org/ifs/levy/tapestryInside.htm) inheriting from :class:`~fractal.LevyC`"""

    limits = -1.2, 2.2, -1.6, 0.6

    def tile(self):
        translations = [(-1j, math.pi * 0.5), (1, -math.pi * 0.5), (1 - 1j, math.pi)]
        for off, theta in translations:
            self.translate(off, theta)

LevyTapestryOutside #

Bases: LevyC

Levy Tapestry inheriting from :class:~fractal.LevyC

Source code in fractal.py
559
560
561
562
563
564
565
class LevyTapestryOutside(LevyC):
    """[Levy Tapestry](https://larryriddle.agnesscott.org/ifs/levy/tapestryOutside.htm) inheriting from :class:`~fractal.LevyC`"""

    limits = -1.1, 2.1, -1.08, 1.08

    def tile(self):
        self.translate(1, math.pi)

Pentadendrite #

Bases: Fractal

Pentadendrite inheriting from :class:~fractal.Fractal

Source code in fractal.py
600
601
602
603
604
605
606
class Pentadendrite(Fractal):
    """[Pentadendrite](https://larryriddle.agnesscott.org/ifs/pentaden/penta.htm) inheriting from :class:`~fractal.Fractal` """

    limits = 0.85, 1.85, -0.152, 1.622

    def __init__(self):
        super().__init__(S0=[0, 1], func_list=IFS_function["pentadendrite"])

Pentigree #

Bases: Fractal

Pentigree inheriting from :class:~fractal.Fractal

Source code in fractal.py
614
615
616
617
618
619
620
621
622
623
624
class Pentigree(Fractal):
    """[Pentigree](https://larryriddle.agnesscott.org/ifs/pentaden/pentigree.htm) inheriting from :class:`~fractal.Fractal`"""
    limits = -0.4, 1.3, -0.312, 0.8

    def __init__(self):
        super().__init__(S0i, IFS_function["pentigree"])

    def tile(self):
        translations = zip(PENTAGON[:4], np.arange(72, 361, 72) * math.pi / 180)
        for offset, angle in translations:
            self.translate(offset, angle)

Terdragon #

Bases: Fractal

Terdragon Fractal inheriting from :class:~fractal.Fractal

Source code in fractal.py
531
532
533
534
535
536
537
class Terdragon(Fractal):
    """[Terdragon](https://larryriddle.agnesscott.org/ifs/heighway/terdragon.htm) Fractal inheriting from :class:`~fractal.Fractal`"""

    limits = (-0.12, 1.12, -0.357, 0.357)

    def __init__(self):
        super().__init__(S0i, func_list=IFS_function["terdragon"])

TwinDragon #

Bases: DragonFractal

Twin Dragon Fractal inheriting from :class:~fractal.DragonFractal

Source code in fractal.py
513
514
515
516
517
518
519
class TwinDragon(DragonFractal):
    """[Twin Dragon](https://larryriddle.agnesscott.org/ifs/heighway/twindragon.htm) Fractal inheriting from :class:`~fractal.DragonFractal`"""

    limits = (-0.4, 1.4, -0.75, 0.75)

    def __init__(self):
        super().__init__(S0_TWIN, IFS_function["twin_dragon"])