diff --git a/docs/notes_120_cell.md b/docs/notes_120_cell.md index bfcce99..b85ae49 100644 --- a/docs/notes_120_cell.md +++ b/docs/notes_120_cell.md @@ -5,13 +5,6 @@ Steps forward - 2. using this, generate a list of all 120 dodecahedra: -[ a b c d e f g h i j k l m n o p q r s t ] <- 20 vertices - -Check that each vertex appears in four of these - -Then - either manually start labelling them, or build an interface to help -with the manual labelling - 1. @@ -30,7 +23,31 @@ the last five. +2. have tried manual labelling and it will drive me crazy before I finish +3. Automated approach based on what I've got so far: + +- should be possible to colour a single dodecahedron from a single face and + one other vertex (to pick a chirality) + +- write a function to do this - the compound-of-four-tetrahedra map can be + more or less hard coded: follow the pattern 1-2-3-4-5, 3-4-5-1-2, etc out + from the inner ring, and map the original face's permutation + +From this: + +- colour the first dodecahedron, picking a chirality + +- the next vertices from each of this dodecahedron's vertices have colours + which come from the first dodeca + +- these can be used to colour the next layer of dodecahedra + +- and so on + +Alternatively: + +- do it by the discrete Hopf fibration, one fibre at a time diff --git a/polytopes.js b/polytopes.js index 01900c0..bcfd12a 100644 --- a/polytopes.js +++ b/polytopes.js @@ -303,7 +303,7 @@ function label_faces_120cell(nodes, faces, cfaces, label) { } -function manual_label_120cell(nodes, links) { +function basic_auto_label_120cell(nodes, links) { const faces = auto_120cell_faces(links); const dodecas = DODECAHEDRA.DODECAHEDRA; @@ -332,11 +332,118 @@ function manual_label_120cell(nodes, links) { } +// manual compound-of-tetrahedra colouring + +function manual_label_120cell(nodes, links) { + label_nodes(nodes, [1, 153, 29, 105], 1); + label_nodes(nodes, [317, 409, 265, 109], 2); + label_nodes(nodes, [221, 337, 25, 509], 3); + label_nodes(nodes, [217, 413, 457, 361], 4); + label_nodes(nodes, [313, 157, 461, 505], 5); + + + // second dodecahedron needs to have opposite chirality + + label_nodes(nodes, [ 165, 33, 117 ], 1); + label_nodes(nodes, [ 161, 465, 517 ], 2); + label_nodes(nodes, [ 417, 469, 365 ], 3); + label_nodes(nodes, [ 341, 37, 513 ], 4); + label_nodes(nodes, [ 421, 269, 113 ], 5); + + // third + + label_nodes(nodes, [ 45, 101, 181 ], 1); + label_nodes(nodes, [ 241, 429, 53 ], 2); + label_nodes(nodes, [ 93, 229 ], 3); + label_nodes(nodes, [ 173, 437 ], 4); + label_nodes(nodes, [ 245, 325 ], 5); + + // fourth (id = 3) + + label_nodes(nodes, [ 89, 169, 49 ], 1); + label_nodes(nodes, [ 321 ], 2); + label_nodes(nodes, [ 425, 177 ], 3); + label_nodes(nodes, [ 97, 225 ], 4); + label_nodes(nodes, [ 41, 433], 5); +} +function meridian_label_120cell(nodes, links) { + const DODECAS = [ + [ + 313, 1, 317, 221, 217, 417, + 341, 421, 165, 161, 465, 469, + 37, 269, 33, 113, 117, 517, + 365, 513 + ], + [ + 513, 365, 517, 117, 113, 577, + 15, 581, 565, 561, 397, 399, + 85, 389, 81, 301, 303, 213, + 293, 209 + ], + [ + 301, 209, 293, 213, 303, + 211, 309, 294, 311, 215, + 310, 210, 214, 312, 295, + 212, 302, 304, 216, 296 + ], + [ + 304, 302, 212, 296, 216, 400, + 398, 84, 392, 88, 16, 580, + 564, 568, 584, 368, 516, 116, + 120, 520 + ], + [ + 368, 516, 116, 120, 520, 272, + 36, 468, 472, 40, 164, 420, + 344, 424, 168, 220, 316, 4, + 320, 224 + ], + [ + 316, 4, 320, 224, 220, 412, + 340, 416, 160, 156, 460, 464, + 32, 268, 28, 108, 112, 512, + 364, 508 + ], + [ + 508, 364, 512, 112, 108, 572, + 14, 576, 560, 556, 394, 396, + 80, 388, 76, 298, 300, 208, + 292, 204 + ], + [ + 300, 298, 204, 292, 208, + 206, 202, 306, 291, 308, + 290, 305, 203, 207, 307, + 289, 201, 297, 299, 205 + ], + [ + 299, 297, 201, 289, 205, 395, + 393, 73, 385, 77, 13, 569, + 553, 557, 573, 361, 505, 105, + 109, 509 + ], + [ + 361, 505, 105, 109, 509, 265, + 25, 457, 461, 29, 153, 409, + 337, 413, 157, 217, 313, 1, + 317, 221 + ] + ] + + let col = 1; + for( const dd of DODECAS ) { + label_nodes(nodes, dd, 5); + col++; + if( col > 5 ) { + col = 1; + } + } +} @@ -344,7 +451,7 @@ export const cell120 = () => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); - manual_label_120cell(nodes, links); + meridian_label_120cell(nodes, links); return { nodes: nodes, diff --git a/testbed.js b/testbed.js index 08cba2f..9ce4fa5 100644 --- a/testbed.js +++ b/testbed.js @@ -361,6 +361,148 @@ function make_dodecahedron(faces, f1, f2) { } +function faces_to_dodecahedron(faces, f1, f2) { + const dodecahedron = [ f1, f2 ]; + + // take f1 as the 'center', get the other four around it from f2 + const fs = find_dodeca_mutuals(faces, f1, f2); + const f3 = fs[0]; + const f6 = fs[1]; + dodecahedron.push(f3); + const f4 = find_dodeca_next(faces, dodecahedron, f1, f3); + dodecahedron.push(f4); + const f5 = find_dodeca_next(faces, dodecahedron, f1, f4); + dodecahedron.push(f5); + dodecahedron.push(f6); + + // get the next ring + + const f7 = find_dodeca_next(faces, dodecahedron, f6, f2); + dodecahedron.push(f7); + const f8 = find_dodeca_next(faces, dodecahedron, f2, f3); + dodecahedron.push(f8); + const f9 = find_dodeca_next(faces, dodecahedron, f3, f4); + dodecahedron.push(f9); + const f10 = find_dodeca_next(faces, dodecahedron, f4, f5); + dodecahedron.push(f10); + const f11 = find_dodeca_next(faces, dodecahedron, f5, f6); + dodecahedron.push(f11); + + // get the last + + const f12 = find_dodeca_next(faces, dodecahedron, f7, f8); + dodecahedron.push(f12); + + return dodecahedron; +} + +// from a face and one neighbouring node, return a dodecahedron + +function face_plus_to_dodecahedron(faces, f1, node) { + const neighbours = find_adjacent_faces(faces, f1); + const nodens = neighbours.filter((f) => f.nodes.includes(node)); + return faces_to_dodecahedron(faces, f1, nodens[0]); // does it matter which? +} + + + + +// for three faces, return their common vertex (if they have one) + +function find_dodeca_vertex(f1, f2, f3) { + const v12 = f1.nodes.filter((n) => f2.nodes.includes(n)); + const v123 = v12.filter((n) => f3.nodes.includes(n)); + if( v123.length === 1 ) { + return v123[0]; + } else { + console.log(`warning: faces ${f1.id} ${f2.id} ${f3.id} don't share 1 vertex`); + return false; + } +} + +const VERTEX_MAP = [ + [ 0, 1, 5 ], + [ 0, 1, 2 ], + [ 0, 2, 3 ], + [ 0, 3, 4 ], + [ 0, 4, 5 ], + [ 1, 5, 6 ], + [ 1, 2, 7 ], + [ 2, 3, 8 ], + [ 3, 4, 9 ], + [ 4, 5, 10 ], + [ 1, 6, 7 ], + [ 2, 7, 8 ], + [ 3, 8, 9 ], + [ 4, 9, 10 ], + [ 5, 6, 10 ], + [ 6, 7, 11 ], + [ 7, 8, 11 ], + [ 8, 9, 11 ], + [ 9, 10, 11 ], + [ 6, 10, 11 ], +]; + + + +function dodecahedron_vertices(faces) { + const face_sets = VERTEX_MAP.map((vs) => vs.map((v) => faces[v])); + return face_sets.map((fs) => find_dodeca_vertex(...fs)); +} + +// p is the permutation of the first face + +function dodecahedron_colours(vertices, p) { + const LEFT_PART = [ + 1, 2, 3, 4, 5, 3, 4, 5, 1, 2, 5, 1, 2, 3, 4, 2, 3, 4, 5, 1, + ]; + const RIGHT_PART = [ + 1, 2, 3, 4, 5, 4, 5, 1, 2, 3, 3, 4, 5, 1, 2, 1, 2, 3, 4, 5, + ]; + const part = LEFT_PART; + const colours = {}; + for( let i = 0; i < 20; i++ ) { + const v = vertices[i]; + const colour = p[part[i] - 1]; + colours[v] = colour; + } + return colours; +} + + +// p is the permutation of the first face + +function colour_one_dodecahedron(faces, face, node, p) { + const dd = face_plus_to_dodecahedron(faces, face, node); + const vertices = dodecahedron_vertices(dd); + return dodecahedron_colours(vertices, p); +} + + +// go along a meridian + +function meridian(faces, startf, startn) { + const dds = [ face_plus_to_dodecahedron(faces, startf, startn) ]; + + while( dds.length < 10 ) { + const dd = dds[dds.length - 1]; + const nextf = dd[11]; // opposite to startf + const neighbours = find_adjacent_faces(faces, nextf); + const nextnbors = neighbours.filter((f) => !dd.includes(f)); + + const nextdd = faces_to_dodecahedron(faces, nextf, nextnbors[0]); + + + dds.push(nextdd); + } + + return dds; + +} + + + + // for a face, pick an edge, and then find the other two faces which // share this edge. These can be used as the starting points for the // first face's two dodecahedra @@ -392,13 +534,20 @@ function dd_fingerprint(dodecahedron) { function make_120cell_cells(faces) { const dodecas = []; const seen = {}; + let i = 1; for( const face of faces ) { const dds = face_to_dodecahedra(faces, face); for( const dd of dds ) { const fp = dd_fingerprint(dd); if( ! (fp in seen) ) { //console.log(`added dodeca ${fp}`); - dodecas.push(dd); + const d = { + id: i, + faces: dd, + nodes: dodecahedron_vertices(dd), + } + dodecas.push(d); + i += 1; seen[fp] = 1; } } @@ -422,19 +571,20 @@ const cell120 = () => { } +function dodeca_nodes(dd) { + const ns = new Set(); + for( const face of dd.faces ) { + for( const node of face.nodes ) { + ns.add(node); + } + } + dd.nodes = Array.from(ns); +} const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); const faces = auto_120cell_faces(links); -const dodecas = make_120cell_cells(faces); - -const ddfaces = dodecas.map((dd) => dd.map((f) => f.id)); - -console.log(JSON.stringify(ddfaces)); - -// for( const dodeca of dodecas ) { -// console.log(dodeca.map((f) => f.id) -// } +//const dodecas = make_120cell_cells(faces);