436 lines
12 KiB
JavaScript
436 lines
12 KiB
JavaScript
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cell120 = POLYTOPES.cell120_inscribed();
|
|
const all5 = gather_5cells(cell120);
|
|
|
|
const c5 = all5[0]
|
|
|
|
const c5s = cell5_tetras(cell120, all5, c5);
|
|
|
|
const celli = c5s.map((c5) => [ "1", "2", "3", "4", "5" ].map((l) => c5[l]));
|
|
|
|
console.log(celli);
|