diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1336378 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +CHANGELOG +========= + +## v1.0 - 16/11/2025 + +It's been [two years](https://mikelynch.org/2023/Sep/02/120-cell/) 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. +` diff --git a/fourDShape.js b/fourDShape.js index 888ebd7..7cd76db 100644 --- a/fourDShape.js +++ b/fourDShape.js @@ -1,9 +1,12 @@ import * as THREE from 'three'; +import { TaperedLink } from './taperedLink.js'; + const HYPERPLANE = 2.0; const W_FORESHORTENING = 0.04; + class FourDShape extends THREE.Group { constructor(node_ms, link_ms, face_ms, structure) { @@ -26,12 +29,12 @@ class FourDShape extends THREE.Group { // if a node/link has no label, use the 0th material - getMaterial(entity, materials) { - if( "label" in entity ) { - return materials[entity.label]; - } else { - return materials[0]; - } + getMaterialLabel(entity) { + if( "label" in entity ) { + return entity.label + } else { + return 0; + } } makeNode(material, v3, scale) { @@ -42,45 +45,23 @@ class FourDShape extends THREE.Group { return sphere; } - makeLink(material, link) { + makeLink(materialLabel, link) { const n1 = this.nodes3[link.source]; const n2 = this.nodes3[link.target]; - const s1 = n1.scale; - const s2 = n2.scale; - const length = n1.v3.distanceTo(n2.v3); - const centre = new THREE.Vector3(); - centre.lerpVectors(n1.v3, n2.v3, 0.5); - const geometry = new THREE.CylinderGeometry( - this.link_scale * s2, this.link_scale * s1, 1, - 16, 1, true - ); - 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.v3); - cyl.rotation.x = Math.PI / 2.0; - this.add(edge); + const s1 = this.link_scale * n1.scale; + const s2 = this.link_scale * n2.scale; + const basematerial = this.link_ms[materialLabel]; + const edge = new TaperedLink(basematerial, materialLabel, n1, n2, s1, s2); + this.add( edge ); return edge; } updateLink(link, links_show) { const n1 = this.nodes3[link.source]; const n2 = this.nodes3[link.target]; - const s1 = n1.scale; - const s2 = n2.scale; - const length = n1.v3.distanceTo(n2.v3); - const centre = new THREE.Vector3(); - centre.lerpVectors(n1.v3, n2.v3, 0.5); - // take the average of the ends as the thickness - as a workaround, - // because I haven't worked out how to reshape tapered links without - // having to reassign a new geometry to every link - const link_mean = this.link_scale * (s1 + s2) * 0.5; - link.object.scale.copy(new THREE.Vector3(link_mean, link_mean, length)); - link.object.position.copy(centre); - link.object.lookAt(n2.v3); - link.object.children[0].rotation.x = Math.PI / 2.0; + const s1 = this.link_scale * n1.scale; + const s2 = this.link_scale * n2.scale; + link.object.update(n1, n2, s1, s2); link.object.visible = (!links_show || link.label in links_show); } @@ -110,15 +91,6 @@ class FourDShape extends THREE.Group { } - fourDtoV3_old(x, y, z, w, rotations) { - const v4 = new THREE.Vector4(x, y, z, w); - for ( const m4 of rotations ) { - v4.applyMatrix4(m4); - } - const k = this.fourDscale(v4.w); - return new THREE.Vector3(v4.x * k, v4.y * k, v4.z * k); - } - fourDscale(w) { return this.hyperplane / ( this.hyperplane + w ); } @@ -140,7 +112,7 @@ class FourDShape extends THREE.Group { for( const n of this.nodes4 ) { const k = this.fourDscale(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] = { v3: v3, scale: k, @@ -149,11 +121,11 @@ class FourDShape extends THREE.Group { }; } for( const l of this.links ) { - const material = this.getMaterial(l, this.link_ms); - l.object = this.makeLink(material, l); + const mLabel = this.getMaterialLabel(l); + l.object = this.makeLink(mLabel, l); } for( const f of this.faces ) { - const material = this.getMaterial(f, this.face_ms); + const material = this.face_ms(this.getMaterialLabel(f)); f.object = this.makeFace(material, f); } } diff --git a/gui.js b/gui.js index baba2ea..8e4755a 100644 --- a/gui.js +++ b/gui.js @@ -2,11 +2,10 @@ import { GUI } from 'lil-gui'; const DEFAULTS = { - nodesize: 0.25, + nodesize: 0.6, nodeopacity: 1, - linksize: 0.2, + linksize: 1.0, linkopacity: 0.75, - link2opacity: 0.75, shape: '120-cell', option: 'none', visibility: 5, @@ -41,7 +40,6 @@ class FourDGUI { inscribe_all: this.link['inscribe_all'], linksize: this.link['linksize'], linkopacity: this.link['linkopacity'], - link2opacity: this.link['linkopacity'], nodesize: this.link['nodesize'], nodeopacity: this.link['nodeopacity'], depth: this.link['depth'], @@ -72,15 +70,10 @@ class FourDGUI { }); this.gui.add(this.params, 'hyperplane', 0.5, 1 / 0.8); this.gui.add(this.params, 'zoom', 0.1, 2.0); - this.gui.add(this.params, 'nodesize', 0, 1); + this.gui.add(this.params, 'nodesize', 0, 1.5); this.gui.add(this.params, 'nodeopacity', 0, 1).onChange(setNodeOpacity); - this.gui.add(this.params, 'linksize', 0, 1); - this.gui.add(this.params, 'linkopacity', 0, 1).onChange( - (v) => setLinkOpacity(v, true) - ); - this.gui.add(this.params, 'link2opacity', 0, 1).onChange( - (v) => setLinkOpacity(v, false) - ); + this.gui.add(this.params, 'linksize', 0, 2); + this.gui.add(this.params, 'linkopacity', 0, 1).onChange(setLinkOpacity); this.gui.addColor(this.params, 'color').onChange(setColor); this.gui.addColor(this.params, 'background').onChange(setBackground); this.gui.add(this.params, 'xRotate', [ 'YW', 'YZ', 'ZW' ]); @@ -143,7 +136,6 @@ class FourDGUI { this.link['zoom'] = this.numParam('zoom', parseFloat); this.link['linksize'] = this.numParam('linksize', parseFloat); this.link['linkopacity'] = this.numParam('linkopacity', parseFloat); - this.link['link2opacity'] = this.numParam('link2opacity', 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)); @@ -163,7 +155,6 @@ class FourDGUI { url.searchParams.append("nodesize", this.params.nodesize.toString()); url.searchParams.append("nodeopacity", this.params.nodesize.toString()); url.searchParams.append("linkopacity", this.params.nodeopacity.toString()); - url.searchParams.append("link2opacity", this.params.link2opacity.toString()); url.searchParams.append("color", this.hexToString(this.params.color)); url.searchParams.append("background", this.hexToString(this.params.background)); url.searchParams.append("hyperplane", this.params.hyperplane.toString()); diff --git a/index.html b/index.html index 7791e9a..a96fe8c 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,17 @@ font-family: sans-serif; padding: 1em; } + div#release_notes { + position: fixed; + top: 0; + left: 0; + width: 50%; + z-index: 2; + font-family: sans-serif; + padding: 1em; + visibility: none; + background: #ffffff; + } div#info { position: fixed; bottom:0; @@ -26,7 +37,10 @@
-