diff --git a/fourDShape.js b/fourDShape.js index c760d3f..32cfb78 100644 --- a/fourDShape.js +++ b/fourDShape.js @@ -6,13 +6,15 @@ const HYPERPLANE = 2.0; class FourDShape extends THREE.Group { - constructor(node_ms, link_ms, structure) { + constructor(node_ms, link_ms, face_ms, structure) { super(); this.node_ms = node_ms; this.link_ms = link_ms; + this.face_ms = face_ms; this.nodes4 = structure.nodes; this.nodes3 = {}; this.links = structure.links; + 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; @@ -71,6 +73,32 @@ class FourDShape extends THREE.Group { link.object.children[0].rotation.x = Math.PI / 2.0; } + + setFaceGeometry(face, geometry) { + const values = []; + for( const f of face.nodes ) { + const v3 = this.nodes3[f].v3; + values.push(v3.x); + values.push(v3.y); + values.push(v3.z); + } + const v3 = this.nodes3[face.nodes[0]].v3; + values.push(v3.x); + values.push(v3.y); + values.push(v3.z); + const vertices = new Float32Array(values); + geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + } + + makeFace(material, face) { + const geometry = new THREE.BufferGeometry(); + this.setFaceGeometry(face, geometry) + const mesh = new THREE.Mesh( geometry, material ); + this.add(mesh); + return mesh; + } + + fourDtoV3(x, y, z, w, rotations) { const v4 = new THREE.Vector4(x, y, z, w); for ( const m4 of rotations ) { @@ -94,6 +122,10 @@ class FourDShape extends THREE.Group { const material = this.getMaterial(l, this.link_ms); l.object = this.makeLink(material, l); } + for( const f of this.faces ) { + const material = this.getMaterial(f, this.face_ms); + f.object = this.makeFace(material, f); + } } @@ -109,9 +141,13 @@ class FourDShape extends THREE.Group { for( const l of this.links ) { this.updateLink(l); } + + for( const f of this.faces ) { + this.setFaceGeometry(f, f.object.geometry); + } } } -export { FourDShape }; \ No newline at end of file +export { FourDShape }; diff --git a/main.js b/main.js index a3725e9..dc6c80f 100644 --- a/main.js +++ b/main.js @@ -8,6 +8,8 @@ import { FourDGUI } from './gui.js'; import { FourDShape } from './fourDShape.js'; import { get_colours } from './colours.js'; +const FACE_OPACITY = 0.3; + // scene, lights and camera const scene = new THREE.Scene(); @@ -40,6 +42,17 @@ const node_ms = node_colours.map((c) => new THREE.MeshStandardMaterial({color: c const link_ms = [ material ]; + +const face_ms = [ + new THREE.MeshLambertMaterial( { color: 0x44ff44 } ) + ]; + +for( const face_m of face_ms ) { + face_m.transparent = true; + face_m.opacity = FACE_OPACITY; +} + + const STRUCTURES = { '5-cell': POLYTOPES.cell5(), '16-cell': POLYTOPES.cell16(), @@ -55,7 +68,8 @@ function createShape(name) { if( shape ) { scene.remove(shape); } - shape = new FourDShape(node_ms, link_ms, STRUCTURES[name]); + console.log(STRUCTURES[name]); + shape = new FourDShape(node_ms, link_ms, face_ms, STRUCTURES[name]); scene.add(shape); } diff --git a/polytopes.js b/polytopes.js index 2a86218..2f63dee 100644 --- a/polytopes.js +++ b/polytopes.js @@ -170,9 +170,82 @@ export const cell24 = () => { } -// notes on coherent indexing -// see table in https://en.wikipedia.org/wiki/120-cell - maybe adapt the -// unit radius table +// face detection for the 120-cell + +// NOTE: all of these return node ids, not nodes + +// return all the links which connect to a node + +function nodes_links(links, nodeid) { + return links.filter((l) => l.source === nodeid || l.target === nodeid); +} + +// filter to remove a link to a given id from a set of links + +function not_to_this(link, nodeid) { + return !(link.source === nodeid || link.target === nodeid); +} + +// given nodes n1, n2, return all neighbours of n2 which are not n1 + +function unmutuals(links, n1id, n2id) { + const nlinks = nodes_links(links, n2id).filter((l) => not_to_this(l, n1id)); + return nlinks.map((l) => { + if( l.source === n2id ) { + return l.target; + } else { + return l.source; + } + }) +} + + +function fingerprint(ids) { + const sids = [...ids]; + sids.sort(); + return sids.join(','); +} + + + +function auto_120cell_faces(links) { + const faces = []; + const seen = {}; + let id = 1; + for( const edge of links ) { + const v1 = edge.source; + const v2 = edge.target; + const n1 = unmutuals(links, v2, v1); + const n2 = unmutuals(links, v1, v2); + const shared = []; + for( const a of n1 ) { + const an = unmutuals(links, v1, a); + for( const d of n2 ) { + const dn = unmutuals(links, v2, d); + for( const x of an ) { + for( const y of dn ) { + if( x == y ) { + shared.push([v1, a, x, d, v2]) + } + } + } + } + } + if( shared.length !== 3 ) { + console.log(`Bad shared faces for ${edge.id} ${v1} ${v2}`); + } + for( const face of shared ) { + const fp = fingerprint(face); + if( !seen[fp] ) { + faces.push({ id: id, nodes: face }); + id++; + seen[fp] = true; + } + } + } + return faces; +} + function make_120cell_vertices() { @@ -310,8 +383,8 @@ export const cell120 = () => { geometry: { node_size: 0.02, link_size: 0.02 - } - + }, + faces: faces } }