From 6436efece25565472b95b4dcc20898ad32467aa4 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 20 Aug 2023 11:18:29 +1000 Subject: [PATCH] Moved the 120-cell dodecahedron detection code to its own file, and tested it colouring every dodecahedron when it builds the 120-cell: looks good --- cell120.js | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++ polytopes.js | 115 ++++++++++-------------------------------- 2 files changed, 164 insertions(+), 88 deletions(-) create mode 100644 cell120.js diff --git a/cell120.js b/cell120.js new file mode 100644 index 0000000..ffe39dc --- /dev/null +++ b/cell120.js @@ -0,0 +1,137 @@ +// trying to go from faces to dodecahedra + + +function shared_vertices(f1, f2) { + return f1.nodes.filter((f) => f2.nodes.includes(f)); +} + + +function adjacent_faces(f1, f2) { + // adjacent faces which share an edge, not just a vertex + const intersect = shared_vertices(f1, f2); + if( intersect.length < 2 ) { + return false; + } + if( intersect.length > 2 ) { + console.log(`warning: faces ${f1.id} and ${f2.id} have too many common vertices`); + } + return true; +} + + +function find_adjacent_faces(faces, face) { + const neighbours = faces.filter((f) => f.id !== face.id && adjacent_faces(f, face)); + return neighbours; +} + + + + +function find_dodeca_mutuals(faces, f1, f2) { + // for any two adjacent faces, find their common neighbours where + // all three share exactly one vertex (this, I think, guarantees that + // all are on the same dodecahedron) + + const n1 = find_adjacent_faces(faces, f1); + const n2 = find_adjacent_faces(faces, f2); + const common = n1.filter((f1) => n2.filter((f2) => f1.id === f2.id).length > 0 ); + // there's one extra here - the third which has two nodes in common with + // both f1 and f2 - filter it out + const mutuals = common.filter((cf) => { + const shared = cf.nodes.filter((n) => f1.nodes.includes(n) && f2.nodes.includes(n)); + return shared.length === 1 + }); + return mutuals; +} + +function find_dodeca_next(faces, dodeca, f1, f2) { + // of a pair of mutuals, return the one we haven't already got + const m = find_dodeca_mutuals(faces, f1, f2); + if( dodeca.filter((f) => f.id === m[0].id ).length > 0 ) { + m.shift(); + } + return m[0]; +} + +// from any two mutual faces, return all the faces in their dodecahedron + +function make_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 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 + +function find_edge_neighbours(faces, face) { + const n1 = face.nodes[0]; + const n2 = face.nodes[1]; + return faces.filter((f) => f.id !== face.id && f.nodes.includes(n1) && f.nodes.includes(n2)); +} + + +// each face is in two dodecahedra: this returns them both + +function face_to_dodecahedra(faces, f) { + const edge_friends = find_edge_neighbours(faces, f); + const d1 = make_dodecahedron(faces, f, edge_friends[0]); + const d2 = make_dodecahedron(faces, f, edge_friends[1]); + return [ d1, d2 ]; +} + +// brute-force calculation of all dodecahedra + +function dd_fingerprint(dodecahedron) { + const ids = dodecahedron.map((face) => face.id); + ids.sort() + return ids.join(','); +} + +export function make_120cell_dodecahedra(faces) { + const dodecas = []; + const seen = {}; + 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); + seen[fp] = 1; + } + } + } + return dodecas; +} diff --git a/polytopes.js b/polytopes.js index 8c3beaa..3fdc206 100644 --- a/polytopes.js +++ b/polytopes.js @@ -1,5 +1,7 @@ import * as PERMUTE from './permute.js'; +import * as CELL120 from './cell120.js'; + function index_nodes(nodes, scale) { let i = 1; for( const n of nodes ) { @@ -281,94 +283,14 @@ function label_nodes(nodes, ids, label) { -function find_edges(links, nid) { - return links.filter((l) => l.source === nid || l.target === nid ); -} - - -function find_adjacent(links, nid) { - return find_edges(links, nid).map((l) => { - if( l.source === nid ) { - return l.target; - } else { - return l.source; - } - }); -} - -function iterate_graph(nodes, links, n, fn) { - const queue = []; - const seen = {}; - const nodes_id = {}; - nodes.map((n) => nodes_id[n.id] = n); - - queue.push(n.id); - seen[n.id] = true; - fn(n); - - while( queue.length > 0 ) { - const v = queue.shift(); - find_adjacent(links, v).map((aid) => { - if( !(aid in seen) ) { - seen[aid] = true; - queue.push(aid); - fn(nodes_id[aid]); - } - }) - } -} -function dumb_label_120cell(nodes, links) { - let l = 0; - iterate_graph(nodes, links, nodes[0], (n) => { - n.label = l; - console.log(`Labelled ${n.id} ${n.label}`); - l++; - if( l > 2 ) { - l = 0; - } - }) -} - - -// stupid tetrahedral labelling -// keeps getting stuck - - -function naive_label_120cell(nodes, links, n) { - const nodes_id = {}; - nodes.map((n) => nodes_id[n.id] = n); - iterate_graph(nodes, links, nodes[0], (n) => { - const cols = new Set(); - const nbors = find_adjacent(links, n.id); - for( const nb of nbors ) { - if( nodes_id[nb].label > 0 ) { - cols.add(nodes_id[nb].label); - } - for( const nb2 of find_adjacent(links, nb) ) { - if( nb2 !== n.id && nodes_id[nb].label > 0 ) { - cols.add(nodes_id[nb2].label); - } - } - } - const pcols = [ 1, 2, 3, 4, 5 ].filter((c) => !cols.has(c)); - if( pcols.length < 1 ) { - console.log(`Got stuck, no options at ${n.id}`); - return false; - } else { - n.label = pcols[0]; - console.log(`found ${pcols.length} colors for node ${n.id}`); - console.log(`applied ${pcols[0]} to node ${n.id}`); - return true; - } - }); -} function label_faces_120cell(nodes, faces, cfaces, label) { const ns = new Set(); + console.log(`label faces from ${cfaces}`); for( const fid of cfaces ) { const face = faces.filter((f)=> f.id === fid ); console.log(face); @@ -378,8 +300,6 @@ function label_faces_120cell(nodes, faces, cfaces, label) { } } } - - label_nodes(nodes, Array.from(ns), label); } @@ -387,15 +307,30 @@ function label_faces_120cell(nodes, faces, cfaces, label) { function manual_label_120cell(nodes, links) { const faces = auto_120cell_faces(links); + const dodecas = CELL120.make_120cell_dodecahedra(faces); //const cfaces = [ 1, 2, 4, 145, 169 ]; + console.log(dodecas); + let colour = 1; + for( const dd of dodecas ) { + label_faces_120cell(nodes, faces, dd.map((f) => f.id), colour); + colour++; + if( colour > 8 ) { + colour = 1; + } + } - label_faces_120cell(nodes, faces, [ - 1, 2, 4, 169, 626, - 145, 149, 553, 173, 171, - 147, 554 -], 4); +// label_faces_120cell(nodes, faces, [ +// 1, 2, 4, 169, 626, +// 145, 149, 553, 173, 171, +// 147, 554 +// ], 2); +// label_faces_120cell(nodes, faces, [ +// 1, 5, 3, 193, 641, +// 217, 221, 565, 197, 195, +// 219, 566 +// ], 3); } @@ -403,6 +338,10 @@ function manual_label_120cell(nodes, links) { + + + + export const cell120 = () => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4);