Compare commits

...

4 Commits

Author SHA1 Message Date
Mike Lynch
c1137f4da2 Updated the CHANGELOG and release number 2026-01-01 18:24:55 +11:00
Mike Lynch
2d63efec7c Merged the 5-cell inscription into the main 120-cell options, fixed a bug where
showing nodes and links was inadvertently broken
2026-01-01 18:20:21 +11:00
Mike Lynch
3df850dfa9 OK I added some checks and I think it's actually correct! 2026-01-01 17:38:43 +11:00
Mike Lynch
fa93a60562 I think this is working for all 120 5-cells 2026-01-01 17:29:55 +11:00
8 changed files with 934 additions and 63 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
## v1.1 - 1/1/2025
The 120-cell now includes a visualisation of its inscribed 5-cells, which honestly
looks like less of a mess than I expected it to.
## v1.0 - 16/11/2025 ## v1.0 - 16/11/2025
It's been [two years](https://mikelynch.org/2023/Sep/02/120-cell/)</a> since It's been [two years](https://mikelynch.org/2023/Sep/02/120-cell/)</a> since

View File

@ -106,27 +106,849 @@ export const LAYERS120 = {
export const CELL120_CELL5 = { export const CELL120_CELL5 = {
"tetras": { "tetras": {
"1": [ 27, 223, 253, 419 ], },
"2": [ 28, 76, 44, 112 ],
"3": [ 264, 238, 283, 197 ],
"4": [ 309, 84, 304, 578 ],
"5": [ 275, 225, 42, 521 ],
},
"cell5s": { "cell5s": {
"1": [ 27, 28, 264, 309, 275 ], "1": [
"2": [ 223, 76, 238, 84, 225 ], 27,
"3": [ 253, 44, 283, 304, 42 ], 28,
"4": [ 419, 112, 197, 578, 521 ], 264,
"5": [ 339, 14, 384, 382, 337 ], 309,
"6": [ 331, 4, 335, 390, 386 ], 275
"7": [ 427, 160, 551, 146, 557 ], ],
"8": [ 265, 60, 64, 295, 246 ], "2": [
"9": [ 473, 100, 495, 213, 462 ], 223,
"10": [ 393, 6, 328, 397, 326 ], 76,
"11": [ 539, 164, 439, 561, 142 ], 238,
"12": [ 511, 122, 456, 595, 181 ], 84,
"13": [ 555, 154, 152, 545, 429 ] 225
}, ],
"3": [
253,
44,
283,
304,
42
],
"4": [
419,
112,
197,
578,
521
],
"5": [
339,
14,
384,
382,
337
],
"6": [
331,
4,
335,
390,
386
],
"7": [
427,
160,
551,
146,
557
],
"8": [
265,
60,
64,
295,
246
],
"9": [
473,
100,
495,
213,
462
],
"10": [
393,
6,
328,
397,
326
],
"11": [
539,
164,
439,
561,
142
],
"12": [
511,
122,
456,
595,
181
],
"13": [
555,
154,
152,
545,
429
],
"14": [
95,
202,
486,
500,
465
],
"15": [
471,
208,
502,
484,
89
],
"16": [
347,
21,
348,
374,
373
],
"17": [
487,
203,
94,
468,
497
],
"18": [
165,
139,
542,
568,
434
],
"19": [
367,
18,
355,
368,
353
],
"20": [
231,
78,
86,
236,
217
],
"21": [
356,
17,
366,
354,
365
],
"22": [
503,
205,
470,
92,
481
],
"23": [
527,
106,
584,
195,
421
],
"24": [
239,
73,
222,
228,
81
],
"25": [
543,
138,
168,
435,
565
],
"26": [
48,
46,
302,
281,
255
],
"27": [
248,
62,
293,
58,
267
],
"28": [
440,
141,
562,
540,
163
],
"29": [
274,
30,
312,
261,
29
],
"30": [
179,
128,
597,
450,
505
],
"31": [
376,
22,
375,
346,
345
],
"32": [
320,
11,
405,
401,
319
],
"33": [
448,
173,
130,
587,
519
],
"34": [
460,
102,
211,
489,
479
],
"35": [
388,
2,
392,
329,
333
],
"36": [
512,
182,
596,
121,
455
],
"37": [
592,
170,
516,
443,
133
],
"38": [
120,
189,
529,
570,
411
],
"39": [
420,
198,
577,
522,
111
],
"40": [
272,
69,
65,
290,
243
],
"41": [
488,
93,
498,
204,
467
],
"42": [
156,
150,
547,
553,
431
],
"43": [
252,
53,
286,
297,
55
],
"44": [
532,
192,
117,
410,
571
],
"45": [
400,
7,
321,
396,
323
],
"46": [
580,
199,
417,
110,
523
],
"47": [
296,
63,
245,
266,
59
],
"48": [
600,
125,
178,
508,
451
],
"49": [
68,
72,
269,
242,
291
],
"50": [
324,
8,
399,
322,
395
],
"51": [
499,
96,
485,
466,
201
],
"52": [
406,
12,
317,
318,
402
],
"53": [
563,
144,
437,
162,
537
],
"54": [
234,
88,
219,
229,
80
],
"55": [
350,
24,
349,
371,
372
],
"56": [
444,
134,
515,
169,
591
],
"57": [
258,
40,
39,
277,
307
],
"58": [
285,
56,
251,
54,
298
],
"59": [
546,
151,
153,
430,
556
],
"60": [
98,
215,
475,
493,
464
],
"61": [
474,
214,
99,
461,
496
],
"62": [
357,
20,
363,
359,
364
],
"63": [
490,
212,
459,
101,
480
],
"64": [
185,
116,
415,
533,
574
],
"65": [
378,
16,
343,
341,
380
],
"66": [
218,
85,
235,
77,
232
],
"67": [
342,
15,
377,
379,
344
],
"68": [
458,
209,
491,
477,
104
],
"69": [
514,
135,
441,
590,
172
],
"70": [
226,
83,
75,
237,
224
],
"71": [
530,
119,
569,
190,
412
],
"72": [
38,
37,
257,
308,
278
],
"73": [
414,
113,
188,
575,
536
],
"74": [
362,
19,
358,
361,
360
],
"75": [
334,
1,
330,
387,
391
],
"76": [
438,
161,
538,
143,
564
],
"77": [
550,
157,
426,
560,
147
],
"78": [
566,
167,
137,
544,
436
],
"79": [
126,
177,
452,
507,
599
],
"80": [
284,
41,
254,
43,
303
],
"81": [
494,
97,
476,
463,
216
],
"82": [
200,
109,
418,
524,
579
],
"83": [
263,
25,
26,
276,
310
],
"84": [
300,
50,
52,
249,
287
],
"85": [
558,
145,
428,
159,
552
],
"86": [
403,
9,
316,
315,
407
],
"87": [
74,
82,
227,
221,
240
],
"88": [
289,
66,
244,
271,
70
],
"89": [
306,
34,
280,
33,
259
],
"90": [
572,
118,
531,
409,
191
],
"91": [
207,
90,
472,
483,
501
],
"92": [
369,
23,
370,
351,
352
],
"93": [
585,
132,
175,
517,
446
],
"94": [
105,
196,
528,
583,
422
],
"95": [
241,
67,
292,
71,
270
],
"96": [
425,
148,
559,
549,
158
],
"97": [
525,
193,
108,
423,
582
],
"98": [
174,
129,
588,
447,
520
],
"99": [
313,
10,
404,
408,
314
],
"100": [
413,
187,
576,
535,
114
],
"101": [
573,
186,
416,
115,
534
],
"102": [
49,
51,
299,
288,
250
],
"103": [
449,
180,
127,
598,
506
],
"104": [
469,
91,
206,
504,
482
],
"105": [
513,
171,
589,
136,
442
],
"106": [
279,
35,
305,
260,
36
],
"107": [
389,
3,
385,
336,
332
],
"108": [
593,
183,
509,
454,
124
],
"109": [
149,
155,
554,
432,
548
],
"110": [
325,
5,
394,
327,
398
],
"111": [
453,
123,
510,
184,
594
],
"112": [
383,
13,
338,
340,
381
],
"113": [
311,
31,
273,
32,
262
],
"114": [
581,
107,
526,
424,
194
],
"115": [
61,
57,
268,
247,
294
],
"116": [
87,
79,
230,
220,
233
],
"117": [
301,
47,
45,
256,
282
],
"118": [
131,
176,
445,
518,
586
],
"119": [
210,
103,
457,
478,
492
],
"120": [
140,
166,
567,
433,
541
]
},
}; };

View File

@ -14,6 +14,7 @@ export const get_colours = (basis) => {
const hsl = colours.map((c) => Color("#" + c).hsl()); const hsl = colours.map((c) => Color("#" + c).hsl());
const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber()); const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber());
resaturated.unshift(basis); resaturated.unshift(basis);
console.log(resaturated);
return resaturated; return resaturated;
} }

