Compare commits
No commits in common. "main" and "feature-snub-24-cell" have entirely different histories.
main
...
feature-sn
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,17 +0,0 @@
|
|||||||
CHANGELOG
|
|
||||||
=========
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
`
|
|
||||||
26
NOTES.md
26
NOTES.md
@ -1,26 +0,0 @@
|
|||||||
# NOTES
|
|
||||||
|
|
||||||
|
|
||||||
New approach for the 5-cells:
|
|
||||||
|
|
||||||
Pick a tetrahedron of an inscribed 600-cell with vertices A, B, C, D
|
|
||||||
|
|
||||||
This gives pairs of vertices:
|
|
||||||
|
|
||||||
AB
|
|
||||||
AC
|
|
||||||
AD
|
|
||||||
BC
|
|
||||||
BD
|
|
||||||
CD
|
|
||||||
|
|
||||||
Each of these gives rise to seven pairs of 5-cells which are on neighboring vertices
|
|
||||||
of the 5 600-cells.
|
|
||||||
|
|
||||||
Try enumerating these and inspecting them to find one or more coherent sets of four
|
|
||||||
5-cells which lie on one tetrahedron from each of the 600-cells.
|
|
||||||
|
|
||||||
(I expect there to be more than one, like how there are two ways to partition the
|
|
||||||
120-cell vertices into 600-cells)
|
|
||||||
|
|
||||||
|
|
||||||
849
cellindex.js
849
cellindex.js
@ -104,855 +104,6 @@ export const LAYERS120 = {
|
|||||||
163,219,271,223,167]
|
163,219,271,223,167]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CELL120_CELL5 = {
|
|
||||||
"tetras": {
|
|
||||||
},
|
|
||||||
"cell5s": {
|
|
||||||
"1": [
|
|
||||||
27,
|
|
||||||
28,
|
|
||||||
264,
|
|
||||||
309,
|
|
||||||
275
|
|
||||||
],
|
|
||||||
"2": [
|
|
||||||
223,
|
|
||||||
76,
|
|
||||||
238,
|
|
||||||
84,
|
|
||||||
225
|
|
||||||
],
|
|
||||||
"3": [
|
|
||||||
253,
|
|
||||||
44,
|
|
||||||
283,
|
|
||||||
304,
|
|
||||||
42
|
|
||||||
],
|
|
||||||
"4": [
|
|
||||||
419,
|
|
||||||
112,
|
|
||||||
197,
|
|
||||||
578,
|
|
||||||
521
|
|
||||||
],
|
|
||||||
"5": [
|
|
||||||
339,
|
|
||||||
14,
|
|
||||||
384,
|
|
||||||
382,
|
|
||||||
337
|
|
||||||
],
|
|
||||||
"6": [
|
|
||||||
331,
|
|
||||||
4,
|
|
||||||
335,
|
|
||||||
390,
|
|
||||||
386
|
|
||||||
],
|
|
||||||
"7": [
|
|
||||||
427,
|
|
||||||
160,
|
|
||||||
551,
|
|
||||||
146,
|
|
||||||
557
|
|
||||||
],
|
|
||||||
"8": [
|
|
||||||
265,
|
|
||||||
60,
|
|
||||||
64,
|
|
||||||
295,
|
|
||||||
246
|
|
||||||
],
|
|
||||||
"9": [
|
|
||||||
473,
|
|
||||||
100,
|
|
||||||
495,
|
|
||||||
213,
|
|
||||||
462
|
|
||||||
],
|
|
||||||
"10": [
|
|
||||||
393,
|
|
||||||
6,
|
|
||||||
328,
|
|
||||||
397,
|
|
||||||
326
|
|
||||||
],
|
|
||||||
"11": [
|
|
||||||
539,
|
|
||||||
164,
|
|
||||||
439,
|
|
||||||
561,
|
|
||||||
142
|
|
||||||
],
|
|
||||||
"12": [
|
|
||||||
511,
|
|
||||||
122,
|
|
||||||
456,
|
|
||||||
595,
|
|
||||||
181
|
|
||||||
],
|
|
||||||
"13": [
|
|
||||||
555,
|
|
||||||
154,
|
|
||||||
152,
|
|
||||||
545,
|
|
||||||
429
|
|
||||||
],
|
|
||||||
"14": [
|
|
||||||
95,
|
|
||||||
202,
|
|
||||||
486,
|
|
||||||
500,
|
|
||||||
465
|
|
||||||
],
|
|
||||||
"15": [
|
|
||||||
471,
|
|
||||||
208,
|
|
||||||
502,
|
|
||||||
484,
|
|
||||||
89
|
|
||||||
],
|
|
||||||
"16": [
|
|
||||||
347,
|
|
||||||
21,
|
|
||||||
348,
|
|
||||||
374,
|
|
||||||
373
|
|
||||||
],
|
|
||||||
"17": [
|
|
||||||
487,
|
|
||||||
203,
|
|
||||||
94,
|
|
||||||
468,
|
|
||||||
497
|
|
||||||
],
|
|
||||||
"18": [
|
|
||||||
165,
|
|
||||||
139,
|
|
||||||
542,
|
|
||||||
568,
|
|
||||||
434
|
|
||||||
],
|
|
||||||
"19": [
|
|
||||||
367,
|
|
||||||
18,
|
|
||||||
355,
|
|
||||||
368,
|
|
||||||
353
|
|
||||||
],
|
|
||||||
"20": [
|
|
||||||
231,
|
|
||||||
78,
|
|
||||||
86,
|
|
||||||
236,
|
|
||||||
217
|
|
||||||
],
|
|
||||||
"21": [
|
|
||||||
356,
|
|
||||||
17,
|
|
||||||
366,
|
|
||||||
354,
|
|
||||||
365
|
|
||||||
],
|
|
||||||
"22": [
|
|
||||||
503,
|
|
||||||
205,
|
|
||||||
470,
|
|
||||||
92,
|
|
||||||
481
|
|
||||||
],
|
|
||||||
"23": [
|
|
||||||
527,
|
|
||||||
106,
|
|
||||||
584,
|
|
||||||
195,
|
|
||||||
421
|
|
||||||
],
|
|
||||||
"24": [
|
|
||||||
239,
|
|
||||||
73,
|
|
||||||
222,
|
|
||||||
228,
|
|
||||||
81
|
|
||||||
],
|
|
||||||
"25": [
|
|
||||||
543,
|
|
||||||
138,
|
|
||||||
168,
|
|
||||||
435,
|
|
||||||
565
|
|
||||||
],
|
|
||||||
"26": [
|
|
||||||
48,
|
|
||||||
46,
|
|
||||||
302,
|
|
||||||
281,
|
|
||||||
255
|
|
||||||
],
|
|
||||||
"27": [
|
|
||||||
248,
|
|
||||||
62,
|
|
||||||
293,
|
|
||||||
58,
|
|
||||||
267
|
|
||||||
],
|
|
||||||
"28": [
|
|
||||||
440,
|
|
||||||
141,
|
|
||||||
562,
|
|
||||||
540,
|
|
||||||
163
|
|
||||||
],
|
|
||||||
"29": [
|
|
||||||
274,
|
|
||||||
30,
|
|
||||||
312,
|
|
||||||
261,
|
|
||||||
29
|
|
||||||
],
|
|
||||||
"30": [
|
|
||||||
179,
|
|
||||||
128,
|
|
||||||
597,
|
|
||||||
450,
|
|
||||||
505
|
|
||||||
],
|
|
||||||
"31": [
|
|
||||||
376,
|
|
||||||
22,
|
|
||||||
375,
|
|
||||||
346,
|
|
||||||
345
|
|
||||||
],
|
|
||||||
"32": [
|
|
||||||
320,
|
|
||||||
11,
|
|
||||||
405,
|
|
||||||
401,
|
|
||||||
319
|
|
||||||
],
|
|
||||||
"33": [
|
|
||||||
448,
|
|
||||||
173,
|
|
||||||
130,
|
|
||||||
587,
|
|
||||||
519
|
|
||||||
],
|
|
||||||
"34": [
|
|
||||||
460,
|
|
||||||
102,
|
|
||||||
211,
|
|
||||||
489,
|
|
||||||
479
|
|
||||||
],
|
|
||||||
"35": [
|
|
||||||
388,
|
|
||||||
2,
|
|
||||||
392,
|
|
||||||
329,
|
|
||||||
333
|
|
||||||
],
|
|
||||||
"36": [
|
|
||||||
512,
|
|
||||||
182,
|
|
||||||
596,
|
|
||||||
121,
|
|
||||||
455
|
|
||||||
],
|
|
||||||
"37": [
|
|
||||||
592,
|
|
||||||
170,
|
|
||||||
516,
|
|
||||||
443,
|
|
||||||
133
|
|
||||||
],
|
|
||||||
"38": [
|
|
||||||
120,
|
|
||||||
189,
|
|
||||||
529,
|
|
||||||
570,
|
|
||||||
411
|
|
||||||
],
|
|
||||||
"39": [
|
|
||||||
420,
|
|
||||||
198,
|
|
||||||
577,
|
|
||||||
522,
|
|
||||||
111
|
|
||||||
],
|
|
||||||
"40": [
|
|
||||||
272,
|
|
||||||
69,
|
|
||||||
65,
|
|
||||||
290,
|
|
||||||
243
|
|
||||||
],
|
|
||||||
"41": [
|
|
||||||
488,
|
|
||||||
93,
|
|
||||||
498,
|
|
||||||
204,
|
|
||||||
467
|
|
||||||
],
|
|
||||||
"42": [
|
|
||||||
156,
|
|
||||||
150,
|
|
||||||
547,
|
|
||||||
553,
|
|
||||||
431
|
|
||||||
],
|
|
||||||
"43": [
|
|
||||||
252,
|
|
||||||
53,
|
|
||||||
286,
|
|
||||||
297,
|
|
||||||
55
|
|
||||||
],
|
|
||||||
"44": [
|
|
||||||
532,
|
|
||||||
192,
|
|
||||||
117,
|
|
||||||
410,
|
|
||||||
571
|
|
||||||
],
|
|
||||||
"45": [
|
|
||||||
400,
|
|
||||||
7,
|
|
||||||
321,
|
|
||||||
396,
|
|
||||||
323
|
|
||||||
],
|
|
||||||
"46": [
|
|
||||||
580,
|
|
||||||
199,
|
|
||||||
417,
|
|
||||||
110,
|
|
||||||
523
|
|
||||||
],
|
|
||||||
"47": [
|
|
||||||
296,
|
|
||||||
63,
|
|
||||||
245,
|
|
||||||
266,
|
|
||||||
59
|
|
||||||
],
|
|
||||||
"48": [
|
|
||||||
600,
|
|
||||||
125,
|
|
||||||
178,
|
|
||||||
508,
|
|
||||||
451
|
|
||||||
],
|
|
||||||
"49": [
|
|
||||||
68,
|
|
||||||
72,
|
|
||||||
269,
|
|
||||||
242,
|
|
||||||
291
|
|
||||||
],
|
|
||||||
"50": [
|
|
||||||
324,
|
|
||||||
8,
|
|
||||||
399,
|
|
||||||
322,
|
|
||||||
395
|
|
||||||
],
|
|
||||||
"51": [
|
|
||||||
499,
|
|
||||||
96,
|
|
||||||
485,
|
|
||||||
466,
|
|
||||||
201
|
|
||||||
],
|
|
||||||
"52": [
|
|
||||||
406,
|
|
||||||
12,
|
|
||||||
317,
|
|
||||||
318,
|
|
||||||
402
|
|
||||||
],
|
|
||||||
"53": [
|
|
||||||
563,
|
|
||||||
144,
|
|
||||||
437,
|
|
||||||
162,
|
|
||||||
537
|
|
||||||
],
|
|
||||||
"54": [
|
|
||||||
234,
|
|
||||||
88,
|
|
||||||
219,
|
|
||||||
229,
|
|
||||||
80
|
|
||||||
],
|
|
||||||
"55": [
|
|
||||||
350,
|
|
||||||
24,
|
|
||||||
349,
|
|
||||||
371,
|
|
||||||
372
|
|
||||||
],
|
|
||||||
"56": [
|
|
||||||
444,
|
|
||||||
134,
|
|
||||||
515,
|
|
||||||
169,
|
|
||||||
591
|
|
||||||
],
|
|
||||||
"57": [
|
|
||||||
258,
|
|
||||||
40,
|
|
||||||
39,
|
|
||||||
277,
|
|
||||||
307
|
|
||||||
],
|
|
||||||
"58": [
|
|
||||||
285,
|
|
||||||
56,
|
|
||||||
251,
|
|
||||||
54,
|
|
||||||
298
|
|
||||||
],
|
|
||||||
"59": [
|
|
||||||
546,
|
|
||||||
151,
|
|
||||||
153,
|
|
||||||
430,
|
|
||||||
556
|
|
||||||
],
|
|
||||||
"60": [
|
|
||||||
98,
|
|
||||||
215,
|
|
||||||
475,
|
|
||||||
493,
|
|
||||||
464
|
|
||||||
],
|
|
||||||
"61": [
|
|
||||||
474,
|
|
||||||
214,
|
|
||||||
99,
|
|
||||||
461,
|
|
||||||
496
|
|
||||||
],
|
|
||||||
"62": [
|
|
||||||
357,
|
|
||||||
20,
|
|
||||||
363,
|
|
||||||
359,
|
|
||||||
364
|
|
||||||
],
|
|
||||||
"63": [
|
|
||||||
490,
|
|
||||||
212,
|
|
||||||
459,
|
|
||||||
101,
|
|
||||||
480
|
|
||||||
],
|
|
||||||
"64": [
|
|
||||||
185,
|
|
||||||
116,
|
|
||||||
415,
|
|
||||||
533,
|
|
||||||
574
|
|
||||||
],
|
|
||||||
"65": [
|
|
||||||
378,
|
|
||||||
16,
|
|
||||||
343,
|
|
||||||
341,
|
|
||||||
380
|
|
||||||
],
|
|
||||||
"66": [
|
|
||||||
218,
|
|
||||||
85,
|
|
||||||
235,
|
|
||||||
77,
|
|
||||||
232
|
|
||||||
],
|
|
||||||
"67": [
|
|
||||||
342,
|
|
||||||
15,
|
|
||||||
377,
|
|
||||||
379,
|
|
||||||
344
|
|
||||||
],
|
|
||||||
"68": [
|
|
||||||
458,
|
|
||||||
209,
|
|
||||||
491,
|
|
||||||
477,
|
|
||||||
104
|
|
||||||
],
|
|
||||||
"69": [
|
|
||||||
514,
|
|
||||||
135,
|
|
||||||
441,
|
|
||||||
590,
|
|
||||||
172
|
|
||||||
],
|
|
||||||
"70": [
|
|
||||||
226,
|
|
||||||
83,
|
|
||||||
75,
|
|
||||||
237,
|
|
||||||
224
|
|
||||||
],
|
|
||||||
"71": [
|
|
||||||
530,
|
|
||||||
119,
|
|
||||||
569,
|
|
||||||
190,
|
|
||||||
412
|
|
||||||
],
|
|
||||||
"72": [
|
|
||||||
38,
|
|
||||||
37,
|
|
||||||
257,
|
|
||||||
308,
|
|
||||||
278
|
|
||||||
],
|
|
||||||
"73": [
|
|
||||||
414,
|
|
||||||
113,
|
|
||||||
188,
|
|
||||||
575,
|
|
||||||
536
|
|
||||||
],
|
|
||||||
"74": [
|
|
||||||
362,
|
|
||||||
19,
|
|
||||||
358,
|
|
||||||
361,
|
|
||||||
360
|
|
||||||
],
|
|
||||||
"75": [
|
|
||||||
334,
|
|
||||||
1,
|
|
||||||
330,
|
|
||||||
387,
|
|
||||||
391
|
|
||||||
],
|
|
||||||
"76": [
|
|
||||||
438,
|
|
||||||
161,
|
|
||||||
538,
|
|
||||||
143,
|
|
||||||
564
|
|
||||||
],
|
|
||||||
"77": [
|
|
||||||
550,
|
|
||||||
157,
|
|
||||||
426,
|
|
||||||
560,
|
|
||||||
147
|
|
||||||
],
|
|
||||||
"78": [
|
|
||||||
566,
|
|
||||||
167,
|
|
||||||
137,
|
|
||||||
544,
|
|
||||||
436
|
|
||||||
],
|
|
||||||
"79": [
|
|
||||||
126,
|
|
||||||
177,
|
|
||||||
452,
|
|
||||||
507,
|
|
||||||
599
|
|
||||||
],
|
|
||||||
"80": [
|
|
||||||
284,
|
|
||||||
41,
|
|
||||||
254,
|
|
||||||
43,
|
|
||||||
303
|
|
||||||
],
|
|
||||||
"81": [
|
|
||||||
494,
|
|
||||||
97,
|
|
||||||
476,
|
|
||||||
463,
|
|
||||||
216
|
|
||||||
],
|
|
||||||
"82": [
|
|
||||||
200,
|
|
||||||
109,
|
|
||||||
418,
|
|
||||||
524,
|
|
||||||
579
|
|
||||||
],
|
|
||||||
"83": [
|
|
||||||
263,
|
|
||||||
25,
|
|
||||||
26,
|
|
||||||
276,
|
|
||||||
310
|
|
||||||
],
|
|
||||||
"84": [
|
|
||||||
300,
|
|
||||||
50,
|
|
||||||
52,
|
|
||||||
249,
|
|
||||||
287
|
|
||||||
],
|
|
||||||
"85": [
|
|
||||||
558,
|
|
||||||
145,
|
|
||||||
428,
|
|
||||||
159,
|
|
||||||
552
|
|
||||||
],
|
|
||||||
"86": [
|
|
||||||
403,
|
|
||||||
9,
|
|
||||||
316,
|
|
||||||
315,
|
|
||||||
407
|
|
||||||
],
|
|
||||||
"87": [
|
|
||||||
74,
|
|
||||||
82,
|
|
||||||
227,
|
|
||||||
221,
|
|
||||||
240
|
|
||||||
],
|
|
||||||
"88": [
|
|
||||||
289,
|
|
||||||
66,
|
|
||||||
244,
|
|
||||||
271,
|
|
||||||
70
|
|
||||||
],
|
|
||||||
"89": [
|
|
||||||
306,
|
|
||||||
34,
|
|
||||||
280,
|
|
||||||
33,
|
|
||||||
259
|
|
||||||
],
|
|
||||||
"90": [
|
|
||||||
572,
|
|
||||||
118,
|
|
||||||
531,
|
|
||||||
409,
|
|
||||||
191
|
|
||||||
],
|
|
||||||
"91": [
|
|
||||||
207,
|
|
||||||
90,
|
|
||||||
472,
|
|
||||||
483,
|
|
||||||
501
|
|
||||||
],
|
|
||||||
"92": [
|
|
||||||
369,
|
|
||||||
23,
|
|
||||||
370,
|
|
||||||
351,
|
|
||||||
352
|
|
||||||
],
|
|
||||||
"93": [
|
|
||||||
585,
|
|
||||||
132,
|
|
||||||
175,
|
|
||||||
517,
|
|
||||||
446
|
|
||||||
],
|
|
||||||
"94": [
|
|
||||||
105,
|
|
||||||
196,
|
|
||||||
528,
|
|
||||||
583,
|
|
||||||
422
|
|
||||||
],
|
|
||||||
"95": [
|
|
||||||
241,
|
|
||||||
67,
|
|
||||||
292,
|
|
||||||
71,
|
|
||||||
270
|
|
||||||
],
|
|
||||||
"96": [
|
|
||||||
425,
|
|
||||||
148,
|
|
||||||
559,
|
|
||||||
549,
|
|
||||||
158
|
|
||||||
],
|
|
||||||
"97": [
|
|
||||||
525,
|
|
||||||
193,
|
|
||||||
108,
|
|
||||||
423,
|
|
||||||
582
|
|
||||||
],
|
|
||||||
"98": [
|
|
||||||
174,
|
|
||||||
129,
|
|
||||||
588,
|
|
||||||
447,
|
|
||||||
520
|
|
||||||
],
|
|
||||||
"99": [
|
|
||||||
313,
|
|
||||||
10,
|
|
||||||
404,
|
|
||||||
408,
|
|
||||||
314
|
|
||||||
],
|
|
||||||
"100": [
|
|
||||||
413,
|
|
||||||
187,
|
|
||||||
576,
|
|
||||||
535,
|
|
||||||
114
|
|
||||||
],
|
|
||||||
"101": [
|
|
||||||
573,
|
|
||||||
186,
|
|
||||||
416,
|
|
||||||
115,
|
|
||||||
534
|
|
||||||
],
|
|
||||||
"102": [
|
|
||||||
49,
|
|
||||||
51,
|
|
||||||
299,
|
|
||||||
288,
|
|
||||||
250
|
|
||||||
],
|
|
||||||
"103": [
|
|
||||||
449,
|
|
||||||
180,
|
|
||||||
127,
|
|
||||||
598,
|
|
||||||
506
|
|
||||||
],
|
|
||||||
"104": [
|
|
||||||
469,
|
|
||||||
91,
|
|
||||||
206,
|
|
||||||
504,
|
|
||||||
482
|
|
||||||
],
|
|
||||||
"105": [
|
|
||||||
513,
|
|
||||||
171,
|
|
||||||
589,
|
|
||||||
136,
|
|
||||||
442
|
|
||||||
],
|
|
||||||
"106": [
|
|
||||||
279,
|
|
||||||
35,
|
|
||||||
305,
|
|
||||||
260,
|
|
||||||
36
|
|
||||||
],
|
|
||||||
"107": [
|
|
||||||
389,
|
|
||||||
3,
|
|
||||||
385,
|
|
||||||
336,
|
|
||||||
332
|
|
||||||
],
|
|
||||||
"108": [
|
|
||||||
593,
|
|
||||||
183,
|
|
||||||
509,
|
|
||||||
454,
|
|
||||||
124
|
|
||||||
],
|
|
||||||
"109": [
|
|
||||||
149,
|
|
||||||
155,
|
|
||||||
554,
|
|
||||||
432,
|
|
||||||
548
|
|
||||||
],
|
|
||||||
"110": [
|
|
||||||
325,
|
|
||||||
5,
|
|
||||||
394,
|
|
||||||
327,
|
|
||||||
398
|
|
||||||
],
|
|
||||||
"111": [
|
|
||||||
453,
|
|
||||||
123,
|
|
||||||
510,
|
|
||||||
184,
|
|
||||||
594
|
|
||||||
],
|
|
||||||
"112": [
|
|
||||||
383,
|
|
||||||
13,
|
|
||||||
338,
|
|
||||||
340,
|
|
||||||
381
|
|
||||||
],
|
|
||||||
"113": [
|
|
||||||
311,
|
|
||||||
31,
|
|
||||||
273,
|
|
||||||
32,
|
|
||||||
262
|
|
||||||
],
|
|
||||||
"114": [
|
|
||||||
581,
|
|
||||||
107,
|
|
||||||
526,
|
|
||||||
424,
|
|
||||||
194
|
|
||||||
],
|
|
||||||
"115": [
|
|
||||||
61,
|
|
||||||
57,
|
|
||||||
268,
|
|
||||||
247,
|
|
||||||
294
|
|
||||||
],
|
|
||||||
"116": [
|
|
||||||
87,
|
|
||||||
79,
|
|
||||||
230,
|
|
||||||
220,
|
|
||||||
233
|
|
||||||
],
|
|
||||||
"117": [
|
|
||||||
301,
|
|
||||||
47,
|
|
||||||
45,
|
|
||||||
256,
|
|
||||||
282
|
|
||||||
],
|
|
||||||
"118": [
|
|
||||||
131,
|
|
||||||
176,
|
|
||||||
445,
|
|
||||||
518,
|
|
||||||
586
|
|
||||||
],
|
|
||||||
"119": [
|
|
||||||
210,
|
|
||||||
103,
|
|
||||||
457,
|
|
||||||
478,
|
|
||||||
492
|
|
||||||
],
|
|
||||||
"120": [
|
|
||||||
140,
|
|
||||||
166,
|
|
||||||
567,
|
|
||||||
433,
|
|
||||||
541
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Schoute's partition via https://arxiv.org/abs/1010.4353
|
// Schoute's partition via https://arxiv.org/abs/1010.4353
|
||||||
|
|
||||||
export const PARTITION600 = {
|
export const PARTITION600 = {
|
||||||
|
|||||||
23
colours.js
23
colours.js
@ -1,21 +1,14 @@
|
|||||||
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().slice(1, 9);
|
scheme.from_hex(hexbasis).scheme("tetrade").variation("hard").distance(0.5);
|
||||||
colours.reverse();
|
const colours = scheme.colors().map((cs) => parseInt('0x' + cs));
|
||||||
const hsl = colours.map((c) => Color("#" + c).hsl());
|
const set = colours.slice(1, 9);
|
||||||
const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber());
|
set.reverse();
|
||||||
resaturated.unshift(basis);
|
set.unshift(colours[0]);
|
||||||
console.log(resaturated);
|
return set;
|
||||||
return resaturated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// basic colours where 0 = blue
|
// basic colours where 0 = blue
|
||||||
@ -35,4 +28,4 @@ export const get_plain_colours = (basis) => {
|
|||||||
0xff9900,
|
0xff9900,
|
||||||
0x000000,
|
0x000000,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -1,492 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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));
|
|
||||||
@ -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,35 @@ 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, links_show) {
|
||||||
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;
|
||||||
|
link.object.visible = (!links_show || link.label in links_show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,41 +100,32 @@ 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);
|
||||||
|
return new THREE.Vector3(v4.x * k, v4.y * k, v4.z * k);
|
||||||
}
|
}
|
||||||
|
|
||||||
fourDtoV3(v4) {
|
|
||||||
const k = this.fourDscale(v4.w);
|
|
||||||
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,
|
|
||||||
label: n.label,
|
label: n.label,
|
||||||
object: this.makeNode(material, v3, k)
|
object: this.makeNode(material, v3)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,16 +134,11 @@ class FourDShape extends THREE.Group {
|
|||||||
render3(rotations, nodes_show, links_show) {
|
render3(rotations, nodes_show, links_show) {
|
||||||
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) );
|
this.nodes3[n.id].object.visible = ( !nodes_show || n.label in nodes_show );
|
||||||
}
|
}
|
||||||
for( const l of this.links ) {
|
for( const l of this.links ) {
|
||||||
this.updateLink(l, links_show);
|
this.updateLink(l, links_show);
|
||||||
|
|||||||
99
gui.js
99
gui.js
@ -2,25 +2,21 @@ import { GUI } from 'lil-gui';
|
|||||||
|
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
nodesize: 0.6,
|
thickness: 1.0,
|
||||||
nodeopacity: 1,
|
nodesize: 2.0,
|
||||||
linksize: 1.0,
|
linkopacity: 0.5,
|
||||||
linkopacity: 0.75,
|
link2opacity: 0.5,
|
||||||
shape: '120-cell',
|
shape: '120-cell',
|
||||||
link2opacity: 0.75,
|
|
||||||
option: 'none',
|
option: 'none',
|
||||||
visibility: 5,
|
visibility: 5,
|
||||||
inscribed: false,
|
inscribed: false,
|
||||||
inscribe_all: false,
|
inscribe_all: false,
|
||||||
color: 0x3293a9,
|
color: 0x3293a9,
|
||||||
background: 0xd4d4d4,
|
background: 0xd4d4d4,
|
||||||
hyperplane: 0.93,
|
hyperplane: 1.5,
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
xRotate: 'YZ',
|
rotation: 'rigid',
|
||||||
yRotate: 'XZ',
|
|
||||||
dtheta: 0,
|
dtheta: 0,
|
||||||
damping: false,
|
|
||||||
captions: true,
|
|
||||||
dpsi: 0,
|
dpsi: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,10 +24,9 @@ const DEFAULTS = {
|
|||||||
|
|
||||||
class FourDGUI {
|
class FourDGUI {
|
||||||
|
|
||||||
constructor(funcs) {
|
constructor(shapes, changeShape, setColor, setBackground, setLinkOpacity, setVisibility) {
|
||||||
this.shapes = funcs.shapes;
|
|
||||||
this.gui = new GUI();
|
this.gui = new GUI();
|
||||||
const SHAPE_NAMES = this.shapes.map((s) => s.name);
|
const SHAPE_NAMES = shapes.map((s) => s.name);
|
||||||
|
|
||||||
this.parseLinkParams();
|
this.parseLinkParams();
|
||||||
const guiObj = this;
|
const guiObj = this;
|
||||||
@ -40,68 +35,54 @@ class FourDGUI {
|
|||||||
option: this.link['option'],
|
option: this.link['option'],
|
||||||
inscribed: this.link['inscribed'],
|
inscribed: this.link['inscribed'],
|
||||||
inscribe_all: this.link['inscribe_all'],
|
inscribe_all: this.link['inscribe_all'],
|
||||||
linksize: this.link['linksize'],
|
thickness: this.link['thickness'],
|
||||||
linkopacity: this.link['linkopacity'],
|
linkopacity: this.link['linkopacity'],
|
||||||
link2opacity: this.link['link2opacity'],
|
link2opacity: this.link['linkopacity'],
|
||||||
nodesize: this.link['nodesize'],
|
nodesize: this.link['nodesize'],
|
||||||
nodeopacity: this.link['nodeopacity'],
|
|
||||||
depth: this.link['depth'],
|
depth: this.link['depth'],
|
||||||
color: this.link['color'],
|
color: this.link['color'],
|
||||||
background: this.link['background'],
|
background: this.link['background'],
|
||||||
hyperplane: this.link['hyperplane'],
|
hyperplane: this.link['hyperplane'],
|
||||||
zoom: this.link['zoom'],
|
zoom: this.link['zoom'],
|
||||||
xRotate: this.link['xRotate'],
|
rotation: this.link['rotation'],
|
||||||
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 ) {
|
|
||||||
console.log(label);
|
|
||||||
console.log(funcs.extras[label]);
|
|
||||||
this.params[label] = funcs.extras[label];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let options_ctrl;
|
let options_ctrl;
|
||||||
this.gui.add(this.params, 'shape', SHAPE_NAMES).onChange((shape) => {
|
this.gui.add(this.params, 'shape', SHAPE_NAMES).onChange((shape) => {
|
||||||
const options = this.getShapeOptions(shape);
|
const options = this.getShapeOptions(shapes, shape);
|
||||||
options_ctrl = options_ctrl.options(options).onChange((option) => {
|
options_ctrl = options_ctrl.options(options).onChange((option) => {
|
||||||
funcs.setVisibility(option)
|
setVisibility(option)
|
||||||
});
|
});
|
||||||
options_ctrl.setValue(options[0])
|
options_ctrl.setValue(options[0])
|
||||||
funcs.changeShape(shape)
|
changeShape(shape)
|
||||||
});
|
});
|
||||||
const options = this.getShapeOptions(this.params['shape']);
|
const options = this.getShapeOptions(shapes, this.params['shape']);
|
||||||
options_ctrl = this.gui.add(this.params, 'option').options(options).onChange((option) => {
|
options_ctrl = this.gui.add(this.params, 'option').options(options).onChange((option) => {
|
||||||
funcs.setVisibility(option)
|
setVisibility(option)
|
||||||
});
|
});
|
||||||
this.gui.add(this.params, 'hyperplane', 0.5, 1 / 0.8);
|
this.gui.add(this.params, 'hyperplane', 1.4, 2.0);
|
||||||
this.gui.add(this.params, 'zoom', 0.1, 2.0);
|
this.gui.add(this.params, 'zoom', 0.1, 2.0);
|
||||||
this.gui.add(this.params, 'nodesize', 0, 1.5);
|
this.gui.add(this.params, 'thickness', 0, 2);
|
||||||
this.gui.add(this.params, 'nodeopacity', 0, 1).onChange(funcs.setNodeOpacity);
|
this.gui.add(this.params, 'linkopacity', 0, 1).onChange(
|
||||||
this.gui.add(this.params, 'linksize', 0, 2);
|
(v) => setLinkOpacity(v, true)
|
||||||
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(
|
||||||
this.gui.add(this.params, 'link2opacity', 0, 1).onChange((v) => funcs.setLinkOpacity(v, false));
|
(v) => setLinkOpacity(v, false)
|
||||||
this.gui.addColor(this.params, 'color').onChange(funcs.setColor);
|
);
|
||||||
this.gui.addColor(this.params, 'background').onChange(funcs.setBackground);
|
this.gui.add(this.params, 'nodesize', 0.1, 4);
|
||||||
this.gui.add(this.params, 'xRotate', [ 'YW', 'YZ', 'ZW' ]);
|
this.gui.addColor(this.params, 'color').onChange(setColor);
|
||||||
this.gui.add(this.params, 'yRotate', [ 'XZ', 'XY', 'XW' ]);
|
this.gui.addColor(this.params, 'background').onChange(setBackground);
|
||||||
this.gui.add(this.params, 'captions').onChange(this.showDocs);
|
this.gui.add(this.params, 'rotation', [ 'rigid', 'tumbling', 'inside-out', 'axisymmetrical' ]);
|
||||||
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) {
|
getShapeOptions(shapes, shape) {
|
||||||
const spec = this.shapes.filter((s) => s.name === shape);
|
const spec = shapes.filter((s) => s.name === shape);
|
||||||
if( spec && spec[0].options ) {
|
if( spec && spec[0].options ) {
|
||||||
return spec[0].options.map((o) => o.name);
|
return spec[0].options.map((o) => o.name);
|
||||||
} else {
|
} else {
|
||||||
@ -137,7 +118,7 @@ class FourDGUI {
|
|||||||
const guiObj = this;
|
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", "rotation", "option" ]) {
|
||||||
const value = this.urlParams.get(param);
|
const value = this.urlParams.get(param);
|
||||||
if( value ) {
|
if( value ) {
|
||||||
this.link[param] = value;
|
this.link[param] = value;
|
||||||
@ -150,11 +131,10 @@ class FourDGUI {
|
|||||||
}
|
}
|
||||||
this.link['hyperplane'] = this.numParam('hyperplane', parseFloat);
|
this.link['hyperplane'] = this.numParam('hyperplane', parseFloat);
|
||||||
this.link['zoom'] = this.numParam('zoom', parseFloat);
|
this.link['zoom'] = this.numParam('zoom', parseFloat);
|
||||||
this.link['linksize'] = this.numParam('linksize', parseFloat);
|
this.link['thickness'] = this.numParam('thickness', parseFloat);
|
||||||
this.link['linkopacity'] = this.numParam('linkopacity', parseFloat);
|
this.link['linkopacity'] = this.numParam('linkopacity', parseFloat);
|
||||||
this.link['link2opacity'] = this.numParam('link2opacity', 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['color'] = this.numParam('color', (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);
|
||||||
@ -168,16 +148,15 @@ class FourDGUI {
|
|||||||
url.searchParams.append("option", this.params.option);
|
url.searchParams.append("option", this.params.option);
|
||||||
url.searchParams.append("inscribed", this.params.inscribed ? 'y': 'n');
|
url.searchParams.append("inscribed", this.params.inscribed ? 'y': 'n');
|
||||||
url.searchParams.append("inscribe_all", this.params.inscribe_all ? 'y': 'n');
|
url.searchParams.append("inscribe_all", this.params.inscribe_all ? 'y': 'n');
|
||||||
url.searchParams.append("linksize", this.params.linksize.toString());
|
url.searchParams.append("thickness", this.params.thickness.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("linkopacity", this.params.thickness.toString());
|
||||||
url.searchParams.append("linkopacity", this.params.nodeopacity.toString());
|
url.searchParams.append("link2opacity", this.params.nodesize.toString());
|
||||||
url.searchParams.append("color", this.hexToString(this.params.color));
|
url.searchParams.append("color", this.hexToString(this.params.color));
|
||||||
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("zoom", this.params.zoom.toString());
|
||||||
url.searchParams.append("xRotate", this.params.xRotate);
|
url.searchParams.append("rotation", this.params.rotation);
|
||||||
url.searchParams.append("yRotate", this.params.yRotate);
|
|
||||||
url.searchParams.append("dtheta", this.params.dtheta.toString());
|
url.searchParams.append("dtheta", this.params.dtheta.toString());
|
||||||
url.searchParams.append("dpsi", this.params.dpsi.toString());
|
url.searchParams.append("dpsi", this.params.dpsi.toString());
|
||||||
this.copyTextToClipboard(url);
|
this.copyTextToClipboard(url);
|
||||||
@ -225,4 +204,4 @@ class FourDGUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export { FourDGUI, DEFAULTS };
|
export { FourDGUI, DEFAULTS };
|
||||||
26
index.html
26
index.html
@ -5,24 +5,6 @@
|
|||||||
<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 {
|
div#info {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom:0;
|
bottom:0;
|
||||||
@ -34,11 +16,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module" src="/main.js"></script>
|
<script type="module" src="/main.js"></script>
|
||||||
<div id="description"></div>
|
<div id="info">by <a target="_blank" href="https://mikelynch.org/">Mike Lynch</a> -
|
||||||
<div id="release_notes"></div>
|
|
||||||
<div id="info"><a href="#" id="show_notes">release 1.1</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>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
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();
|
|
||||||
156
main.js
156
main.js
@ -1,31 +1,15 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
|
||||||
const RELEASE_NOTES = `
|
|
||||||
<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';
|
||||||
import { rotfn } from './rotation.js';
|
import { get_rotation } from './rotation.js';
|
||||||
import { FourDGUI, DEFAULTS } from './gui.js';
|
import { FourDGUI, DEFAULTS } from './gui.js';
|
||||||
import { FourDShape } from './fourDShape.js';
|
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;
|
const CAMERA_K = 10;
|
||||||
|
|
||||||
// scene, lights and camera
|
// scene, lights and camera
|
||||||
|
|
||||||
@ -47,37 +31,30 @@ camera.lookAt(0, 0, 0);
|
|||||||
|
|
||||||
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 material = new THREE.MeshStandardMaterial({ color: DEFAULTS.color });
|
||||||
const node_colours = get_colours(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;
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
console.log("link_ms", link_ms);
|
|
||||||
|
|
||||||
|
|
||||||
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 ) {
|
||||||
@ -108,96 +85,44 @@ function createShape(name, option) {
|
|||||||
setVisibility(option ? option : structure.options[0].name);
|
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 setColors(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_color(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
|
|
||||||
// on them individually. And also set the base materials as they
|
|
||||||
// will get updated from it when the shape changes
|
|
||||||
|
|
||||||
function setLinkOpacity(o, primary) {
|
function setLinkOpacity(o, primary) {
|
||||||
link_ms.map((lm) => lm.opacity = o);
|
if( structure.nolink2opacity ) {
|
||||||
if( shape ) {
|
link_ms.map((lm) => lm.opacity = o);
|
||||||
shape.links.map((l) => {
|
} else {
|
||||||
if( (primary && l.label == 0) || (!primary && l.label !== 0) ) {
|
if( primary ) {
|
||||||
l.object.material.opacity = o
|
link_ms[0].opacity = o;
|
||||||
}
|
} else {
|
||||||
});
|
link_ms.slice(1).map((lm) => lm.opacity = o);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNodeOpacity(o) {
|
|
||||||
node_ms.map((nm) => nm.opacity = o);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let gui;
|
let gui;
|
||||||
|
|
||||||
|
|
||||||
function changeShape() {
|
function changeShape() {
|
||||||
createShape(gui.params.shape);
|
createShape(gui.params.shape);
|
||||||
displayDocs(gui.params.shape);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,16 +134,12 @@ function setVisibility(option_name) {
|
|||||||
|
|
||||||
|
|
||||||
gui = new FourDGUI(
|
gui = new FourDGUI(
|
||||||
{
|
STRUCTURES,
|
||||||
shapes: STRUCTURES,
|
changeShape,
|
||||||
changeShape: changeShape,
|
setColors,
|
||||||
setColors: setColors,
|
setBackground,
|
||||||
setBackground: setBackground,
|
setLinkOpacity,
|
||||||
setNodeOpacity: setNodeOpacity,
|
setVisibility
|
||||||
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
|
||||||
@ -263,7 +184,6 @@ renderer.domElement.addEventListener("pointerup", (event) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
createShape(gui.params.shape, gui.params.option);
|
createShape(gui.params.shape, gui.params.option);
|
||||||
displayDocs(gui.params.shape);
|
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame( animate );
|
requestAnimationFrame( animate );
|
||||||
@ -277,15 +197,13 @@ function animate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rotations = [
|
const rotations = get_rotation(gui.params.rotation, theta, psi);
|
||||||
rotfn[gui.params.xRotate](theta),
|
|
||||||
rotfn[gui.params.yRotate](psi)
|
|
||||||
];
|
|
||||||
shape.hyperplane = 1 / gui.params.hyperplane;
|
|
||||||
camera.position.set(0, 0, gui.params.zoom * CAMERA_K * 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, node_show, link_show);
|
shape.render3(rotations, node_show, link_show);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
58
package-lock.json
generated
58
package-lock.json
generated
@ -4,9 +4,7 @@
|
|||||||
"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.19.0",
|
||||||
"three": "^0.154.0"
|
"three": "^0.154.0"
|
||||||
@ -367,48 +365,11 @@
|
|||||||
"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.20",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
||||||
@ -460,11 +421,6 @@
|
|||||||
"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.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.19.0.tgz",
|
||||||
@ -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,9 +509,9 @@
|
|||||||
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
|
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.5.3",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
|
||||||
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^4.2.3",
|
|
||||||
"color-scheme": "^1.0.1",
|
"color-scheme": "^1.0.1",
|
||||||
"lil-gui": "^0.19.0",
|
"lil-gui": "^0.19.0",
|
||||||
"three": "^0.154.0"
|
"three": "^0.154.0"
|
||||||
|
|||||||
324
polytopes.js
324
polytopes.js
@ -55,38 +55,19 @@ export function auto_detect_edges(nodes, neighbours, debug=false) {
|
|||||||
return links;
|
return links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const linkTest = () => {
|
|
||||||
return {
|
|
||||||
name: 'linky',
|
|
||||||
nodes: [
|
|
||||||
{ id:1, label: 1, x: -1, y: -1, z:-1, w: 0 },
|
|
||||||
{ id:2, label: 2, x: 1, y: 1, z: 1, w: 0 },
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
{ id: 1, source: 1, target: 2 }
|
|
||||||
],
|
|
||||||
options: [ { name: '--' }],
|
|
||||||
description: `link`,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// too small and simple to calculate
|
// too small and simple to calculate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const cell5 = () => {
|
export const cell5 = () => {
|
||||||
const c1 = Math.sqrt(5) / 4;
|
const r5 = Math.sqrt(5);
|
||||||
|
const r2 = Math.sqrt(2) / 2;
|
||||||
return {
|
return {
|
||||||
name: '5-cell',
|
name: '5-cell',
|
||||||
nodes: [
|
nodes: [
|
||||||
{id:1, label: 1, x: c1, y: c1, z: c1, w: -0.25 },
|
{id:1, label: 1, x: r2, y: r2, z: r2, w: -r2 / r5 },
|
||||||
{id:2, label: 2, x: c1, y: -c1, z: -c1, w: -0.25 },
|
{id:2, label: 2, x: r2, y: -r2, z: -r2, w: -r2 / r5 },
|
||||||
{id:3, label: 3, x: -c1, y: c1, z: -c1, w: -0.25 },
|
{id:3, label: 3, x: -r2, y: r2, z: -r2, w: -r2 / r5 },
|
||||||
{id:4, label: 4, x: -c1, y: -c1, z: c1, w: -0.25 },
|
{id:4, label: 4, x: -r2, y: -r2, z: r2, w: -r2 / r5 },
|
||||||
{id:5, label: 5, x: 0, y: 0, z: 0, w: 1 },
|
{id:5, label: 5, x: 0, y: 0, z: 0, w: 4 * r2 / r5 },
|
||||||
],
|
],
|
||||||
links: [
|
links: [
|
||||||
{ id:1, source:1, target: 2},
|
{ id:1, source:1, target: 2},
|
||||||
@ -100,12 +81,11 @@ export const cell5 = () => {
|
|||||||
{ id:9, source:3, target: 5},
|
{ id:9, source:3, target: 5},
|
||||||
{ id:10, source:4, target: 5},
|
{ id:10, source:4, target: 5},
|
||||||
],
|
],
|
||||||
options: [ { name: '--' }],
|
geometry: {
|
||||||
description: `Five tetrahedra joined at ten faces with three
|
node_size: 0.02,
|
||||||
tetrahedra around each edge. The 5-cell is the simplest regular
|
link_size: 0.02
|
||||||
four-D polytope and the four-dimensional analogue of the tetrahedron.
|
},
|
||||||
A corresponding polytope, or simplex, exists for every n-dimensional
|
options: [ { name: '--' }]
|
||||||
space.`,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,18 +104,18 @@ export const cell16 = () => {
|
|||||||
nodes[1].label = 4;
|
nodes[1].label = 4;
|
||||||
|
|
||||||
index_nodes(nodes);
|
index_nodes(nodes);
|
||||||
scale_nodes(nodes, 0.5);
|
scale_nodes(nodes, 0.75);
|
||||||
const links = auto_detect_edges(nodes, 6);
|
const links = auto_detect_edges(nodes, 6);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: '16-cell',
|
name: '16-cell',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
options: [ { name: '--' }],
|
geometry: {
|
||||||
description: `Sixteen tetrahedra joined at 32 faces with four
|
node_size: 0.02,
|
||||||
tetrahedra around each edge. The 16-cell is the four-dimensional
|
link_size: 0.02
|
||||||
analogue of the octahedron and is dual to the tesseract. Every
|
},
|
||||||
n-dimensional space has a corresponding polytope in this family.`,
|
options: [ { name: '--' }]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,7 +133,7 @@ export const tesseract = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scale_nodes(nodes, 0.5);
|
scale_nodes(nodes, Math.sqrt(2) / 2);
|
||||||
const links = auto_detect_edges(nodes, 4);
|
const links = auto_detect_edges(nodes, 4);
|
||||||
links.map((l) => { l.label = 0 });
|
links.map((l) => { l.label = 0 });
|
||||||
|
|
||||||
@ -166,20 +146,18 @@ export const tesseract = () => {
|
|||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'Tesseract',
|
name: 'tesseract',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
options: [
|
options: [
|
||||||
{ name: 'none', links: [ 0 ] },
|
{ name: 'none', links: [ 0 ] },
|
||||||
{ name: 'one 16-cell', links: [ 0, 1 ] },
|
{ name: 'one 16-cell', links: [ 0, 1 ] },
|
||||||
{ name: 'both 16-cells', links: [ 0, 1, 2 ] },
|
{ name: 'both 16-cells', links: [ 0, 1, 2 ] },
|
||||||
],
|
],
|
||||||
description: `The most well-known four-dimensional shape, the
|
|
||||||
tesseract is analogous to the cube, and is constructed by placing two
|
|
||||||
cubes in parallel hyperplanes and joining their corresponding
|
|
||||||
vertices. It consists of eight cubes joined at 32 face with three
|
|
||||||
cubes around each edge, and is dual to the 16-cell. Every
|
|
||||||
n-dimensional space has a cube analogue or measure polytope.`,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +183,6 @@ export const cell24 = () => {
|
|||||||
n.label = CELL24_INDEXING[axes[0]][axes[1]];
|
n.label = CELL24_INDEXING[axes[0]][axes[1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
scale_nodes(nodes, Math.sqrt(2) / 2);
|
|
||||||
index_nodes(nodes);
|
index_nodes(nodes);
|
||||||
const links = auto_detect_edges(nodes, 8);
|
const links = auto_detect_edges(nodes, 8);
|
||||||
links.map((l) => l.label = 0);
|
links.map((l) => l.label = 0);
|
||||||
@ -229,16 +206,16 @@ export const cell24 = () => {
|
|||||||
name: '24-cell',
|
name: '24-cell',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
base: {},
|
base: {},
|
||||||
options: [
|
options: [
|
||||||
{ name: 'none', links: [ 0 ] },
|
{ name: 'none', links: [ 0 ] },
|
||||||
{ name: 'one 16-cell', links: [ 0, 1 ] },
|
{ name: 'one 16-cell', links: [ 0, 1 ] },
|
||||||
{ name: 'three 16-cells', links: [ 0, 1, 2, 3 ] }
|
{ name: 'three 16-cells', links: [ 0, 1, 2, 3 ] }
|
||||||
],
|
]
|
||||||
description: `A unique object without an exact analogue in higher
|
|
||||||
or lower dimensions, the 24-cell is made of twenty-four octahedra
|
|
||||||
joined at 96 faces, with three around each edge. The 24-cell is
|
|
||||||
self-dual.`,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +319,7 @@ export function make_120cell_vertices() {
|
|||||||
PERMUTE.coordinates([2, 1, phi, phiinv], 0, true),
|
PERMUTE.coordinates([2, 1, phi, phiinv], 0, true),
|
||||||
].flat();
|
].flat();
|
||||||
index_nodes(nodes);
|
index_nodes(nodes);
|
||||||
scale_nodes(nodes, 0.25 * Math.sqrt(2));
|
scale_nodes(nodes, 0.5);
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,10 +393,12 @@ export const cell120_layered = (max) => {
|
|||||||
name: '120-cell layered',
|
name: '120-cell layered',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
nolink2opacity: true,
|
nolink2opacity: true,
|
||||||
options: options,
|
options: options
|
||||||
description: `This version of the 120-cell lets you explore its
|
|
||||||
structure by building each layer from the 'north pole' onwards.`,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,60 +422,24 @@ 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,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
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
|
|
||||||
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.`,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const cell120_inscribed_cell5 = () => {
|
|
||||||
const nodes = make_120cell_vertices();
|
|
||||||
const links = auto_detect_edges(nodes, 4);
|
|
||||||
|
|
||||||
for( const cstr in CELLINDEX.INDEX120 ) {
|
|
||||||
label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
|
|
||||||
}
|
|
||||||
|
|
||||||
links.map((l) => l.label = 0);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: '120-cell-5-cell',
|
|
||||||
nodes: nodes,
|
|
||||||
links: links,
|
|
||||||
options: [
|
|
||||||
{ name: "5-cells", links: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ] },
|
|
||||||
],
|
|
||||||
description: `The 120-cell with one of its 5-cells.`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function partition_coord(i, coords, invert) {
|
function partition_coord(i, coords, invert) {
|
||||||
@ -572,7 +515,7 @@ export function make_600cell_vertices() {
|
|||||||
|
|
||||||
|
|
||||||
index_nodes(nodes);
|
index_nodes(nodes);
|
||||||
scale_nodes(nodes, 0.5);
|
scale_nodes(nodes, 0.75);
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,16 +557,15 @@ export const cell600 = () => {
|
|||||||
name: '600-cell',
|
name: '600-cell',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
options: [
|
options: [
|
||||||
{ name: "none", links: [ 0 ]},
|
{ name: "none", links: [ 0 ]},
|
||||||
{ name: "one 24-cell", links: [ 0, 1 ] },
|
{ name: "one 24-cell", links: [ 0, 1 ] },
|
||||||
{ name: "five 24-cells", links: [ 0, 1, 2, 3, 4, 5 ] }
|
{ name: "five 24-cells", links: [ 0, 1, 2, 3, 4, 5 ] }
|
||||||
],
|
]
|
||||||
description: `The 600-cell is the four-dimensional analogue of the
|
|
||||||
icosahedron, and consists of 600 tetrahedra joined at 1200 faces
|
|
||||||
with five tetrahedra around each edge. It is dual to the 120-cell.
|
|
||||||
Its 120 vertices can be partitioned into five sets which form the
|
|
||||||
vertices of five inscribed 24-cells.`,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,10 +605,12 @@ export const cell600_layered = () => {
|
|||||||
name: '600-cell layered',
|
name: '600-cell layered',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
nolink2opacity: true,
|
nolink2opacity: true,
|
||||||
options: options,
|
options: options
|
||||||
description: `This version of the 600-cell lets you explore its
|
|
||||||
structure by building each layer from the 'north pole' onwards.`,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -684,18 +628,19 @@ export const snub24cell = () => {
|
|||||||
return sn && tn;
|
return sn && tn;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(nodes);
|
||||||
|
|
||||||
links.map((l) => l.label = 0);
|
links.map((l) => l.label = 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'Snub 24-cell',
|
name: 'snub 24-cell',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
options: [ { name: "--" } ],
|
options: [ { name: "--" } ],
|
||||||
description: `The snub 24-cell is a semiregular polytope which
|
|
||||||
connects the 24-cell with the 600-cell. It consists of 24 icosahedra
|
|
||||||
and 120 tetrahedra, and is constructed by removing one of the
|
|
||||||
five inscribed 24-cells from a 600-cell.`
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -735,7 +680,6 @@ function make_dodecahedron_vertices() {
|
|||||||
{ x: -phi, y: phiinv, z:0, w: 0 , label: 4},
|
{ x: -phi, y: phiinv, z:0, w: 0 , label: 4},
|
||||||
{ x: -phi, y: -phiinv, z:0, w: 0 , label: 2},
|
{ x: -phi, y: -phiinv, z:0, w: 0 , label: 2},
|
||||||
];
|
];
|
||||||
scale_nodes(nodes, 1 / Math.sqrt(3));
|
|
||||||
index_nodes(nodes);
|
index_nodes(nodes);
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
@ -753,146 +697,18 @@ export const dodecahedron = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'Dodecahedron',
|
name: 'dodecahedron',
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
|
geometry: {
|
||||||
|
node_size: 0.02,
|
||||||
|
link_size: 0.02
|
||||||
|
},
|
||||||
options: [
|
options: [
|
||||||
{ name: "none", links: [ 0 ]},
|
{ name: "none", links: [ 0 ]},
|
||||||
{ name: "one tetrahedron", links: [ 0, 1 ] },
|
{ name: "one tetrahedron", links: [ 0, 1 ] },
|
||||||
{ name: "five tetrahedra", links: [ 0, 1, 2, 3, 4, 5 ] }
|
{ name: "five tetrahedra", links: [ 0, 1, 2, 3, 4, 5 ] }
|
||||||
],
|
]
|
||||||
description: `The dodecahedron is a three-dimensional polyhedron
|
|
||||||
which is included here so that you can see the partition of its
|
|
||||||
vertices into five interlocked tetrahedra. This structure is the
|
|
||||||
basis for the partition of the 120-cell's vertices into five
|
|
||||||
600-cells.`
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const tetrahedron = () => {
|
|
||||||
const r2 = Math.sqrt(2);
|
|
||||||
const r3 = Math.sqrt(3);
|
|
||||||
return {
|
|
||||||
name: 'Tetrahedron',
|
|
||||||
nodes: [
|
|
||||||
{id:1, label: 1, x: 2 * r2 / 3, y: 0, z: -1/3, w: 0 },
|
|
||||||
{id:2, label: 2, x: -r2 / 3, y: r2 / r3, z: -1/3, w: 0 },
|
|
||||||
{id:3, label: 3, x: -r2 / 3, y: -r2 / r3, z: -1/3, w: 0 },
|
|
||||||
{id:4, label: 4, x: 0, y: 0, z: 1, w: 0 },
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
{ id:1, source:1, target: 2},
|
|
||||||
{ id:2, source:1, target: 3},
|
|
||||||
{ id:3, source:1, target: 4},
|
|
||||||
{ id:4, source:2, target: 3},
|
|
||||||
{ id:5, source:2, target: 4},
|
|
||||||
{ id:6, source:3, target: 4},
|
|
||||||
],
|
|
||||||
options: [ { name: '--' }],
|
|
||||||
description: `The simplest three-dimensional polytope, consisting of four triangles joined at six edges. The 5-cell is its four-dimensional analogue.`,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const octahedron = () => {
|
|
||||||
const nodes = [
|
|
||||||
{id: 1, label: 1, x: 1, y: 0, z: 0, w: 0},
|
|
||||||
{id: 2, label: 1, x: -1, y: 0, z: 0, w: 0},
|
|
||||||
{id: 3, label: 2, x: 0, y: 1, z: 0, w: 0},
|
|
||||||
{id: 4, label: 2, x: 0, y: -1, z: 0, w: 0},
|
|
||||||
{id: 5, label: 3, x: 0, y: 0, z: 1, w: 0},
|
|
||||||
{id: 6, label: 3, x: 0, y: 0, z: -1, w: 0},
|
|
||||||
];
|
|
||||||
const links = [
|
|
||||||
{id:1, source: 1, target: 3},
|
|
||||||
{id:2, source: 1, target: 4},
|
|
||||||
{id:3, source: 1, target: 5},
|
|
||||||
{id:4, source: 1, target: 6},
|
|
||||||
{id:5, source: 2, target: 3},
|
|
||||||
{id:6, source: 2, target: 4},
|
|
||||||
{id:7, source: 2, target: 5},
|
|
||||||
{id:8, source: 2, target: 6},
|
|
||||||
{id:9, source: 3, target: 5},
|
|
||||||
{id:10, source: 3, target: 6},
|
|
||||||
{id:11, source: 4, target: 5},
|
|
||||||
{id:12, source: 4, target: 6},
|
|
||||||
]
|
|
||||||
links.map((l) => { l.label = 0 });
|
|
||||||
return {
|
|
||||||
name: 'Octahedron',
|
|
||||||
nodes: nodes,
|
|
||||||
links: links,
|
|
||||||
options: [ { name: '--' }],
|
|
||||||
description: `The three-dimensional cross-polytope, the 16-cell is its four-dimensional analogue.`,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const cube = () => {
|
|
||||||
const nodes = [
|
|
||||||
{id: 1, label: 1, x: 1, y: 1, z: 1, w: 0},
|
|
||||||
{id: 2, label: 2, x: -1, y: 1, z: 1, w: 0},
|
|
||||||
{id: 3, label: 2, x: 1, y: -1, z: 1, w: 0},
|
|
||||||
{id: 4, label: 1, x: -1, y: -1, z: 1, w: 0},
|
|
||||||
{id: 5, label: 2, x: 1, y: 1, z: -1, w: 0},
|
|
||||||
{id: 6, label: 1, x: -1, y: 1, z: -1, w: 0},
|
|
||||||
{id: 7, label: 1, x: 1, y: -1, z: -1, w: 0},
|
|
||||||
{id: 8, label: 2, x: -1, y: -1, z: -1, w: 0},
|
|
||||||
];
|
|
||||||
scale_nodes(nodes, 1/Math.sqrt(3));
|
|
||||||
const links = auto_detect_edges(nodes, 3);
|
|
||||||
links.map((l) => { l.label = 0 });
|
|
||||||
return {
|
|
||||||
name: 'Cube',
|
|
||||||
nodes: nodes,
|
|
||||||
links: links,
|
|
||||||
options: [ { name: '--' }],
|
|
||||||
description: `The three-dimensional measure polytope, the tesseract is its four-dimensional analogue.`,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function make_icosahedron_vertices() {
|
|
||||||
const phi = 0.5 * (1 + Math.sqrt(5));
|
|
||||||
|
|
||||||
const nodes = [
|
|
||||||
{ x: 0, y: 1, z: phi, w: 0, label: 1 },
|
|
||||||
{ x: 0, y: -1, z: phi, w: 0, label: 1 },
|
|
||||||
{ x: 0, y: 1, z: -phi, w: 0, label: 1 },
|
|
||||||
{ x: 0, y: -1, z: -phi, w: 0, label: 1 },
|
|
||||||
{ x: 1, y: phi, z: 0, w: 0, label: 2 },
|
|
||||||
{ x: -1, y: phi, z: 0, w: 0, label: 2 },
|
|
||||||
{ x: 1, y: -phi, z: 0, w: 0, label: 2 },
|
|
||||||
{ x: -1, y: -phi, z: 0, w: 0, label: 2 },
|
|
||||||
{ x: phi, y: 0, z: 1, w: 0, label: 3},
|
|
||||||
{ x: phi, y: 0, z: -1, w: 0, label: 3},
|
|
||||||
{ x: -phi, y: 0, z: 1, w: 0, label: 3},
|
|
||||||
{ x: -phi, y: 0, z: -1, w: 0, label: 3},
|
|
||||||
];
|
|
||||||
|
|
||||||
scale_nodes(nodes, 1/Math.sqrt((5 + Math.sqrt(5)) / 2));
|
|
||||||
index_nodes(nodes);
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const icosahedron = () => {
|
|
||||||
const nodes = make_icosahedron_vertices();
|
|
||||||
const links = auto_detect_edges(nodes, 5);
|
|
||||||
links.map((l) => l.label = 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'Icosahedron',
|
|
||||||
nodes: nodes,
|
|
||||||
links: links,
|
|
||||||
options: [
|
|
||||||
{ name: "--"},
|
|
||||||
],
|
|
||||||
description: `The icosahedron is a twenty-sided polyhedron and is dual to the dodecahedron. Its four-dimensional analogue is the 600-cell.`
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -900,10 +716,6 @@ export const icosahedron = () => {
|
|||||||
|
|
||||||
export const build_all = () => {
|
export const build_all = () => {
|
||||||
return [
|
return [
|
||||||
tetrahedron(),
|
|
||||||
octahedron(),
|
|
||||||
cube(),
|
|
||||||
icosahedron(),
|
|
||||||
dodecahedron(),
|
dodecahedron(),
|
||||||
cell5(),
|
cell5(),
|
||||||
cell16(),
|
cell16(),
|
||||||
@ -915,8 +727,4 @@ export const build_all = () => {
|
|||||||
cell120_inscribed(),
|
cell120_inscribed(),
|
||||||
cell120_layered()
|
cell120_layered()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const radii = (shape) => {
|
|
||||||
return shape.nodes.map(n => Math.sqrt(n.x * n.x + n.y * n.y + n.z * n.z + n.w * n.w))
|
|
||||||
}
|
|
||||||
19
rotation.js
19
rotation.js
@ -81,5 +81,24 @@ export const rotfn = {
|
|||||||
ZW: rotZW,
|
ZW: rotZW,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rotMode = {
|
||||||
|
'rigid': [ rotYW, rotXW ],
|
||||||
|
'tumbling': [ rotYW, rotXZ ],
|
||||||
|
'inside-out': [ rotYW, rotXY ],
|
||||||
|
'axisymmetrical': [ rotZW, rotXY ]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const get_rotation = (mode, theta, psi) => {
|
||||||
|
const fns = rotMode[mode];
|
||||||
|
return [ fns[0](theta), fns[1](psi) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// [
|
||||||
|
// rotfn[gui.params.xRotate](theta),
|
||||||
|
// rotfn[gui.params.yRotate](psi)
|
||||||
|
// ];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
const EPSILON = 0.001;
|
|
||||||
|
|
||||||
class TaperedLink extends THREE.Group {
|
|
||||||
|
|
||||||
constructor(baseMaterial, color_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.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_color(colors) {
|
|
||||||
this.material.color = new THREE.Color(colors[this.color_i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export { TaperedLink };
|
|
||||||
Loading…
x
Reference in New Issue
Block a user