diff --git a/CHANGELOG.md b/CHANGELOG.md index 1336378..da9dd9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +## v1.1 - 1/1/2025 + +The 120-cell now includes a visualisation of its inscribed 5-cells, which honestly +looks like less of a mess than I expected it to. + ## v1.0 - 16/11/2025 It's been [two years](https://mikelynch.org/2023/Sep/02/120-cell/) since diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..5e59b50 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,26 @@ +# NOTES + + +New approach for the 5-cells: + +Pick a tetrahedron of an inscribed 600-cell with vertices A, B, C, D + +This gives pairs of vertices: + +AB +AC +AD +BC +BD +CD + +Each of these gives rise to seven pairs of 5-cells which are on neighboring vertices +of the 5 600-cells. + +Try enumerating these and inspecting them to find one or more coherent sets of four +5-cells which lie on one tetrahedron from each of the 600-cells. + +(I expect there to be more than one, like how there are two ways to partition the +120-cell vertices into 600-cells) + + diff --git a/cellindex.js b/cellindex.js index 366673a..0640a7a 100644 --- a/cellindex.js +++ b/cellindex.js @@ -104,6 +104,855 @@ export const LAYERS120 = { 163,219,271,223,167] }; +export const CELL120_CELL5 = { + "tetras": { + }, + "cell5s": { + "1": [ + 27, + 28, + 264, + 309, + 275 + ], + "2": [ + 223, + 76, + 238, + 84, + 225 + ], + "3": [ + 253, + 44, + 283, + 304, + 42 + ], + "4": [ + 419, + 112, + 197, + 578, + 521 + ], + "5": [ + 339, + 14, + 384, + 382, + 337 + ], + "6": [ + 331, + 4, + 335, + 390, + 386 + ], + "7": [ + 427, + 160, + 551, + 146, + 557 + ], + "8": [ + 265, + 60, + 64, + 295, + 246 + ], + "9": [ + 473, + 100, + 495, + 213, + 462 + ], + "10": [ + 393, + 6, + 328, + 397, + 326 + ], + "11": [ + 539, + 164, + 439, + 561, + 142 + ], + "12": [ + 511, + 122, + 456, + 595, + 181 + ], + "13": [ + 555, + 154, + 152, + 545, + 429 + ], + "14": [ + 95, + 202, + 486, + 500, + 465 + ], + "15": [ + 471, + 208, + 502, + 484, + 89 + ], + "16": [ + 347, + 21, + 348, + 374, + 373 + ], + "17": [ + 487, + 203, + 94, + 468, + 497 + ], + "18": [ + 165, + 139, + 542, + 568, + 434 + ], + "19": [ + 367, + 18, + 355, + 368, + 353 + ], + "20": [ + 231, + 78, + 86, + 236, + 217 + ], + "21": [ + 356, + 17, + 366, + 354, + 365 + ], + "22": [ + 503, + 205, + 470, + 92, + 481 + ], + "23": [ + 527, + 106, + 584, + 195, + 421 + ], + "24": [ + 239, + 73, + 222, + 228, + 81 + ], + "25": [ + 543, + 138, + 168, + 435, + 565 + ], + "26": [ + 48, + 46, + 302, + 281, + 255 + ], + "27": [ + 248, + 62, + 293, + 58, + 267 + ], + "28": [ + 440, + 141, + 562, + 540, + 163 + ], + "29": [ + 274, + 30, + 312, + 261, + 29 + ], + "30": [ + 179, + 128, + 597, + 450, + 505 + ], + "31": [ + 376, + 22, + 375, + 346, + 345 + ], + "32": [ + 320, + 11, + 405, + 401, + 319 + ], + "33": [ + 448, + 173, + 130, + 587, + 519 + ], + "34": [ + 460, + 102, + 211, + 489, + 479 + ], + "35": [ + 388, + 2, + 392, + 329, + 333 + ], + "36": [ + 512, + 182, + 596, + 121, + 455 + ], + "37": [ + 592, + 170, + 516, + 443, + 133 + ], + "38": [ + 120, + 189, + 529, + 570, + 411 + ], + "39": [ + 420, + 198, + 577, + 522, + 111 + ], + "40": [ + 272, + 69, + 65, + 290, + 243 + ], + "41": [ + 488, + 93, + 498, + 204, + 467 + ], + "42": [ + 156, + 150, + 547, + 553, + 431 + ], + "43": [ + 252, + 53, + 286, + 297, + 55 + ], + "44": [ + 532, + 192, + 117, + 410, + 571 + ], + "45": [ + 400, + 7, + 321, + 396, + 323 + ], + "46": [ + 580, + 199, + 417, + 110, + 523 + ], + "47": [ + 296, + 63, + 245, + 266, + 59 + ], + "48": [ + 600, + 125, + 178, + 508, + 451 + ], + "49": [ + 68, + 72, + 269, + 242, + 291 + ], + "50": [ + 324, + 8, + 399, + 322, + 395 + ], + "51": [ + 499, + 96, + 485, + 466, + 201 + ], + "52": [ + 406, + 12, + 317, + 318, + 402 + ], + "53": [ + 563, + 144, + 437, + 162, + 537 + ], + "54": [ + 234, + 88, + 219, + 229, + 80 + ], + "55": [ + 350, + 24, + 349, + 371, + 372 + ], + "56": [ + 444, + 134, + 515, + 169, + 591 + ], + "57": [ + 258, + 40, + 39, + 277, + 307 + ], + "58": [ + 285, + 56, + 251, + 54, + 298 + ], + "59": [ + 546, + 151, + 153, + 430, + 556 + ], + "60": [ + 98, + 215, + 475, + 493, + 464 + ], + "61": [ + 474, + 214, + 99, + 461, + 496 + ], + "62": [ + 357, + 20, + 363, + 359, + 364 + ], + "63": [ + 490, + 212, + 459, + 101, + 480 + ], + "64": [ + 185, + 116, + 415, + 533, + 574 + ], + "65": [ + 378, + 16, + 343, + 341, + 380 + ], + "66": [ + 218, + 85, + 235, + 77, + 232 + ], + "67": [ + 342, + 15, + 377, + 379, + 344 + ], + "68": [ + 458, + 209, + 491, + 477, + 104 + ], + "69": [ + 514, + 135, + 441, + 590, + 172 + ], + "70": [ + 226, + 83, + 75, + 237, + 224 + ], + "71": [ + 530, + 119, + 569, + 190, + 412 + ], + "72": [ + 38, + 37, + 257, + 308, + 278 + ], + "73": [ + 414, + 113, + 188, + 575, + 536 + ], + "74": [ + 362, + 19, + 358, + 361, + 360 + ], + "75": [ + 334, + 1, + 330, + 387, + 391 + ], + "76": [ + 438, + 161, + 538, + 143, + 564 + ], + "77": [ + 550, + 157, + 426, + 560, + 147 + ], + "78": [ + 566, + 167, + 137, + 544, + 436 + ], + "79": [ + 126, + 177, + 452, + 507, + 599 + ], + "80": [ + 284, + 41, + 254, + 43, + 303 + ], + "81": [ + 494, + 97, + 476, + 463, + 216 + ], + "82": [ + 200, + 109, + 418, + 524, + 579 + ], + "83": [ + 263, + 25, + 26, + 276, + 310 + ], + "84": [ + 300, + 50, + 52, + 249, + 287 + ], + "85": [ + 558, + 145, + 428, + 159, + 552 + ], + "86": [ + 403, + 9, + 316, + 315, + 407 + ], + "87": [ + 74, + 82, + 227, + 221, + 240 + ], + "88": [ + 289, + 66, + 244, + 271, + 70 + ], + "89": [ + 306, + 34, + 280, + 33, + 259 + ], + "90": [ + 572, + 118, + 531, + 409, + 191 + ], + "91": [ + 207, + 90, + 472, + 483, + 501 + ], + "92": [ + 369, + 23, + 370, + 351, + 352 + ], + "93": [ + 585, + 132, + 175, + 517, + 446 + ], + "94": [ + 105, + 196, + 528, + 583, + 422 + ], + "95": [ + 241, + 67, + 292, + 71, + 270 + ], + "96": [ + 425, + 148, + 559, + 549, + 158 + ], + "97": [ + 525, + 193, + 108, + 423, + 582 + ], + "98": [ + 174, + 129, + 588, + 447, + 520 + ], + "99": [ + 313, + 10, + 404, + 408, + 314 + ], + "100": [ + 413, + 187, + 576, + 535, + 114 + ], + "101": [ + 573, + 186, + 416, + 115, + 534 + ], + "102": [ + 49, + 51, + 299, + 288, + 250 + ], + "103": [ + 449, + 180, + 127, + 598, + 506 + ], + "104": [ + 469, + 91, + 206, + 504, + 482 + ], + "105": [ + 513, + 171, + 589, + 136, + 442 + ], + "106": [ + 279, + 35, + 305, + 260, + 36 + ], + "107": [ + 389, + 3, + 385, + 336, + 332 + ], + "108": [ + 593, + 183, + 509, + 454, + 124 + ], + "109": [ + 149, + 155, + 554, + 432, + 548 + ], + "110": [ + 325, + 5, + 394, + 327, + 398 + ], + "111": [ + 453, + 123, + 510, + 184, + 594 + ], + "112": [ + 383, + 13, + 338, + 340, + 381 + ], + "113": [ + 311, + 31, + 273, + 32, + 262 + ], + "114": [ + 581, + 107, + 526, + 424, + 194 + ], + "115": [ + 61, + 57, + 268, + 247, + 294 + ], + "116": [ + 87, + 79, + 230, + 220, + 233 + ], + "117": [ + 301, + 47, + 45, + 256, + 282 + ], + "118": [ + 131, + 176, + 445, + 518, + 586 + ], + "119": [ + 210, + 103, + 457, + 478, + 492 + ], + "120": [ + 140, + 166, + 567, + 433, + 541 + ] + }, + +}; + + // Schoute's partition via https://arxiv.org/abs/1010.4353 export const PARTITION600 = { diff --git a/colours.js b/colours.js index f85f25a..7fda5d8 100644 --- a/colours.js +++ b/colours.js @@ -14,6 +14,7 @@ export const get_colours = (basis) => { const hsl = colours.map((c) => Color("#" + c).hsl()); const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber()); resaturated.unshift(basis); + console.log(resaturated); return resaturated; } @@ -34,4 +35,4 @@ export const get_plain_colours = (basis) => { 0xff9900, 0x000000, ] -} \ No newline at end of file +} diff --git a/explore_120 b/explore_120 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/explore_120 @@ -0,0 +1 @@ + diff --git a/explore_120cell.js b/explore_120cell.js new file mode 100644 index 0000000..b88a891 --- /dev/null +++ b/explore_120cell.js @@ -0,0 +1,492 @@ + +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)); diff --git a/fourDShape.js b/fourDShape.js index 7cd76db..7aaeca4 100644 --- a/fourDShape.js +++ b/fourDShape.js @@ -62,7 +62,7 @@ class FourDShape extends THREE.Group { const s1 = this.link_scale * n1.scale; const s2 = this.link_scale * n2.scale; link.object.update(n1, n2, s1, s2); - link.object.visible = (!links_show || link.label in links_show); + link.object.visible = (!links_show || links_show.includes(link.label)); } @@ -143,7 +143,7 @@ class FourDShape extends THREE.Group { this.nodes3[n.id].scale = k * this.foreshortening; this.nodes3[n.id].object.position.copy(v3); this.nodes3[n.id].object.scale.copy(s3); - this.nodes3[n.id].object.visible = ( !nodes_show || n.label in nodes_show ); + this.nodes3[n.id].object.visible = ( !nodes_show || nodes_show.includes(n.label) ); } for( const l of this.links ) { this.updateLink(l, links_show); diff --git a/gui.js b/gui.js index 8e4755a..646dd5a 100644 --- a/gui.js +++ b/gui.js @@ -7,6 +7,7 @@ const DEFAULTS = { linksize: 1.0, linkopacity: 0.75, shape: '120-cell', + link2opacity: 0.75, option: 'none', visibility: 5, inscribed: false, @@ -15,8 +16,8 @@ const DEFAULTS = { background: 0xd4d4d4, hyperplane: 0.93, zoom: 1, - xRotate: 'YW', - yRotate: 'XW', + xRotate: 'YZ', + yRotate: 'XZ', dtheta: 0, damping: false, captions: true, @@ -27,9 +28,10 @@ const DEFAULTS = { class FourDGUI { - constructor(shapes, changeShape, setColor, setBackground, setNodeOpacity,setLinkOpacity, setVisibility, showDocs) { + constructor(funcs) { + this.shapes = funcs.shapes; this.gui = new GUI(); - const SHAPE_NAMES = shapes.map((s) => s.name); + const SHAPE_NAMES = this.shapes.map((s) => s.name); this.parseLinkParams(); const guiObj = this; @@ -40,6 +42,7 @@ class FourDGUI { inscribe_all: this.link['inscribe_all'], linksize: this.link['linksize'], linkopacity: this.link['linkopacity'], + link2opacity: this.link['link2opacity'], nodesize: this.link['nodesize'], nodeopacity: this.link['nodeopacity'], depth: this.link['depth'], @@ -53,39 +56,52 @@ class FourDGUI { captions: true, dtheta: this.link['dtheta'], dpsi: this.link['dpsi'], - "copy link": function () { guiObj.copyUrl() } + "copy link": function () { guiObj.copyUrl() }, }; + if( funcs.extras ) { + for( const label in funcs.extras ) { + console.log(label); + console.log(funcs.extras[label]); + this.params[label] = funcs.extras[label]; + } + } let options_ctrl; this.gui.add(this.params, 'shape', SHAPE_NAMES).onChange((shape) => { - const options = this.getShapeOptions(shapes, shape); + const options = this.getShapeOptions(shape); options_ctrl = options_ctrl.options(options).onChange((option) => { - setVisibility(option) + funcs.setVisibility(option) }); options_ctrl.setValue(options[0]) - changeShape(shape) + funcs.changeShape(shape) }); - const options = this.getShapeOptions(shapes, this.params['shape']); + const options = this.getShapeOptions(this.params['shape']); options_ctrl = this.gui.add(this.params, 'option').options(options).onChange((option) => { - setVisibility(option) + funcs.setVisibility(option) }); this.gui.add(this.params, 'hyperplane', 0.5, 1 / 0.8); this.gui.add(this.params, 'zoom', 0.1, 2.0); this.gui.add(this.params, 'nodesize', 0, 1.5); - this.gui.add(this.params, 'nodeopacity', 0, 1).onChange(setNodeOpacity); + this.gui.add(this.params, 'nodeopacity', 0, 1).onChange(funcs.setNodeOpacity); this.gui.add(this.params, 'linksize', 0, 2); - this.gui.add(this.params, 'linkopacity', 0, 1).onChange(setLinkOpacity); - this.gui.addColor(this.params, 'color').onChange(setColor); - this.gui.addColor(this.params, 'background').onChange(setBackground); + console.log(funcs.setLinkOpacity); + this.gui.add(this.params, 'linkopacity', 0, 1).onChange((v) => funcs.setLinkOpacity(v, true)); + this.gui.add(this.params, 'link2opacity', 0, 1).onChange((v) => funcs.setLinkOpacity(v, false)); + this.gui.addColor(this.params, 'color').onChange(funcs.setColor); + this.gui.addColor(this.params, 'background').onChange(funcs.setBackground); this.gui.add(this.params, 'xRotate', [ 'YW', 'YZ', 'ZW' ]); this.gui.add(this.params, 'yRotate', [ 'XZ', 'XY', 'XW' ]); - this.gui.add(this.params, 'captions').onChange(showDocs); + this.gui.add(this.params, 'captions').onChange(this.showDocs); this.gui.add(this.params, 'damping'); this.gui.add(this.params, 'copy link'); - + if( funcs.extras ) { + for( const label in funcs.extras ) { + this.gui.add(this.params, label); + } + } } - getShapeOptions(shapes, shape) { - const spec = shapes.filter((s) => s.name === shape); + getShapeOptions(shape) { + const spec = this.shapes.filter((s) => s.name === shape); if( spec && spec[0].options ) { return spec[0].options.map((o) => o.name); } else { @@ -136,6 +152,7 @@ class FourDGUI { this.link['zoom'] = this.numParam('zoom', parseFloat); this.link['linksize'] = this.numParam('linksize', parseFloat); this.link['linkopacity'] = this.numParam('linkopacity', parseFloat); + this.link['link2opacity'] = this.numParam('link2opacity', parseFloat); this.link['nodesize'] = this.numParam('nodesize', parseFloat); this.link['nodeopacity'] = this.numParam('nodeopacity', parseFloat); this.link['color'] = this.numParam('color', (s) => guiObj.stringToHex(s)); diff --git a/index.html b/index.html index 03e203b..0460f61 100644 --- a/index.html +++ b/index.html @@ -36,7 +36,7 @@
-
release 1.0 | +
release 1.1 | by Mike Lynch | source
diff --git a/main.js b/main.js index 16591e6..ee7b5c9 100644 --- a/main.js +++ b/main.js @@ -68,6 +68,7 @@ link_ms.map((m) => { } ); +console.log("link_ms", link_ms); const face_ms = [ @@ -168,7 +169,11 @@ function setBackground(c) { function setLinkOpacity(o, primary) { link_ms.map((lm) => lm.opacity = o); if( shape ) { - shape.links.map((l) => l.object.material.opacity = o); + shape.links.map((l) => { + if( (primary && l.label == 0) || (!primary && l.label !== 0) ) { + l.object.material.opacity = o + } + }); } } @@ -186,6 +191,8 @@ function changeShape() { } function setVisibility(option_name) { + console.log("setVisibility", option_name); + console.log(structure.options); const option = structure.options.filter((o) => o.name === option_name); if( option.length ) { node_show = option[0].nodes; @@ -197,14 +204,16 @@ function setVisibility(option_name) { gui = new FourDGUI( - STRUCTURES, - changeShape, - setColors, - setBackground, - setNodeOpacity, - setLinkOpacity, - setVisibility, - showDocs + { + shapes: STRUCTURES, + changeShape: changeShape, + setColors: setColors, + setBackground: setBackground, + setNodeOpacity: setNodeOpacity, + setLinkOpacity: setLinkOpacity, + setVisibility: setVisibility, + showDocs: showDocs, + } ); // these are here to pick up colour settings from the URL params diff --git a/polytopes.js b/polytopes.js index d39a834..8fc7669 100644 --- a/polytopes.js +++ b/polytopes.js @@ -443,6 +443,15 @@ export const cell120_inscribed = () => { links.push(...links600); } + const CELL5S = CELLINDEX.CELL120_CELL5.cell5s; + + for( const c5 in CELL5S ) { + const nodes5 = nodes.filter((n) => CELL5S[c5].includes(n.id)); + const links5 = auto_detect_edges(nodes5, 5); + links5.map((l) => l.label = 8); + links.push(...links5); + } + return { name: '120-cell', nodes: nodes, @@ -450,17 +459,44 @@ export const cell120_inscribed = () => { options: [ { name: "none", links: [ 0 ]}, { name: "one inscribed 600-cell", links: [ 0, 1 ] }, - { name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] } + { name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] }, + { name: "120 inscribed 5-cells", links: [ 0, 8 ] }, ], description: `The 120-cell is the four-dimensional analogue of the dodecahedron, and consists of 120 dodecahedra joined at 720 faces, with three dodecahedra around each edge. It is dual to the 600-cell, - and five 600-cells can be inscribed in its vertices.`, + and five 600-cells can be inscribed in its vertices. The converse + of this allows 120 5-cells (each of which has one vertex in each + of the 5 600-cells) to be inscribed in the 120-cell.`, } } +export const cell120_inscribed_cell5 = () => { + const nodes = make_120cell_vertices(); + const links = auto_detect_edges(nodes, 4); + + for( const cstr in CELLINDEX.INDEX120 ) { + label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr)); + } + + links.map((l) => l.label = 0); + + + + + return { + name: '120-cell-5-cell', + nodes: nodes, + links: links, + options: [ + { name: "5-cells", links: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ] }, + ], + description: `The 120-cell with one of its 5-cells.`, + } +} + function partition_coord(i, coords, invert) { @@ -864,7 +900,6 @@ export const icosahedron = () => { export const build_all = () => { return [ - linkTest(), tetrahedron(), octahedron(), cube(),