Compare commits
18 Commits
1bf3187f33
...
92c0fa6c27
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92c0fa6c27 | ||
|
|
301609fd41 | ||
|
|
8cc2622ae4 | ||
|
|
7bf5adcb3a | ||
|
|
3c874326a6 | ||
|
|
29a9d60fdf | ||
|
|
93d8a31f71 | ||
|
|
e809e72083 | ||
|
|
128788c490 | ||
|
|
0ef3263b32 | ||
|
|
64cb9540b2 | ||
|
|
14d5186a45 | ||
|
|
da085a7ed0 | ||
|
|
29925962d0 | ||
|
|
36b48b24e6 | ||
|
|
36d3eaff93 | ||
|
|
70247110f3 | ||
|
|
637b327db5 |
87
600_CELL_MAPPING.md
Normal file
87
600_CELL_MAPPING.md
Normal file
@ -0,0 +1,87 @@
|
||||
# 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
|
||||
@ -1,6 +1,12 @@
|
||||
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
|
||||
|
||||
29
NOTES.md
29
NOTES.md
@ -1,26 +1,15 @@
|
||||
# NOTES
|
||||
|
||||
|
||||
New approach for the 5-cells:
|
||||
## Labelling the inscribed 600-cells in a 120-cell
|
||||
|
||||
Pick a tetrahedron of an inscribed 600-cell with vertices A, B, C, D
|
||||
|
||||
This gives pairs of vertices:
|
||||
|
||||
AB
|
||||
AC
|
||||
AD
|
||||
BC
|
||||
BD
|
||||
CD
|
||||
|
||||
Each of these gives rise to seven pairs of 5-cells which are on neighboring vertices
|
||||
of the 5 600-cells.
|
||||
|
||||
Try enumerating these and inspecting them to find one or more coherent sets of four
|
||||
5-cells which lie on one tetrahedron from each of the 600-cells.
|
||||
|
||||
(I expect there to be more than one, like how there are two ways to partition the
|
||||
120-cell vertices into 600-cells)
|
||||
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)
|
||||
|
||||
@ -452,22 +452,22 @@ function coherent_5cells(cell120, all5) {
|
||||
|
||||
|
||||
|
||||
function coherent_all() {
|
||||
|
||||
|
||||
const cell120 = POLYTOPES.cell120_inscribed();
|
||||
const all5 = gather_5cells(cell120);
|
||||
|
||||
const cell120 = POLYTOPES.cell120_inscribed();
|
||||
const all5 = gather_5cells(cell120);
|
||||
const c5s = coherent_5cells(cell120, all5);
|
||||
|
||||
const c5s = coherent_5cells(cell120, all5);
|
||||
|
||||
const celli = c5s.map((c5) => [ "1", "2", "3", "4", "5" ].map((l) => c5[l]));
|
||||
const celli = c5s.map((c5) => [ "1", "2", "3", "4", "5" ].map((l) => c5[l]));
|
||||
|
||||
|
||||
// check it because I don't believe it yet
|
||||
// check it because I don't believe it yet
|
||||
|
||||
const vertex_check = {};
|
||||
const vertex_check = {};
|
||||
|
||||
for( const c5 of celli ) {
|
||||
for( const c5 of celli ) {
|
||||
for( const l in c5 ) {
|
||||
const v = c5[l];
|
||||
if( v in vertex_check ) {
|
||||
@ -475,18 +475,80 @@ for( const c5 of celli ) {
|
||||
}
|
||||
vertex_check[v] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( let i = 1; i < 601; i++ ) {
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const idict = {};
|
||||
for( let i = 1; i < 121; i++ ) {
|
||||
idict[i] = celli[i - 1];
|
||||
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);
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(idict, null, 2));
|
||||
|
||||
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();
|
||||
|
||||
1572
cellindex.js
1572
cellindex.js
File diff suppressed because it is too large
Load Diff
@ -9,8 +9,8 @@ export const get_colours = (basis) => {
|
||||
const luminance = hslb['color'][2];
|
||||
const scheme = new ColorScheme;
|
||||
scheme.from_hue(hue).scheme("tetrade").distance(0.75);
|
||||
const scolours = scheme.colors();
|
||||
const colours = [ ...scolours, ...scolours, ...scolours, ...scolours, ...scolours, ...scolours, ...scolours, ...scolours ];
|
||||
const colours = scheme.colors();
|
||||
colours.reverse();
|
||||
const hsl = colours.map((c) => Color("#" + c).hsl());
|
||||
const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber());
|
||||
resaturated.unshift(basis);
|
||||
|
||||
16
gui.js
16
gui.js
@ -6,13 +6,13 @@ const DEFAULTS = {
|
||||
nodeopacity: 1,
|
||||
linksize: 1.0,
|
||||
linkopacity: 0.75,
|
||||
shape: '120-cell',
|
||||
shape: '600-cell',
|
||||
link2opacity: 0.75,
|
||||
option: 'none',
|
||||
visibility: 5,
|
||||
inscribed: false,
|
||||
inscribe_all: false,
|
||||
color: 0x3293a9,
|
||||
colour: 0x3293a9,
|
||||
background: 0xd4d4d4,
|
||||
hyperplane: 0.93,
|
||||
zoom: 1,
|
||||
@ -46,7 +46,7 @@ class FourDGUI {
|
||||
nodesize: this.link['nodesize'],
|
||||
nodeopacity: this.link['nodeopacity'],
|
||||
depth: this.link['depth'],
|
||||
color: this.link['color'],
|
||||
colour: this.link['colour'],
|
||||
background: this.link['background'],
|
||||
hyperplane: this.link['hyperplane'],
|
||||
zoom: this.link['zoom'],
|
||||
@ -60,8 +60,6 @@ class FourDGUI {
|
||||
};
|
||||
if( funcs.extras ) {
|
||||
for( const label in funcs.extras ) {
|
||||
console.log(label);
|
||||
console.log(funcs.extras[label]);
|
||||
this.params[label] = funcs.extras[label];
|
||||
}
|
||||
}
|
||||
@ -83,10 +81,9 @@ class FourDGUI {
|
||||
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);
|
||||
console.log(funcs.setLinkOpacity);
|
||||
this.gui.add(this.params, 'linkopacity', 0, 1).onChange((v) => funcs.setLinkOpacity(v, true));
|
||||
this.gui.add(this.params, 'link2opacity', 0, 1).onChange((v) => funcs.setLinkOpacity(v, false));
|
||||
this.gui.addColor(this.params, 'color').onChange(funcs.setColor);
|
||||
this.gui.addColor(this.params, '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, 'yRotate', [ 'XZ', 'XY', 'XW' ]);
|
||||
@ -155,7 +152,7 @@ class FourDGUI {
|
||||
this.link['link2opacity'] = this.numParam('link2opacity', parseFloat);
|
||||
this.link['nodesize'] = this.numParam('nodesize', parseFloat);
|
||||
this.link['nodeopacity'] = this.numParam('nodeopacity', parseFloat);
|
||||
this.link['color'] = this.numParam('color', (s) => guiObj.stringToHex(s));
|
||||
this.link['colour'] = this.numParam('colour', (s) => guiObj.stringToHex(s));
|
||||
this.link['background'] = this.numParam('background', (s) => guiObj.stringToHex(s));
|
||||
this.link['dpsi'] = this.numParam('dpsi', parseFloat);
|
||||
this.link['dtheta'] = this.numParam('dtheta', parseFloat);
|
||||
@ -172,7 +169,7 @@ class FourDGUI {
|
||||
url.searchParams.append("nodesize", this.params.nodesize.toString());
|
||||
url.searchParams.append("nodeopacity", this.params.nodesize.toString());
|
||||
url.searchParams.append("linkopacity", this.params.nodeopacity.toString());
|
||||
url.searchParams.append("color", this.hexToString(this.params.color));
|
||||
url.searchParams.append("colour", this.hexToString(this.params.colour));
|
||||
url.searchParams.append("background", this.hexToString(this.params.background));
|
||||
url.searchParams.append("hyperplane", this.params.hyperplane.toString());
|
||||
url.searchParams.append("zoom", this.params.zoom.toString());
|
||||
@ -190,7 +187,6 @@ class FourDGUI {
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
console.log('Async: Copying to clipboard was successful!');
|
||||
}, function(err) {
|
||||
console.error('Async: Could not copy text: ', err);
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<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.2</a> |
|
||||
<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>
|
||||
|
||||
409
label_inscribed_600cell.js
Normal file
409
label_inscribed_600cell.js
Normal file
@ -0,0 +1,409 @@
|
||||
|
||||
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));
|
||||
20
main.js
20
main.js
@ -1,6 +1,12 @@
|
||||
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>
|
||||
@ -60,7 +66,8 @@ document.body.appendChild( renderer.domElement );
|
||||
// set up colours and materials for gui callbacks
|
||||
|
||||
scene.background = new THREE.Color(DEFAULTS.background);
|
||||
const node_colours = get_colours(DEFAULTS.color);
|
||||
const node_colours = get_colours(DEFAULTS.colour);
|
||||
|
||||
|
||||
const node_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c}));
|
||||
const link_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c}));
|
||||
@ -77,7 +84,6 @@ link_ms.map((m) => {
|
||||
}
|
||||
);
|
||||
|
||||
console.log("link_ms", link_ms);
|
||||
|
||||
|
||||
const face_ms = [
|
||||
@ -155,7 +161,7 @@ relnotes.addEventListener('click', releaseNotes);
|
||||
// callbacks to do things which are triggered by controls: reset the shape,
|
||||
// change the colors. Otherwise we just read stuff from gui.params.
|
||||
|
||||
function setColors(c) {
|
||||
function setColours(c) {
|
||||
const nc = get_colours(c);
|
||||
for( let i = 0; i < node_ms.length; i++ ) {
|
||||
node_ms[i].color = new THREE.Color(nc[i]);
|
||||
@ -163,7 +169,7 @@ function setColors(c) {
|
||||
}
|
||||
if( shape ) {
|
||||
// taperedLink.set_color updates according to the link index
|
||||
shape.links.map((l) => l.object.set_color(nc));
|
||||
shape.links.map((l) => l.object.set_colour(nc));
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,8 +206,6 @@ function changeShape() {
|
||||
}
|
||||
|
||||
function setVisibility(option_name) {
|
||||
console.log("setVisibility", option_name);
|
||||
console.log(structure.options);
|
||||
const option = structure.options.filter((o) => o.name === option_name);
|
||||
if( option.length ) {
|
||||
node_show = option[0].nodes;
|
||||
@ -216,7 +220,7 @@ gui = new FourDGUI(
|
||||
{
|
||||
shapes: STRUCTURES,
|
||||
changeShape: changeShape,
|
||||
setColors: setColors,
|
||||
setColours: setColours,
|
||||
setBackground: setBackground,
|
||||
setNodeOpacity: setNodeOpacity,
|
||||
setLinkOpacity: setLinkOpacity,
|
||||
@ -226,7 +230,7 @@ gui = new FourDGUI(
|
||||
);
|
||||
|
||||
// these are here to pick up colour settings from the URL params
|
||||
setColors(gui.params.color);
|
||||
setColours(gui.params.colour);
|
||||
setBackground(gui.params.background);
|
||||
|
||||
const dragK = 0.005;
|
||||
|
||||
209
polytopes.js
209
polytopes.js
@ -216,6 +216,7 @@ export const cell24 = () => {
|
||||
links16.map((l) => l.label = p);
|
||||
links.push(...links16);
|
||||
}
|
||||
// colour links `
|
||||
// links.map((l) => {
|
||||
// const ls = [ l.source, l.target ].map((nid) => node_by_id(nodes, nid).label);
|
||||
// for ( const c of [1, 2, 3] ) {
|
||||
@ -348,9 +349,39 @@ export function make_120cell_vertices() {
|
||||
|
||||
|
||||
|
||||
export function make_120cell_vertices_alt() {
|
||||
// unit radius version to make it easier to partition one of the 600-cells
|
||||
const phi = 0.5 * (1 + Math.sqrt(5));
|
||||
const r5 = Math.sqrt(5);
|
||||
const phi2 = phi * phi;
|
||||
const phiinv = 1 / phi;
|
||||
const phi2inv = 1 / phi2;
|
||||
const r8 = Math.sqrt(8);
|
||||
const p8 = phi / r8;
|
||||
const r58 = r5 / r8;
|
||||
const ir8 = 1 / r8;
|
||||
const ip8 = phiinv / r8;
|
||||
const p28 = phi2 / r8;
|
||||
const ip28 = phi2inv / r8;
|
||||
|
||||
const nodes = [
|
||||
PERMUTE.coordinates([1, 0, 0, 0], 1),
|
||||
PERMUTE.coordinates([0.5, 0.5, 0.5, 0.5], 1),
|
||||
PERMUTE.coordinates([0, phiinv * 0.5, 0.5, phi * 0.5], 1, true),
|
||||
PERMUTE.coordinates([p8, p8, p8, ip28], 0, true),
|
||||
PERMUTE.coordinates([ir8, ir8, ir8, r58], 0, true),
|
||||
PERMUTE.coordinates([ip8, ip8, ip8, p28], 0, true),
|
||||
PERMUTE.coordinates([0, ip8, p8, r58], 0, true),
|
||||
PERMUTE.coordinates([0, ip28, ir8, p28], 0, true),
|
||||
PERMUTE.coordinates([ip8, ir8, p8, 2 * ir8], 0, true),
|
||||
].flat();
|
||||
index_nodes(nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
function label_nodes(nodes, ids, label) {
|
||||
|
||||
export function label_nodes(nodes, ids, label) {
|
||||
nodes.filter((n) => ids.includes(n.id)).map((n) => n.label = label);
|
||||
}
|
||||
|
||||
@ -443,15 +474,6 @@ export const cell120_inscribed = () => {
|
||||
links.push(...links600);
|
||||
}
|
||||
|
||||
const CELL5S = CELLINDEX.CELL120_CELL5.cell5s;
|
||||
|
||||
for( const c5 in CELL5S ) {
|
||||
const nodes5 = nodes.filter((n) => CELL5S[c5].includes(n.id));
|
||||
const links5 = auto_detect_edges(nodes5, 5);
|
||||
links5.map((l) => l.label = 8);
|
||||
links.push(...links5);
|
||||
}
|
||||
|
||||
return {
|
||||
name: '120-cell',
|
||||
nodes: nodes,
|
||||
@ -460,22 +482,73 @@ export const cell120_inscribed = () => {
|
||||
{ name: "none", links: [ 0 ]},
|
||||
{ name: "one inscribed 600-cell", links: [ 0, 1 ] },
|
||||
{ name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] },
|
||||
{ name: "120 inscribed 5-cells", links: [ 0, 8 ] },
|
||||
],
|
||||
description: `The 120-cell is the four-dimensional analogue of the
|
||||
dodecahedron, and consists of 120 dodecahedra joined at 720 faces,
|
||||
with three dodecahedra around each edge. It is dual to the 600-cell,
|
||||
and five 600-cells can be inscribed in its vertices. The converse
|
||||
of this allows 120 5-cells (each of which has one vertex in each
|
||||
of the 5 600-cells) to be inscribed in the 120-cell.`,
|
||||
and five 600-cells can be inscribed in its vertices.` ,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const cell120_alt = () => {
|
||||
const nodes = make_120cell_vertices_alt();
|
||||
const links = auto_detect_edges(nodes, 4);
|
||||
|
||||
links.map((l) => l.label = 0);
|
||||
|
||||
return {
|
||||
name: '120-cell-alt',
|
||||
nodes: nodes,
|
||||
links: links,
|
||||
options: [
|
||||
{ name: "none", links: [ 0 ]},
|
||||
],
|
||||
description: `alt 120-cell`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const cell120_inscribed_cell5 = () => {
|
||||
const nodes = make_120cell_vertices();
|
||||
const links = [];
|
||||
const links = auto_detect_edges(nodes, 4);
|
||||
|
||||
links.map((l) => l.label = 0);
|
||||
|
||||
const CELL5S = CELLINDEX.CELL120_CELL5;
|
||||
|
||||
for( const c5 in CELL5S ) {
|
||||
const nodes5 = nodes.filter((n) => CELL5S[c5].nodes.includes(n.id));
|
||||
const links5 = auto_detect_edges(nodes5, 5);
|
||||
links5.map((l) => l.label = CELL5S[c5].label);
|
||||
links.push(...links5);
|
||||
nodes5.map((n) => n.label = CELL5S[c5].label);
|
||||
}
|
||||
|
||||
return {
|
||||
name: '120-cell with 5-cells',
|
||||
nodes: nodes,
|
||||
links: links,
|
||||
options: [
|
||||
{ name: "all", links: [0, 1, 2, 3, 4, 5] },
|
||||
{ name: "24", links: [0, 1 ] },
|
||||
{ name: "48", links: [0, 1, 2 ] },
|
||||
{ name: "72", links: [0, 1, 2, 3 ] },
|
||||
{ name: "96", links: [0, 1, 2, 3, 4 ] },
|
||||
{ name: "hide 1200-cell", links: [ 1, 2, 3, 4, 5 ] },
|
||||
],
|
||||
description: `The vertices of the 120-cell can also be partitioned
|
||||
into 120 5-cells: each 5-cell has one vertex in each of the five
|
||||
600-cells which can be inscribed in the 120-cell. The colours here
|
||||
are taken from the partition of the inscribed 600-cells' vertices
|
||||
into five 24-cells.`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const cell120_inscribe_cell5_subset = () => {
|
||||
const nodes = make_120cell_vertices();
|
||||
const links = auto_detect_edges(nodes, 4);
|
||||
|
||||
for( const cstr in CELLINDEX.INDEX120 ) {
|
||||
label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
|
||||
@ -483,29 +556,100 @@ export const cell120_inscribed_cell5 = () => {
|
||||
|
||||
links.map((l) => l.label = 0);
|
||||
|
||||
const CELL5S = CELLINDEX.CELL120_CELL5.cell5s;
|
||||
const CELL5S = CELLINDEX.CELL120_CELL5.some_cell5s;
|
||||
const link5s = [];
|
||||
const nodes_subset = [];
|
||||
|
||||
for( const c5 in CELL5S ) {
|
||||
const nodes5 = nodes.filter((n) => CELL5S[c5].includes(n.id));
|
||||
const links5 = auto_detect_edges(nodes5, 5);
|
||||
links5.map((l) => l.label = Number(c5));
|
||||
links.push(...links5);
|
||||
const cell5links = auto_detect_edges(nodes5, 5);
|
||||
cell5links.map((l) => l.label = 8);
|
||||
link5s.push(...cell5links);
|
||||
for( const n5 of nodes5 ) {
|
||||
nodes_subset.push(n5);
|
||||
}
|
||||
}
|
||||
|
||||
const show_links = Array.from({ length: 128 }, (_, i) => i);
|
||||
// now add the links of the inscribed 600-cells which have the
|
||||
// node subset in them
|
||||
|
||||
const link_subset = (l) => {
|
||||
const source = nodes_subset.filter((n) => n.id === l.source);
|
||||
const target = nodes_subset.filter((n) => n.id === l.target);
|
||||
return source.length === 1 && target.length === 1;
|
||||
};
|
||||
|
||||
for( const p of [ 1, 2, 3, 4, 5 ]) {
|
||||
const nodes600 = nodes.filter((n) => n.label === p);
|
||||
const links600 = auto_detect_edges(nodes600, 12);
|
||||
links600.map((l) => l.label = p);
|
||||
for( const link of links600 ) {
|
||||
if( link_subset(link) ) {
|
||||
link5s.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: '120 5-cells',
|
||||
nodes: nodes,
|
||||
links: links,
|
||||
name: '120-cell 5-cell subset',
|
||||
nodes: nodes_subset,
|
||||
links: link5s,
|
||||
options: [
|
||||
{ name: "none", links: show_links},
|
||||
{ name: "none", links: [ 0, 1, 2, 3, 4, 5, 8 ]},
|
||||
],
|
||||
description: `The 120 5-cells from the 120-cell, without the latter's links. This colouring is pretty arbitrary, being based on the algorithm which partitioned the nodes: a later version will have something that's based on the symmetries of the 600-cells which each of the 5-cells has its nodes in.`,
|
||||
description: `Showing only thirteen of the inscribed 5-cells and the links between them on the inscribed 600-cells`,
|
||||
}
|
||||
}
|
||||
|
||||
export const cell120_test_metamap = () => {
|
||||
const o600 = cell600();
|
||||
const nodes = make_120cell_vertices();
|
||||
const links = auto_detect_edges(nodes, 4);
|
||||
|
||||
for( const cstr in CELLINDEX.INDEX120 ) {
|
||||
label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
|
||||
}
|
||||
|
||||
links.map((l) => l.label = 6);
|
||||
|
||||
const mm = CELLINDEX.CELL600_METAMAP;
|
||||
const mm_ids = Object.keys(mm).map((i) => Number(i));
|
||||
console.log(mm_ids);
|
||||
|
||||
for( const p of [ 1 ]) {
|
||||
const nodes600 = nodes.filter((n) => n.label === p);
|
||||
const links600 = auto_detect_edges(nodes600, 12);
|
||||
console.log(links600);
|
||||
const mm_links = links600.filter((l) => {
|
||||
//console.log(`${l.id} ${l.source} ${l.target}`);
|
||||
return mm_ids.includes(l.source) && mm_ids.includes(l.target)
|
||||
});
|
||||
console.log(mm_links);
|
||||
mm_links.map((l) => l.label = p);
|
||||
links.push(...mm_links);
|
||||
}
|
||||
|
||||
nodes.map((n) => {
|
||||
if( mm_ids.includes(n.id) ) {
|
||||
const mapid = mm[n.id];
|
||||
const n600 = o600.nodes.filter((n) => n.id === mapid);
|
||||
n.label = n600[0].label;
|
||||
} else {
|
||||
n.label = 6;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name: '120-cell-metamap',
|
||||
nodes: nodes,
|
||||
links: links,
|
||||
options: [
|
||||
{ name: "none", links: [ 0 ]},
|
||||
{ name: "one inscribed 600-cell", links: [ 0, 1 ] },
|
||||
],
|
||||
description: `foo`,
|
||||
}
|
||||
}
|
||||
|
||||
function partition_coord(i, coords, invert) {
|
||||
const j = invert ? -i : i;
|
||||
@ -640,7 +784,12 @@ export const cell600_layered = () => {
|
||||
const nodes = make_600cell_vertices();
|
||||
const links = auto_detect_edges(nodes, 12);
|
||||
|
||||
nodes.map((n) => n.label = 9); // make all invisible by default
|
||||
const plabels = {};
|
||||
|
||||
nodes.map((n) => {
|
||||
plabels[n.id] = n.label;
|
||||
n.label = 9
|
||||
}); // make all invisible by default
|
||||
|
||||
for (const cstr in CELLINDEX.LAYERS600 ) {
|
||||
label_nodes(nodes, CELLINDEX.LAYERS600[cstr], Number(cstr));
|
||||
@ -655,6 +804,12 @@ export const cell600_layered = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// recolour nodes according to 24-cell partition
|
||||
|
||||
nodes.map((n) => n.label = 8 + plabels[n.id]);
|
||||
|
||||
const node_c = [ 8, 9, 10, 11, 12, 13, 14, 15 ];
|
||||
|
||||
const options = [];
|
||||
const layers = [];
|
||||
|
||||
@ -663,7 +818,7 @@ export const cell600_layered = () => {
|
||||
options.push({
|
||||
name: CELLINDEX.LAYER_NAMES[i],
|
||||
links: [...layers],
|
||||
nodes: [...layers]
|
||||
nodes: [...node_c, ...layers]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -4,11 +4,11 @@ const EPSILON = 0.001;
|
||||
|
||||
class TaperedLink extends THREE.Group {
|
||||
|
||||
constructor(baseMaterial, color_i, n1, n2, r1, r2) {
|
||||
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.color_i = color_i;
|
||||
this.colour_i = colour_i;
|
||||
this.material = baseMaterial.clone();
|
||||
this.material.clippingPlanes = [ cplane ];
|
||||
this.object = new THREE.Mesh( geometry, this.material );
|
||||
@ -56,8 +56,9 @@ class TaperedLink extends THREE.Group {
|
||||
|
||||
}
|
||||
|
||||
set_color(colors) {
|
||||
this.material.color = new THREE.Color(colors[this.color_i]);
|
||||
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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user