First published on Homingo Learn.
An isometric cube is three polygons that have to meet along identical edges, and a vertex that is off by even one user unit can leave a gap or an overlap. This script emits the three faces with integer coordinates and checks the edges before you trust the SVG.
Name the points once
All geometry hangs off a center (100, 100) and two offsets, set in cx, cy, dx, dy = 100, 100, round(50 * sqrt(3)), 50. The program names seven points: C, T, B, UR, LR, LL, UL. Each face is a list of those names.
For a 30 degree edge with a vertical rise of 50, the exact horizontal run is 50 * sqrt(3), which is 86.603. The code rounds it to 87 so every coordinate is an integer and the printed points are exact. The price is that the cube is only approximately isometric: the slanted edges are very slightly off true 30 degrees.
from collections import Counter from math import sqrt cx, cy, dx, dy = 100, 100, round(50 * sqrt(3)), 50 print(f"dx = round(50*sqrt(3)) = {dx}; exact value {50 * sqrt(3):.3f}") C, T, B = (cx, cy), (cx, cy - 2 * dy), (cx, cy + 2 * dy) UR, LR = (cx + dx, cy - dy), (cx + dx, cy + dy) LL, UL = (cx - dx, cy + dy), (cx - dx, cy - dy) faces = {"top": [T, UR, C, UL], "right": [C, UR, LR, B], "left": [C, B, LL, UL]} hexagon = [T, UR, LR, B, LL, UL] def ring(poly): return list(zip(poly, poly[1:] + poly[:1])) def edges(poly): return [frozenset(e) for e in ring(poly)] def area2(poly): return abs(sum(x1 * y2 - x2 * y1 for (x1, y1), (x2, y2) in ring(poly))) def check(faces): all_edges = [e for f in faces.values() for e in edges(f)] assert max(all_edges.count(e) for e in set(all_edges)) <= 2, "edge used 3+ times" outline = {e for e in all_edges if all_edges.count(e) == 1} hex_edges = set(edges(hexagon)) assert outline == hex_edges, ( f"outline differs from hexagon: {len(outline - hex_edges)} extra, " f"{len(hex_edges - outline)} missing edges" ) areas = [area2(f) for f in faces.values()] assert len(set(areas)) == 1, f"doubled areas differ: {areas}" assert sum(areas) == area2(hexagon), "faces do not tile the hexagon" shared = sum(all_edges.count(e) == 2 for e in set(all_edges)) return shared, areas[0], len(outline) for name, poly in faces.items(): pts = " ".join(f"{x},{y}" for x, y in poly) print(f'<polygon id="{name}" points="{pts}"/>') shared, area, outline_n = check(faces) print(f"shared edges: {shared}, outline edges: {outline_n}") print(f"doubled area per face: {area} (even: {area % 2 == 0}), hexagon: {area2(hexagon)}") broken = dict(faces) broken["right"] = [C, (UR[0] + 1, UR[1]), LR, B] try: check(broken) print("broken cube passed (unexpected)") except AssertionError as err: print("broken vertex caught:", err) Compare edges, not vertices
Faces list their vertices in their own order, so a shared edge can run in opposite directions in two polygons. Here the top face walks UR to C while the right face walks C to UR. The edges function stores each edge as a frozenset of its two endpoints, so direction does not matter. ring pairs the last vertex with the first so the closing edge is included.
dx = round(50*sqrt(3)) = 87; exact value 86.603 <polygon id="top" points="100,0 187,50 100,100 13,50"/> <polygon id="right" points="100,100 187,50 187,150 100,200"/> <polygon id="left" points="100,100 100,200 13,150 13,50"/> shared edges: 3, outline edges: 6 doubled area per face: 17400 (even: True), hexagon: 52200 broken vertex caught: outline differs from hexagon: 3 extra, 1 missing edges The check function makes four assertions, and the output shows the intact cube passed all of them:
- No edge appears three or more times across the faces.
- The edges that appear exactly once, the outline, equal the edge set of the hand-listed
hexagon. The printed counts are 3 shared edges and 6 outline edges. - The three faces have the same doubled area, 17400 each.
- Those areas sum to the hexagon's doubled area: 3 × 17400 = 52200.
Areas are computed with the shoelace formula and left doubled, so no division happens. Equal face areas are a property of a cube in this projection, not of polygon sets in general.
Watch it fail
Passing checks only mean something if the checks can fail. The last block copies the faces and moves one vertex of the right face by one unit in x: (UR[0] + 1, UR[1]). The result:
broken vertex caught: outline differs from hexagon: 3 extra, 1 missing edges The moved vertex breaks two things at once. The top face's UR-C edge no longer has a partner, and the right face now has two edges that match nothing. That makes 3 extra outline edges. The hexagon's UR-LR edge no longer appears in the outline, which is the 1 missing. The outline assertion fired, so the area assertions never ran in this case. I did not test whether they would also have caught it.
One limit of this test: the faces reuse the same named points, so they agree by construction. The check earns its keep when someone later edits a coordinate by hand, which is what the broken case simulates.
Emitting the SVG
The program prints one <polygon id="..." points="..."/> per face. Put them inside an <svg> element with a viewBox, and add fills yourself so the faces differ. The vertices span x from 13 to 187 and y from 0 to 200. In a 0 0 200 200 viewBox, only the top and bottom points sit on the edge. A stroke there would be clipped, since a stroke is centered on the path and half of it falls outside. Leave strokes off or widen the viewBox first.
Where this stops being enough
These checks cover this one cube. They show the faces share exact edges and that their areas add up to the hexagon's. They do not test the interior of the faces for overlap point by point.
The program also draws nothing. Antialiasing can show a faint seam between two adjacent polygons even when their shared edge has identical coordinates, so exact coordinates remove one cause of seams but cannot rule out others. You have to look at the rendered result.
Finally, the outline-equals-hexagon test depends on the hand-written hexagon being correct. Once you stack cubes, rotate them, or need correct occlusion, you need a drawing order and visibility logic, and a fixed list of three polygons will not be enough.
Disclosure: I'm the founder of Homingo; Homingo sells this skill, and this article was written with AI assistance, with the code run before publishing. This is one small idea behind what we package in Isometric Explainers, our Claude Code skill: Claude Code draws how-it-works machines for your docs, blogs and landing pages, with live readouts and built-in layout, motion and geometry checks, and one command turns the same figure into a 1080x1920 explainer reel. Skill Rs 1,499 (about US$17), Complete Kit Rs 2,499 (about US$28; adds 3 more machine reel projects, the finished demo reel, 4 video lessons and a written tutorial), one-time, prices in INR, international cards accepted; it needs Claude access with Claude Code and Node.js 20.10+, Claude plans not included: Isometric Explainers. Everything above works without it.