Soccer Ball Goldberg

examples/soccer-ball-goldberg/

A soccer ball as a Goldberg polyhedron: a bmesh icosphere truncated at 1/3 per edge, faces ordered by link-topology walks, panels bound by face vertex count.

Rendered headless by the example itself. Select it to enlarge.

witnesses Goldberg invariants are closed forms — counts, degree 3, uniform edges, planar faces, one circumsphere — and panels bind by vertex count, never enumeration order.

tags bmesh mesh

runs on 4.5 LTS · 5.1 · 5.2 LTS

blender --background --python examples/soccer-ball-goldberg/soccer_ball_goldberg.py --

falsify it Breaks the contract on purpose and must exit 13

blender --background --python examples/soccer-ball-goldberg/soccer_ball_goldberg.py -- --invert-bind

A runnable example that builds a soccer ball as a Goldberg polyhedron: a bmesh icosphere truncated at exactly 1/3 of every edge, yielding the truncated icosahedron — 12 pentagons, 20 hexagons — with every face ordered by walking the source mesh's own link topology (fan walk per vertex, loop order per triangle). Nothing is hand-listed, following mesh-editing-and-bmesh and the always-free-bmesh rule.

Sibling, not twin: bmesh-gear witnesses parametric extrusion ownership (counts from construction parameters, watertightness). This witnesses polyhedral topology invariants — the truncated icosahedron is an Archimedean solid, so its counts, vertex degree, edge uniformity, face planarity, and circumsphere are all closed forms — plus per-face-class material binding driven by face vertex count, never by enumeration order.

What it witnesses: truncation at 1/3 makes every new edge the same length (a/3), which is what puts all 60 vertices on one sphere. The check asserts, with tolerances printed on success:

Tolerance basis: mesh coordinates are float32 and create_icosphere's own trig lands the invariants at a deterministic ~9e-6 noise floor (measured values are byte-identical on 4.5.11 and 5.1.2). The 3e-5 gates sit ~3x above that floor; any genuine contract break is orders of magnitude larger.

Version witness: check output is byte-identical on Blender 4.5.11 LTS and 5.1.2 — same counts, same deviations. One authoring hazard surfaced and is noted in the code: Object.to_mesh_clear() takes no argument on current Blender (passing the mesh raises TypeError) — see depsgraph-and-evaluated-data.

The render is the proof: the faceted Goldberg cage is smoothed by an unapplied Subsurf modifier (panel materials carry through Catmull-Clark per face class), and the ball is grounded by its depsgraph-evaluated lowest vertex. Invert the panel binding and the still inverts with it: white pentagons on a black ball, wrong on sight.

Staging (render path only)#

None of this touches the checked mesh, its binding, or any check.

Stage deviation#

The floor is dark mown turf rather than the near-black studio floor: a ball alone on a black stage read as an object in a void, and the pitch is the context that makes it a match ball. The back wall and warm wedge are the default stage; measured stage luminance 0.217 sits inside the calibration set.

Run#

# Cheap correctness check (no render) — the CI check:
blender --background --python soccer_ball_goldberg.py --

# Falsifier: swap pentagon/hexagon slots. Must exit non-zero.
blender --background --python soccer_ball_goldberg.py -- --invert-bind

# Also render a still (EEVEE on a GPU host; use --engine cycles on GPU-less hosts):
blender --background --python soccer_ball_goldberg.py -- --output ball.png
blender --background --python soccer_ball_goldberg.py -- --output ball.png --engine cycles

Exit codes#

Per-script sequential checks. 9 is a valid check code; there is no rule against it.

CodeMeaning
0Success
1Uncaught exception (FATAL wrapper)
2argparse / usage
3Topology ≠ 60/90/32
4Euler characteristic ≠ 2
5Face census ≠ 12 pentagons + 20 hexagons
6Vertex degree not uniform 3; also --output produced no file
7Non-manifold edges
8Edge lengths not uniform
9Face planarity off
10Framing gate (--output path only; gallery_framing)
12Panel material count ≠ 2
13Panel binding not by vertex count (--invert-bind lands here)
14Centroid off origin
15Circumradius not uniform

The blender-smoke workflow runs the check on Blender 5.2 LTS and 4.5 LTS (5.1 on the weekly cron, the needs-5.1 PR label, or manual dispatch). Smoke does not pass --output. Its catalog falsifier is --invert-bind (expects exit 13).

Source

examples/soccer-ball-goldberg/soccer_ball_goldberg.py View the script on GitHub →
A white soccer ball with black pentagon panels and dark stitched seams, pressed into mown pitch turf on a chalked penalty spot with a touchline behind it.