geomotif.motifs.fractals
¶
Fractals: the same shape at every scale, reached three different ways.
Three constructions share this page because they produce the same kind of object by very different means, and the difference is worth seeing:
- Grammars. Koch, Hilbert, Gosper, the dragons and the Sierpinski curves
are each an axiom, a rewrite rule and a turn angle, drawn with a turtle --
see :class:
~geomotif.LSystemMotif. Every one of them below is four lines. - Recursion. :class:
SierpinskiCarpet, :class:CantorSet, :class:PythagorasTree, :class:HTreeand :class:ApollonianGasketare shapes that place smaller copies of themselves, which is a statement about squares and circles rather than about a path, so they are built directly. - Chance. :class:
IFSAttractorand :class:BarnsleyFernplay the chaos game: pick one contracting map at random, apply it, plot the point, repeat. The attractor appears without ever being drawn, and comes back as loose points rather than as a stroke.
A grammar fractal is sized by its :attr:~geomotif.LSystemMotif.step, which
is the length of one turtle move -- so its overall size is that step times a
power of the grammar's scale factor, and it changes when you change the depth.
That is the nature of the construction rather than an oversight; call
:meth:~geomotif.Design.fit when you need a fractal at a particular size. The
recursive and chaos-game motifs are not built a step at a time and do take a
plain size.
Classes:
| Name | Description |
|---|---|
KochCurve |
The original: replace the middle third of every segment with a spike. |
KochSnowflake |
Three Koch curves around a triangle: finite area, infinite perimeter. |
KochAntisnowflake |
The snowflake with its spikes turned inward, which is a different shape. |
MinkowskiSausage |
Koch's idea on a square grid: a battlement instead of a spike. |
MinkowskiIsland |
Four Minkowski sausages around a square: the quadratic Koch island. |
SierpinskiTriangle |
The gasket, drawn as one unbroken stroke that closes on itself. |
SierpinskiArrowhead |
The gasket approached along a curve instead of through a triangle. |
DragonCurve |
The Heighway dragon: fold a strip of paper in half, repeatedly, unfold. |
TwinDragon |
Two Heighway dragons back to back, enclosing a region that tiles. |
Terdragon |
The dragon done in thirds: one segment becomes three at 120 degrees. |
LevyCCurve |
Paul Levy's C curve: two half-size copies at right angles, forever. |
HilbertCurve |
The space-filling curve that keeps neighbours together. |
MooreCurve |
Hilbert's curve closed into a loop. |
PeanoCurve |
The first space-filling curve ever published, from 1890. |
GosperCurve |
The flowsnake: a space-filling curve on a hexagonal grid. |
VicsekFractal |
The box fractal: a plus sign made of plus signs. |
SierpinskiCarpet |
A square with its middle ninth removed, then again in each ninth. |
CantorSet |
Take out the middle third. Repeat. Stack the rounds to see it happen. |
PythagorasTree |
Squares on the legs of right triangles, all the way up. |
HTree |
An H whose serifs are smaller H's, and so on down. |
ApollonianGasket |
Circles packed into circles, filling every gap they leave. |
IFSMap |
One contracting map of an iterated function system, and how often to pick it. |
IFSAttractor |
The chaos game: pick a map at random, apply it, plot the point, repeat. |
BarnsleyFern |
Four affine maps and a coin, producing a fern. |
KochCurve
dataclass
¶
Bases: LSystemMotif
The original: replace the middle third of every segment with a spike.
Helge von Koch's 1904 curve, published as an example of something continuous that has a tangent nowhere. Each round quadruples the segment count while thirding the length, so the curve between two fixed points grows without limit -- which is the whole point of it.
KochSnowflake
dataclass
¶
Bases: LSystemMotif
Three Koch curves around a triangle: finite area, infinite perimeter.
The perimeter multiplies by 4/3 every round and never stops; the area converges to exactly 8/5 of the starting triangle. Both facts are easy to check on the geometry this builds, and the test suite does.
KochAntisnowflake
dataclass
¶
Bases: LSystemMotif
The snowflake with its spikes turned inward, which is a different shape.
One sign change in the rule, and the star becomes three bites taken out of
a triangle. Worth having next to :class:KochSnowflake precisely because
the grammars differ by so little and the results by so much.
MinkowskiSausage
dataclass
¶
Bases: LSystemMotif
Koch's idea on a square grid: a battlement instead of a spike.
Every segment becomes eight of a quarter the length, all of them axis aligned, which makes it the fractal of choice when the output has to look deliberate rather than organic. Eight pieces at a quarter scale puts its dimension at exactly three halves.
MinkowskiIsland
dataclass
¶
Bases: LSystemMotif
Four Minkowski sausages around a square: the quadratic Koch island.
What :class:KochSnowflake is to :class:KochCurve. The coastline never
stops growing while the area it encloses stays exactly that of the
starting square, which is the tidiest statement of the coastline paradox
there is.
SierpinskiTriangle
dataclass
¶
Bases: LSystemMotif
The gasket, drawn as one unbroken stroke that closes on itself.
The triangle with its middle removed, then again, forever. Drawing it without lifting the pen is the trick the grammar performs: the naive construction is a pile of separate triangles, while this is a single closed path that happens to trace all of them.
The same set of points falls out of :class:IFSAttractor played as a
chaos game -- one arrives as a stroke, the other as a cloud.
SierpinskiArrowhead
dataclass
¶
Bases: LSystemMotif
The gasket approached along a curve instead of through a triangle.
Two rules that swap roles at every round, tracing an open path from one
corner of the gasket to another. The limit is the same set as
:class:SierpinskiTriangle, reached by a route that looks nothing like
it at any finite depth.
DragonCurve
dataclass
¶
Bases: LSystemMotif
The Heighway dragon: fold a strip of paper in half, repeatedly, unfold.
Every crease is a right angle and the curve never crosses itself, which is far from obvious at any depth past about six. Four dragons fit together around a point with no gap, so the shape tiles the plane.
TwinDragon
dataclass
¶
Bases: LSystemMotif
Two Heighway dragons back to back, enclosing a region that tiles.
The Davis-Knuth dragon. Joining the pair closes the curve, and the region inside it is the fundamental domain of the base -1+i number system -- the reason this shape turns up in radix arithmetic as well as in art.
Terdragon
dataclass
¶
Bases: LSystemMotif
The dragon done in thirds: one segment becomes three at 120 degrees.
Threefold rather than twofold symmetry, and a much lacier result than the Heighway curve for the same number of segments.
LevyCCurve
dataclass
¶
Bases: LSystemMotif
Paul Levy's C curve: two half-size copies at right angles, forever.
It starts as a simple bracket and turns into a cauliflower. Unlike the dragons it overlaps itself freely, which is what gives the finished curve its dense, ruffled edge.
HilbertCurve
dataclass
¶
Bases: LSystemMotif
The space-filling curve that keeps neighbours together.
Hilbert's curve visits every cell of a square grid, and two cells close along the curve are always close on the plane. That property is why it is the standard order for laying out image tiles, database keys and anything else where locality has to survive being flattened to one dimension.
X and Y drive the rewriting without ever drawing: the turtle only
moves on F.
MooreCurve
dataclass
¶
Bases: LSystemMotif
Hilbert's curve closed into a loop.
Four Hilbert curves arranged so the walk returns to where it began, which makes it the space-filling curve to use when the traversal has to be cyclic rather than to start and stop somewhere.
PeanoCurve
dataclass
¶
Bases: LSystemMotif
The first space-filling curve ever published, from 1890.
Giuseppe Peano's construction predates Hilbert's by a year and divides the square into nine rather than four. It reads as a comb of combs, and it was the result that forced mathematics to take the idea of dimension seriously.
GosperCurve
dataclass
¶
Bases: LSystemMotif
The flowsnake: a space-filling curve on a hexagonal grid.
Bill Gosper's curve fills a region whose own boundary is fractal -- the Gosper island, which tiles the plane in sevens. The most beautiful thing in this module, and it is two rewrite rules.
VicsekFractal
dataclass
¶
Bases: LSystemMotif
The box fractal: a plus sign made of plus signs.
Tamas Vicsek's construction, which keeps only the center and the four edge-midpoints of each subdivided square. The saltire cross the outline traces is what stochastic versions of it are used to model -- percolation clusters and diffusion fronts.
SierpinskiCarpet
dataclass
¶
Bases: PolygonMotif
A square with its middle ninth removed, then again in each ninth.
The two-dimensional Cantor set, and the shape whose limit contains a copy of every possible one-dimensional curve. Drawn as outlines: the outer square plus every hole cut out of it, which is both what a plotter wants and what makes the construction legible.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
Rounds of subdivision. The hole count is |
4
|
size
|
float
|
Side of the outer square. |
200.0
|
center
|
(float, float)
|
Middle of the carpet. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
hole_count |
Return how many holes this carpet has at its depth. |
CantorSet
dataclass
¶
Bases: PolygonMotif
Take out the middle third. Repeat. Stack the rounds to see it happen.
The set that survives is uncountable and has length zero, which is the single most useful counterexample in analysis. Drawn the way it is always drawn: one row of bars per round, so the construction is visible rather than just its limit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
Rounds to draw. Round zero is the single unbroken bar, so |
6
|
width
|
float
|
Length of the first bar. |
240.0
|
gap
|
float
|
Vertical distance between consecutive rows. |
14.0
|
center
|
(float, float)
|
Middle of the whole stack. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
bar_count |
Return how many bars this stack draws in total. |
PythagorasTree
dataclass
¶
PythagorasTree(depth: int = 8, size: float = 60.0, lean: float = pi / 4.0, base: Point = (0.0, 0.0))
Bases: PolygonMotif
Squares on the legs of right triangles, all the way up.
Albert Bosman's 1942 construction, and a picture of the Pythagorean theorem rather than an illustration of it: the two child squares have the combined area of their parent at every level, whatever the lean, so the tree's total area grows by exactly one trunk per level.
lean is the angle at the left corner of the triangle sitting on each
square. A quarter of a right angle makes the symmetric tree; anything else
tips it, and values near zero or a right angle draw a fern-like frond.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
Branching rounds. The square count is |
8
|
size
|
float
|
Side of the trunk square. |
60.0
|
lean
|
float
|
Apex angle of the triangle, in radians. Must be strictly between zero and a right angle. |
pi / 4.0
|
base
|
(float, float)
|
Midpoint of the trunk's bottom edge -- where the tree stands. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
square_count |
Return how many squares this tree draws in total. |
HTree
dataclass
¶
Bases: PolygonMotif
An H whose serifs are smaller H's, and so on down.
Each round adds a perpendicular segment at both ends of every existing one, shortened by the square root of two so the arms of successive levels stay in proportion. It is the standard layout for a clock distribution network on a chip, because every leaf is exactly the same wire length from the root.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
Branching rounds. The segment count is |
6
|
size
|
float
|
Length of the first, longest bar. |
220.0
|
center
|
(float, float)
|
Middle of the tree, which is the middle of that first bar. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
segment_count |
Return how many bars this tree draws in total. |
ApollonianGasket
dataclass
¶
ApollonianGasket(depth: int = 5, radius: float = 150.0, min_radius: float = 0.004, center: Point = (0.0, 0.0))
Bases: Motif
Circles packed into circles, filling every gap they leave.
Start with three circles that touch, drop the largest circle that fits in the gap between them, and repeat on the three new gaps. Apollonius of Perga posed the underlying problem; Descartes' circle theorem solves it in one line, which is what this uses.
The default packing is the integral gasket (-1, 2, 2, 3): every circle
in it has an integer curvature, which is a fact about this particular
starting configuration rather than about gaskets in general.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
Rounds of gap-filling. Each round triples the number of gaps. |
5
|
radius
|
float
|
Radius of the outer circle. |
150.0
|
min_radius
|
float
|
Stop subdividing once a circle would be smaller than this fraction of the outer radius. Necessary as well as kind: the packing is infinite, and depth alone does not bound how small it gets. |
0.004
|
center
|
(float, float)
|
Middle of the outer circle. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
circles |
Return every circle in the packing as a |
circles
¶
Return every circle in the packing as a (center, radius) pair.
Source code in src/geomotif/motifs/fractals.py
IFSMap
dataclass
¶
IFSMap(transform: Affine, weight: float = 1.0)
One contracting map of an iterated function system, and how often to pick it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transform
|
Affine
|
The map itself. It must contract -- shrink distances -- or the chaos game runs away instead of settling onto an attractor. |
required |
weight
|
float
|
Relative probability of choosing this map. Weights are normalized, so only their ratios matter. Setting it near each map's area scale factor is what keeps the resulting cloud evenly dense. |
1.0
|
IFSAttractor
dataclass
¶
IFSAttractor(maps: tuple[IFSMap, ...] = _GASKET_MAPS, count: int = 20000, size: float = 240.0, center: Point = (0.0, 0.0), *, seed: int = 0)
Bases: Motif
The chaos game: pick a map at random, apply it, plot the point, repeat.
Michael Barnsley's construction, and the most surprising thing in this module. A handful of contracting affine maps have exactly one compact set they leave unchanged, and iterating them at random converges onto it from any starting point whatsoever -- so the attractor draws itself without anyone ever computing where it is.
The default maps halve towards the corners of a triangle and produce the
Sierpinski gasket, which is the same set :class:SierpinskiTriangle
traces as a single stroke. Comparing the two is the cheapest way to see
what a fractal actually is, as opposed to how one is drawn.
Produces loose points rather than a stroke: the order they arrive in is random, so joining them would draw noise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
maps
|
tuple of IFSMap
|
The system to iterate. Each map should contract; the weights are relative and get normalized. |
_GASKET_MAPS
|
count
|
int
|
Points to plot. More fills the attractor in more finely, and costs linearly. |
20000
|
size
|
float
|
Largest extent of the finished cloud. The attractor is measured and rescaled, since its own coordinates depend on the maps. |
240.0
|
seed
|
int
|
Seeds a private generator, so the same seed always gives the same cloud and the design stays reproducible from its metadata. |
0
|
center
|
(float, float)
|
Middle of the cloud's bounding box. |
(0.0, 0.0)
|
BarnsleyFern
dataclass
¶
Bases: Motif
Four affine maps and a coin, producing a fern.
The canonical iterated function system, and still the most persuasive argument that a very short description can encode a very complicated shape: twenty-four numbers, listed in the source, and the result has a stem, fronds and leaflets that were never drawn.
The same engine with different numbers is :class:IFSAttractor. This is
here because the fern is the example everybody arrives looking for.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
count
|
int
|
Points to plot. The fern needs tens of thousands before its leaflets fill in. |
40000
|
size
|
float
|
Largest extent of the finished plant, which is its height. |
300.0
|
seed
|
int
|
Seeds a private generator, so the same seed always gives the same fern. |
0
|
center
|
(float, float)
|
Middle of the plant's bounding box. |
(0.0, 0.0)
|