View File

@ -419,6 +419,38 @@ function cell5_tetras(cell120, all5, c5) {
} }
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;
}
@ -426,10 +458,35 @@ function cell5_tetras(cell120, all5, c5) {
const cell120 = POLYTOPES.cell120_inscribed(); const cell120 = POLYTOPES.cell120_inscribed();
const all5 = gather_5cells(cell120); const all5 = gather_5cells(cell120);
const c5 = all5[0] const c5s = coherent_5cells(cell120, all5);
const c5s = cell5_tetras(cell120, all5, c5);
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]));
console.log(celli);
// 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));

View File

@ -62,7 +62,7 @@ class FourDShape extends THREE.Group {
const s1 = this.link_scale * n1.scale; const s1 = this.link_scale * n1.scale;
const s2 = this.link_scale * n2.scale; const s2 = this.link_scale * n2.scale;
link.object.update(n1, n2, s1, s2); link.object.update(n1, n2, s1, s2);
link.object.visible = (!links_show || link.label in links_show); link.object.visible = (!links_show || links_show.includes(link.label));
} }
@ -143,7 +143,7 @@ class FourDShape extends THREE.Group {
this.nodes3[n.id].scale = k * this.foreshortening; this.nodes3[n.id].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(s3);
this.nodes3[n.id].object.visible = ( !nodes_show || n.label in nodes_show ); this.nodes3[n.id].object.visible = ( !nodes_show || nodes_show.includes(n.label) );
} }
for( const l of this.links ) { for( const l of this.links ) {
this.updateLink(l, links_show); this.updateLink(l, links_show);

View File

@ -36,7 +36,7 @@
<script type="module" src="/main.js"></script> <script type="module" src="/main.js"></script>
<div id="description"></div> <div id="description"></div>
<div id="release_notes"></div> <div id="release_notes"></div>
<div id="info"><a href="#" id="show_notes">release 1.0</a> | <div id="info"><a href="#" id="show_notes">release 1.1</a> |
by <a target="_blank" href="https://mikelynch.org/">Mike Lynch</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> <a target="_blank" href="https://git.tilde.town/bombinans/fourdjs">source</a></div>

View File

@ -68,6 +68,8 @@ link_ms.map((m) => {
} }
); );
console.log("link_ms", link_ms);
const face_ms = [ const face_ms = [
new THREE.MeshStandardMaterial( { color: 0x44ff44 } ) new THREE.MeshStandardMaterial( { color: 0x44ff44 } )
@ -189,6 +191,8 @@ function changeShape() {
} }
function setVisibility(option_name) { function setVisibility(option_name) {
console.log("setVisibility", option_name);
console.log(structure.options);
const option = structure.options.filter((o) => o.name === option_name); const option = structure.options.filter((o) => o.name === option_name);
if( option.length ) { if( option.length ) {
node_show = option[0].nodes; node_show = option[0].nodes;
@ -209,10 +213,6 @@ gui = new FourDGUI(
setLinkOpacity: setLinkOpacity, setLinkOpacity: setLinkOpacity,
setVisibility: setVisibility, setVisibility: setVisibility,
showDocs: showDocs, showDocs: showDocs,
extras: {
"Show an alert": function() { alert("hi there") },
"Show a different one": function() { alert('yowza')},
},
} }
); );

View File

@ -443,6 +443,15 @@ export const cell120_inscribed = () => {
links.push(...links600); 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 { return {
name: '120-cell', name: '120-cell',
nodes: nodes, nodes: nodes,
@ -450,12 +459,15 @@ export const cell120_inscribed = () => {
options: [ options: [
{ name: "none", links: [ 0 ]}, { name: "none", links: [ 0 ]},
{ name: "one inscribed 600-cell", links: [ 0, 1 ] }, { name: "one inscribed 600-cell", links: [ 0, 1 ] },
{ name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] } { name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] },
{ name: "120 inscribed 5-cells", links: [ 0, 8 ] },
], ],
description: `The 120-cell is the four-dimensional analogue of the description: `The 120-cell is the four-dimensional analogue of the
dodecahedron, and consists of 120 dodecahedra joined at 720 faces, dodecahedron, and consists of 120 dodecahedra joined at 720 faces,
with three dodecahedra around each edge. It is dual to the 600-cell, with three dodecahedra around each edge. It is dual to the 600-cell,
and five 600-cells can be inscribed in its vertices.`, and five 600-cells can be inscribed in its vertices. The converse
of this allows 120 5-cells (each of which has one vertex in each
of the 5 600-cells) to be inscribed in the 120-cell.`,
} }
} }
@ -463,39 +475,14 @@ export const cell120_inscribed = () => {
export const cell120_inscribed_cell5 = () => { export const cell120_inscribed_cell5 = () => {
const nodes = make_120cell_vertices(); const nodes = make_120cell_vertices();
//const links = auto_detect_edges(nodes, 4); const links = auto_detect_edges(nodes, 4);
for( const cstr in CELLINDEX.INDEX120 ) { for( const cstr in CELLINDEX.INDEX120 ) {
label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr)); label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
} }
//links.map((l) => l.label = 0); links.map((l) => l.label = 0);
const links = [];
/* for( const p of [ 1, 2 ]) {
const nodes600 = nodes.filter((n) => n.label === p);
const links600 = auto_detect_edges(nodes600, 12);
links600.map((l) => l.label = p);
links.push(...links600);
}
*/ const TETRAS = CELLINDEX.CELL120_CELL5.tetras;
const CELL5S = CELLINDEX.CELL120_CELL5.cell5s;
for( const t in TETRAS ) {
const nodes5 = nodes.filter((n) => TETRAS[t].includes(n.id));
const links5 = auto_detect_edges(nodes5, 4);
links5.map((l) => l.label = t);
links.push(...links5);
}
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 = 0);
links.push(...links5);
}
@ -504,7 +491,7 @@ export const cell120_inscribed_cell5 = () => {
nodes: nodes, nodes: nodes,
links: links, links: links,
options: [ options: [
{ name: "5-cells", links: [ 0, 1, 2, 3, 4, 5, 6 ] }, { name: "5-cells", links: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ] },
], ],
description: `The 120-cell with one of its 5-cells.`, description: `The 120-cell with one of its 5-cells.`,
} }
@ -926,7 +913,6 @@ export const build_all = () => {
cell600(), cell600(),
cell600_layered(), cell600_layered(),
cell120_inscribed(), cell120_inscribed(),
cell120_inscribed_cell5(),
cell120_layered() cell120_layered()
]; ];
} }