From 2808b256a287519ec14ec221cd8cfdd6adf96bc8 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Mon, 21 Aug 2023 17:25:51 +1000 Subject: [PATCH 1/4] Manual compound-of-tetrahedra colouring --- polytopes.js | 20 +++++++++++++++++++- testbed.js | 5 ++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/polytopes.js b/polytopes.js index 01900c0..46e8f32 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,7 +332,25 @@ 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); + + +} diff --git a/testbed.js b/testbed.js index 08cba2f..8e051ea 100644 --- a/testbed.js +++ b/testbed.js @@ -432,7 +432,10 @@ const dodecas = make_120cell_cells(faces); const ddfaces = dodecas.map((dd) => dd.map((f) => f.id)); -console.log(JSON.stringify(ddfaces)); +//console.log(JSON.stringify(ddfaces)); + +const dd0 = dodecas[0]; + // for( const dodeca of dodecas ) { // console.log(dodeca.map((f) => f.id) From 8a926f0552b792028a5c7b338430ca761609a3c0 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Fri, 25 Aug 2023 18:08:22 +1000 Subject: [PATCH 2/4] Successfully auto-coloured a single dodecahedron --- docs/notes_120_cell.md | 31 +++++++--- polytopes.js | 33 ++++++++++- testbed.js | 132 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 174 insertions(+), 22 deletions(-) diff --git a/docs/notes_120_cell.md b/docs/notes_120_cell.md index badbaed..aa44b7a 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 46e8f32..7ebb991 100644 --- a/polytopes.js +++ b/polytopes.js @@ -341,6 +341,7 @@ function manual_label_120cell(nodes, links) { 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); @@ -349,10 +350,38 @@ function manual_label_120cell(nodes, links) { 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 semiautomatic_label_120cell(nodes, links) { + const COLOURS = { + 1: [ 313, 157, 461, 505 ], + 2: [ 1, 153, 29, 105 ], + 3: [ 317, 409, 265, 109 ], + 4: [ 221, 337, 25, 509 ], + 5: [ 217, 413, 457, 361 ] + }; + for( const col in COLOURS ) { + label_nodes(nodes, COLOURS[col], col); + } +} @@ -362,7 +391,7 @@ export const cell120 = () => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); - manual_label_120cell(nodes, links); + semiautomatic_label_120cell(nodes, links); return { nodes: nodes, diff --git a/testbed.js b/testbed.js index 8e051ea..989916a 100644 --- a/testbed.js +++ b/testbed.js @@ -360,6 +360,107 @@ function make_dodecahedron(faces, f1, f2) { return dodecahedron; } +// goal - a version of the above which collates the nodes to +// a standard 'layout' on the dodecahedron, so that it's then easy +// to colour them automatically + + + + + + + + + +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; +} + +// 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; + } +} + + +function dodecahedron_vertices(dodeca) { + 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 ], + ]; + return VERTEX_MAP.map((vs) => find_dodeca_vertex(...vs.map((v) => dd[v]))); +} + +function dodecahedron_colours(vertices, left) { + const PARTITION = [ + 1, 2, 3, 4, 5, 3, 4, 5, 1, 2, 5, 1, 2, 3, 4, 2, 3, 4, 5, 1, + ]; + const colours = { 1: [], 2: [], 3: [], 4: [], 5: [] }; + for( let i = 0; i < 20; i++ ) { + colours[PARTITION[i]].push(vertices[i]); + } + return colours; +} + + + + // 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 @@ -392,13 +493,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 + } + dodeca_nodes(d); + dodecas.push(d); + i += 1; seen[fp] = 1; } } @@ -422,22 +530,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)); - -const dd0 = dodecas[0]; - - -// for( const dodeca of dodecas ) { -// console.log(dodeca.map((f) => f.id) -// } +//const dodecas = make_120cell_cells(faces); From 3b806c3796944a5ff22da1d6355a799d1731b04f Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 26 Aug 2023 13:54:44 +1000 Subject: [PATCH 3/4] Basic function to colour a single dodecahedron defined by an face and a neighbouring vertex --- testbed.js | 102 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/testbed.js b/testbed.js index 989916a..38d4464 100644 --- a/testbed.js +++ b/testbed.js @@ -360,17 +360,6 @@ function make_dodecahedron(faces, f1, f2) { return dodecahedron; } -// goal - a version of the above which collates the nodes to -// a standard 'layout' on the dodecahedron, so that it's then easy -// to colour them automatically - - - - - - - - function faces_to_dodecahedron(faces, f1, f2) { const dodecahedron = [ f1, f2 ]; @@ -407,6 +396,17 @@ function faces_to_dodecahedron(faces, f1, f2) { 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) { @@ -420,45 +420,65 @@ function find_dodeca_vertex(f1, f2, f3) { } } +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(dodeca) { - 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 ], - ]; - return VERTEX_MAP.map((vs) => find_dodeca_vertex(...vs.map((v) => dd[v]))); + + +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)); } -function dodecahedron_colours(vertices, left) { - const PARTITION = [ +// 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 colours = { 1: [], 2: [], 3: [], 4: [], 5: [] }; + 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++ ) { - colours[PARTITION[i]].push(vertices[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); +} + + @@ -502,9 +522,9 @@ function make_120cell_cells(faces) { //console.log(`added dodeca ${fp}`); const d = { id: i, - faces: dd + faces: dd, + nodes: dodecahedron_vertices(dd), } - dodeca_nodes(d); dodecas.push(d); i += 1; seen[fp] = 1; From f4176b9ced5db0753ba96a2f357c3b3704fbe219 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 26 Aug 2023 16:02:48 +1000 Subject: [PATCH 4/4] successfully traversed a great circle --- polytopes.js | 86 ++++++++++++++++++++++++++++++++++++++++++++-------- testbed.js | 21 +++++++++++++ 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/polytopes.js b/polytopes.js index 7ebb991..bcfd12a 100644 --- a/polytopes.js +++ b/polytopes.js @@ -370,28 +370,88 @@ function manual_label_120cell(nodes, links) { } -function semiautomatic_label_120cell(nodes, links) { - const COLOURS = { - 1: [ 313, 157, 461, 505 ], - 2: [ 1, 153, 29, 105 ], - 3: [ 317, 409, 265, 109 ], - 4: [ 221, 337, 25, 509 ], - 5: [ 217, 413, 457, 361 ] - }; - for( const col in COLOURS ) { - label_nodes(nodes, COLOURS[col], col); +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; + } } } - - export const cell120 = () => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); - semiautomatic_label_120cell(nodes, links); + meridian_label_120cell(nodes, links); return { nodes: nodes, diff --git a/testbed.js b/testbed.js index 38d4464..9ce4fa5 100644 --- a/testbed.js +++ b/testbed.js @@ -479,6 +479,27 @@ function colour_one_dodecahedron(faces, face, node, 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; + +} +