Compare commits
5 Commits
main
...
feature-pi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
055f790e73 | ||
|
|
32cae6854f | ||
|
|
e0b3ad967b | ||
|
|
eb176ece32 | ||
|
|
37218678d5 |
@ -1,11 +1,6 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
## v1.2 - 18/1/2026
|
||||
|
||||
Added a second visualisation of the 120-cell's 5-cells without the 120-cell links
|
||||
and with more colours added so you can get a sense of the individual 5-cells.
|
||||
|
||||
## v1.1 - 1/1/2026
|
||||
|
||||
The 120-cell now includes a visualisation of its inscribed 5-cells, which honestly
|
||||
|
||||
@ -9,11 +9,12 @@ export const get_colours = (basis) => {
|
||||
const luminance = hslb['color'][2];
|
||||
const scheme = new ColorScheme;
|
||||
scheme.from_hue(hue).scheme("tetrade").distance(0.75);
|
||||
const scolours = scheme.colors();
|
||||
const colours = [ ...scolours, ...scolours, ...scolours, ...scolours, ...scolours, ...scolours, ...scolours, ...scolours ];
|
||||
const colours = scheme.colors().slice(1, 9);
|
||||
colours.reverse();
|
||||
const hsl = colours.map((c) => Color("#" + c).hsl());
|
||||
const resaturated = hsl.map((hslc) => hslc.saturationl(saturation).rgbNumber());
|
||||
resaturated.unshift(basis);
|
||||
console.log(resaturated);
|
||||
return resaturated;
|
||||
}
|
||||
|
||||
|
||||
23
index.html
23
index.html
@ -14,6 +14,19 @@
|
||||
font-family: sans-serif;
|
||||
padding: 1em;
|
||||
}
|
||||
div#container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
canvas {
|
||||
width: 512px;
|
||||
height: 512px;
|
||||
image-rendering: crisp-edges; /* for firefox */;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
div#giflink {
|
||||
}
|
||||
div#release_notes {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -34,11 +47,15 @@
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="/main.js"></script>
|
||||
<div id="container">
|
||||
<canvas id="canvas" style="width: 512px; height: 512px"></canvas>
|
||||
</div>
|
||||
<div id="giflink"></div>
|
||||
<div id="description"></div>
|
||||
<div id="release_notes"></div>
|
||||
<div id="info"><a href="#" id="show_notes">release 1.2</a> |
|
||||
<div id="info">this is a bitcrushed version of <a href="">FourJS</a> which I hacked up for <a href="https://genuary.art">Genuary 2026</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://etc.mikelynch.org/genuary26/">Back</a> |
|
||||
<a target="_blank" href="https://git.tilde.town/bombinans/fourdjs/src/branch/feature-pixels/">source</a></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
117
main.js
117
main.js
@ -1,10 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
|
||||
const RELEASE_NOTES = `
|
||||
<p><b>v1.2 - 18/1/2026</b></p>
|
||||
|
||||
<p>Added a second visualisation of the 120-cell's 5-cells without the 120-cell links and with more colours added so you can get a sense of the individual 5-cells.</p>
|
||||
|
||||
<p><b>v1.1 - 1/1/2026</b></p>
|
||||
|
||||
<p>The 120-cell now includes a visualisation of its inscribed 5-cells, which honestly
|
||||
@ -34,7 +30,7 @@ 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 camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 1000 );
|
||||
const light = new THREE.PointLight(0xffffff, 2);
|
||||
light.position.set(10, 10, 10);
|
||||
scene.add(light);
|
||||
@ -49,16 +45,22 @@ camera.position.set(0, 0, CAMERA_K / 2);
|
||||
camera.lookAt(0, 0, 0);
|
||||
//camera.position.z = 4;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
renderer.setSize( window.innerWidth, window.innerHeight );
|
||||
const canvas = document.getElementById("canvas");
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
preserveDrawingBuffer: true,
|
||||
canvas: canvas
|
||||
});
|
||||
renderer.setSize(40,40);
|
||||
|
||||
canvas.style.width="400px";
|
||||
canvas.style.height="400px";
|
||||
|
||||
renderer.localClippingEnabled = true;
|
||||
|
||||
|
||||
document.body.appendChild( renderer.domElement );
|
||||
|
||||
// set up colours and materials for gui callbacks
|
||||
|
||||
scene.background = new THREE.Color(DEFAULTS.background);
|
||||
const node_colours = get_colours(DEFAULTS.color);
|
||||
|
||||
@ -112,42 +114,6 @@ function createShape(name, option) {
|
||||
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
|
||||
@ -173,7 +139,7 @@ function setBackground(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
|
||||
// will get updated from it when the shape changes
|
||||
|
||||
function setLinkOpacity(o, primary) {
|
||||
link_ms.map((lm) => lm.opacity = o);
|
||||
@ -191,12 +157,11 @@ function setNodeOpacity(o) {
|
||||
}
|
||||
|
||||
|
||||
let gui;
|
||||
let gui;
|
||||
|
||||
|
||||
function changeShape() {
|
||||
createShape(gui.params.shape);
|
||||
displayDocs(gui.params.shape);
|
||||
}
|
||||
|
||||
function setVisibility(option_name) {
|
||||
@ -221,7 +186,7 @@ gui = new FourDGUI(
|
||||
setNodeOpacity: setNodeOpacity,
|
||||
setLinkOpacity: setLinkOpacity,
|
||||
setVisibility: setVisibility,
|
||||
showDocs: showDocs,
|
||||
showDocs: () => {},
|
||||
}
|
||||
);
|
||||
|
||||
@ -231,6 +196,8 @@ setBackground(gui.params.background);
|
||||
|
||||
const dragK = 0.005;
|
||||
const damping = 0.99;
|
||||
const dtheta = 2 * Math.PI / 480;
|
||||
const dpsi = 2 * Math.PI / 480;
|
||||
|
||||
let theta = 0;
|
||||
let psi = 0;
|
||||
@ -239,49 +206,22 @@ let psi0 = 0;
|
||||
let dragx0 = 0;
|
||||
let dragy0 = 0;
|
||||
let dragging = false;
|
||||
let frame = 0;
|
||||
const FRAME_MAX = 600;
|
||||
let completed = false;
|
||||
|
||||
|
||||
renderer.domElement.addEventListener("pointerdown", (event) => {
|
||||
if( event.buttons === 1 ) {
|
||||
theta0 = theta;
|
||||
psi0 = psi;
|
||||
dragx0 = event.clientX;
|
||||
dragy0 = event.clientY;
|
||||
dragging = true;
|
||||
}
|
||||
})
|
||||
|
||||
renderer.domElement.addEventListener("pointermove", (event) => {
|
||||
if( event.buttons === 1 ) {
|
||||
const theta1 = theta0 + (event.clientX - dragx0) * dragK;
|
||||
const psi1 = psi0 + (event.clientY - dragy0) * dragK;
|
||||
gui.params.dtheta = theta1 - theta;
|
||||
gui.params.dpsi = psi1 - psi;
|
||||
theta = theta1;
|
||||
psi = psi1;
|
||||
}
|
||||
})
|
||||
|
||||
renderer.domElement.addEventListener("pointerup", (event) => {
|
||||
dragging = false;
|
||||
})
|
||||
const giflink = document.getElementById("giflink");
|
||||
|
||||
createShape(gui.params.shape, gui.params.option);
|
||||
displayDocs(gui.params.shape);
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame( animate );
|
||||
|
||||
if( ! dragging ) {
|
||||
theta += gui.params.dtheta;
|
||||
psi += gui.params.dpsi;
|
||||
if( gui.params.damping ) {
|
||||
gui.params.dtheta = gui.params.dtheta * damping;
|
||||
gui.params.dpsi = gui.params.dpsi * damping;
|
||||
}
|
||||
}
|
||||
theta += dtheta;
|
||||
psi += dpsi;
|
||||
|
||||
const rotations = [
|
||||
const rotations = [
|
||||
rotfn[gui.params.xRotate](theta),
|
||||
rotfn[gui.params.yRotate](psi)
|
||||
];
|
||||
@ -295,5 +235,14 @@ function animate() {
|
||||
|
||||
|
||||
renderer.render( scene, camera );
|
||||
if( frame < FRAME_MAX ) {
|
||||
console.log(`gif frame ${frame}`);
|
||||
const data = canvas.toDataURL("image/png", 1);
|
||||
const img = document.createElement("img");
|
||||
img.setAttribute('src', data);
|
||||
giflink.append(img);
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
35
package-lock.json
generated
35
package-lock.json
generated
@ -4,10 +4,10 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "fourdjs",
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"color-scheme": "^1.0.1",
|
||||
"gl-gif": "^3.1.0",
|
||||
"lil-gui": "^0.19.0",
|
||||
"three": "^0.154.0"
|
||||
},
|
||||
@ -367,6 +367,12 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/canvas-pixels": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/canvas-pixels/-/canvas-pixels-0.0.0.tgz",
|
||||
"integrity": "sha512-3XBmW3GbXKHyOJagEKQxYpSP2uVE2Vs9PDvlrJk7PycIVbDMN6rUMUYCfVanjXAIJ4/77Bl6/qMiJCHDwr1MHA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
@ -409,6 +415,14 @@
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dtype": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dtype/-/dtype-0.1.0.tgz",
|
||||
"integrity": "sha512-BXcer1Q6oDjU7DnJDq/G9YMQX9RSB4DCPvj/bxuflAdCv2tLzV9ZYFjw5sEeSdkcaB59tIzBe8hkd2S4j054tA==",
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
||||
@ -460,6 +474,16 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gl-gif": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/gl-gif/-/gl-gif-3.1.0.tgz",
|
||||
"integrity": "sha512-ejNqRMlB2Fs/saaSzNlTMF4+OlOwVoCZNOtnWhl5lt/gvlzw/Z7u0TTHYxo6JkOoJIu/5nB8YFFZliSQCbIpPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"canvas-pixels": "0.0.0",
|
||||
"tab64": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
@ -555,6 +579,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tab64": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tab64/-/tab64-0.0.1.tgz",
|
||||
"integrity": "sha512-GTZEo5wqncJDESZqBEC95bPZim+OPP23QDAIiL5s6mlrLzIovn03YM9xP6ZRA/+7rHt0Ug+eSRzwBDi0l1BpfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dtype": "~0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.154.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.154.0.tgz",
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"color-scheme": "^1.0.1",
|
||||
"gl-gif": "^3.1.0",
|
||||
"lil-gui": "^0.19.0",
|
||||
"three": "^0.154.0"
|
||||
},
|
||||
|
||||
29
polytopes.js
29
polytopes.js
@ -475,7 +475,7 @@ export const cell120_inscribed = () => {
|
||||
|
||||
export const cell120_inscribed_cell5 = () => {
|
||||
const nodes = make_120cell_vertices();
|
||||
const links = [];
|
||||
const links = auto_detect_edges(nodes, 4);
|
||||
|
||||
for( const cstr in CELLINDEX.INDEX120 ) {
|
||||
label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
|
||||
@ -483,25 +483,17 @@ export const cell120_inscribed_cell5 = () => {
|
||||
|
||||
links.map((l) => l.label = 0);
|
||||
|
||||
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 = Number(c5));
|
||||
links.push(...links5);
|
||||
}
|
||||
|
||||
const show_links = Array.from({ length: 128 }, (_, i) => i);
|
||||
|
||||
return {
|
||||
name: '120 5-cells',
|
||||
name: '120-cell-5-cell',
|
||||
nodes: nodes,
|
||||
links: links,
|
||||
options: [
|
||||
{ name: "none", links: show_links},
|
||||
],
|
||||
description: `The 120 5-cells from the 120-cell, without the latter's links. This colouring is pretty arbitrary, being based on the algorithm which partitioned the nodes: a later version will have something that's based on the symmetries of the 600-cells which each of the 5-cells has its nodes in.`,
|
||||
{ name: "5-cells", links: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ] },
|
||||
],
|
||||
description: `The 120-cell with one of its 5-cells.`,
|
||||
}
|
||||
}
|
||||
|
||||
@ -908,21 +900,12 @@ export const icosahedron = () => {
|
||||
|
||||
export const build_all = () => {
|
||||
return [
|
||||
tetrahedron(),
|
||||
octahedron(),
|
||||
cube(),
|
||||
icosahedron(),
|
||||
dodecahedron(),
|
||||
cell5(),
|
||||
cell16(),
|
||||
tesseract(),
|
||||
cell24(),
|
||||
snub24cell(),
|
||||
cell600(),
|
||||
cell600_layered(),
|
||||
cell600(),
|
||||
cell120_inscribed(),
|
||||
cell120_inscribed_cell5(),
|
||||
cell120_layered()
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
base: '/fourjs/',
|
||||
base: '/genuary26/04/',
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user