move to vite, landing page, add more shapes

This commit is contained in:
Vincent van der Wal
2026-07-19 10:04:08 +02:00
parent 25595d8281
commit f724cbff6d
25 changed files with 2841 additions and 5017 deletions
+119
View File
@@ -0,0 +1,119 @@
import * as THREE from 'three';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { ConvexGeometry } from 'three/addons/geometries/ConvexGeometry.js';
import { createStage } from './lib/stage.js';
import { makeTube } from './lib/edgeTubes.js';
// The vector equilibrium (cuboctahedron): the only form where every vertex is
// exactly as far from the centre as from its neighbours - Fuller's "zero point"
// of the isotropic vector matrix. Its 12 vertices plus centre are the 13 circles
// of Metatron's Cube, and 12 spheres kiss one central sphere on those points.
const { scene, colors, onUpdate } = createStage();
const params = {
scale: 2,
edges: true,
spokes: true,
fill: true,
spheres: false,
spin: false,
};
const group = new THREE.Group();
scene.add( group );
let parts = [];
function renderMatrix() {
for ( const part of parts ) group.remove( part );
parts = [];
const a = params.scale;
// all permutations of (+-a, +-a, 0): the 12 cuboctahedron vertices
const vertices = [];
for ( const s1 of [ -1, 1 ] ) {
for ( const s2 of [ -1, 1 ] ) {
vertices.push( new THREE.Vector3( s1 * a, s2 * a, 0 ) );
vertices.push( new THREE.Vector3( s1 * a, 0, s2 * a ) );
vertices.push( new THREE.Vector3( 0, s1 * a, s2 * a ) );
}
}
const edgeLength = a * Math.SQRT2; // equals the distance from centre to vertex
const edgeMaterial = new THREE.MeshBasicMaterial( { color: colors.line } );
const spokeMaterial = new THREE.MeshBasicMaterial( { color: colors.line, opacity: 0.35, transparent: true } );
const edges = new THREE.Group();
for ( let i = 0; i < vertices.length; i++ ) {
for ( let j = i + 1; j < vertices.length; j++ ) {
if ( Math.abs( vertices[ i ].distanceTo( vertices[ j ] ) - edgeLength ) < 1e-6 ) {
edges.add( makeTube( vertices[ i ], vertices[ j ], edgeMaterial ) );
}
}
}
edges.visible = params.edges;
group.add( edges );
parts.push( edges );
// the 12 radial spokes are as long as the edges - that's the equilibrium
const spokes = new THREE.Group();
const centre = new THREE.Vector3( 0, 0, 0 );
for ( const vertex of vertices ) {
spokes.add( makeTube( centre, vertex, spokeMaterial, 0.02 ) );
}
spokes.visible = params.spokes;
group.add( spokes );
parts.push( spokes );
const fillMaterial = new THREE.MeshStandardMaterial( {
color: colors.fills.fire,
metalness: 0.7,
roughness: 0.3,
opacity: 0.8,
transparent: true,
} );
// the fill casts no shadow: only the edges and spokes project, as outlines
const fill = new THREE.Mesh( new ConvexGeometry( vertices ), fillMaterial );
fill.visible = params.fill;
group.add( fill );
parts.push( fill );
// 12 spheres of radius edge/2 kiss each other on the edges and all kiss the
// 13th sphere in the centre: closest packing of equal spheres
const sphereRadius = edgeLength / 2;
const sphereMaterial = new THREE.MeshStandardMaterial( {
color: colors.fills.water,
metalness: 0.7,
roughness: 0.3,
opacity: 0.8,
transparent: true,
} );
const spheres = new THREE.Group();
const sphereGeometry = new THREE.SphereGeometry( sphereRadius, 48, 48 );
for ( const point of [ centre, ...vertices ] ) {
const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.position.copy( point );
sphere.castShadow = true;
spheres.add( sphere );
}
spheres.visible = params.spheres;
group.add( spheres );
parts.push( spheres );
}
renderMatrix();
onUpdate( ( time ) => {
if ( params.spin ) group.rotation.y = time * 0.15;
} );
const gui = new GUI();
gui.add( params, 'scale', 1, 4 ).step( 0.1 ).onChange( renderMatrix );
gui.add( params, 'edges' ).onChange( ( value ) => { parts[ 0 ].visible = value; } );
gui.add( params, 'spokes' ).onChange( ( value ) => { parts[ 1 ].visible = value; } );
gui.add( params, 'fill' ).onChange( ( value ) => { parts[ 2 ].visible = value; } );
gui.add( params, 'spheres' ).onChange( ( value ) => { parts[ 3 ].visible = value; } );
gui.add( params, 'spin' );