geomotif.motifs.illusions
¶
Impossible figures and interference patterns.
Two different tricks share this module because both are about what a drawing can say that an object cannot.
The impossible figures -- :class:PenroseTriangle, :class:PenroseStairs,
:class:ImpossibleCube and the honestly ambiguous :class:NeckerCube -- all
lean on the same property of a parallel projection: it throws away depth. In an
isometric view, going one unit up is drawn exactly like going one unit away
along each of the two horizontal axes, so a figure whose ends fail to meet in
space by (t, t, t) meets itself perfectly on the page. That is not a fudge
in the drawing; it is the whole of why these figures work, and both Penrose
constructions here are built on it directly rather than by nudging coordinates
until they line up.
:class:CafeWall and :class:MoirePattern are the other kind: nothing about
them is impossible, and they still refuse to be seen straight. The cafe wall's
mortar lines are exactly parallel and the moire's fringes are not drawn at all
-- they are what two regular patterns make between them.
Classes:
| Name | Description |
|---|---|
PenroseTriangle |
The tribar: three square beams meeting at three right angles. |
PenroseStairs |
The endless staircase: four flights, every step up, back where you began. |
NeckerCube |
A wireframe cube with nothing to say which face is in front. |
ImpossibleCube |
The same cube, told two contradictory things about which face is nearer. |
CafeWall |
Parallel mortar lines that refuse to look parallel. |
MoirePattern |
Two regular patterns laid over each other, and the fringes between them. |
PenroseTriangle
dataclass
¶
Bases: Motif
The tribar: three square beams meeting at three right angles.
Each beam is drawn as the silhouette of a long cuboid seen isometrically, and the three are the same beam turned by a third of a revolution. Every beam passes in front of the next one round, which is the whole trick: locally each joint is an ordinary right angle, and following them round gets you back underneath where you started.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
float
|
Width of the finished figure. |
240.0
|
thickness
|
float
|
Beam width as a fraction of its length. Thin beams give the spidery version, fat ones the chunky Escher version. |
0.25
|
center
|
(float, float)
|
Middle of the figure. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
beams |
Return the three beams as their outlines, before any is hidden. |
beams
¶
Return the three beams as their outlines, before any is hidden.
Source code in src/geomotif/motifs/illusions.py
PenroseStairs
dataclass
¶
PenroseStairs(steps: int = 5, rise: float = 0.4, width: float = 1.8, size: float = 280.0, center: Point = (0.0, 0.0))
Bases: Motif
The endless staircase: four flights, every step up, back where you began.
Built in space and then flattened, rather than drawn flat and fudged. Four
flights of equal step count run round a rectangle, each step rising by
rise; after a full circuit the walk has failed to close by exactly
(t, t, t), and an isometric view sends that to nothing. Two opposite
flights have to be longer than the other two by four times the rise for the
error to come out equal on all three axes -- which is why a real drawing of
this staircase is never quite square.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
steps
|
int
|
Steps per flight. |
5
|
rise
|
float
|
Height of one step, in units of the short flight's tread. |
0.4
|
width
|
float
|
How deep each tread is, in the same units. |
1.8
|
size
|
float
|
Width of the finished figure. |
280.0
|
center
|
(float, float)
|
Middle of the figure. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
walk |
Return the corners of the stepped band's outer edge, in space. |
walk
¶
Return the corners of the stepped band's outer edge, in space.
Three points per step: the foot of the riser, its top, and the far end of the tread.
Source code in src/geomotif/motifs/illusions.py
NeckerCube
dataclass
¶
NeckerCube(size: float = 180.0, depth: float = 0.45, angle: float = pi / 4.0, center: Point = (0.0, 0.0))
Bases: _CubeBase
A wireframe cube with nothing to say which face is in front.
All twelve edges drawn, none broken. Louis Necker noticed in 1832 that the same drawing flips between two solid cubes as you look at it, and it does so because nothing in it is wrong -- the drawing is simply true of both.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
float
|
Width of the finished figure. |
180.0
|
depth
|
float
|
How far the far face is offset, as a fraction of the near face's width. |
0.45
|
angle
|
float
|
Which way it is offset, in radians. |
pi / 4.0
|
center
|
(float, float)
|
Middle of the figure. |
(0.0, 0.0)
|
ImpossibleCube
dataclass
¶
ImpossibleCube(size: float = 180.0, depth: float = 0.45, angle: float = pi / 4.0, center: Point = (0.0, 0.0), gap: float = 0.06)
Bases: _CubeBase
The same cube, told two contradictory things about which face is nearer.
The near and far faces cross each other twice. At one crossing the far edge is broken, which says the near face is in front; at the other the near edge is broken, which says the opposite. Either break alone would be an ordinary solid cube; together they are Escher's.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
float
|
As :class: |
180.0
|
depth
|
float
|
As :class: |
180.0
|
angle
|
float
|
As :class: |
180.0
|
center
|
float
|
As :class: |
180.0
|
gap
|
float
|
Length of the break, as a fraction of the size. |
0.06
|
CafeWall
dataclass
¶
CafeWall(cols: int = 8, rows: int = 6, size: float = 40.0, mortar: float = 3.0, shift: float = 0.5, hatch: int = 4, center: Point = (0.0, 0.0))
Bases: Motif
Parallel mortar lines that refuse to look parallel.
Rows of tiles, every other row shifted sideways, with a line of mortar between them. The dark tiles are hatched rather than filled, which is what a plotter can draw -- and the illusion needs only the contrast, not the ink. Every mortar line is exactly horizontal; none of them looks it.
Named for a cafe in Bristol whose tiling did this to passers-by.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cols
|
int
|
How many tiles across and down. |
8
|
rows
|
int
|
How many tiles across and down. |
8
|
size
|
float
|
Side of one tile. |
40.0
|
mortar
|
float
|
Gap between rows. |
3.0
|
shift
|
float
|
How far every other row is displaced, as a fraction of a tile. The illusion is strongest around a quarter to a half. |
0.5
|
hatch
|
int
|
Lines drawn across each dark tile. |
4
|
center
|
(float, float)
|
Middle of the wall. |
(0.0, 0.0)
|
MoirePattern
dataclass
¶
MoirePattern(kind: MoireKind = 'rings', count: int = 34, spacing: float = 6.0, offset: float = 26.0, angle: float = 0.06, center: Point = (0.0, 0.0))
Bases: Motif
Two regular patterns laid over each other, and the fringes between them.
Nothing draws the fringes. They are where the two patterns nearly agree, and they move much faster than either pattern does -- shift one grating by a hair and the bands sweep across the whole figure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
|
'rings'
|
count
|
int
|
Lines or circles in each of the two patterns. |
34
|
spacing
|
float
|
Distance between neighbouring lines or circles. |
6.0
|
offset
|
float
|
How far apart the two patterns' middles are. |
26.0
|
angle
|
float
|
How far the second pattern is turned, in radians. A very small angle
gives very wide fringes. Concentric circles look the same however far
you turn them, so |
0.06
|
center
|
(float, float)
|
Middle of the first pattern. |
(0.0, 0.0)
|
Methods:
| Name | Description |
|---|---|
family |
Return one of the two patterns, placed and turned. |
family
¶
family(at: Point, turn: float) -> tuple[Path, ...]
Return one of the two patterns, placed and turned.