410 lines
11 KiB
JavaScript
410 lines
11 KiB
JavaScript
|
|
import * as POLYTOPES from './polytopes.js';
|
|
|
|
import * as CELLINDEX from './cellindex.js';
|
|
|
|
// script to help me label the vertices of one of the inscribed 600-cells of a 120-cell
|
|
// with Schoute's partition (which is used to label the main 600-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;
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
function round_dist(raw) {
|
|
return Math.floor(raw * 1000) / 1000;
|
|
}
|
|
|
|
export function make_one_600cell() {
|
|
const nodes = POLYTOPES.make_120cell_vertices();
|
|
const links = POLYTOPES.auto_detect_edges(nodes, 4);
|
|
for( const cstr in CELLINDEX.INDEX120 ) {
|
|
POLYTOPES.label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
|
|
}
|
|
|
|
links.map((l) => l.label = 0);
|
|
|
|
const nodes600 = nodes.filter((n) => n.label === 1);
|
|
const links600 = POLYTOPES.auto_detect_edges(nodes600, 12);
|
|
links600.map((l) => l.label = 1);
|
|
|
|
|
|
return {
|
|
nodes: nodes600,
|
|
links: links600
|
|
}
|
|
}
|
|
|
|
|
|
export function base_600cell() {
|
|
const nodes = POLYTOPES.make_600cell_vertices();
|
|
const links = POLYTOPES.auto_detect_edges(nodes, 12);
|
|
|
|
links.map((l) => l.label = 0);
|
|
|
|
for( const p of [1, 2, 3, 4, 5]) {
|
|
const nodes24 = nodes.filter((n) => n.label === p);
|
|
}
|
|
|
|
return {
|
|
nodes: nodes,
|
|
links: links,
|
|
};
|
|
}
|
|
|
|
export function distance_groups(shape) {
|
|
// get list of other nodes by distance
|
|
// sort them and dump them out
|
|
const dists = {};
|
|
|
|
shape.nodes.map((n) => {
|
|
const draw = dist(shape.nodes[0], n);
|
|
const dtrunc = round_dist(draw);
|
|
if( !(dtrunc in dists) ) {
|
|
dists[dtrunc] = [];
|
|
}
|
|
dists[dtrunc].push(n.id);
|
|
});
|
|
return dists;
|
|
}
|
|
|
|
export function insc600_layers(cell600) {
|
|
const layers = distance_groups(cell600);
|
|
/* const sorted = Object.keys(layers).sort((a,b) => a - b);
|
|
for( const d of sorted ) {
|
|
const ids = layers[d].map((n) => n.id);
|
|
console.log(`Layer at distance ${d}`);
|
|
console.log(ids);
|
|
} */
|
|
return layers;
|
|
}
|
|
|
|
export function neighbours(shape, nid) {
|
|
const links = shape.links.filter((l) => l.source === nid || l.target == nid );
|
|
const nodes = links.map((l) => {
|
|
if( l.source === nid ) {
|
|
return l.target;
|
|
} else {
|
|
return l.source;
|
|
}
|
|
});
|
|
return nodes;
|
|
}
|
|
|
|
export function neighbours_in_subset(shape, subset, nid) {
|
|
// shape = nodes, links
|
|
// subset = a list of ids
|
|
// n = an id
|
|
// returns all of n's neighbours which are in subset
|
|
|
|
const all_nbors = neighbours(shape, nid);
|
|
return all_nbors.filter((n) => subset.includes(n));
|
|
}
|
|
|
|
export function face_vertices(shape, f1, f2, f3) {
|
|
// for f1/f2/f3 forming a triangular face, return the two vertices of the
|
|
// adjacent tetrahedra
|
|
|
|
const n1 = neighbours(shape, f1).filter((n) => n !== f2 && n !== f3);
|
|
const n2 = neighbours(shape, f2).filter((n) => n !== f1 && n !== f3);
|
|
const n3 = neighbours(shape, f3).filter((n) => n !== f1 && n !== f2);
|
|
|
|
const ns = n1.filter((n) => n2.includes(n) && n3.includes(n));
|
|
return ns;
|
|
}
|
|
|
|
|
|
export function shared_neighbours(shape, nodes) {
|
|
|
|
let ns = shape.nodes.map((n) => n.id);
|
|
for( const n of nodes ) {
|
|
ns = neighbours_in_subset(shape, ns, n);
|
|
}
|
|
|
|
return ns;
|
|
}
|
|
|
|
|
|
|
|
export function layer_neighbours(cell600, layer) {
|
|
console.log("Layer neighbours");
|
|
for( const n of layer ) {
|
|
console.log(`n = ${n}`);
|
|
const nbors = neighbours_in_subset(cell600, layer, n);
|
|
console.log(` Vertex ${n} neighbours: ` + JSON.stringify(nbors));
|
|
}
|
|
}
|
|
|
|
|
|
const ARCTIC_I_FACES = [
|
|
[ 419, 223, 253 ],
|
|
[ 419, 253, 331 ],
|
|
[ 419, 331, 427 ],
|
|
[ 419, 427, 339 ],
|
|
[ 419, 339, 223 ],
|
|
[ 253, 223, 265 ],
|
|
[ 331, 253, 473 ],
|
|
[ 427, 331, 539 ],
|
|
[ 339, 427, 555 ],
|
|
[ 511, 339, 223 ],
|
|
[ 223, 511, 265 ],
|
|
[ 253, 265, 473 ],
|
|
[ 331, 473, 539 ],
|
|
[ 427, 539, 555 ],
|
|
[ 339, 555, 511 ],
|
|
[ 393, 265, 511 ],
|
|
[ 393, 473, 265 ],
|
|
[ 393, 539, 473 ],
|
|
[ 393, 555, 539 ],
|
|
[ 393, 555, 511 ]
|
|
];
|
|
|
|
export const ARCTIC_FACES = ARCTIC_I_FACES.map((f) => {
|
|
return f.map((nid) => CELLINDEX.CELL600_METAMAP[nid]);
|
|
});
|
|
|
|
export const TEMPERATE_PENTAGONS_I = [
|
|
[ 499, 179, 471, 367, 131 ],
|
|
[ 131, 367, 165, 313, 449 ],
|
|
[ 131, 449, 185, 258, 499 ],
|
|
[ 499, 258, 140, 274, 179 ],
|
|
[ 179, 274, 527, 95, 471 ],
|
|
[ 471, 95, 347, 165, 367 ],
|
|
[ 347, 573, 105, 313, 165 ],
|
|
[ 313, 105, 585, 185, 449 ],
|
|
[ 185, 585, 306, 140, 258 ],
|
|
[ 140, 306, 207, 527, 274 ],
|
|
[ 527, 207, 573, 347, 95 ],
|
|
[ 105, 573, 207, 306, 585 ],
|
|
];
|
|
|
|
export const TEMPERATE_PENTAGONS = TEMPERATE_PENTAGONS_I.map((f) => {
|
|
return f.map((nid) => CELLINDEX.CELL600_METAMAP[nid])
|
|
});
|
|
|
|
|
|
export function layer_two(cell600, centre, faces) {
|
|
|
|
for ( const face of faces ) {
|
|
const n2 = face_vertices(cell600, face[0], face[1], face[2]);
|
|
console.log(face, n2);
|
|
}
|
|
}
|
|
|
|
|
|
export function layer_three(shape, pentagons) {
|
|
for ( const pentagon of pentagons ) {
|
|
console.log(pentagon);
|
|
const s = shared_neighbours(shape, pentagon);
|
|
console.log(s);
|
|
console.log("\n");
|
|
}
|
|
}
|
|
|
|
export const TEMPERATE_APICES = [
|
|
563,
|
|
513,
|
|
285,
|
|
324,
|
|
231,
|
|
487,
|
|
413,
|
|
425,
|
|
378,
|
|
388,
|
|
543,
|
|
289,
|
|
];
|
|
|
|
// this one generates the mapping to the base 600 cell as well, unlike
|
|
// previous versions where I did the mapping by hand
|
|
|
|
export function equator(i600, b600, apices) {
|
|
const pairs = [];
|
|
// get all 30 of the edges on the temperate dodeca
|
|
for( let i = 0; i < 11; i++ ) {
|
|
for( let j = i + 1; j < 12; j++ ) {
|
|
const s = shared_neighbours(i600, [ apices[i], apices[j] ]);
|
|
if( s.length > 0 ) {
|
|
const e = s.filter((n) => !(n in CELLINDEX.CELL600_METAMAP));
|
|
pairs.push([apices[i], apices[j], e]);
|
|
}
|
|
}
|
|
}
|
|
const MAPPED = Object.values(CELLINDEX.CELL600_METAMAP);
|
|
const eq = {};
|
|
for( const pair of pairs ) {
|
|
const b1 = CELLINDEX.CELL600_METAMAP[pair[0]];
|
|
const b2 = CELLINDEX.CELL600_METAMAP[pair[1]];
|
|
const s = shared_neighbours(b600, [ b1, b2 ]);
|
|
const e = s.filter((n) => !MAPPED.includes(n));
|
|
if( e.length !== 1 ) {
|
|
console.log(`Bad value at ${pair}`);
|
|
} else {
|
|
eq[pair[2]] = e[0];
|
|
}
|
|
}
|
|
return eq;
|
|
}
|
|
|
|
|
|
|
|
|
|
export function antipode(shape, nid) {
|
|
const n0 = shape.nodes.filter((n) => n.id === nid)[0];
|
|
if( !n0 ) {
|
|
throw new Error(`antipodes error: couldn't find node ${nid} in shape`);
|
|
}
|
|
const dists = shape.nodes.map((n) => [ dist(n, n0), n ]);
|
|
dists.sort((a, b) => b[0] - a[0]);
|
|
return dists[0][1];
|
|
}
|
|
|
|
|
|
export function check_antipodes() {
|
|
const c600 = base_600cell();
|
|
const seen = {};
|
|
c600.nodes.map((n) => {
|
|
const a = antipode(c600, n.id);
|
|
if( !seen[a.id] && !seen[n.id] ) {
|
|
seen[a.id] = true;
|
|
seen[n.id] = true;
|
|
console.log(`${n.id} - ${n.label} / ${a.id} - ${a.label}`);
|
|
if( n.label !== a.label ) {
|
|
console.lot("MISMATCH");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
export function meta600_label(b600, iid) {
|
|
const bid = CELLINDEX.CELL600_METAMAP[iid];
|
|
const bn = b600.nodes.filter((n) => bid === n.id);
|
|
return bn[0].label;
|
|
}
|
|
|
|
|
|
export function map_antipodes() {
|
|
const b600 = base_600cell();
|
|
const i600 = make_one_600cell();
|
|
const already = [];
|
|
const antimap = {};
|
|
for( const inid in CELLINDEX.CELL600_METAMAP ) {
|
|
const bnid = CELLINDEX.CELL600_METAMAP[inid];
|
|
const banti = antipode(b600, Number(bnid));
|
|
const ianti = antipode(i600, Number(inid));
|
|
if( CELLINDEX.CELL600_METAMAP[ianti.id] ) {
|
|
//console.log(`Anti ${ianti.id} is already mapped`);
|
|
already.push(ianti.id);
|
|
const l1 = meta600_label(b600, inid);
|
|
const l2 = meta600_label(b600, Number(ianti.id));
|
|
//console.log(`labels: ${l1} ${l2}`);
|
|
} else {
|
|
antimap[ianti.id] = banti.id;
|
|
}
|
|
}
|
|
console.log(JSON.stringify(antimap, null, 2));
|
|
}
|
|
|
|
|
|
export function check_metamap_completeness() {
|
|
const b600 = base_600cell();
|
|
const i600 = make_one_600cell();
|
|
const labels = {};
|
|
const bids = {};
|
|
const mm = CELLINDEX.CELL600_METAMAP;
|
|
for( const i of i600.nodes ) {
|
|
if( i.id in mm ) {
|
|
const ml = meta600_label(b600, i.id);
|
|
if( !(ml in labels) ) {
|
|
labels[ml] = [];
|
|
}
|
|
labels[ml].push(i.id);
|
|
bids[mm[i.id]] = 1;
|
|
} else {
|
|
console.log(`inscribed node ${i.id} is not in metamap`);
|
|
}
|
|
}
|
|
for( const b of b600.nodes ) {
|
|
if( !(b.id in bids) ) {
|
|
console.log(`base mode ${b.id} is not mapped`);
|
|
}
|
|
}
|
|
for ( const label in labels ) {
|
|
console.log(`label ${label} has ${labels[label].length} nodes`);
|
|
}
|
|
}
|
|
|
|
// this gives a mapping from cell-120-ids of one inscribed 600-cell to the
|
|
// metamap labels, which I can then [checks notes] use to colour the 5-cells.
|
|
|
|
export function metamap_to_labels() {
|
|
const b600 = base_600cell();
|
|
const i600 = make_one_600cell();
|
|
const mapping = {};
|
|
for( const inode of i600.nodes ) {
|
|
mapping[inode.id] = meta600_label(b600, inode.id);;
|
|
}
|
|
|
|
return mapping;
|
|
}
|
|
|
|
export function cell5_labels() {
|
|
const labels = metamap_to_labels();
|
|
|
|
// now build a dict of the 120 cell5s with the colours from the above
|
|
|
|
const cell5map = {};
|
|
const CELL5S = CELLINDEX.CELL120_CELL5.cell5s;
|
|
|
|
for( const c5i in CELL5S ) {
|
|
const n1 = CELL5S[c5i][0]; // label 1 node;
|
|
const ml = labels[n1];
|
|
cell5map[c5i] = ml;
|
|
}
|
|
return cell5map;
|
|
}
|
|
|
|
export function rebuild_cell5_index() {
|
|
|
|
const labels = metamap_to_labels();
|
|
const new_cell5s = {};
|
|
const CELL5S = CELLINDEX.CELL120_CELL5;
|
|
|
|
for( const c5i in CELL5S ) {
|
|
const n1 = CELL5S[c5i][0]; // label 1 node;
|
|
const ml = labels[n1];
|
|
new_cell5s[c5i] = {
|
|
nodes: CELL5S[c5i],
|
|
label: ml
|
|
}
|
|
}
|
|
return new_cell5s;
|
|
}
|
|
|
|
const nc5 = rebuild_cell5_index();
|
|
|
|
console.log(JSON.stringify(nc5, null, 4));
|