clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / renderers / edge / src / languages / squaring-polyhedra.ts
squaring-polyhedra.ts 3.6K
1// Skeletons of common convex polyhedra as edge lists. Every one is a2// polyhedral (3-connected planar) graph, so any edge can serve as the battery.34export interface PolyhedronPreset {5 vertices: string[]6 edges: Array<[string, string]>7}89function cycleEdges(names: string[]): Array<[string, string]> {10 return names.map((name, index) => [name, names[(index + 1) % names.length] ?? name])11}1213function fromFaces(faces: string[][]): PolyhedronPreset {14 const vertices: string[] = []15 const seen = new Set<string>()16 const edges: Array<[string, string]> = []17 for (const face of faces) {18 for (const vertex of face) {19 if (!vertices.includes(vertex)) vertices.push(vertex)20 }21 for (const [a, b] of cycleEdges(face)) {22 const key = a < b ? `${a}|${b}` : `${b}|${a}`23 if (seen.has(key)) continue24 seen.add(key)25 edges.push([a, b])26 }27 }28 return { vertices, edges }29}3031function ring(prefix: string, count: number): string[] {32 return Array.from({ length: count }, (_, index) => `${prefix}${index + 1}`)33}3435function prism(count: number): PolyhedronPreset {36 const top = ring('a', count)37 const bottom = ring('b', count)38 return {39 vertices: [...top, ...bottom],40 edges: [...cycleEdges(top), ...cycleEdges(bottom), ...top.map((name, index) => [name, bottom[index] ?? name] as [string, string])],41 }42}4344function antiprism(count: number): PolyhedronPreset {45 const top = ring('a', count)46 const bottom = ring('b', count)47 const band = top.flatMap((name, index) => [[name, bottom[index] ?? name], [name, bottom[(index + 1) % count] ?? name]] as Array<[string, string]>)48 return { vertices: [...top, ...bottom], edges: [...cycleEdges(top), ...cycleEdges(bottom), ...band] }49}5051function pyramid(count: number): PolyhedronPreset {52 const base = ring('b', count)53 return { vertices: ['apex', ...base], edges: [...cycleEdges(base), ...base.map(name => ['apex', name] as [string, string])] }54}5556const DODECAHEDRON_FACES = [57 [0, 1, 2, 3, 4], [0, 5, 10, 6, 1], [1, 6, 11, 7, 2], [2, 7, 12, 8, 3], [3, 8, 13, 9, 4], [4, 9, 14, 5, 0],58 [15, 10, 5, 14, 19], [16, 11, 6, 10, 15], [17, 12, 7, 11, 16], [18, 13, 8, 12, 17], [19, 14, 9, 13, 18], [15, 16, 17, 18, 19],59].map(face => face.map(index => `v${index}`))6061function icosahedron(): PolyhedronPreset {62 const upper = ring('u', 5)63 const lower = ring('l', 5)64 const band = upper.flatMap((name, index) => [[name, lower[index] ?? name], [name, lower[(index + 1) % 5] ?? name]] as Array<[string, string]>)65 return {66 vertices: ['top', ...upper, ...lower, 'bottom'],67 edges: [68 ...upper.map(name => ['top', name] as [string, string]),69 ...cycleEdges(upper),70 ...band,71 ...cycleEdges(lower),72 ...lower.map(name => ['bottom', name] as [string, string]),73 ],74 }75}7677export const POLYHEDRA: Record<string, () => PolyhedronPreset> = {78 tetrahedron: () => fromFaces([['a', 'b', 'c'], ['a', 'c', 'd'], ['a', 'd', 'b'], ['b', 'd', 'c']]),79 cube: () => prism(4),80 octahedron: () => fromFaces([81 ['top', 'a', 'b'], ['top', 'b', 'c'], ['top', 'c', 'd'], ['top', 'd', 'a'],82 ['bottom', 'b', 'a'], ['bottom', 'c', 'b'], ['bottom', 'd', 'c'], ['bottom', 'a', 'd'],83 ]),84 dodecahedron: () => fromFaces(DODECAHEDRON_FACES),85 icosahedron,86 'triangular-prism': () => prism(3),87 'pentagonal-prism': () => prism(5),88 'hexagonal-prism': () => prism(6),89 'square-pyramid': () => pyramid(4),90 'pentagonal-pyramid': () => pyramid(5),91 'square-antiprism': () => antiprism(4),92 'tetragonal-antiwedge': () => fromFaces([['a', 'b', 'c', 'd'], ['a', 'd', 'e', 'f'], ['a', 'f', 'b'], ['b', 'f', 'e'], ['b', 'e', 'c'], ['c', 'e', 'd']]),93}9495export const POLYHEDRON_NAMES = Object.keys(POLYHEDRA)