174 lines
3.4 KiB
JavaScript
174 lines
3.4 KiB
JavaScript
|
|
|
|
import * as POLYTOPES from './polytopes.js';
|
|
|
|
// face detection for the 600-cell
|
|
|
|
|
|
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 make_600cell() {
|
|
const nodes = POLYTOPES.make_600cell_vertices();
|
|
const links = POLYTOPES.auto_detect_edges(nodes, 12);
|
|
return {
|
|
nodes: nodes,
|
|
links: links
|
|
}
|
|
}
|
|
|
|
export function link_to_tetras(nodes, links, link) {
|
|
const n1 = link.source;
|
|
const n2 = link.target;
|
|
const nl1 = nodes_links(links, n1).filter((l) => l.id !== link.id);
|
|
const nl2 = nodes_links(links, n2).filter((l) => l.id !== link.id);
|
|
const p1 = new Set();
|
|
const p = new Set();
|
|
for( const nl of nl1 ) {
|
|
if( nl.source !== n1 ) {
|
|
p1.add(nl.source);
|
|
}
|
|
if( nl.target !== n1 ) {
|
|
p1.add(nl.target);
|
|
}
|
|
}
|
|
for( const nl of nl2 ) {
|
|
if( nl.source !== n2 && p1.has(nl.source) ) {
|
|
p.add(nl.source);
|
|
}
|
|
if( nl.target !== n2 && p1.has(nl.target) ) {
|
|
p.add(nl.target);
|
|
}
|
|
}
|
|
const lp = Array.from(p);
|
|
const seen = {};
|
|
const tetras = [];
|
|
for( const p1 of lp ) {
|
|
for( const p2 of lp ) {
|
|
if( p1 != p2 ) {
|
|
if( linked(links, p1, p2) ) {
|
|
const fp = fingerprint([n1, n2, p1, p2]);
|
|
if( !seen[fp] ) {
|
|
seen[fp] = true;
|
|
tetras.push({fingerprint: fp, nodes: [n1, n2, p1, p2]})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return tetras;
|
|
}
|
|
|
|
|
|
export function auto_600cell_cells(nodes, links) {
|
|
const seen = {};
|
|
const tetras = [];
|
|
links.map((link) => {
|
|
link_to_tetras(nodes, links, link).map((lt) => {
|
|
if( !seen[lt.fingerprint] ) {
|
|
seen[lt.fingerprint] = true;
|
|
tetras.push(lt.nodes);
|
|
}
|
|
})
|
|
});
|
|
|
|
return tetras;
|
|
}
|
|
|
|
|
|
function node_by_id(nodes, nid) {
|
|
const ns = nodes.filter((n) => n.id === nid);
|
|
return ns[0];
|
|
}
|
|
|
|
|
|
export function tetra_w(nodes, tetra) {
|
|
let w = 0;
|
|
for( const nid of tetra ) {
|
|
const node = node_by_id(nodes, nid);
|
|
w += node.w;
|
|
}
|
|
return w / 4;
|
|
}
|
|
|
|
export function sorted_600cells() {
|
|
const cell600 = make_600cell();
|
|
const tetras = auto_600cell_cells(cell600.nodes, cell600.links);
|
|
const layers = tetras.map((t) => { return { "nodes": t, w: tetra_w(cell600.nodes, t) } });
|
|
layers.sort((a, b) => b.w - a.w);
|
|
return layers;
|
|
}
|
|
|
|
// const cell600 = make_600cell();
|
|
|
|
// const layers = sorted_600cells(cell600.nodes, cell600.links);
|
|
// for( const cell of layers ) {
|
|
// // const fp = fingerprint(cell.nodes);
|
|
// console.log(`${cell.w} ${cell.nodes}`);
|
|
// }
|
|
|
|
export function make_layered_600cell() {
|
|
const tetras = sorted_600cells()
|
|
|
|
const LAYERS = [
|
|
[ "00", 20 ],
|
|
[ "01", 20 ],
|
|
[ "02", 30 ],
|
|
[ "03", 60 ],
|
|
[ "04", 60 ],
|
|
[ "05", 60 ],
|
|
[ "06", 20 ],
|
|
[ "07", 60 ],
|
|
[ "08", 20 ],
|
|
[ "09", 60 ],
|
|
[ "10", 60 ],
|
|
[ "11", 60 ],
|
|
[ "12", 30 ],
|
|
[ "13", 20 ],
|
|
[ "14", 20 ]
|
|
];
|
|
|
|
const vertices = {};
|
|
const seen = {};
|
|
let i = 0;
|
|
|
|
for( const layer of LAYERS ) {
|
|
const label = layer[0];
|
|
const n = layer[1];
|
|
vertices[label] = [];
|
|
console.log(`Layer ${label} starting at ${i}`);
|
|
for( const t of tetras.slice(i, i + n) ) {
|
|
console.log(t);
|
|
for( const n of t.nodes ) {
|
|
if( !seen[n] ) {
|
|
vertices[label].push(n);
|
|
seen[n] = true;
|
|
}
|
|
}
|
|
}
|
|
i += n;
|
|
}
|
|
return JSON.stringify(vertices);
|
|
}
|
|
|
|
|