Compare commits
No commits in common. "main" and "experiments-120-cell" have entirely different histories.
main
...
experiment
@ -1,87 +0,0 @@
|
|||||||
# 600-cell mapping
|
|
||||||
|
|
||||||
These are the nodes from a 120-cell which are on one of its inscribed 600-cells,
|
|
||||||
sorted into layers (just the first half because the second half mirror these ones)
|
|
||||||
|
|
||||||
Start: [ 27 ]
|
|
||||||
|
|
||||||
0: [ 27 ]
|
|
||||||
0.618:
|
|
||||||
[
|
|
||||||
223, 253, 265, 331,
|
|
||||||
339, 393, 419, 427,
|
|
||||||
473, 511, 539, 555
|
|
||||||
]
|
|
||||||
|
|
||||||
1
|
|
||||||
[
|
|
||||||
95, 105, 131, 140, 165, 179,
|
|
||||||
185, 207, 258, 274, 306,
|
|
||||||
313, 347, 367, 449, 471,
|
|
||||||
499, 527, 573, 585
|
|
||||||
]
|
|
||||||
|
|
||||||
1.175
|
|
||||||
[
|
|
||||||
231, 285, 289, 324,
|
|
||||||
378, 388, 413, 425,
|
|
||||||
487, 513, 543, 563
|
|
||||||
]
|
|
||||||
|
|
||||||
1.414
|
|
||||||
[
|
|
||||||
48, 49, 61, 68, 74, 87, 234,
|
|
||||||
239, 241, 248, 300, 301, 356, 357,
|
|
||||||
369, 376, 403, 406, 444, 453, 460,
|
|
||||||
469, 490, 503, 525, 532, 572, 581,
|
|
||||||
592, 593
|
|
||||||
]
|
|
||||||
|
|
||||||
## Manual mapping progress
|
|
||||||
|
|
||||||
Starting from 27 on the inscribed 600 cell and 1 on the primary 600 cell, here are
|
|
||||||
the two arctic circles
|
|
||||||
|
|
||||||
Pole: 27: 1
|
|
||||||
|
|
||||||
Arctic circle:
|
|
||||||
|
|
||||||
419: 41
|
|
||||||
223: 49
|
|
||||||
253: 45
|
|
||||||
331: 53
|
|
||||||
427: 109
|
|
||||||
339: 105
|
|
||||||
511: 51
|
|
||||||
265: 107
|
|
||||||
473: 111
|
|
||||||
539: 55
|
|
||||||
555: 43
|
|
||||||
393: 47
|
|
||||||
|
|
||||||
Next: for each face on this icosahedron, find the other vertex - these are the next 20
|
|
||||||
|
|
||||||
367
|
|
||||||
131
|
|
||||||
499
|
|
||||||
179
|
|
||||||
471
|
|
||||||
165
|
|
||||||
449
|
|
||||||
258
|
|
||||||
274
|
|
||||||
95
|
|
||||||
347
|
|
||||||
313
|
|
||||||
185
|
|
||||||
140
|
|
||||||
527
|
|
||||||
573
|
|
||||||
105
|
|
||||||
585
|
|
||||||
306
|
|
||||||
207
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Then - for
|
|
||||||
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,28 +0,0 @@
|
|||||||
CHANGELOG
|
|
||||||
=========
|
|
||||||
|
|
||||||
## v1.3 - 7/2/2026
|
|
||||||
|
|
||||||
Went to inordinate lengths to apply the partition of the 600-cell (into five
|
|
||||||
24-cells) to the 5-cell inscription in the 120-cell, so that they could be coloured
|
|
||||||
in a way which reveals some of that symmetry.
|
|
||||||
|
|
||||||
## v1.2 - 18/1/2026
|
|
||||||
|
|
||||||
Added a second visualisation of the 120-cell's 5-cells without the 120-cell links
|
|
||||||
and with more colours added so you can get a sense of the individual 5-cells.
|
|
||||||
|
|
||||||
## v1.1 - 1/1/2026
|
|
||||||
|
|
||||||
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/)</a> since
|
|
||||||
I first made this, and I haven't updated it in a while, but I got tapered links to
|
|
||||||
work without too much performance overhead, so that seemed worth a version.
|
|
||||||
|
|
||||||
The results flicker a bit at low opacities but otherwise I'm pretty happy with
|
|
||||||
it.
|
|
||||||
`
|
|
||||||
15
NOTES.md
15
NOTES.md
@ -1,15 +0,0 @@
|
|||||||
# NOTES
|
|
||||||
|
|
||||||
|
|
||||||
## Labelling the inscribed 600-cells in a 120-cell
|
|
||||||
|
|
||||||
I want to apply the partition of the 600-cell into five 24-cells to the inscribed
|
|
||||||
600-cells in the 120-cell, so that I can use this partition to colour the inscribed
|
|
||||||
5-cells - since each 5-cell has a vertex in each of the 600-cells, getting a
|
|
||||||
partition for just one will be enough.
|
|
||||||
|
|
||||||
The challenge is that the 600-cells in the 120-cell are rotated differently to the
|
|
||||||
coordinates for the original 600-cell. And I don't have enough maths to line them up.
|
|
||||||
|
|
||||||
What about - given one of the inscribed 600-cell, find all of the 24-cells? (there
|
|
||||||
are 25 possible ones so sorting them out will be a pain)
|
|
||||||
@ -1,554 +0,0 @@
|
|||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function coherent_all() {
|
|
||||||
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function coherent_one_set() {
|
|
||||||
|
|
||||||
|
|
||||||
const cell120 = POLYTOPES.cell120_inscribed();
|
|
||||||
const all5 = gather_5cells(cell120);
|
|
||||||
|
|
||||||
const c5ns = cell5_tetras(cell120, all5, all5[0]);
|
|
||||||
|
|
||||||
|
|
||||||
const celli = c5ns.map((c5) => [ "1", "2", "3", "4", "5" ].map((l) => c5[l]));
|
|
||||||
|
|
||||||
|
|
||||||
const idict = {};
|
|
||||||
|
|
||||||
for( let i = 0; i < celli.length; i++ ) {
|
|
||||||
idict[i + 1] = celli[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(JSON.stringify(idict, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
function cell120_csv() {
|
|
||||||
const cell120 = POLYTOPES.cell120_inscribed();
|
|
||||||
const coords = [ 'x', 'y', 'z', 'w' ];
|
|
||||||
console.log("id,label,x,y,z,w,zeroes");
|
|
||||||
for( const n of cell120.nodes ) {
|
|
||||||
const zc = coords.filter((c) => n[c] === 0);
|
|
||||||
console.log(`${n.id},${n.label},${n.x},${n.y},${n.z},${n.w},${zc.length}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function cell600_links(cell600, n) {
|
|
||||||
const links = cell600.links.filter((l) => l.source === n.id || l.target === n.id);
|
|
||||||
const nbors = links.map((l) => {
|
|
||||||
if( l.source === n.id ) {
|
|
||||||
return l.target;
|
|
||||||
} else {
|
|
||||||
return l.source;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return nbors.sort((a, b) => a - b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function cell600_csv() {
|
|
||||||
const cell600 = POLYTOPES.cell600();
|
|
||||||
console.log("id,label,x,y,z,w,d");
|
|
||||||
const n0 = cell600.nodes[0];
|
|
||||||
for( const n of cell600.nodes ) {
|
|
||||||
const d = dist(n0, n);
|
|
||||||
const nbors = cell600_links(cell600, n);
|
|
||||||
const nids = nbors.join(',');
|
|
||||||
console.log(`${n.id},${n.label},${n.x},${n.y},${n.z},${n.w},${d},${nids}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cell600_csv();
|
|
||||||
|
|
||||||
2602
cellindex.js
2602
cellindex.js
File diff suppressed because it is too large
Load Diff
23
colours.js
23
colours.js
@ -1,29 +1,12 @@
|
|||||||
import ColorScheme from 'color-scheme';
|
import ColorScheme from 'color-scheme';
|
||||||
import Color from 'color';
|
|
||||||
|
|
||||||
export const get_colours = (basis) => {
|
export const get_colours = (basis) => {
|
||||||
const basis_c = Color(basis);
|
|
||||||
const hslb = basis_c.hsl();
|
|
||||||
const hue = hslb['color'][0];
|
|
||||||
const saturation = hslb['color'][1];
|
|
||||||
const luminance = hslb['color'][2];
|
|
||||||
const scheme = new ColorScheme;
|
const scheme = new ColorScheme;
|
||||||
scheme.from_hue(hue).scheme("tetrade").distance(0.75);
|
const hexbasis = basis.toString(16).padStart(6, "0");
|
||||||
const colours = scheme.colors();
|
scheme.from_hex(hexbasis).scheme("tetrade").variation("hard").distance(0.5);
|
||||||
colours.reverse();
|
return scheme.colors().map((cs) => parseInt('0x' + cs));
|
||||||
const hsl = colours.map((c) => Color("#" + c).hsl());
|
|
||||||
const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber());
|
|
||||||
resaturated.unshift(basis);
|
|
||||||
return resaturated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// basic colours where 0 = blue
|
|
||||||
// 1 - dark blue
|
|
||||||
// 2 - white
|
|
||||||
// 3 - light cyan
|
|
||||||
// 4 - light orange
|
|
||||||
// 5 - dark orange
|
|
||||||
|
|
||||||
export const get_plain_colours = (basis) => {
|
export const get_plain_colours = (basis) => {
|
||||||
return [
|
return [
|
||||||
basis,
|
basis,
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
104
fourDShape.js
104
fourDShape.js
@ -1,10 +1,7 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
|
||||||
import { TaperedLink } from './taperedLink.js';
|
|
||||||
|
|
||||||
|
|
||||||
const HYPERPLANE = 2.0;
|
const HYPERPLANE = 2.0;
|
||||||
const W_FORESHORTENING = 0.04;
|
|
||||||
|
|
||||||
|
|
||||||
class FourDShape extends THREE.Group {
|
class FourDShape extends THREE.Group {
|
||||||
@ -18,10 +15,11 @@ class FourDShape extends THREE.Group {
|
|||||||
this.nodes3 = {};
|
this.nodes3 = {};
|
||||||
this.links = structure.links;
|
this.links = structure.links;
|
||||||
this.faces = ( "faces" in structure ) ? structure.faces : [];
|
this.faces = ( "faces" in structure ) ? structure.faces : [];
|
||||||
|
this.node_size = structure.geometry.node_size;
|
||||||
|
this.link_size = structure.geometry.link_size;
|
||||||
this.node_scale = 1;
|
this.node_scale = 1;
|
||||||
this.link_scale = 1;
|
this.link_scale = 1;
|
||||||
this.hyperplane = HYPERPLANE;
|
this.hyperplane = HYPERPLANE;
|
||||||
this.foreshortening = W_FORESHORTENING;
|
|
||||||
this.initShapes();
|
this.initShapes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,15 +27,15 @@ class FourDShape extends THREE.Group {
|
|||||||
|
|
||||||
// if a node/link has no label, use the 0th material
|
// if a node/link has no label, use the 0th material
|
||||||
|
|
||||||
getMaterialLabel(entity) {
|
getMaterial(entity, materials) {
|
||||||
if( "label" in entity ) {
|
if( "label" in entity ) {
|
||||||
return entity.label
|
return materials[entity.label];
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return materials[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeNode(material, v3, scale) {
|
makeNode(material, v3) {
|
||||||
const geometry = new THREE.SphereGeometry(this.node_size);
|
const geometry = new THREE.SphereGeometry(this.node_size);
|
||||||
const sphere = new THREE.Mesh(geometry, material);
|
const sphere = new THREE.Mesh(geometry, material);
|
||||||
sphere.position.copy(v3);
|
sphere.position.copy(v3);
|
||||||
@ -45,24 +43,34 @@ class FourDShape extends THREE.Group {
|
|||||||
return sphere;
|
return sphere;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeLink(materialLabel, link) {
|
makeLink(material, link) {
|
||||||
const n1 = this.nodes3[link.source];
|
const n1 = this.nodes3[link.source].v3;
|
||||||
const n2 = this.nodes3[link.target];
|
const n2 = this.nodes3[link.target].v3;
|
||||||
const s1 = this.link_scale * n1.scale;
|
const length = n1.distanceTo(n2);
|
||||||
const s2 = this.link_scale * n2.scale;
|
const centre = new THREE.Vector3();
|
||||||
const basematerial = this.link_ms[materialLabel];
|
centre.lerpVectors(n1, n2, 0.5);
|
||||||
const edge = new TaperedLink(basematerial, materialLabel, n1, n2, s1, s2);
|
const geometry = new THREE.CylinderGeometry(this.link_size, this.link_size, 1);
|
||||||
this.add( edge );
|
const cyl = new THREE.Mesh(geometry, material);
|
||||||
|
const edge = new THREE.Group();
|
||||||
|
edge.add(cyl);
|
||||||
|
edge.position.copy(centre);
|
||||||
|
edge.scale.copy(new THREE.Vector3(1, 1, length));
|
||||||
|
edge.lookAt(n2);
|
||||||
|
cyl.rotation.x = Math.PI / 2.0;
|
||||||
|
this.add(edge);
|
||||||
return edge;
|
return edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLink(link, links_show) {
|
updateLink(link) {
|
||||||
const n1 = this.nodes3[link.source];
|
const n1 = this.nodes3[link.source].v3;
|
||||||
const n2 = this.nodes3[link.target];
|
const n2 = this.nodes3[link.target].v3;
|
||||||
const s1 = this.link_scale * n1.scale;
|
const length = n1.distanceTo(n2);
|
||||||
const s2 = this.link_scale * n2.scale;
|
const centre = new THREE.Vector3();
|
||||||
link.object.update(n1, n2, s1, s2);
|
centre.lerpVectors(n1, n2, 0.5);
|
||||||
link.object.visible = (!links_show || links_show.includes(link.label));
|
link.object.scale.copy(new THREE.Vector3(this.link_scale, this.link_scale, length));
|
||||||
|
link.object.position.copy(centre);
|
||||||
|
link.object.lookAt(n2);
|
||||||
|
link.object.children[0].rotation.x = Math.PI / 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,62 +99,47 @@ class FourDShape extends THREE.Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fourDscale(w) {
|
fourDtoV3(x, y, z, w, rotations) {
|
||||||
return this.hyperplane / ( this.hyperplane + w );
|
|
||||||
}
|
|
||||||
|
|
||||||
fourDrotate(x, y, z, w, rotations) {
|
|
||||||
const v4 = new THREE.Vector4(x, y, z, w);
|
const v4 = new THREE.Vector4(x, y, z, w);
|
||||||
for ( const m4 of rotations ) {
|
for ( const m4 of rotations ) {
|
||||||
v4.applyMatrix4(m4);
|
v4.applyMatrix4(m4);
|
||||||
}
|
}
|
||||||
return v4;
|
const k = this.hyperplane / (this.hyperplane + v4.w);
|
||||||
}
|
|
||||||
|
|
||||||
fourDtoV3(v4) {
|
|
||||||
const k = this.fourDscale(v4.w);
|
|
||||||
return new THREE.Vector3(v4.x * k, v4.y * k, v4.z * k);
|
return new THREE.Vector3(v4.x * k, v4.y * k, v4.z * k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
initShapes() {
|
initShapes() {
|
||||||
for( const n of this.nodes4 ) {
|
for( const n of this.nodes4 ) {
|
||||||
const k = this.fourDscale(n.w);
|
const v3 = this.fourDtoV3(n.x, n.y, n.z, n.w, []);
|
||||||
const v3 = new THREE.Vector3(n.x * k, n.y * k, n.z * k);
|
const material = this.getMaterial(n, this.node_ms);
|
||||||
const material = this.node_ms[this.getMaterialLabel(n)];
|
|
||||||
this.nodes3[n.id] = {
|
this.nodes3[n.id] = {
|
||||||
v3: v3,
|
v3: v3,
|
||||||
scale: k,
|
object: this.makeNode(material, v3)
|
||||||
label: n.label,
|
|
||||||
object: this.makeNode(material, v3, k)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
for( const l of this.links ) {
|
for( const l of this.links ) {
|
||||||
const mLabel = this.getMaterialLabel(l);
|
const material = this.getMaterial(l, this.link_ms);
|
||||||
l.object = this.makeLink(mLabel, l);
|
l.object = this.makeLink(material, l);
|
||||||
}
|
}
|
||||||
for( const f of this.faces ) {
|
for( const f of this.faces ) {
|
||||||
const material = this.face_ms(this.getMaterialLabel(f));
|
const material = this.getMaterial(f, this.face_ms);
|
||||||
f.object = this.makeFace(material, f);
|
f.object = this.makeFace(material, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render3(rotations, nodes_show, links_show) {
|
render3(rotations) {
|
||||||
this.scalev3 = new THREE.Vector3(this.node_scale, this.node_scale, this.node_scale);
|
this.scalev3 = new THREE.Vector3(this.node_scale, this.node_scale, this.node_scale);
|
||||||
for( const n of this.nodes4 ) {
|
for( const n of this.nodes4 ) {
|
||||||
const v4 = this.fourDrotate(n.x, n.y, n.z, n.w, rotations);
|
const v3 = this.fourDtoV3(n.x, n.y, n.z, n.w, rotations);
|
||||||
const k = this.fourDscale(v4.w);
|
|
||||||
const v3 = new THREE.Vector3(v4.x * k, v4.y * k, v4.z * k);
|
|
||||||
const s4 = k * this.node_scale * this.foreshortening;
|
|
||||||
const s3 = new THREE.Vector3(s4, s4, s4);
|
|
||||||
this.nodes3[n.id].v3 = v3;
|
this.nodes3[n.id].v3 = v3;
|
||||||
this.nodes3[n.id].scale = k * this.foreshortening;
|
|
||||||
this.nodes3[n.id].object.position.copy(v3);
|
this.nodes3[n.id].object.position.copy(v3);
|
||||||
this.nodes3[n.id].object.scale.copy(s3);
|
this.nodes3[n.id].object.scale.copy(this.scalev3);
|
||||||
this.nodes3[n.id].object.visible = ( !nodes_show || nodes_show.includes(n.label) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for( const l of this.links ) {
|
for( const l of this.links ) {
|
||||||
this.updateLink(l, links_show);
|
this.updateLink(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
for( const f of this.faces ) {
|
for( const f of this.faces ) {
|
||||||
@ -154,6 +147,7 @@ class FourDShape extends THREE.Group {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { FourDShape };
|
export { FourDShape };
|
||||||
|
|||||||
120
gui.js
120
gui.js
@ -1,26 +1,18 @@
|
|||||||
import { GUI } from 'lil-gui';
|
import { GUI } from 'lil-gui';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
nodesize: 0.6,
|
thickness: 0.25,
|
||||||
nodeopacity: 1,
|
nodesize: 1.25,
|
||||||
linksize: 1.0,
|
linkopacity: 0.5,
|
||||||
linkopacity: 0.75,
|
shape: '120-cell',
|
||||||
shape: '600-cell',
|
color: 0x3293a9,
|
||||||
link2opacity: 0.75,
|
|
||||||
option: 'none',
|
|
||||||
visibility: 5,
|
|
||||||
inscribed: false,
|
|
||||||
inscribe_all: false,
|
|
||||||
colour: 0x3293a9,
|
|
||||||
background: 0xd4d4d4,
|
background: 0xd4d4d4,
|
||||||
hyperplane: 0.93,
|
hyperplane: 2,
|
||||||
zoom: 1,
|
xRotate: 'YW',
|
||||||
xRotate: 'YZ',
|
|
||||||
yRotate: 'XZ',
|
yRotate: 'XZ',
|
||||||
dtheta: 0,
|
dtheta: 0,
|
||||||
damping: false,
|
|
||||||
captions: true,
|
|
||||||
dpsi: 0,
|
dpsi: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,82 +20,41 @@ const DEFAULTS = {
|
|||||||
|
|
||||||
class FourDGUI {
|
class FourDGUI {
|
||||||
|
|
||||||
constructor(funcs) {
|
constructor(createShape, setColor, setBackground, setLinkOpacity) {
|
||||||
this.shapes = funcs.shapes;
|
|
||||||
this.gui = new GUI();
|
this.gui = new GUI();
|
||||||
const SHAPE_NAMES = this.shapes.map((s) => s.name);
|
|
||||||
|
|
||||||
this.parseLinkParams();
|
this.parseLinkParams();
|
||||||
const guiObj = this;
|
const guiObj = this;
|
||||||
this.params = {
|
this.params = {
|
||||||
shape: this.link['shape'],
|
shape: this.link['shape'],
|
||||||
option: this.link['option'],
|
thickness: this.link['thickness'],
|
||||||
inscribed: this.link['inscribed'],
|
|
||||||
inscribe_all: this.link['inscribe_all'],
|
|
||||||
linksize: this.link['linksize'],
|
|
||||||
linkopacity: this.link['linkopacity'],
|
linkopacity: this.link['linkopacity'],
|
||||||
link2opacity: this.link['link2opacity'],
|
|
||||||
nodesize: this.link['nodesize'],
|
nodesize: this.link['nodesize'],
|
||||||
nodeopacity: this.link['nodeopacity'],
|
color: this.link['color'],
|
||||||
depth: this.link['depth'],
|
|
||||||
colour: this.link['colour'],
|
|
||||||
background: this.link['background'],
|
background: this.link['background'],
|
||||||
hyperplane: this.link['hyperplane'],
|
hyperplane: this.link['hyperplane'],
|
||||||
zoom: this.link['zoom'],
|
|
||||||
xRotate: this.link['xRotate'],
|
xRotate: this.link['xRotate'],
|
||||||
yRotate: this.link['yRotate'],
|
yRotate: this.link['yRotate'],
|
||||||
damping: false,
|
damping: false,
|
||||||
captions: true,
|
|
||||||
dtheta: this.link['dtheta'],
|
dtheta: this.link['dtheta'],
|
||||||
dpsi: this.link['dpsi'],
|
dpsi: this.link['dpsi'],
|
||||||
"copy link": function () { guiObj.copyUrl() },
|
"copy link": function () { guiObj.copyUrl() }
|
||||||
};
|
};
|
||||||
if( funcs.extras ) {
|
|
||||||
for( const label in funcs.extras ) {
|
this.gui.add(this.params, 'shape',
|
||||||
this.params[label] = funcs.extras[label];
|
[ '5-cell', '16-cell', 'tesseract', '24-cell', '600-cell', '120-cell', '120-cell-inscribed' ]
|
||||||
}
|
).onChange(createShape)
|
||||||
}
|
|
||||||
let options_ctrl;
|
this.gui.add(this.params, 'hyperplane', 1.5, 2.25);
|
||||||
this.gui.add(this.params, 'shape', SHAPE_NAMES).onChange((shape) => {
|
this.gui.add(this.params, 'thickness', 0.1, 2);
|
||||||
const options = this.getShapeOptions(shape);
|
this.gui.add(this.params, 'linkopacity', 0, 1).onChange(setLinkOpacity);
|
||||||
options_ctrl = options_ctrl.options(options).onChange((option) => {
|
this.gui.add(this.params, 'nodesize', 0.1, 4);
|
||||||
funcs.setVisibility(option)
|
this.gui.addColor(this.params, 'color').onChange(setColor);
|
||||||
});
|
this.gui.addColor(this.params, 'background').onChange(setBackground);
|
||||||
options_ctrl.setValue(options[0])
|
|
||||||
funcs.changeShape(shape)
|
|
||||||
});
|
|
||||||
const options = this.getShapeOptions(this.params['shape']);
|
|
||||||
options_ctrl = this.gui.add(this.params, 'option').options(options).onChange((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(funcs.setNodeOpacity);
|
|
||||||
this.gui.add(this.params, 'linksize', 0, 2);
|
|
||||||
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, 'colour').onChange(funcs.setColours);
|
|
||||||
this.gui.addColor(this.params, 'background').onChange(funcs.setBackground);
|
|
||||||
this.gui.add(this.params, 'xRotate', [ 'YW', 'YZ', 'ZW' ]);
|
this.gui.add(this.params, 'xRotate', [ 'YW', 'YZ', 'ZW' ]);
|
||||||
this.gui.add(this.params, 'yRotate', [ 'XZ', 'XY', 'XW' ]);
|
this.gui.add(this.params, 'yRotate', [ 'XZ', 'XY', 'XW' ]);
|
||||||
this.gui.add(this.params, 'captions').onChange(this.showDocs);
|
|
||||||
this.gui.add(this.params, 'damping');
|
this.gui.add(this.params, 'damping');
|
||||||
this.gui.add(this.params, 'copy link');
|
this.gui.add(this.params, 'copy link');
|
||||||
if( funcs.extras ) {
|
|
||||||
for( const label in funcs.extras ) {
|
|
||||||
this.gui.add(this.params, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -131,10 +82,9 @@ class FourDGUI {
|
|||||||
parseLinkParams() {
|
parseLinkParams() {
|
||||||
this.linkUrl = new URL(window.location.toLocaleString());
|
this.linkUrl = new URL(window.location.toLocaleString());
|
||||||
this.link = {};
|
this.link = {};
|
||||||
const guiObj = this;
|
|
||||||
|
|
||||||
this.urlParams = this.linkUrl.searchParams;
|
this.urlParams = this.linkUrl.searchParams;
|
||||||
for( const param of [ "shape", "xRotate", "yRotate", "option" ]) {
|
for( const param of [ "shape", "xRotate", "yRotate" ]) {
|
||||||
const value = this.urlParams.get(param);
|
const value = this.urlParams.get(param);
|
||||||
if( value ) {
|
if( value ) {
|
||||||
this.link[param] = value;
|
this.link[param] = value;
|
||||||
@ -142,17 +92,12 @@ class FourDGUI {
|
|||||||
this.link[param] = DEFAULTS[param];
|
this.link[param] = DEFAULTS[param];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for( const param of [ "inscribed", "inscribe_all"] ) {
|
const guiObj = this;
|
||||||
this.link[param] = ( this.urlParams.get(param) === 'y' );
|
|
||||||
}
|
|
||||||
this.link['hyperplane'] = this.numParam('hyperplane', parseFloat);
|
this.link['hyperplane'] = this.numParam('hyperplane', parseFloat);
|
||||||
this.link['zoom'] = this.numParam('zoom', parseFloat);
|
this.link['thickness'] = this.numParam('thickness', parseFloat);
|
||||||
this.link['linksize'] = this.numParam('linksize', parseFloat);
|
|
||||||
this.link['linkopacity'] = this.numParam('linkopacity', parseFloat);
|
this.link['linkopacity'] = this.numParam('linkopacity', parseFloat);
|
||||||
this.link['link2opacity'] = this.numParam('link2opacity', parseFloat);
|
|
||||||
this.link['nodesize'] = this.numParam('nodesize', 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));
|
||||||
this.link['colour'] = this.numParam('colour', (s) => guiObj.stringToHex(s));
|
|
||||||
this.link['background'] = this.numParam('background', (s) => guiObj.stringToHex(s));
|
this.link['background'] = this.numParam('background', (s) => guiObj.stringToHex(s));
|
||||||
this.link['dpsi'] = this.numParam('dpsi', parseFloat);
|
this.link['dpsi'] = this.numParam('dpsi', parseFloat);
|
||||||
this.link['dtheta'] = this.numParam('dtheta', parseFloat);
|
this.link['dtheta'] = this.numParam('dtheta', parseFloat);
|
||||||
@ -162,17 +107,11 @@ class FourDGUI {
|
|||||||
copyUrl() {
|
copyUrl() {
|
||||||
const url = new URL(this.linkUrl.origin + this.linkUrl.pathname);
|
const url = new URL(this.linkUrl.origin + this.linkUrl.pathname);
|
||||||
url.searchParams.append("shape", this.params.shape);
|
url.searchParams.append("shape", this.params.shape);
|
||||||
url.searchParams.append("option", this.params.option);
|
url.searchParams.append("thickness", this.params.thickness.toString());
|
||||||
url.searchParams.append("inscribed", this.params.inscribed ? 'y': 'n');
|
|
||||||
url.searchParams.append("inscribe_all", this.params.inscribe_all ? 'y': 'n');
|
|
||||||
url.searchParams.append("linksize", this.params.linksize.toString());
|
|
||||||
url.searchParams.append("nodesize", this.params.nodesize.toString());
|
url.searchParams.append("nodesize", this.params.nodesize.toString());
|
||||||
url.searchParams.append("nodeopacity", this.params.nodesize.toString());
|
url.searchParams.append("color", this.hexToString(this.params.color));
|
||||||
url.searchParams.append("linkopacity", this.params.nodeopacity.toString());
|
|
||||||
url.searchParams.append("colour", this.hexToString(this.params.colour));
|
|
||||||
url.searchParams.append("background", this.hexToString(this.params.background));
|
url.searchParams.append("background", this.hexToString(this.params.background));
|
||||||
url.searchParams.append("hyperplane", this.params.hyperplane.toString());
|
url.searchParams.append("hyperplane", this.params.hyperplane.toString());
|
||||||
url.searchParams.append("zoom", this.params.zoom.toString());
|
|
||||||
url.searchParams.append("xRotate", this.params.xRotate);
|
url.searchParams.append("xRotate", this.params.xRotate);
|
||||||
url.searchParams.append("yRotate", this.params.yRotate);
|
url.searchParams.append("yRotate", this.params.yRotate);
|
||||||
url.searchParams.append("dtheta", this.params.dtheta.toString());
|
url.searchParams.append("dtheta", this.params.dtheta.toString());
|
||||||
@ -187,6 +126,7 @@ class FourDGUI {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigator.clipboard.writeText(text).then(function() {
|
navigator.clipboard.writeText(text).then(function() {
|
||||||
|
console.log('Async: Copying to clipboard was successful!');
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
console.error('Async: Could not copy text: ', err);
|
console.error('Async: Could not copy text: ', err);
|
||||||
});
|
});
|
||||||
|
|||||||
31
index.html
31
index.html
@ -5,40 +5,9 @@
|
|||||||
<title>FourD</title>
|
<title>FourD</title>
|
||||||
<style>
|
<style>
|
||||||
body { margin: 0; }
|
body { margin: 0; }
|
||||||
div#description {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 20%;
|
|
||||||
z-index: 2;
|
|
||||||
font-family: sans-serif;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
div#release_notes {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 20%;
|
|
||||||
z-index: 2;
|
|
||||||
padding: 1em;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
div#info {
|
|
||||||
position: fixed;
|
|
||||||
bottom:0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 2;
|
|
||||||
border:0.5em;
|
|
||||||
font-family: sans-serif }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module" src="/main.js"></script>
|
<script type="module" src="/main.js"></script>
|
||||||
<div id="description"></div>
|
|
||||||
<div id="release_notes"></div>
|
|
||||||
<div id="info"><a href="#" id="show_notes">release 1.3</a> |
|
|
||||||
|
|
||||||
by <a target="_blank" href="https://mikelynch.org/">Mike Lynch</a> |
|
|
||||||
<a target="_blank" href="https://git.tilde.town/bombinans/fourdjs">source</a></div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,409 +0,0 @@
|
|||||||
|
|
||||||
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));
|
|
||||||
173
layer600cell.js
173
layer600cell.js
@ -1,173 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
119
linktest.js
119
linktest.js
@ -1,119 +0,0 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
||||||
import { GUI } from 'lil-gui';
|
|
||||||
|
|
||||||
import { TaperedLink } from './taperedLink.js';
|
|
||||||
|
|
||||||
const FACE_OPACITY = 0.3;
|
|
||||||
const CAMERA_K = 5;
|
|
||||||
|
|
||||||
// scene, lights and camera
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const scene = new THREE.Scene();
|
|
||||||
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
|
|
||||||
const light = new THREE.PointLight(0xffffff, 2);
|
|
||||||
light.position.set(10, 10, 10);
|
|
||||||
scene.add(light);
|
|
||||||
const light2 = new THREE.PointLight(0xffffff, 2);
|
|
||||||
light2.position.set(-10, 5, 10);
|
|
||||||
scene.add(light);
|
|
||||||
const amblight = new THREE.AmbientLight(0xffffff, 0.5);
|
|
||||||
scene.add(amblight);
|
|
||||||
|
|
||||||
camera.position.set(0, 0, CAMERA_K / 2);
|
|
||||||
|
|
||||||
camera.lookAt(0, 0, 0);
|
|
||||||
camera.position.z = 8;
|
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({antialias: true});
|
|
||||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
||||||
|
|
||||||
renderer.localClippingEnabled = true;
|
|
||||||
|
|
||||||
const controls = new OrbitControls( camera, renderer.domElement );
|
|
||||||
|
|
||||||
|
|
||||||
controls.autoRotate = true;
|
|
||||||
|
|
||||||
document.body.appendChild( renderer.domElement );
|
|
||||||
|
|
||||||
const NODEC = 0x3293a9;
|
|
||||||
const LINKC = 0x00ff88;
|
|
||||||
const BACKGROUNDC = 0xd4d4d4;
|
|
||||||
|
|
||||||
scene.background = new THREE.Color(BACKGROUNDC);
|
|
||||||
const material = new THREE.MeshStandardMaterial({ color: LINKC });
|
|
||||||
|
|
||||||
material.transparent = true;
|
|
||||||
material.opacity = 0.7;
|
|
||||||
|
|
||||||
const node_mat = new THREE.MeshStandardMaterial({ color: NODEC });
|
|
||||||
|
|
||||||
node_mat.transparent = true;
|
|
||||||
node_mat.opacity = 0.5;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
r1: 0.5,
|
|
||||||
r2: 0.6,
|
|
||||||
sync: false,
|
|
||||||
l: 9,
|
|
||||||
rotx: 1,
|
|
||||||
roty: 0,
|
|
||||||
rotz: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const gui = new GUI();
|
|
||||||
|
|
||||||
gui.add(params, "r1", 0.01, 1.5);
|
|
||||||
gui.add(params, "r2", 0.01, 1.5);
|
|
||||||
gui.add(params, "sync");
|
|
||||||
gui.add(params, "l", 0, 10);
|
|
||||||
gui.add(params, "rotx", 0, 4);
|
|
||||||
gui.add(params, "roty", 0, 4);
|
|
||||||
gui.add(params, "rotz", 0, 4);
|
|
||||||
|
|
||||||
function makeNode(material, pos, r) {
|
|
||||||
const geometry = new THREE.SphereGeometry(1);
|
|
||||||
const sphere = new THREE.Mesh(geometry, material);
|
|
||||||
const node = {
|
|
||||||
v3: pos,
|
|
||||||
object: sphere
|
|
||||||
};
|
|
||||||
updateNode(node, pos, r);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNode(node, pos, r) {
|
|
||||||
node.v3 = pos;
|
|
||||||
node.object.scale.copy(new THREE.Vector3(r, r, r));
|
|
||||||
node.object.position.copy(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const n1 = makeNode(node_mat, new THREE.Vector3(-params["l"], -1, -1), params["r1"]);
|
|
||||||
const n2 = makeNode(node_mat, new THREE.Vector3(params["l"], 1, 1), params["r2"]);
|
|
||||||
|
|
||||||
const tl = new TaperedLink(material, n1, n2, params["r1"], params["r2"]);
|
|
||||||
|
|
||||||
scene.add(n1.object);
|
|
||||||
scene.add(n2.object);
|
|
||||||
|
|
||||||
scene.add(tl);
|
|
||||||
|
|
||||||
function animate() {
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
|
|
||||||
const r1 = params["r1"];
|
|
||||||
const r2 = params["sync"] ? r1 : params["r2"]
|
|
||||||
|
|
||||||
updateNode(n1, new THREE.Vector3(- params["l"], -1, -1), r1);
|
|
||||||
updateNode(n2, new THREE.Vector3(params["l"], 1, 1), r2);
|
|
||||||
tl.update(n1, n2, r1, r2, params["rotx"], params["roty"], params["rotz"]);
|
|
||||||
controls.update();
|
|
||||||
renderer.render(scene, camera);
|
|
||||||
}
|
|
||||||
animate();
|
|
||||||
201
main.js
201
main.js
@ -1,31 +1,5 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
|
||||||
const RELEASE_NOTES = `
|
|
||||||
<p><b>v1.3 - 7/2/2026</b></p>
|
|
||||||
|
|
||||||
<p>Went to inordinate lengths to apply the partition of the 600-cell (into five
|
|
||||||
24-cells) to the 5-cell inscription in the 120-cell, so that they could be coloured
|
|
||||||
in a way which reveals some of that symmetry.</p>
|
|
||||||
|
|
||||||
<p><b>v1.2 - 18/1/2026</b></p>
|
|
||||||
|
|
||||||
<p>Added a second visualisation of the 120-cell's 5-cells without the 120-cell links and with more colours added so you can get a sense of the individual 5-cells.</p>
|
|
||||||
|
|
||||||
<p><b>v1.1 - 1/1/2026</b></p>
|
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
<p><b>v1.0 - 16/11/2025</b></p>
|
|
||||||
|
|
||||||
<p>It's been <a target="_blank" href="https://mikelynch.org/2023/Sep/02/120-cell/">two years</a> since
|
|
||||||
I first made this, and I haven't updated it in a while, but I got tapered links to
|
|
||||||
work without too much performance overhead, so that seemed worth a version.</p>
|
|
||||||
|
|
||||||
<p>The results flicker a bit at low opacities but otherwise I'm pretty happy with
|
|
||||||
it.</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import * as POLYTOPES from './polytopes.js';
|
import * as POLYTOPES from './polytopes.js';
|
||||||
@ -35,7 +9,6 @@ import { FourDShape } from './fourDShape.js';
|
|||||||
import { get_colours } from './colours.js';
|
import { get_colours } from './colours.js';
|
||||||
|
|
||||||
const FACE_OPACITY = 0.3;
|
const FACE_OPACITY = 0.3;
|
||||||
const CAMERA_K = 5;
|
|
||||||
|
|
||||||
// scene, lights and camera
|
// scene, lights and camera
|
||||||
|
|
||||||
@ -50,44 +23,34 @@ scene.add(light);
|
|||||||
const amblight = new THREE.AmbientLight(0xffffff, 0.5);
|
const amblight = new THREE.AmbientLight(0xffffff, 0.5);
|
||||||
scene.add(amblight);
|
scene.add(amblight);
|
||||||
|
|
||||||
camera.position.set(0, 0, CAMERA_K / 2);
|
camera.position.z = 4;
|
||||||
|
|
||||||
camera.lookAt(0, 0, 0);
|
|
||||||
//camera.position.z = 4;
|
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({antialias: true});
|
const renderer = new THREE.WebGLRenderer({antialias: true});
|
||||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
||||||
|
|
||||||
renderer.localClippingEnabled = true;
|
|
||||||
|
|
||||||
|
|
||||||
document.body.appendChild( renderer.domElement );
|
document.body.appendChild( renderer.domElement );
|
||||||
|
|
||||||
// set up colours and materials for gui callbacks
|
// set up colours and materials for gui callbacks
|
||||||
|
|
||||||
scene.background = new THREE.Color(DEFAULTS.background);
|
scene.background = new THREE.Color(DEFAULTS.background);
|
||||||
const node_colours = get_colours(DEFAULTS.colour);
|
const material = new THREE.MeshStandardMaterial({ color: DEFAULTS.color });
|
||||||
|
const node_colours = get_colours(DEFAULTS.color);
|
||||||
|
|
||||||
|
|
||||||
|
material.transparent = true;
|
||||||
|
material.opacity = 0.5;
|
||||||
|
|
||||||
|
|
||||||
const node_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c}));
|
const node_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c}));
|
||||||
const link_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c}));
|
const link_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c}));
|
||||||
|
|
||||||
node_ms.map((m) => {
|
|
||||||
m.transparent = true;
|
|
||||||
m.opacity = 1.0;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
link_ms.map((m) => {
|
link_ms.map((m) => {
|
||||||
m.transparent = true;
|
m.transparent = true;
|
||||||
m.opacity = 0.5;
|
m.opacity = 0.5;
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const face_ms = [
|
const face_ms = [
|
||||||
new THREE.MeshStandardMaterial( { color: 0x44ff44 } )
|
new THREE.MeshLambertMaterial( { color: 0x44ff44 } )
|
||||||
];
|
];
|
||||||
|
|
||||||
for( const face_m of face_ms ) {
|
for( const face_m of face_ms ) {
|
||||||
@ -96,141 +59,54 @@ for( const face_m of face_ms ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const STRUCTURES = POLYTOPES.build_all();
|
const STRUCTURES = {
|
||||||
|
'5-cell': POLYTOPES.cell5(),
|
||||||
const STRUCTURES_BY_NAME = {};
|
'16-cell': POLYTOPES.cell16(),
|
||||||
|
'tesseract': POLYTOPES.tesseract(),
|
||||||
STRUCTURES.map((s) => STRUCTURES_BY_NAME[s.name] = s);
|
'24-cell': POLYTOPES.cell24(),
|
||||||
|
'120-cell': POLYTOPES.cell120(),
|
||||||
|
'120-cell-inscribed': POLYTOPES.cell120_inscribed(),
|
||||||
|
'600-cell': POLYTOPES.cell600()
|
||||||
|
};
|
||||||
|
|
||||||
let shape = false;
|
let shape = false;
|
||||||
let structure = false;
|
|
||||||
let node_show = [];
|
|
||||||
let link_show = [];
|
|
||||||
|
|
||||||
|
function createShape(name) {
|
||||||
function createShape(name, option) {
|
|
||||||
if( shape ) {
|
if( shape ) {
|
||||||
scene.remove(shape);
|
scene.remove(shape);
|
||||||
}
|
}
|
||||||
structure = STRUCTURES_BY_NAME[name];
|
shape = new FourDShape(node_ms, link_ms, face_ms, STRUCTURES[name]);
|
||||||
shape = new FourDShape(node_ms, link_ms, face_ms, structure);
|
|
||||||
scene.add(shape);
|
scene.add(shape);
|
||||||
setVisibility(option ? option : structure.options[0].name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayDocs(name) {
|
|
||||||
const docdiv = document.getElementById("description");
|
|
||||||
const description = STRUCTURES_BY_NAME[name].description;
|
|
||||||
if( description ) {
|
|
||||||
docdiv.innerHTML =`<p>${name}</p><p>${description}</p>`;
|
|
||||||
} else {
|
|
||||||
docdiv.innerHTML =`<p>${name}</p>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDocs(visible) {
|
|
||||||
const docdiv = document.getElementById("description");
|
|
||||||
if( visible ) {
|
|
||||||
docdiv.style.display = '';
|
|
||||||
} else {
|
|
||||||
docdiv.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function releaseNotes() {
|
|
||||||
showDocs(false);
|
|
||||||
const reldiv = document.getElementById("release_notes");
|
|
||||||
reldiv.style.display = '';
|
|
||||||
reldiv.innerHTML = RELEASE_NOTES + '<p><a id="no_notes" href="#">[hide]</a>';
|
|
||||||
const goaway = document.getElementById("no_notes");
|
|
||||||
goaway.addEventListener('click', noNotes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function noNotes() {
|
|
||||||
const reldiv = document.getElementById("release_notes");
|
|
||||||
reldiv.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
const relnotes = document.getElementById('show_notes');
|
|
||||||
|
|
||||||
relnotes.addEventListener('click', releaseNotes);
|
|
||||||
|
|
||||||
|
|
||||||
// initialise gui and read params from URL
|
// initialise gui and read params from URL
|
||||||
|
|
||||||
// callbacks to do things which are triggered by controls: reset the shape,
|
// callbacks to do things which are triggered by controls: reset the shape,
|
||||||
// change the colors. Otherwise we just read stuff from gui.params.
|
// change the colors. Otherwise we just read stuff from gui.params.
|
||||||
|
|
||||||
function setColours(c) {
|
function setColors(c) {
|
||||||
const nc = get_colours(c);
|
const nc = get_colours(c);
|
||||||
for( let i = 0; i < node_ms.length; i++ ) {
|
for( let i = 0; i < node_ms.length; i++ ) {
|
||||||
node_ms[i].color = new THREE.Color(nc[i]);
|
node_ms[i].color = new THREE.Color(nc[i]);
|
||||||
link_ms[i].color = new THREE.Color(nc[i]);
|
link_ms[i].color = new THREE.Color(nc[i]);
|
||||||
}
|
}
|
||||||
if( shape ) {
|
material.color = new THREE.Color(c);
|
||||||
// taperedLink.set_color updates according to the link index
|
|
||||||
shape.links.map((l) => l.object.set_colour(nc));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBackground(c) {
|
function setBackground(c) {
|
||||||
scene.background = new THREE.Color(c)
|
scene.background = new THREE.Color(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// taperedLinks have their own materials so we have to set opacity
|
function setLinkOpacity(o) {
|
||||||
// on them individually. And also set the base materials as they
|
for( const lm of link_ms ) {
|
||||||
// will get updated from it when the shape changes
|
lm.opacity = o;
|
||||||
|
|
||||||
function setLinkOpacity(o, primary) {
|
|
||||||
link_ms.map((lm) => lm.opacity = o);
|
|
||||||
if( shape ) {
|
|
||||||
shape.links.map((l) => {
|
|
||||||
if( (primary && l.label == 0) || (!primary && l.label !== 0) ) {
|
|
||||||
l.object.material.opacity = o
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNodeOpacity(o) {
|
|
||||||
node_ms.map((nm) => nm.opacity = o);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let gui;
|
|
||||||
|
|
||||||
|
|
||||||
function changeShape() {
|
|
||||||
createShape(gui.params.shape);
|
|
||||||
displayDocs(gui.params.shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setVisibility(option_name) {
|
|
||||||
const option = structure.options.filter((o) => o.name === option_name);
|
|
||||||
if( option.length ) {
|
|
||||||
node_show = option[0].nodes;
|
|
||||||
link_show = option[0].links;
|
|
||||||
} else {
|
|
||||||
console.log(`Error: option '${option_name}' not found`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gui = new FourDGUI(createShape, setColors, setBackground, setLinkOpacity);
|
||||||
gui = new FourDGUI(
|
|
||||||
{
|
|
||||||
shapes: STRUCTURES,
|
|
||||||
changeShape: changeShape,
|
|
||||||
setColours: setColours,
|
|
||||||
setBackground: setBackground,
|
|
||||||
setNodeOpacity: setNodeOpacity,
|
|
||||||
setLinkOpacity: setLinkOpacity,
|
|
||||||
setVisibility: setVisibility,
|
|
||||||
showDocs: showDocs,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// these are here to pick up colour settings from the URL params
|
// these are here to pick up colour settings from the URL params
|
||||||
setColours(gui.params.colour);
|
setColors(gui.params.color);
|
||||||
setBackground(gui.params.background);
|
setBackground(gui.params.background);
|
||||||
|
|
||||||
const dragK = 0.005;
|
const dragK = 0.005;
|
||||||
@ -270,8 +146,7 @@ renderer.domElement.addEventListener("pointerup", (event) => {
|
|||||||
dragging = false;
|
dragging = false;
|
||||||
})
|
})
|
||||||
|
|
||||||
createShape(gui.params.shape, gui.params.option);
|
createShape(gui.params.shape);
|
||||||
displayDocs(gui.params.shape);
|
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame( animate );
|
requestAnimationFrame( animate );
|
||||||
@ -289,14 +164,10 @@ function animate() {
|
|||||||
rotfn[gui.params.xRotate](theta),
|
rotfn[gui.params.xRotate](theta),
|
||||||
rotfn[gui.params.yRotate](psi)
|
rotfn[gui.params.yRotate](psi)
|
||||||
];
|
];
|
||||||
shape.hyperplane = 1 / gui.params.hyperplane;
|
shape.hyperplane = gui.params.hyperplane;
|
||||||
camera.position.set(0, 0, gui.params.zoom * CAMERA_K * gui.params.hyperplane);
|
shape.link_scale = gui.params.thickness;
|
||||||
|
|
||||||
shape.node_scale = gui.params.nodesize;
|
shape.node_scale = gui.params.nodesize;
|
||||||
shape.link_scale = gui.params.linksize * gui.params.nodesize * 0.5;
|
shape.render3(rotations);
|
||||||
shape.render3(rotations, node_show, link_show);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
renderer.render( scene, camera );
|
renderer.render( scene, camera );
|
||||||
}
|
}
|
||||||
|
|||||||
270
package-lock.json
generated
270
package-lock.json
generated
@ -4,11 +4,9 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fourdjs",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^4.2.3",
|
|
||||||
"color-scheme": "^1.0.1",
|
"color-scheme": "^1.0.1",
|
||||||
"lil-gui": "^0.19.0",
|
"lil-gui": "^0.18.2",
|
||||||
"three": "^0.154.0"
|
"three": "^0.154.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -16,9 +14,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.15.tgz",
|
||||||
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
|
"integrity": "sha512-wlkQBWb79/jeEEoRmrxt/yhn5T1lU236OCNpnfRzaCJHZ/5gf82uYx1qmADTBWE0AR/v7FiozE1auk2riyQd3w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -32,9 +30,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.15.tgz",
|
||||||
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
|
"integrity": "sha512-NI/gnWcMl2kXt1HJKOn2H69SYn4YNheKo6NZt1hyfKWdMbaGadxjZIkcj4Gjk/WPxnbFXs9/3HjGHaknCqjrww==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -48,9 +46,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
|
"integrity": "sha512-FM9NQamSaEm/IZIhegF76aiLnng1kEsZl2eve/emxDeReVfRuRNmvT28l6hoFD9TsCxpK+i4v8LPpEj74T7yjA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -64,9 +62,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.15.tgz",
|
||||||
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
|
"integrity": "sha512-XmrFwEOYauKte9QjS6hz60FpOCnw4zaPAb7XV7O4lx1r39XjJhTN7ZpXqJh4sN6q60zbP6QwAVVA8N/wUyBH/w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -80,9 +78,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
|
"integrity": "sha512-bMqBmpw1e//7Fh5GLetSZaeo9zSC4/CMtrVFdj+bqKPGJuKyfNJ5Nf2m3LknKZTS+Q4oyPiON+v3eaJ59sLB5A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -96,9 +94,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.15.tgz",
|
||||||
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
|
"integrity": "sha512-LoTK5N3bOmNI9zVLCeTgnk5Rk0WdUTrr9dyDAQGVMrNTh9EAPuNwSTCgaKOKiDpverOa0htPcO9NwslSE5xuLA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -112,9 +110,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
|
"integrity": "sha512-62jX5n30VzgrjAjOk5orYeHFq6sqjvsIj1QesXvn5OZtdt5Gdj0vUNJy9NIpjfdNdqr76jjtzBJKf+h2uzYuTQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -128,9 +126,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.15.tgz",
|
||||||
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
|
"integrity": "sha512-dT4URUv6ir45ZkBqhwZwyFV6cH61k8MttIwhThp2BGiVtagYvCToF+Bggyx2VI57RG4Fbt21f9TmXaYx0DeUJg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -144,9 +142,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.15.tgz",
|
||||||
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
|
"integrity": "sha512-BWncQeuWDgYv0jTNzJjaNgleduV4tMbQjmk/zpPh/lUdMcNEAxy+jvneDJ6RJkrqloG7tB9S9rCrtfk/kuplsQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -160,9 +158,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.15.tgz",
|
||||||
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
|
"integrity": "sha512-JPXORvgHRHITqfms1dWT/GbEY89u848dC08o0yK3fNskhp0t2TuNUnsrrSgOdH28ceb1hJuwyr8R/1RnyPwocw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -176,9 +174,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.15.tgz",
|
||||||
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
|
"integrity": "sha512-kArPI0DopjJCEplsVj/H+2Qgzz7vdFSacHNsgoAKpPS6W/Ndh8Oe24HRDQ5QCu4jHgN6XOtfFfLpRx3TXv/mEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -192,9 +190,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.15.tgz",
|
||||||
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
|
"integrity": "sha512-b/tmngUfO02E00c1XnNTw/0DmloKjb6XQeqxaYuzGwHe0fHVgx5/D6CWi+XH1DvkszjBUkK9BX7n1ARTOst59w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@ -208,9 +206,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.15.tgz",
|
||||||
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
|
"integrity": "sha512-KXPY69MWw79QJkyvUYb2ex/OgnN/8N/Aw5UDPlgoRtoEfcBqfeLodPr42UojV3NdkoO4u10NXQdamWm1YEzSKw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -224,9 +222,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.15.tgz",
|
||||||
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
|
"integrity": "sha512-komK3NEAeeGRnvFEjX1SfVg6EmkfIi5aKzevdvJqMydYr9N+pRQK0PGJXk+bhoPZwOUgLO4l99FZmLGk/L1jWg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -240,9 +238,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.15.tgz",
|
||||||
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
|
"integrity": "sha512-632T5Ts6gQ2WiMLWRRyeflPAm44u2E/s/TJvn+BP6M5mnHSk93cieaypj3VSMYO2ePTCRqAFXtuYi1yv8uZJNA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -256,9 +254,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
|
"integrity": "sha512-MsHtX0NgvRHsoOtYkuxyk4Vkmvk3PLRWfA4okK7c+6dT0Fu4SUqXAr9y4Q3d8vUf1VWWb6YutpL4XNe400iQ1g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -272,9 +270,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
|
"integrity": "sha512-djST6s+jQiwxMIVQ5rlt24JFIAr4uwUnzceuFL7BQT4CbrRtqBPueS4GjXSiIpmwVri1Icj/9pFRJ7/aScvT+A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -288,9 +286,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
|
"integrity": "sha512-naeRhUIvhsgeounjkF5mvrNAVMGAm6EJWiabskeE5yOeBbLp7T89tAEw0j5Jm/CZAwyLe3c67zyCWH6fsBLCpw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -304,9 +302,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
|
"integrity": "sha512-qkT2+WxyKbNIKV1AEhI8QiSIgTHMcRctzSaa/I3kVgMS5dl3fOeoqkb7pW76KwxHoriImhx7Mg3TwN/auMDsyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -320,9 +318,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.15.tgz",
|
||||||
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
|
"integrity": "sha512-HC4/feP+pB2Vb+cMPUjAnFyERs+HJN7E6KaeBlFdBv799MhD+aPJlfi/yk36SED58J9TPwI8MAcVpJgej4ud0A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -336,9 +334,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.15.tgz",
|
||||||
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
|
"integrity": "sha512-ovjwoRXI+gf52EVF60u9sSDj7myPixPxqzD5CmkEUmvs+W9Xd0iqISVBQn8xcx4ciIaIVlWCuTbYDOXOnOL44Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -352,9 +350,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.15.tgz",
|
||||||
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
|
"integrity": "sha512-imUxH9a3WJARyAvrG7srLyiK73XdX83NXQkjKvQ+7vPh3ZxoLrzvPkQKKw2DwZ+RV2ZB6vBfNHP8XScAmQC3aA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -367,52 +365,15 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/color": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-convert": "^2.0.1",
|
|
||||||
"color-string": "^1.9.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-convert": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-name": "~1.1.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-name": {
|
|
||||||
"version": "1.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
|
||||||
},
|
|
||||||
"node_modules/color-scheme": {
|
"node_modules/color-scheme": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-scheme/-/color-scheme-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-scheme/-/color-scheme-1.0.1.tgz",
|
||||||
"integrity": "sha512-4x+ya6+z6g9DaTFSfVzTZc8TSjxHuDT40NB43N3XPUkQlF6uujhwH8aeMeq8HBgoQQog/vrYgJ16mt/eVTRXwQ=="
|
"integrity": "sha512-4x+ya6+z6g9DaTFSfVzTZc8TSjxHuDT40NB43N3XPUkQlF6uujhwH8aeMeq8HBgoQQog/vrYgJ16mt/eVTRXwQ=="
|
||||||
},
|
},
|
||||||
"node_modules/color-string": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
|
||||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-name": "^1.0.0",
|
|
||||||
"simple-swizzle": "^0.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.15",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.15.tgz",
|
||||||
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
|
"integrity": "sha512-3WOOLhrvuTGPRzQPU6waSDWrDTnQriia72McWcn6UCi43GhCHrXH4S59hKMeez+IITmdUuUyvbU9JIp+t3xlPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -422,34 +383,34 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/android-arm": "0.18.20",
|
"@esbuild/android-arm": "0.18.15",
|
||||||
"@esbuild/android-arm64": "0.18.20",
|
"@esbuild/android-arm64": "0.18.15",
|
||||||
"@esbuild/android-x64": "0.18.20",
|
"@esbuild/android-x64": "0.18.15",
|
||||||
"@esbuild/darwin-arm64": "0.18.20",
|
"@esbuild/darwin-arm64": "0.18.15",
|
||||||
"@esbuild/darwin-x64": "0.18.20",
|
"@esbuild/darwin-x64": "0.18.15",
|
||||||
"@esbuild/freebsd-arm64": "0.18.20",
|
"@esbuild/freebsd-arm64": "0.18.15",
|
||||||
"@esbuild/freebsd-x64": "0.18.20",
|
"@esbuild/freebsd-x64": "0.18.15",
|
||||||
"@esbuild/linux-arm": "0.18.20",
|
"@esbuild/linux-arm": "0.18.15",
|
||||||
"@esbuild/linux-arm64": "0.18.20",
|
"@esbuild/linux-arm64": "0.18.15",
|
||||||
"@esbuild/linux-ia32": "0.18.20",
|
"@esbuild/linux-ia32": "0.18.15",
|
||||||
"@esbuild/linux-loong64": "0.18.20",
|
"@esbuild/linux-loong64": "0.18.15",
|
||||||
"@esbuild/linux-mips64el": "0.18.20",
|
"@esbuild/linux-mips64el": "0.18.15",
|
||||||
"@esbuild/linux-ppc64": "0.18.20",
|
"@esbuild/linux-ppc64": "0.18.15",
|
||||||
"@esbuild/linux-riscv64": "0.18.20",
|
"@esbuild/linux-riscv64": "0.18.15",
|
||||||
"@esbuild/linux-s390x": "0.18.20",
|
"@esbuild/linux-s390x": "0.18.15",
|
||||||
"@esbuild/linux-x64": "0.18.20",
|
"@esbuild/linux-x64": "0.18.15",
|
||||||
"@esbuild/netbsd-x64": "0.18.20",
|
"@esbuild/netbsd-x64": "0.18.15",
|
||||||
"@esbuild/openbsd-x64": "0.18.20",
|
"@esbuild/openbsd-x64": "0.18.15",
|
||||||
"@esbuild/sunos-x64": "0.18.20",
|
"@esbuild/sunos-x64": "0.18.15",
|
||||||
"@esbuild/win32-arm64": "0.18.20",
|
"@esbuild/win32-arm64": "0.18.15",
|
||||||
"@esbuild/win32-ia32": "0.18.20",
|
"@esbuild/win32-ia32": "0.18.15",
|
||||||
"@esbuild/win32-x64": "0.18.20"
|
"@esbuild/win32-x64": "0.18.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@ -460,15 +421,10 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-arrayish": {
|
|
||||||
"version": "0.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
|
||||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
|
||||||
},
|
|
||||||
"node_modules/lil-gui": {
|
"node_modules/lil-gui": {
|
||||||
"version": "0.19.0",
|
"version": "0.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.18.2.tgz",
|
||||||
"integrity": "sha512-02/Z7rPng3GXWFwkQVj1hQaJYo2fIEYctqe0ima5uI/N2HEagB9ZGCQKkVWr3UuKfTr0arto3Q9prTB8sxtJJw=="
|
"integrity": "sha512-DgdrLy3/KGC0PiQLKgOcJMPItP4xY4iWgJ9+91Zaxfr8GCTmMps05QS9w9jW7yspILlbscbquwjOwxmWnSx5Uw=="
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.6",
|
"version": "3.3.6",
|
||||||
@ -495,9 +451,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.27",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz",
|
||||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
"integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -523,9 +479,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.29.4",
|
"version": "3.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.3.tgz",
|
||||||
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
|
"integrity": "sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@ -538,14 +494,6 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/simple-swizzle": {
|
|
||||||
"version": "0.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
|
||||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
|
||||||
"dependencies": {
|
|
||||||
"is-arrayish": "^0.3.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
@ -561,14 +509,14 @@
|
|||||||
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
|
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.5.3",
|
"version": "4.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.6.tgz",
|
||||||
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
"integrity": "sha512-EY6Mm8vJ++S3D4tNAckaZfw3JwG3wa794Vt70M6cNJ6NxT87yhq7EC8Rcap3ahyHdo8AhCmV9PTk+vG1HiYn1A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.26",
|
||||||
"rollup": "^3.27.1"
|
"rollup": "^3.25.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^4.2.3",
|
|
||||||
"color-scheme": "^1.0.1",
|
"color-scheme": "^1.0.1",
|
||||||
"lil-gui": "^0.19.0",
|
"lil-gui": "^0.18.2",
|
||||||
"three": "^0.154.0"
|
"three": "^0.154.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
826
polytopes.js
826
polytopes.js
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
const EPSILON = 0.001;
|
|
||||||
|
|
||||||
class TaperedLink extends THREE.Group {
|
|
||||||
|
|
||||||
constructor(baseMaterial, colour_i, n1, n2, r1, r2) {
|
|
||||||
super();
|
|
||||||
const geometry = new THREE.ConeGeometry( 1, 1, 16, true );
|
|
||||||
const cplane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0.5);
|
|
||||||
this.colour_i = colour_i;
|
|
||||||
this.material = baseMaterial.clone();
|
|
||||||
this.material.clippingPlanes = [ cplane ];
|
|
||||||
this.object = new THREE.Mesh( geometry, this.material );
|
|
||||||
this.add( this.object );
|
|
||||||
this.update(n1, n2, r1, r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(n1, n2, r1, r2) {
|
|
||||||
const kraw = r1 - r2;
|
|
||||||
let k = ( Math.abs(kraw) < EPSILON ) ? EPSILON : kraw;
|
|
||||||
let nbase = n1.v3;
|
|
||||||
let napex = n2.v3;
|
|
||||||
let rbase = r1;
|
|
||||||
let rapex = r2;
|
|
||||||
if( k < 0 ) {
|
|
||||||
nbase = n2.v3;
|
|
||||||
napex = n1.v3;
|
|
||||||
rbase = r2;
|
|
||||||
rapex = r1;
|
|
||||||
k = -k;
|
|
||||||
}
|
|
||||||
|
|
||||||
const l = nbase.distanceTo(napex);
|
|
||||||
const lapex = l * rapex / k;
|
|
||||||
const h = l + lapex;
|
|
||||||
this.scale.copy(new THREE.Vector3(rbase, rbase, h));
|
|
||||||
const h_offset = 0.5 * h / l;
|
|
||||||
const pos = new THREE.Vector3();
|
|
||||||
pos.lerpVectors(nbase, napex, h_offset);
|
|
||||||
|
|
||||||
this.position.copy(pos); // the group, not the cone!!
|
|
||||||
|
|
||||||
this.lookAt(nbase);
|
|
||||||
this.children[0].rotation.x = 3 * Math.PI / 2.0;
|
|
||||||
this.visible = true;
|
|
||||||
const clipnorm = new THREE.Vector3();
|
|
||||||
clipnorm.copy(napex);
|
|
||||||
clipnorm.sub(nbase);
|
|
||||||
clipnorm.negate();
|
|
||||||
clipnorm.normalize();
|
|
||||||
this.material.clippingPlanes[0].setFromNormalAndCoplanarPoint(
|
|
||||||
clipnorm, napex
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
set_colour(colours) {
|
|
||||||
console.log(`taperedLink.set_colour {this.colour_i} {colours[this.colour_i]}`);
|
|
||||||
this.material.color = new THREE.Color(colours[this.colour_i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export { TaperedLink };
|
|
||||||
@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
// code for generating the 120-cell labels
|
//testbed for playing with stuff in node repl
|
||||||
// has some overlap with permute - FIXME
|
|
||||||
|
|
||||||
const THREE = require('three');
|
const THREE =require('three');
|
||||||
|
|
||||||
function pandita(a) {
|
function pandita(a) {
|
||||||
const n = a.length;
|
const n = a.length;
|
||||||
@ -134,7 +133,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;
|
return (n1.x - n2.x) ** 2 + (n1.y - n2.y) ** 2 + (n1.z - n2.z) ** 2 + (n1.w - n2.w) ** 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function auto_detect_edges(nodes, neighbours, debug=false) {
|
function auto_detect_edges(nodes, neighbours, debug=false) {
|
||||||
const seen = {};
|
const seen = {};
|
||||||
const nnodes = nodes.length;
|
const nnodes = nodes.length;
|
||||||
const links = [];
|
const links = [];
|
||||||
@ -169,7 +168,7 @@ export function auto_detect_edges(nodes, neighbours, debug=false) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function make_120cell_vertices() {
|
function make_120cell_vertices() {
|
||||||
const phi = 0.5 * (1 + Math.sqrt(5));
|
const phi = 0.5 * (1 + Math.sqrt(5));
|
||||||
const r5 = Math.sqrt(5);
|
const r5 = Math.sqrt(5);
|
||||||
const phi2 = phi * phi;
|
const phi2 = phi * phi;
|
||||||
@ -231,7 +230,7 @@ function fingerprint(ids) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function auto_120cell_faces(links) {
|
function auto_120cell_faces(links) {
|
||||||
const faces = [];
|
const faces = [];
|
||||||
const seen = {};
|
const seen = {};
|
||||||
let id = 1;
|
let id = 1;
|
||||||
@ -487,7 +486,7 @@ function colour_next_dodeca_maybe(nodes, links, faces, colours, dd, nextf, nextd
|
|||||||
const nextvs = dodecahedron_vertices(nextdd);
|
const nextvs = dodecahedron_vertices(nextdd);
|
||||||
// get the initial colour permutations from the existing labels;
|
// get the initial colour permutations from the existing labels;
|
||||||
const p = [];
|
const p = [];
|
||||||
for( let i = 0; i < 5; i ++ ) {
|
for( i = 0; i < 5; i ++ ) {
|
||||||
p[i] = colours[nextvs[i]];
|
p[i] = colours[nextvs[i]];
|
||||||
}
|
}
|
||||||
const nlabels = colour_dodecahedron_from_face(nextdd, p);
|
const nlabels = colour_dodecahedron_from_face(nextdd, p);
|
||||||
@ -530,33 +529,6 @@ function meridian(nodes, links, faces, startf, startn, dir=11, max=10) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function meridian_bump(nodes, links, faces, startf, startn, bumpdir=6) {
|
|
||||||
const o = face_plus_to_dodecahedron(faces, startf, startn);
|
|
||||||
const dir = 11;
|
|
||||||
const max = 10;
|
|
||||||
|
|
||||||
const colours = colour_dodecahedron_from_face(o, [ 1, 2, 3, 4, 5 ] );
|
|
||||||
|
|
||||||
const dds = follow_meridian(nodes, links, faces, colours, o, dir, max);
|
|
||||||
|
|
||||||
const dd4 = dds[4];
|
|
||||||
const nextf = dd4[bumpdir];
|
|
||||||
const bump = follow_face_to_dodeca(faces, dd4, nextf);
|
|
||||||
const ncolours = colour_next_dodeca_maybe(nodes, links, faces, colours, dd4, nextf, bump);
|
|
||||||
|
|
||||||
add_colours(colours, ncolours);
|
|
||||||
|
|
||||||
const labels = { 1: [], 2:[], 3:[], 4:[], 5:[] };
|
|
||||||
for( const vstr in colours ) {
|
|
||||||
labels[colours[vstr]].push(Number(vstr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { dodecahedra: dds, labels: labels };
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function all_meridians(nodes, links, faces, startf, startn) {
|
function all_meridians(nodes, links, faces, startf, startn) {
|
||||||
const o = face_plus_to_dodecahedron(faces, startf, startn);
|
const o = face_plus_to_dodecahedron(faces, startf, startn);
|
||||||
|
|
||||||
@ -696,9 +668,8 @@ function arctic(nodes, links, faces, startf, startn, max) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this is the final one that works for the whole 120-cell
|
|
||||||
|
|
||||||
export function label_120cell(nodes, links, faces, startf, startn) {
|
function arctic_two(nodes, links, faces, startf, startn) {
|
||||||
const pole = face_plus_to_dodecahedron(faces, startf, startn);
|
const pole = face_plus_to_dodecahedron(faces, startf, startn);
|
||||||
const dds = [ pole ];
|
const dds = [ pole ];
|
||||||
|
|
||||||
@ -717,6 +688,10 @@ export function label_120cell(nodes, links, faces, startf, startn) {
|
|||||||
seen[dd_fingerprint(nextdd)] = true;
|
seen[dd_fingerprint(nextdd)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go around all of the arctic circle and grow all faces
|
||||||
|
|
||||||
|
// 1, 12, 20, 12, 30 = 75
|
||||||
|
// 0 1 13, 33, 45
|
||||||
|
|
||||||
for( const a of dds.slice(1, 13) ) {
|
for( const a of dds.slice(1, 13) ) {
|
||||||
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
||||||
@ -747,8 +722,6 @@ export function label_120cell(nodes, links, faces, startf, startn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the 30 equatorials?
|
// the 30 equatorials?
|
||||||
|
|
||||||
|
|
||||||
for( const a of dds.slice(13, 46) ) {
|
for( const a of dds.slice(13, 46) ) {
|
||||||
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
const [ nextdd, ncolours ] = follow_and_colour(
|
||||||
@ -777,10 +750,7 @@ export function label_120cell(nodes, links, faces, startf, startn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// this should get the rest or explode!
|
// this should get the rest or explode!
|
||||||
|
|
||||||
|
|
||||||
for( const a of dds ) {
|
for( const a of dds ) {
|
||||||
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
const [ nextdd, ncolours ] = follow_and_colour(
|
||||||
@ -808,124 +778,6 @@ export function label_120cell(nodes, links, faces, startf, startn) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function cell120_layers(nodes, links, faces, startf, startn, max_layer) {
|
|
||||||
const pole = face_plus_to_dodecahedron(faces, startf, startn);
|
|
||||||
const dds = [ pole ];
|
|
||||||
const dd_families = { "0": [ pole ] }
|
|
||||||
|
|
||||||
const seen = {};
|
|
||||||
seen[dd_fingerprint(pole)] = true;
|
|
||||||
|
|
||||||
const colours = colour_dodecahedron_from_face(dds[0], [ 1, 2, 3, 4, 5 ] );
|
|
||||||
const vs = dodecahedron_vertices(dds[0]);
|
|
||||||
|
|
||||||
// arctic
|
|
||||||
|
|
||||||
if( max_layer > 0 ) {
|
|
||||||
dd_families["1"] = [];
|
|
||||||
|
|
||||||
for( const face of pole ) {
|
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
|
||||||
nodes, links, faces, colours, pole, face
|
|
||||||
);
|
|
||||||
add_colours(colours, ncolours);
|
|
||||||
dds.push(nextdd);
|
|
||||||
dd_families["1"].push(nextdd);
|
|
||||||
seen[dd_fingerprint(nextdd)] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// subarctic - interstitial
|
|
||||||
|
|
||||||
if( max_layer > 1 ) {
|
|
||||||
dd_families["2"] = [];
|
|
||||||
|
|
||||||
for( const a of dd_families["1"] ) {
|
|
||||||
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
|
||||||
nodes, links, faces, colours, a, a[i]
|
|
||||||
);
|
|
||||||
const fp = dd_fingerprint(nextdd);
|
|
||||||
if( !(fp in seen) ) {
|
|
||||||
add_colours(colours, ncolours);
|
|
||||||
dds.push(nextdd);
|
|
||||||
dd_families["2"].push(nextdd);
|
|
||||||
seen[fp] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// tropic of cancer
|
|
||||||
|
|
||||||
if( max_layer > 2 ) {
|
|
||||||
dd_families["3"] = [];
|
|
||||||
|
|
||||||
for( const a of dd_families["1"] ) {
|
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
|
||||||
nodes, links, faces, colours, a, a[11]
|
|
||||||
);
|
|
||||||
const fp = dd_fingerprint(nextdd);
|
|
||||||
if( !(fp in seen) ) {
|
|
||||||
add_colours(colours, ncolours);
|
|
||||||
dds.push(nextdd);
|
|
||||||
dd_families["3"].push(nextdd);
|
|
||||||
seen[fp] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( max_layer > 3 ) {
|
|
||||||
// equator
|
|
||||||
|
|
||||||
dd_families["4"] = [];
|
|
||||||
|
|
||||||
for( const a of dds.slice(13, 46) ) {
|
|
||||||
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
|
||||||
nodes, links, faces, colours, a, a[i]
|
|
||||||
);
|
|
||||||
const fp = dd_fingerprint(nextdd);
|
|
||||||
if( !(fp in seen) ) {
|
|
||||||
add_colours(colours, ncolours);
|
|
||||||
dd_families["4"].push(nextdd);
|
|
||||||
dds.push(nextdd);
|
|
||||||
seen[fp] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( max_layer > 4 ) {
|
|
||||||
dd_families["5"] = [];
|
|
||||||
|
|
||||||
for( const a of dd_families["4"] ) {
|
|
||||||
for( const i of [ 6, 7, 8, 9, 10 ] ) {
|
|
||||||
const [ nextdd, ncolours ] = follow_and_colour(
|
|
||||||
nodes, links, faces, colours, a, a[i]
|
|
||||||
);
|
|
||||||
const fp = dd_fingerprint(nextdd);
|
|
||||||
if( !(fp in seen) ) {
|
|
||||||
add_colours(colours, ncolours);
|
|
||||||
dds.push(nextdd);
|
|
||||||
dd_families["5"].push(nextdd);
|
|
||||||
seen[fp] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const labels = { 1: [], 2:[], 3:[], 4:[], 5:[] };
|
|
||||||
for( const vstr in colours ) {
|
|
||||||
labels[colours[vstr]].push(Number(vstr));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return { dodecahedra: dds, labels: labels, families: dd_families };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// for a face, pick an edge, and then find the other two faces which
|
// for a face, pick an edge, and then find the other two faces which
|
||||||
// share this edge. These can be used as the starting points for the
|
// share this edge. These can be used as the starting points for the
|
||||||
// first face's two dodecahedra
|
// first face's two dodecahedra
|
||||||
@ -963,6 +815,7 @@ function make_120cell_cells(faces) {
|
|||||||
for( const dd of dds ) {
|
for( const dd of dds ) {
|
||||||
const fp = dd_fingerprint(dd);
|
const fp = dd_fingerprint(dd);
|
||||||
if( ! (fp in seen) ) {
|
if( ! (fp in seen) ) {
|
||||||
|
//console.log(`added dodeca ${fp}`);
|
||||||
const d = {
|
const d = {
|
||||||
id: i,
|
id: i,
|
||||||
faces: dd,
|
faces: dd,
|
||||||
@ -1042,7 +895,7 @@ function meridian_label_120cell(nodes) {
|
|||||||
//label_nodes(nodes, [313], 6);
|
//label_nodes(nodes, [313], 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
1
|
|
||||||
function check_120cell_nodes(nodes) {
|
function check_120cell_nodes(nodes) {
|
||||||
nodes.map((n) => {
|
nodes.map((n) => {
|
||||||
const vs = find_adjacent_labels(nodes, links, n.id);
|
const vs = find_adjacent_labels(nodes, links, n.id);
|
||||||
@ -1055,153 +908,24 @@ function check_120cell_nodes(nodes) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_dodecahedron_vertices() {
|
|
||||||
const phi = 0.5 * (1 + Math.sqrt(5));
|
|
||||||
const phiinv = 1 / phi;
|
|
||||||
|
|
||||||
const nodes = [
|
const nodes = make_120cell_vertices();
|
||||||
{ x: 1, y: 1, z: 1, w: 0 },
|
const links = auto_detect_edges(nodes, 4);
|
||||||
{ x: 1, y: 1, z: -1, w: 0 },
|
const faces = auto_120cell_faces(links);
|
||||||
{ x: 1, y: -1, z: 1, w: 0 },
|
|
||||||
{ x: 1, y: -1, z: -1, w: 0 },
|
|
||||||
{ x: -1, y: 1, z: 1, w: 0 },
|
|
||||||
{ x: -1, y: 1, z: -1, w: 0 },
|
|
||||||
{ x: -1, y: -1, z: 1, w: 0 },
|
|
||||||
{ x: -1, y: -1, z: -1, w: 0 }
|
|
||||||
].flat();
|
|
||||||
scale_nodes(nodes, 0.5);
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// this one does the coherent indexing / partition into 600-cells
|
console.log("Calculating 120-cell colours")
|
||||||
|
|
||||||
export function make_labelled_120cell() {
|
const a2 = arctic_two(nodes, links, faces, faces[0], 341)
|
||||||
const nodes = make_120cell_vertices();
|
|
||||||
const links = auto_detect_edges(nodes, 4);
|
|
||||||
const faces = auto_120cell_faces(links);
|
|
||||||
|
|
||||||
const labelled = label_120cell(nodes, links, faces, faces[0], 341);
|
console.log(`got ${a2.dodecahedra.length}`);
|
||||||
return labelled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the w-distance of a dodecahedron's centroid
|
const labels = a2.labels;
|
||||||
|
|
||||||
export function dd_w_distance(nodes, dd) {
|
console.log("labelling nodes");
|
||||||
const vertices = new Set();
|
for( const cstr in labels ) {
|
||||||
dd.map((f) => f.nodes.map((n) => vertices.add(n)));
|
label_nodes(nodes, labels[cstr], Number(cstr));
|
||||||
|
|
||||||
let w = 0;
|
|
||||||
for( const nid of vertices ) {
|
|
||||||
const node = node_by_id(nodes, nid);
|
|
||||||
w += node.w;
|
|
||||||
}
|
|
||||||
return w / 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function sort_dds_w(nodes, dds) {
|
|
||||||
dds.sort((a, b) => {
|
|
||||||
return dd_w_distance(nodes, a) - dd_w_distance(nodes, b)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function find_antipode_dd(nodes, links, dd) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// notes because I'm too sick to continue working on this today
|
|
||||||
// the vertices which aren't counted by the layers are showing
|
|
||||||
// up in the visualisation because they are labelled "0" by default -
|
|
||||||
// for this to work better there needs to be a value which is never
|
|
||||||
// displayed.
|
|
||||||
|
|
||||||
// in the current layer algorithm, layer '5' (the one after the equator)
|
|
||||||
// is too greedy
|
|
||||||
|
|
||||||
export function make_layered_120cell(max_layer) {
|
|
||||||
const nodes = make_120cell_vertices();
|
|
||||||
const links = auto_detect_edges(nodes, 4);
|
|
||||||
const faces = auto_120cell_faces(links);
|
|
||||||
|
|
||||||
const labelled = label_120cell(nodes, links, faces, faces[628], 250, max_layer);
|
|
||||||
|
|
||||||
// get layers from sorted w-distance order
|
|
||||||
|
|
||||||
const dds = labelled.dodecahedra;
|
|
||||||
dds.sort((a, b) => dd_w_distance(nodes, b) - dd_w_distance(nodes, a));
|
|
||||||
|
|
||||||
const LAYERS = [
|
|
||||||
[ "0", 1 ],
|
|
||||||
[ "1", 12 ],
|
|
||||||
[ "2", 20 ],
|
|
||||||
[ "3", 12 ],
|
|
||||||
[ "4", 30 ],
|
|
||||||
[ "5", 12 ],
|
|
||||||
[ "6", 20 ],
|
|
||||||
[ "7", 12 ],
|
|
||||||
[ "8", 1]
|
|
||||||
];
|
|
||||||
|
|
||||||
const layer_dds = labelled["families"];
|
|
||||||
const vertices_layers = {};
|
|
||||||
const seen = {};
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
for( const layer of LAYERS ) {
|
|
||||||
const label = layer[0];
|
|
||||||
const n = layer[1];
|
|
||||||
vertices_layers[label] = [];
|
|
||||||
console.log(`Layer ${label} starting at ${i}`);
|
|
||||||
for( const dd of dds.slice(i, i + n) ) {
|
|
||||||
console.log(dd_w_distance(nodes, dd));
|
|
||||||
for( const face of dd ) {
|
|
||||||
for( const n of face.nodes ) {
|
|
||||||
if( !seen[n] ) {
|
|
||||||
vertices_layers[label].push(n);
|
|
||||||
seen[n] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += n;
|
|
||||||
}
|
|
||||||
return JSON.stringify(vertices_layers);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function make_meridians(bumpdir) {
|
|
||||||
const nodes = make_120cell_vertices();
|
|
||||||
const links = auto_detect_edges(nodes, 4);
|
|
||||||
const faces = auto_120cell_faces(links);
|
|
||||||
|
|
||||||
const mbs ={}
|
|
||||||
for( const bumpdir of [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ] ) {
|
|
||||||
mbs[bumpdir] = meridian_bump(nodes, links, faces, faces[0], 341, bumpdir)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mbs;
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// console.log("Calculating 120-cell colours")
|
|
||||||
|
|
||||||
// const a2 = arctic_two(nodes, links, faces, faces[0], 341)
|
|
||||||
|
|
||||||
// console.log(`got ${a2.dodecahedra.length}`);
|
|
||||||
|
|
||||||
// const labels = a2.labels;
|
|
||||||
|
|
||||||
// console.log("labelling nodes");
|
|
||||||
// for( const cstr in labels ) {
|
|
||||||
// label_nodes(nodes, labels[cstr], Number(cstr));
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -3,14 +3,5 @@
|
|||||||
import { defineConfig, loadEnv } from 'vite';
|
import { defineConfig, loadEnv } from 'vite';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: '/fourjs/',
|
base: '/fourjs/'
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
manualChunks: {
|
|
||||||
threejs: [ 'three' ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user