diff --git a/cellindex.js b/cellindex.js index b9294cc..366673a 100644 --- a/cellindex.js +++ b/cellindex.js @@ -1,4 +1,17 @@ -export const INDEX = { + +export const LAYER_NAMES = { + 0: "North pole", + 1: "Arctic circle", + 2: "North temperate", + 3: "Tropic of Cancer", + 4: "Equator", + 5: "Tropic of Capricorn", + 6: "South temperate", + 7: "Antarctic circle (all)" +}; + + +export const INDEX120 = { "1": [ 27,38,48,49,61,68,74,87,95,98,105,120, 126,131,140,149,156,165,174, 179,185,200,207,210,218,223,226,231,234,239,241,248,252,253,258,263, @@ -46,7 +59,7 @@ export const INDEX = { }; -export const LAYERS = { +export const LAYERS120 = { "0": [154,266,158,222,218,250,254,162,268,156,160,252,256,166,270,272, 164,220,224,168], "1": [2,318,314,30,414,510,362,26,506,410,338,458,462,110, @@ -90,3 +103,86 @@ export const LAYERS = { "7":[217,153,221,157,265,161,165,269,249,253,251,255,267,159,155, 163,219,271,223,167] }; + +// Schoute's partition via https://arxiv.org/abs/1010.4353 + +export const PARTITION600 = { + + "2,0,0,0": 1, + "0,2,0,0": 1, + "0,0,2,0": 1, + "0,0,0,2": 1, + "1,1,1,1": 1, + "1,1,-1,-1": 1, + "1,-1,1,-1": 1, + "1,-1,-1,1": 1, + "1,-1,-1,-1": 1, + "1,-1,1,1": 1, + "1,1,-1,1": 1, + "1,1,1,-1": 1, + + "k,0,-t,-1": 2, + "0,k,1,-t": 2, + "t,-1,k,0": 2, + "1,t,0,k": 2, + "t,k,0,-1": 2, + "1,0,k,t": 2, + "k,-t,-1,0": 2, + "0,1,-t,k": 2, + "1,k,t,0": 2, + "t,0,-1,k": 2, + "0,t,-k,-1": 2, + "k,-1,0,-t": 2, + + "t,0,1,k": 3, + "0,t,-k,1": 3, + "1,-k,-t,0": 3, + "k,1,0,-t": 3, + "0,k,1,t": 3, + "t,1,-k,0": 3, + "k,0,t,-1": 3, + "1,-t,0,k": 3, + "t,-k,0,-1": 3, + "0,1,-t,-k": 3, + "1,0,-k,t": 3, + "k,t,1,0": 3, + + "t,0,-1,-k": 4, + "0,t,k,-1": 4, + "1,-k,t,0": 4, + "k,1,0,t": 4, + "t,1,k,0": 4, + "0,k,-1,-t": 4, + "1,-t,0,-k": 4, + "k,0,-t,1": 4, + "0,1,t,k": 4, + "t,-k,0,1": 4, + "k,t,-1,0": 4, + "1,0,k,-t": 4, + + "k,0,t,1": 5, + "0,k,-1,t": 5, + "t,-1,-k,0": 5, + "1,t,0,-k": 5, + "1,0,-k,-t": 5, + "t,k,0,1": 5, + "0,1,t,-k": 5, + "k,-t,1,0": 5, + "t,0,1,-k": 5, + "1,k,-t,0": 5, + "k,-1,0,t": 5, + "0,t,k,1": 5 +}; + + + +export const LAYERS600 = { + "0":[2,42,46,50,54,106,110,44,48,52,56,108,112], + "1":[10,12,14,16,18,20,22,24,34,36,38,40,98,102,100,104,90,92,94,96], + "2":[26,58,74,28,76,30,60,32,62,78,80,64], + "3":[3,4,5,6,7,8,65,113,81,66,114,82,67,115,83,68,116,84,69,117,85,70,118,86,71,119,87,72,120,88], + "4":[25,29,27,31,57,61,59,63,73,75,77,79], + "5":[97,101,99,103,33,35,37,39,89,91,93,95,9,11,13,15,17,19,21,23], + "6":[41,105,49,43,51,45,107,47,109,53,55,111], + "7":[1] +}; diff --git a/layer600cell.js b/layer600cell.js new file mode 100644 index 0000000..1c185b4 --- /dev/null +++ b/layer600cell.js @@ -0,0 +1,173 @@ + + +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); +} + + diff --git a/polytopes.js b/polytopes.js index aceeb25..d42059a 100644 --- a/polytopes.js +++ b/polytopes.js @@ -1,6 +1,6 @@ import * as PERMUTE from './permute.js'; -import * as CELL120 from './cellindex.js'; +import * as CELLINDEX from './cellindex.js'; function index_nodes(nodes, scale) { let i = 1; @@ -22,7 +22,7 @@ function dist2(n1, n2) { return (n1.x - n2.x) ** 2 + (n1.y - n2.y) ** 2 + (n1.z - n2.z) ** 2 + (n1.w - n2.w) ** 2; } -function auto_detect_edges(nodes, neighbours, debug=false) { +export function auto_detect_edges(nodes, neighbours, debug=false) { const seen = {}; const nnodes = nodes.length; const links = []; @@ -359,23 +359,13 @@ function link_labels(nodes, link) { // layer and the links follow that export const cell120_layered = (max) => { - const LAYER_NAMES = { - 0: "North pole", - 1: "Arctic circle", - 2: "North temperate", - 3: "Tropic of Cancer", - 4: "Equator", - 5: "Tropic of Capricorn", - 6: "South temperate", - 7: "Antarctic circle (all)" - }; const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); nodes.map((n) => n.label = 9); // make all invisible by default - for (const cstr in CELL120.LAYERS ) { - label_nodes(nodes, CELL120.LAYERS[cstr], Number(cstr)); + for (const cstr in CELLINDEX.LAYERS120 ) { + label_nodes(nodes, CELLINDEX.LAYERS120[cstr], Number(cstr)); } links.map((l) => { @@ -393,7 +383,7 @@ export const cell120_layered = (max) => { for( const i of [ 0, 1, 2, 3, 4, 5, 6, 7 ] ) { layers.push(i); options.push({ - name: LAYER_NAMES[i], + name: CELLINDEX.LAYER_NAMES[i], links: [...layers], nodes: [...layers] }) @@ -419,8 +409,8 @@ export const cell120_inscribed = () => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); - for( const cstr in CELL120.INDEX ) { - label_nodes(nodes, CELL120.INDEX[cstr], Number(cstr)); + for( const cstr in CELLINDEX.INDEX120 ) { + label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr)); } links.map((l) => l.label = 0); @@ -450,76 +440,6 @@ export const cell120_inscribed = () => { -// Schoute's partition via https://arxiv.org/abs/1010.4353 - -const partition600 = { - - "2,0,0,0": 1, - "0,2,0,0": 1, - "0,0,2,0": 1, - "0,0,0,2": 1, - "1,1,1,1": 1, - "1,1,-1,-1": 1, - "1,-1,1,-1": 1, - "1,-1,-1,1": 1, - "1,-1,-1,-1": 1, - "1,-1,1,1": 1, - "1,1,-1,1": 1, - "1,1,1,-1": 1, - - "k,0,-t,-1": 2, - "0,k,1,-t": 2, - "t,-1,k,0": 2, - "1,t,0,k": 2, - "t,k,0,-1": 2, - "1,0,k,t": 2, - "k,-t,-1,0": 2, - "0,1,-t,k": 2, - "1,k,t,0": 2, - "t,0,-1,k": 2, - "0,t,-k,-1": 2, - "k,-1,0,-t": 2, - - "t,0,1,k": 3, - "0,t,-k,1": 3, - "1,-k,-t,0": 3, - "k,1,0,-t": 3, - "0,k,1,t": 3, - "t,1,-k,0": 3, - "k,0,t,-1": 3, - "1,-t,0,k": 3, - "t,-k,0,-1": 3, - "0,1,-t,-k": 3, - "1,0,-k,t": 3, - "k,t,1,0": 3, - - "t,0,-1,-k": 4, - "0,t,k,-1": 4, - "1,-k,t,0": 4, - "k,1,0,t": 4, - "t,1,k,0": 4, - "0,k,-1,-t": 4, - "1,-t,0,-k": 4, - "k,0,-t,1": 4, - "0,1,t,k": 4, - "t,-k,0,1": 4, - "k,t,-1,0": 4, - "1,0,k,-t": 4, - - "k,0,t,1": 5, - "0,k,-1,t": 5, - "t,-1,-k,0": 5, - "1,t,0,-k": 5, - "1,0,-k,-t": 5, - "t,k,0,1": 5, - "0,1,t,-k": 5, - "k,-t,1,0": 5, - "t,0,1,-k": 5, - "1,k,-t,0": 5, - "k,-1,0,t": 5, - "0,t,k,1": 5 -}; - function partition_coord(i, coords, invert) { @@ -560,7 +480,7 @@ function map_coord(i, coords, values) { } -function make_600cell_vertices() { +export function make_600cell_vertices() { const coords = { 0: '0', 1: '1', @@ -584,7 +504,7 @@ function make_600cell_vertices() { ].flat(); for( const n of nodes ) { - n.label = label_vertex(n, coords, partition600); + n.label = label_vertex(n, coords, CELLINDEX.PARTITION600); } for( const n of nodes ) { @@ -649,6 +569,60 @@ export const cell600 = () => { } } + +export const cell600_layered = () => { + const nodes = make_600cell_vertices(); + const links = auto_detect_edges(nodes, 12); + + nodes.map((n) => n.label = 9); // make all invisible by default + + for (const cstr in CELLINDEX.LAYERS600 ) { + label_nodes(nodes, CELLINDEX.LAYERS600[cstr], Number(cstr)); + } + + links.map((l) => { + const labels = link_labels(nodes, l); + if( labels[0] >= labels[1] ) { + l.label = labels[0]; + } else { + l.label = labels[1]; + } + }); + + const options = []; + const layers = []; + + for( const i of [ 0, 1, 2, 3, 4, 5, 6, 7 ] ) { + layers.push(i); + options.push({ + name: CELLINDEX.LAYER_NAMES[i], + links: [...layers], + nodes: [...layers] + }) + } + + return { + name: '600-cell layered', + nodes: nodes, + links: links, + geometry: { + node_size: 0.02, + link_size: 0.02 + }, + nolink2opacity: true, + options: options + } + + +} + + + + + + + + function make_dodecahedron_vertices() { const phi = 0.5 * (1 + Math.sqrt(5)); const phiinv = 1 / phi; @@ -721,6 +695,7 @@ export const build_all = () => { tesseract(), cell24(), cell600(), + cell600_layered(), cell120_inscribed(), cell120_layered() ];