import * as POLYTOPES from './polytopes.js'; // exploring more inscriptions of the 120-cell function choice(a) { const r = Math.floor(Math.random() * a.length); return a[r]; } export function nodes_links(links, nodeid) { return links.filter((l) => l.source === nodeid || l.target === nodeid); } export function linked(links, n1, n2) { const ls = nodes_links(nodes_links(links, n1), n2); if( ls.length ) { return ls[0] } else { return false; } } function fingerprint(ids) { const sids = [...ids]; sids.sort(); return sids.join(','); } export function dist(n1, n2) { return Math.sqrt((n1.x - n2.x) ** 2 + (n1.y - n2.y) ** 2 + (n1.z - n2.z) ** 2 + (n1.w - n2.w) ** 2); } export function make_120cell() { const nodes = POLYTOPES.make_120cell_vertices(); const links = POLYTOPES.auto_detect_edges(nodes, 4); return { nodes: nodes, links: links } } function round_dist(raw) { return Math.floor(raw * 100000) / 100000; } export function distance_groups(cell120) { // get list of other nodes by distance // sort them and dump them out const dists = {}; cell120.nodes.map((n) => { const draw = dist(cell120.nodes[0], n); const dtrunc = round_dist(draw); if( !(dtrunc in dists) ) { dists[dtrunc] = []; } dists[dtrunc].push(n); }); return dists; } function distance_group(cell120, n0, chord) { const nodes = [] cell120.nodes.map((n) => { const d = round_dist(dist(n0, n)); if( d == chord ) { nodes.push(n); } }); // filter and return those whose chord is also the same const equidistant = []; for( const n1 of nodes ) { for( const n2 of nodes ) { if( n2.id > n1.id ) { if( round_dist(dist(n1, n2)) == chord ) { equidistant.push([n1, n2]); } } } } return equidistant; } export function chord_survey() { const cell120 = POLYTOPES.cell120_inscribed(); const dgroups = distance_groups(cell120); const dists = Object.keys(dgroups); dists.sort(); for( const d of dists ) { const g0 = dgroups[d][0]; dgroups[d].map((g) => { console.log(`${g0.id}-${g.id}: ${round_dist(dist(g0, g))}`); }); } } function overlap(c1, c2) { for( const l in c1 ) { if( c1[l] === c2[l] ) { return true; } } return false; } function c5match(c1, c2) { for( const l in c1 ) { if( c1[l] != c2[l] ) { return false; } } return true; } export function gather_5cells(cell120) { const CHORD5 = round_dist(Math.sqrt(2.5)); const bins = []; const all = []; cell120.nodes.filter((n) => n.label === 1).map((n) => { const cells = [ ]; const g = distance_group(cell120, n, CHORD5); for( const pair of g ) { let seen = false; for( const cell of cells ) { const c = Object.values(cell); if( c.includes(pair[0].id) || c.includes(pair[1].id) ) { if( !c.includes(pair[0].id) ) { cell[pair[0].label] = pair[0].id; } if( !c.includes(pair[1].id) ) { cell[pair[1].label] = pair[1].id; } seen = true; break; } } if( !seen ) { const cell = {}; cell[1]= n.id; cell[pair[0].label] = pair[0].id; cell[pair[1].label] = pair[1].id; cells.push(cell); } } all.push(...cells); }); return all; } function audit_5cells(cells) { // this verifies that for each label (a 600-cell set), each of its // vertices is in exactly 7 5-cells. It checks out. ['1','2','3','4','5'].map((l) => { const sets = {}; for( const cell of cells ) { const lv = cell[l]; if( !(lv in sets) ) { sets[lv] = []; } sets[lv].push(cell); } for( const lv in sets ) { const ok = ( sets[lv].length === 7 ) ? 'ok' : 'miss'; console.log(`${l},${lv},${sets[lv].length},${ok}`); } }); } function try_120_5_cells_fails(cell120, cells, l) { // iterate over every vertex in the 600-cell defined by label l, // get all 7 5-cells including that vertex, and add them if they are // disjoint with what we already have // this always runs out of disjoint nodes early const vertices = cell120.nodes.filter((n) => n.label === l); const cellset = []; for( const v of vertices ) { console.log(`Vertex ${v.id}`); const vcells = cells.filter((c) => c[l] === v.id); const overlap_any = (cs, c) => { for( const seen of cs ) { if( overlap(seen, c) ) { console.log("overlap"); console.log(c); return true; } } return false; } const disjoint = vcells.filter((c) => ! overlap_any(cellset, c)); console.log(`Found ${disjoint.length} disjoint cells`); if( disjoint.length > 0 ) { cellset.push(choice(disjoint)); } } console.log(`Found total of ${cellset.length} disjoint cells`); //console.log(cellset); } function overlap_any(cs, c) { for( const seen of cs ) { if( overlap(seen, c) ) { return true; } } return false; } function explore_disjoint(cell120, all5, l) { const a = all5[0]; const overlaps = all5.filter((c) => overlap(c, a)); console.log(a); console.log(overlaps.length); console.log(overlaps); } // select a five-cell from a starting vertex v // find a neighbor of v vn on its 600 cell, find all of the 5-cells which include // vn. Then see if we can find any from that set which are similiar neighbours to // the other four vertices in the first 5-cell // the idea is that the 600-cells are a guide to finding the right subset of // 5-cells function neighbours600(cell120, vid) { const v = cell120.nodes.filter((node) => node.id === vid)[0]; const label = v.label; const links = cell120.links.filter((l) => { return l.label === v.label && (l.source === v.id || l.target == v.id ); }); const nodes = links.map((l) => { if( l.source === v.id ) { return l.target; } else { return l.source; } }); return nodes; } function cell120node(cell120, nid) { return cell120.nodes.filter((n) => n.id === nid)[0]; } function node_dist(cell120, aid, bid) { const a = cell120node(cell120, aid); const b = cell120node(cell120, bid); return dist(a, b); } function print_row(v1, v2, p, v5) { console.log(`${v1.id},${v2.id},${p},${v5[1]},${v5[2]},${v5[3]},${v5[4]},${v5[5]}`); } // for a pair of vertices which are on the same inscribed 600 cell, // this returns all 7 pairs of 5-cells which contain v1 and v2 and // which are also evenly spaced (ie every pair of vertices on the // same 600-cell is one edge apart) function find_adjoining_5cells(cell120, all5, v1, v2) { const DIST600 = round_dist(node_dist(cell120, v1.id, v2.id)); const v15s = all5.filter((c5) => c5[v1.label] === v1.id); const v25s = all5.filter((c5) => c5[v2.label] === v2.id); let p = 0; const c5pairs = []; for( const v5a of v15s ) { for( const v5b of v25s ) { let match = true; const d = {}; for( const label in v5a ) { d[label] = round_dist(node_dist(cell120, v5a[label], v5b[label])); if( d[label] != DIST600 ) { match = false; } } if( match ) { c5pairs.push([ v5a, v5b ]); } } } return c5pairs; } function tetras(cell120, v) { // given a vertex v, find all of the 600-cell tetras it's on const n600s = neighbours600(cell120, v.id); // need to find all sets of three neighbours which are neighbours: there // should be 20 of these because they're faces of an icosahedron const tetras = new Set; for( const v2id of n600s ) { // find mutual neighbours of the first two const n2600s = neighbours600(cell120, v2id); const mutuals = n2600s.filter((nid) => { return nid != v2id && nid != v.id && n600s.includes(nid) }); for( const nm of mutuals ) { const nnms = neighbours600(cell120, nm); const mutuals2 = nnms.filter((nid) => { return nid != nm && nid != v2id && nid != v.id && mutuals.includes(nid) }); for( const m2 of mutuals2 ) { const t = [ v.id, v2id, nm, m2 ]; t.sort((a, b) => a - b); const tstr = t.join(','); tetras.add(tstr); } } } const tarray = []; for( const t of tetras ) { const ta = t.split(',').map((v) => Number(v)); tarray.push(ta); } return tarray; } function vertices(hedra) { const v = new Set; for ( const h of hedra) { for( const p of h ) { v.add(p); } } return Array.from(v); } function str5cell(c5) { return ["1","2","3","4","5"].map((l) => String(c5[l]).padStart(3, '0')).join('-'); } function tetra_sets(cell120, all5, tetra) { // given a tetrahedron on a 600-cell, find the sets of adjacent 5-cells on // all of the pairs // this is ass-backwards. Need to find tetras on the other 4 vertices of a 5-cell const vs = tetra.map((tid) => cell120node(cell120, tid)); const pairs = [[0,1], [0,2], [0, 3], [1, 2], [1, 3], [2, 3]]; for( const p of pairs ) { const v1 = vs[p[0]]; const v2 = vs[p[1]]; const c5pairs = find_adjoining_5cells(cell120, all5, v1, v2); console.log(v1.id, v2.id); console.log(c5pairs.map((p) => str5cell(p[0]) + " " + str5cell(p[1]))); } } function cell5_neighbourhoods(cell120, all5, c5) { const neighbours = {} for( const l in c5 ) { const v = cell120node(cell120, c5[l]); neighbours[l] = vertices(tetras(cell120, v)); } // now take the set of all 5-cells and filter it to only those whose vertices // are in the neighour sets. On first inspection there are 13? const n5cells = all5.filter((c5) => { for( const l in c5 ) { if( ! neighbours[l].includes(c5[l]) ) { return false; } } return true; }); return n5cells; } function cell5_tetras(cell120, all5, c5) { const nb = cell5_neighbourhoods(cell120, all5, c5); const v1 = cell120node(cell120, c5["1"]); const ts = tetras(cell120, v1); const c5s = []; for( const t of ts ) { const nt = nb.filter((n) => { for( const l in n ) { if( t.includes(n[l]) ) { return true; } } return false }); for( const nc5 of nt ) { const exact = c5s.filter((c) => c5match(c, nc5)); if( exact.length === 0 ) { const o = c5s.filter((c) => overlap(c, nc5)); if( o.length > 0 ) { console.log("Overlap", c5, o); } else { c5s.push(nc5); } } } } return c5s; } function coherent_5cells_r(cell120, all5, c5s, c50) { // Find next set of c5s, see if there are any we haven't seen, // recurse into those ones const c5ns = cell5_tetras(cell120, all5, c50); const c5unseen = c5ns.filter((c5) => { const matched = c5s.filter((c5b) => c5match(c5b, c5)); return matched.length === 0; }); for( const c5u of c5unseen ) { c5s.push(c5u); } for( const c5u of c5unseen ) { coherent_5cells_r(cell120, all5, c5s, c5u); } } function coherent_5cells(cell120, all5) { // pick a starting point, collect coherent 5_cells, continue till // there aren't any new ones const c5set = []; let c5 = all5[0]; const c5s = []; coherent_5cells_r(cell120, all5, c5s, c5); return c5s; } const cell120 = POLYTOPES.cell120_inscribed(); const all5 = gather_5cells(cell120); const c5s = coherent_5cells(cell120, all5); const celli = c5s.map((c5) => [ "1", "2", "3", "4", "5" ].map((l) => c5[l])); // check it because I don't believe it yet const vertex_check = {}; for( const c5 of celli ) { for( const l in c5 ) { const v = c5[l]; if( v in vertex_check ) { console.log(`Double count vertex ${v}`); } vertex_check[v] = 1; } } for( let i = 1; i < 601; i++ ) { if( !vertex_check[i] ) { console.log(`v ${i} missing`); } } const idict = {}; for( let i = 1; i < 121; i++ ) { idict[i] = celli[i - 1]; } console.log(JSON.stringify(idict, null, 2));