|
@@ -13,52 +13,112 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
const isDarkMode = darkMatcher.matches;
|
|
|
const colors = isDarkMode ? darkColors : lightColors;
|
|
|
|
|
|
+ const fontLoader = new THREE.FontLoader();
|
|
|
+ const fontPromise = new Promise((resolve) => {
|
|
|
+ fontLoader.load('../resources/threejs/fonts/helvetiker_regular.typeface.json', resolve);
|
|
|
+ });
|
|
|
+
|
|
|
const diagrams = {
|
|
|
BoxBufferGeometry: {
|
|
|
- create() {
|
|
|
- const width = 8;
|
|
|
- const height = 8;
|
|
|
- const depth = 8;
|
|
|
- const geometry = new THREE.BoxBufferGeometry(width, height, depth);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ width: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ height: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ depth: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ widthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ depthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ },
|
|
|
+ create(width = 8, height = 8, depth = 8) {
|
|
|
+ return new THREE.BoxBufferGeometry(width, height, depth);
|
|
|
+ },
|
|
|
+ create2(width = 8, height = 8, depth = 8, widthSegments = 4, heightSegments = 4, depthSegments = 4) {
|
|
|
+ return new THREE.BoxBufferGeometry(
|
|
|
+ width, height, depth,
|
|
|
+ widthSegments, heightSegments, depthSegments);
|
|
|
},
|
|
|
},
|
|
|
CircleBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const segments = 24;
|
|
|
- const geometry = new THREE.CircleBufferGeometry(radius, segments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ segments: { type: 'range', min: 1, max: 50, },
|
|
|
+ thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ },
|
|
|
+ create(radius = 7, segments = 24) {
|
|
|
+ return new THREE.CircleBufferGeometry(radius, segments);
|
|
|
+ },
|
|
|
+ create2(radius = 7, segments = 24, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5) {
|
|
|
+ return new THREE.CircleBufferGeometry(
|
|
|
+ radius, segments, thetaStart, thetaLength);
|
|
|
},
|
|
|
},
|
|
|
ConeBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 6;
|
|
|
- const height = 8;
|
|
|
- const segments = 16;
|
|
|
- const geometry = new THREE.ConeBufferGeometry(radius, height, segments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ height: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ radialSegments: { type: 'range', min: 1, max: 50, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ openEnded: { type: 'bool', },
|
|
|
+ thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ },
|
|
|
+ create(radius = 6, height = 8, radialSegments = 16) {
|
|
|
+ return new THREE.ConeBufferGeometry(radius, height, radialSegments);
|
|
|
+ },
|
|
|
+ create2(radius = 6, height = 8, radialSegments = 16, heightSegments = 2, openEnded = true, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5) {
|
|
|
+ return new THREE.ConeBufferGeometry(
|
|
|
+ radius, height,
|
|
|
+ radialSegments, heightSegments,
|
|
|
+ openEnded,
|
|
|
+ thetaStart, thetaLength);
|
|
|
},
|
|
|
},
|
|
|
CylinderBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radiusTop = 4;
|
|
|
- const radiusBottom = 4;
|
|
|
- const height = 8;
|
|
|
- const radialSegments = 12;
|
|
|
- const geometry = new THREE.CylinderBufferGeometry(radiusTop, radiusBottom, height, radialSegments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radiusTop: { type: 'range', min: 0, max: 10, precision: 1, },
|
|
|
+ radiusBottom: { type: 'range', min: 0, max: 10, precision: 1, },
|
|
|
+ height: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ radialSegments: { type: 'range', min: 1, max: 50, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ openEnded: { type: 'bool', },
|
|
|
+ thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ },
|
|
|
+ create(radiusTop = 4, radiusBottom = 4, height = 8, radialSegments = 12) {
|
|
|
+ return new THREE.CylinderBufferGeometry(
|
|
|
+ radiusTop, radiusBottom, height, radialSegments);
|
|
|
+ },
|
|
|
+ create2(radiusTop = 4, radiusBottom = 4, height = 8, radialSegments = 12, heightSegments = 2, openEnded = false, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5) {
|
|
|
+ return new THREE.CylinderBufferGeometry(
|
|
|
+ radiusTop, radiusBottom, height,
|
|
|
+ radialSegments, heightSegments,
|
|
|
+ openEnded,
|
|
|
+ thetaStart, thetaLength);
|
|
|
},
|
|
|
},
|
|
|
DodecahedronBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const geometry = new THREE.DodecahedronBufferGeometry(radius);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ detail: { type: 'range', min: 1, max: 5, precision: 0, },
|
|
|
+ },
|
|
|
+ create(radius = 7) {
|
|
|
+ return new THREE.DodecahedronBufferGeometry(radius);
|
|
|
+ },
|
|
|
+ create2(radius = 7, detail = 2) {
|
|
|
+ return new THREE.DodecahedronBufferGeometry(radius, detail);
|
|
|
},
|
|
|
},
|
|
|
ExtrudeBufferGeometry: {
|
|
|
- create() {
|
|
|
+ ui: {
|
|
|
+ steps: { type: 'range', min: 1, max: 10, },
|
|
|
+ depth: { type: 'range', min: 1, max: 20, precision: 1, },
|
|
|
+ bevelEnabled: { type: 'bool', },
|
|
|
+ bevelThickness: { type: 'range', min: 0.1, max: 3, },
|
|
|
+ bevelSize: { type: 'range', min: 0.1, max:3, },
|
|
|
+ bevelSegments: { type: 'range', min: 0, max: 8, },
|
|
|
+ },
|
|
|
+ addConstCode: false,
|
|
|
+ create(steps = 2, depth = 2, bevelEnabled = true, bevelThickness = 1, bevelSize = 1, bevelSegments = 2) {
|
|
|
const shape = new THREE.Shape();
|
|
|
const x = -2.5;
|
|
|
const y = -5;
|
|
@@ -71,26 +131,59 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
|
|
|
|
|
|
const extrudeSettings = {
|
|
|
- steps: 2,
|
|
|
- depth: 2,
|
|
|
- bevelEnabled: true,
|
|
|
- bevelThickness: 1,
|
|
|
- bevelSize: 1,
|
|
|
- bevelSegments: 2,
|
|
|
+ steps,
|
|
|
+ depth,
|
|
|
+ bevelEnabled,
|
|
|
+ bevelThickness,
|
|
|
+ bevelSize,
|
|
|
+ bevelSegments,
|
|
|
};
|
|
|
|
|
|
const geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
|
|
|
return geometry;
|
|
|
},
|
|
|
+ src: `
|
|
|
+const shape = new THREE.Shape();
|
|
|
+const x = -2.5;
|
|
|
+const y = -5;
|
|
|
+shape.moveTo(x + 2.5, y + 2.5);
|
|
|
+shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
|
|
|
+shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
|
|
|
+shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
|
|
|
+shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
|
|
|
+shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
|
|
|
+shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
|
|
|
+
|
|
|
+const extrudeSettings = {
|
|
|
+ steps: 2, // ui: steps
|
|
|
+ depth: 2, // ui: depth
|
|
|
+ bevelEnabled: true, // ui: bevelEnabled
|
|
|
+ bevelThickness: 1, // ui: bevelThickness
|
|
|
+ bevelSize: 1, // ui: bevelSize
|
|
|
+ bevelSegments: 2, // ui: bevelSegments
|
|
|
+};
|
|
|
+
|
|
|
+const geometry = THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
|
|
|
+`,
|
|
|
},
|
|
|
IcosahedronBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const geometry = new THREE.IcosahedronBufferGeometry(radius);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ detail: { type: 'range', min: 1, max: 5, precision: 0, },
|
|
|
+ },
|
|
|
+ create(radius = 7) {
|
|
|
+ return new THREE.IcosahedronBufferGeometry(radius);
|
|
|
+ },
|
|
|
+ create2(radius = 7, detail = 2) {
|
|
|
+ return new THREE.IcosahedronBufferGeometry(radius, detail);
|
|
|
},
|
|
|
},
|
|
|
LatheBufferGeometry: {
|
|
|
+ ui: {
|
|
|
+ segments: { type: 'range', min: 1, max: 50, },
|
|
|
+ phiStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ phiLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ },
|
|
|
create() {
|
|
|
const points = [];
|
|
|
for (let i = 0; i < 10; ++i) {
|
|
@@ -99,15 +192,31 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
const geometry = new THREE.LatheBufferGeometry(points);
|
|
|
return geometry;
|
|
|
},
|
|
|
+ create2(segments = 12, phiStart = Math.PI * 0.25, phiLength = Math.PI * 1.5) {
|
|
|
+ const points = [];
|
|
|
+ for (let i = 0; i < 10; ++i) {
|
|
|
+ points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * .8));
|
|
|
+ }
|
|
|
+ return new THREE.LatheBufferGeometry(points, segments, phiStart, phiLength);
|
|
|
+ },
|
|
|
},
|
|
|
OctahedronBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const geometry = new THREE.OctahedronBufferGeometry(radius);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ detail: { type: 'range', min: 1, max: 5, precision: 0, },
|
|
|
+ },
|
|
|
+ create(radius = 7) {
|
|
|
+ return new THREE.OctahedronBufferGeometry(radius);
|
|
|
+ },
|
|
|
+ create2(radius = 7, detail = 2) {
|
|
|
+ return new THREE.OctahedronBufferGeometry(radius, detail);
|
|
|
},
|
|
|
},
|
|
|
ParametricBufferGeometry: {
|
|
|
+ ui: {
|
|
|
+ stacks: { type: 'range', min: 1, max: 50, },
|
|
|
+ slices: { type: 'range', min: 1, max: 50, },
|
|
|
+ },
|
|
|
/*
|
|
|
from: https://github.com/mrdoob/three.js/blob/b8d8a8625465bd634aa68e5846354d69f34d2ff5/examples/js/ParametricGeometries.js
|
|
|
|
|
@@ -134,7 +243,7 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
THE SOFTWARE.
|
|
|
|
|
|
*/
|
|
|
- create() {
|
|
|
+ create(slices = 25, stacks = 25) {
|
|
|
// from: https://github.com/mrdoob/three.js/blob/b8d8a8625465bd634aa68e5846354d69f34d2ff5/examples/js/ParametricGeometries.js
|
|
|
function klein(v, u, target) {
|
|
|
u *= Math.PI;
|
|
@@ -157,24 +266,31 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
target.set(x, y, z).multiplyScalar(0.75);
|
|
|
}
|
|
|
|
|
|
- const slices = 25;
|
|
|
- const stacks = 25;
|
|
|
- const geometry = new THREE.ParametricBufferGeometry(klein, slices, stacks);
|
|
|
- return geometry;
|
|
|
+ return new THREE.ParametricBufferGeometry(klein, slices, stacks);
|
|
|
},
|
|
|
},
|
|
|
PlaneBufferGeometry: {
|
|
|
- create() {
|
|
|
- const width = 9;
|
|
|
- const height = 9;
|
|
|
- const widthSegments = 2;
|
|
|
- const heightSegments = 2;
|
|
|
- const geometry = new THREE.PlaneBufferGeometry(width, height, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ width: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ height: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ widthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ },
|
|
|
+ create(width = 9, height = 9) {
|
|
|
+ return new THREE.PlaneBufferGeometry(width, height);
|
|
|
+ },
|
|
|
+ create2(width = 9, height = 9, widthSegments = 2, heightSegments = 2) {
|
|
|
+ return new THREE.PlaneBufferGeometry(
|
|
|
+ width, height,
|
|
|
+ widthSegments, heightSegments);
|
|
|
},
|
|
|
},
|
|
|
PolyhedronBufferGeometry: {
|
|
|
- create() {
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ detail: { type: 'range', min: 1, max: 5, precision: 0, },
|
|
|
+ },
|
|
|
+ create(radius = 7, detail = 2) {
|
|
|
const verticesOfCube = [
|
|
|
-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
|
|
|
-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1,
|
|
@@ -187,22 +303,33 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
2, 3, 7, 7, 6, 2,
|
|
|
4, 5, 6, 6, 7, 4,
|
|
|
];
|
|
|
- const radius = 7;
|
|
|
- const detail = 2;
|
|
|
- const geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, radius, detail);
|
|
|
- return geometry;
|
|
|
+ return new THREE.PolyhedronBufferGeometry(
|
|
|
+ verticesOfCube, indicesOfFaces, radius, detail);
|
|
|
},
|
|
|
},
|
|
|
RingBufferGeometry: {
|
|
|
- create() {
|
|
|
- const innerRadius = 2;
|
|
|
- const outerRadius = 7;
|
|
|
- const segments = 18;
|
|
|
- const geometry = new THREE.RingBufferGeometry(innerRadius, outerRadius, segments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ innerRadius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ outerRadius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ thetaSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ phiSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ },
|
|
|
+ create(innerRadius = 2, outerRadius = 7, thetaSegments = 18) {
|
|
|
+ return new THREE.RingBufferGeometry(innerRadius, outerRadius, thetaSegments);
|
|
|
+ },
|
|
|
+ create2(innerRadius = 2, outerRadius = 7, thetaSegments = 18, phiSegments = 2, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5) {
|
|
|
+ return new THREE.RingBufferGeometry(
|
|
|
+ innerRadius, outerRadius,
|
|
|
+ thetaSegments, phiSegments,
|
|
|
+ thetaStart, thetaLength);
|
|
|
},
|
|
|
},
|
|
|
ShapeBufferGeometry: {
|
|
|
+ ui: {
|
|
|
+ curveSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ },
|
|
|
create() {
|
|
|
const shape = new THREE.Shape();
|
|
|
const x = -2.5;
|
|
@@ -217,25 +344,68 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
const geometry = new THREE.ShapeBufferGeometry(shape);
|
|
|
return geometry;
|
|
|
},
|
|
|
+ create2(curveSegments = 5) {
|
|
|
+ const shape = new THREE.Shape();
|
|
|
+ const x = -2.5;
|
|
|
+ const y = -5;
|
|
|
+ shape.moveTo(x + 2.5, y + 2.5);
|
|
|
+ shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
|
|
|
+ shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
|
|
|
+ shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
|
|
|
+ shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
|
|
|
+ shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
|
|
|
+ shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
|
|
|
+ return new THREE.ShapeBufferGeometry(shape, curveSegments);
|
|
|
+ },
|
|
|
},
|
|
|
SphereBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 12;
|
|
|
- const heightSegments = 8;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ widthSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ phiStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ phiLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
|
|
|
+ thetaStart: { type: 'range', min: 0, max: 1, mult: Math.PI },
|
|
|
+ thetaLength: { type: 'range', min: 0, max: 1, mult: Math.PI },
|
|
|
+ },
|
|
|
+ create(radius = 7, widthSegments = 12, heightSegments = 8) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
+ },
|
|
|
+ create2(radius = 7, widthSegments = 12, heightSegments = 8, phiStart = Math.PI * 0.25, phiLength = Math.PI * 1.5, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 0.5) {
|
|
|
+ return new THREE.SphereBufferGeometry(
|
|
|
+ radius,
|
|
|
+ widthSegments, heightSegments,
|
|
|
+ phiStart, phiLength,
|
|
|
+ thetaStart, thetaLength);
|
|
|
},
|
|
|
},
|
|
|
TetrahedronBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const geometry = new THREE.TetrahedronBufferGeometry(radius);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ detail: { type: 'range', min: 1, max: 5, precision: 0, },
|
|
|
+ },
|
|
|
+ create(radius = 7) {
|
|
|
+ return new THREE.TetrahedronBufferGeometry(radius);
|
|
|
+ },
|
|
|
+ create2(radius = 7, detail = 2) {
|
|
|
+ return new THREE.TetrahedronBufferGeometry(radius, detail);
|
|
|
},
|
|
|
},
|
|
|
TextBufferGeometry: {
|
|
|
- async create() {
|
|
|
+ ui: {
|
|
|
+ text: { type: 'text', maxLength: 30, },
|
|
|
+ size: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ height: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ curveSegments: { type: 'range', min: 1, max: 20, },
|
|
|
+ // font', fonts ).onChange( generateGeometry );
|
|
|
+ // weight', weights ).onChange( generateGeometry );
|
|
|
+ bevelEnabled: { type: 'bool', },
|
|
|
+ bevelThickness: { type: 'range', min: 0.1, max: 3, },
|
|
|
+ bevelSize: { type: 'range', min: 0.1, max:3, },
|
|
|
+ bevelSegments: { type: 'range', min: 0, max: 8, },
|
|
|
+ },
|
|
|
+ addConstCode: false,
|
|
|
+ async create(text = 'three.js', size = 3, height = 0.2, curveSegments = 12, bevelEnabled = true, bevelThickness = 0.15, bevelSize = 0.3, bevelSegments = 5) {
|
|
|
const loader = new THREE.FontLoader();
|
|
|
// promisify font loading
|
|
|
function loadFont(url) {
|
|
@@ -243,44 +413,72 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
loader.load(url, resolve, undefined, reject);
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
const font = await loadFont('/threejs/resources/threejs/fonts/helvetiker_regular.typeface.json');
|
|
|
- return new THREE.TextBufferGeometry('three.js', {
|
|
|
+ return new THREE.TextBufferGeometry(text, {
|
|
|
font: font,
|
|
|
- size: 3.0,
|
|
|
- height: .2,
|
|
|
- curveSegments: 12,
|
|
|
- bevelEnabled: true,
|
|
|
- bevelThickness: 0.15,
|
|
|
- bevelSize: .3,
|
|
|
- bevelSegments: 5,
|
|
|
+ size,
|
|
|
+ height,
|
|
|
+ curveSegments,
|
|
|
+ bevelEnabled,
|
|
|
+ bevelThickness,
|
|
|
+ bevelSize,
|
|
|
+ bevelSegments,
|
|
|
});
|
|
|
},
|
|
|
+ src: `
|
|
|
+const loader = new THREE.FontLoader();
|
|
|
+
|
|
|
+loader.load('../resources/threejs/fonts/helvetiker_regular.typeface.json', (font) => {
|
|
|
+ const text = 'three.js'; // ui: text
|
|
|
+ const geometry = new THREE.TextBufferGeometry(text, {
|
|
|
+ font: font,
|
|
|
+ size: 3, // ui: size
|
|
|
+ height: 0.2, // ui: height
|
|
|
+ curveSegments: 12, // ui: curveSegments
|
|
|
+ bevelEnabled: true, // ui: bevelEnabled
|
|
|
+ bevelThickness: 0.15, // ui: bevelThickness
|
|
|
+ bevelSize: 0.3, // ui: bevelSize
|
|
|
+ bevelSegments: 5, // ui: bevelSegments
|
|
|
+ });
|
|
|
+ ...
|
|
|
+});
|
|
|
+ `,
|
|
|
},
|
|
|
TorusBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 5;
|
|
|
- const tubeRadius = 2;
|
|
|
- const radialSegments = 8;
|
|
|
- const tubularSegments = 24;
|
|
|
- const geometry = new THREE.TorusBufferGeometry(radius, tubeRadius, radialSegments, tubularSegments);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ tubeRadius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ radialSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ tubularSegments: { type: 'range', min: 1, max: 100, },
|
|
|
+ },
|
|
|
+ create(radius = 5, tubeRadius = 2, radialSegments = 8, tubularSegments = 24) {
|
|
|
+ return new THREE.TorusBufferGeometry(
|
|
|
+ radius, tubeRadius,
|
|
|
+ radialSegments, tubularSegments);
|
|
|
},
|
|
|
},
|
|
|
TorusKnotBufferGeometry: {
|
|
|
- create() {
|
|
|
- const radius = 3.5;
|
|
|
- const tube = 1.5;
|
|
|
- const radialSegments = 8;
|
|
|
- const tubularSegments = 64;
|
|
|
- const p = 2;
|
|
|
- const q = 3;
|
|
|
- const geometry = new THREE.TorusKnotBufferGeometry(radius, tube, tubularSegments, radialSegments, p, q);
|
|
|
- return geometry;
|
|
|
+ ui: {
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ tubeRadius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ radialSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ tubularSegments: { type: 'range', min: 1, max: 100, },
|
|
|
+ p: { type: 'range', min: 1, max: 20, },
|
|
|
+ q: { type: 'range', min: 1, max: 20, },
|
|
|
+ },
|
|
|
+ create(radius = 3.5, tubeRadius = 1.5, radialSegments = 8, tubularSegments = 64, p = 2, q = 3) {
|
|
|
+ return new THREE.TorusKnotBufferGeometry(
|
|
|
+ radius, tubeRadius, tubularSegments, radialSegments, p, q);
|
|
|
},
|
|
|
},
|
|
|
TubeBufferGeometry: {
|
|
|
- create() {
|
|
|
+ ui: {
|
|
|
+ tubularSegments: { type: 'range', min: 1, max: 100, },
|
|
|
+ radius: { type: 'range', min: 1, max: 10, precision: 1, },
|
|
|
+ radialSegments: { type: 'range', min: 1, max: 30, },
|
|
|
+ closed: { type: 'bool', },
|
|
|
+ },
|
|
|
+ create(tubularSegments = 20, radius = 1, radialSegments = 8, closed = false) {
|
|
|
class CustomSinCurve extends THREE.Curve {
|
|
|
constructor(scale) {
|
|
|
super();
|
|
@@ -295,36 +493,55 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
}
|
|
|
|
|
|
const path = new CustomSinCurve(4);
|
|
|
- const tubularSegments = 20;
|
|
|
- const radius = 1;
|
|
|
- const radialSegments = 8;
|
|
|
- const closed = false;
|
|
|
- const geometry = new THREE.TubeBufferGeometry(path, tubularSegments, radius, radialSegments, closed);
|
|
|
- return geometry;
|
|
|
+ return new THREE.TubeBufferGeometry(
|
|
|
+ path, tubularSegments, radius, radialSegments, closed);
|
|
|
},
|
|
|
},
|
|
|
EdgesGeometry: {
|
|
|
- create() {
|
|
|
- const width = 8;
|
|
|
- const height = 8;
|
|
|
- const depth = 8;
|
|
|
- const thresholdAngle = 15;
|
|
|
- const geometry = new THREE.EdgesGeometry(
|
|
|
- new THREE.BoxBufferGeometry(width, height, depth),
|
|
|
- thresholdAngle);
|
|
|
- return { lineGeometry: geometry };
|
|
|
+ ui: {
|
|
|
+ widthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ depthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ },
|
|
|
+ create(widthSegments = 2, heightSegments = 2, depthSegments = 2) {
|
|
|
+ const size = 8;
|
|
|
+ return {
|
|
|
+ lineGeometry: new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(
|
|
|
+ size, size, size,
|
|
|
+ widthSegments, heightSegments, depthSegments)),
|
|
|
+ };
|
|
|
},
|
|
|
nonBuffer: false,
|
|
|
+ src: `
|
|
|
+const size = 8;
|
|
|
+const geometry = new THREE.EdgesGeometry(
|
|
|
+ new THREE.BoxBufferGeometry(
|
|
|
+ size, size, size,
|
|
|
+ widthSegments, heightSegments, depthSegments));
|
|
|
+`,
|
|
|
},
|
|
|
WireframeGeometry: {
|
|
|
- create() {
|
|
|
- const width = 8;
|
|
|
- const height = 8;
|
|
|
- const depth = 8;
|
|
|
- const geometry = new THREE.WireframeGeometry(new THREE.BoxBufferGeometry(width, height, depth));
|
|
|
- return { lineGeometry: geometry };
|
|
|
+ ui: {
|
|
|
+ widthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ heightSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ depthSegments: { type: 'range', min: 1, max: 10, },
|
|
|
+ },
|
|
|
+ create(widthSegments = 2, heightSegments = 2, depthSegments = 2) {
|
|
|
+ const size = 8;
|
|
|
+ return {
|
|
|
+ lineGeometry: new THREE.WireframeGeometry(new THREE.BoxBufferGeometry(
|
|
|
+ size, size, size,
|
|
|
+ widthSegments, heightSegments, depthSegments)),
|
|
|
+ };
|
|
|
},
|
|
|
nonBuffer: false,
|
|
|
+ src: `
|
|
|
+const size = 8;
|
|
|
+const geometry = new THREE.WireframeGeometry(
|
|
|
+ new THREE.BoxBufferGeometry(
|
|
|
+ size, size, size,
|
|
|
+ widthSegments, heightSegments, depthSegments));
|
|
|
+`,
|
|
|
},
|
|
|
Points: {
|
|
|
create() {
|
|
@@ -362,83 +579,49 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
},
|
|
|
},
|
|
|
SphereBufferGeometryLow: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 5;
|
|
|
- const heightSegments = 3;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(radius = 7, widthSegments = 5, heightSegments = 3) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
},
|
|
|
},
|
|
|
SphereBufferGeometryMedium: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 24;
|
|
|
- const heightSegments = 10;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(radius = 7, widthSegments = 24, heightSegments = 10) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
},
|
|
|
},
|
|
|
SphereBufferGeometryHigh: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 50;
|
|
|
- const heightSegments = 50;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(radius = 7, widthSegments = 50, heightSegments = 50) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
},
|
|
|
},
|
|
|
SphereBufferGeometryLowSmooth: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 5;
|
|
|
- const heightSegments = 3;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(radius = 7, widthSegments = 5, heightSegments = 3) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
},
|
|
|
showLines: false,
|
|
|
flatShading: false,
|
|
|
},
|
|
|
SphereBufferGeometryMediumSmooth: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 24;
|
|
|
- const heightSegments = 10;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(radius = 7, widthSegments = 24, heightSegments = 10) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
},
|
|
|
showLines: false,
|
|
|
flatShading: false,
|
|
|
},
|
|
|
SphereBufferGeometryHighSmooth: {
|
|
|
- create() {
|
|
|
- const radius = 7;
|
|
|
- const widthSegments = 50;
|
|
|
- const heightSegments = 50;
|
|
|
- const geometry = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(radius = 7, widthSegments = 50, heightSegments = 50) {
|
|
|
+ return new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments);
|
|
|
},
|
|
|
showLines: false,
|
|
|
flatShading: false,
|
|
|
},
|
|
|
PlaneBufferGeometryLow: {
|
|
|
- create() {
|
|
|
- const width = 9;
|
|
|
- const height = 9;
|
|
|
- const widthSegments = 1;
|
|
|
- const heightSegments = 1;
|
|
|
- const geometry = new THREE.PlaneBufferGeometry(width, height, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(width = 9, height = 9, widthSegments = 1, heightSegments = 1) {
|
|
|
+ return new THREE.PlaneBufferGeometry(width, height, widthSegments, heightSegments);
|
|
|
},
|
|
|
},
|
|
|
PlaneBufferGeometryHigh: {
|
|
|
- create() {
|
|
|
- const width = 9;
|
|
|
- const height = 9;
|
|
|
- const widthSegments = 10;
|
|
|
- const heightSegments = 10;
|
|
|
- const geometry = new THREE.PlaneBufferGeometry(width, height, widthSegments, heightSegments);
|
|
|
- return geometry;
|
|
|
+ create(width = 9, height = 9, widthSegments = 10, heightSegments = 10) {
|
|
|
+ return new THREE.PlaneBufferGeometry(width, height, widthSegments, heightSegments);
|
|
|
},
|
|
|
},
|
|
|
};
|
|
@@ -467,8 +650,9 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
return addElem(parent, 'div', className);
|
|
|
}
|
|
|
|
|
|
- [...document.querySelectorAll('[data-diagram]')].forEach(createDiagram);
|
|
|
- [...document.querySelectorAll('[data-primitive]')].forEach(createPrimitiveDOM);
|
|
|
+ const primitives = {};
|
|
|
+ document.querySelectorAll('[data-diagram]').forEach(createDiagram);
|
|
|
+ document.querySelectorAll('[data-primitive]').forEach(createPrimitiveDOM);
|
|
|
|
|
|
function createPrimitiveDOM(base) {
|
|
|
const name = base.dataset.primitive;
|
|
@@ -491,18 +675,49 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
}
|
|
|
addDiv(right, '.note').innerHTML = text;
|
|
|
|
|
|
- const rawLines = info.create.toString()
|
|
|
- .replace(/ +return geometry;\n/, '')
|
|
|
- .replace(/ +return { lineGeometry: geometry };\n/, '')
|
|
|
- .split(/\n/);
|
|
|
- const createRE = /^( *)[^ ]/;
|
|
|
- const m = createRE.exec(rawLines[1]);
|
|
|
- const prefixLen = m[1].length;
|
|
|
- const trimmedLines = rawLines.slice(1, rawLines.length - 1).map(line => line.substring(prefixLen));
|
|
|
+ // I get that this is super brittle. I think I'd have to
|
|
|
+ // work through a bunch more examples to come up with a better
|
|
|
+ // structure. Also, I don't want to generate actual code and
|
|
|
+ // use eval. (maybe a bad striction)
|
|
|
+
|
|
|
+ function makeExample(elem, createFn, src) {
|
|
|
+ const rawLines = createFn.toString().replace(/return (new THREE\.[a-zA-Z]+Geometry)/, 'const geometry = $1').split(/\n/);
|
|
|
+ const createRE = /^ *(?:function *)*create\d*\((.*?)\)/;
|
|
|
+ const indentRE = /^( *)[^ ]/;
|
|
|
+ const m = indentRE.exec(rawLines[1]);
|
|
|
+ const prefixLen = m[1].length;
|
|
|
+ const m2 = createRE.exec(rawLines[0]);
|
|
|
+ const argString = m2[1].trim();
|
|
|
+ const trimmedLines = src
|
|
|
+ ? src.split('\n').slice(1, -1)
|
|
|
+ : rawLines.slice(1, rawLines.length - 1).map(line => line.substring(prefixLen));
|
|
|
+ if (info.addConstCode !== false && argString) {
|
|
|
+ const lines = argString.split(',').map((arg) => {
|
|
|
+ return `const ${arg.trim()}; // ui: ${arg.trim().split(' ')[0]}`;
|
|
|
+ });
|
|
|
+ const lineNdx = trimmedLines.findIndex(l => l.indexOf('const geometry') >= 0);
|
|
|
+ trimmedLines.splice(lineNdx < 0 ? 0 : lineNdx, 0, ...lines);
|
|
|
+ }
|
|
|
|
|
|
- addElem(base, 'pre', 'prettyprint showmods', trimmedLines.join('\n'));
|
|
|
+ addElem(base, 'pre', 'prettyprint showmods', trimmedLines.join('\n'));
|
|
|
|
|
|
- return createLiveImage(elem, info);
|
|
|
+ createLiveImage(elem, Object.assign({}, info, {create: createFn}), name);
|
|
|
+ }
|
|
|
+
|
|
|
+ makeExample(elem, info.create, info.src);
|
|
|
+
|
|
|
+ {
|
|
|
+ let i = 2;
|
|
|
+ for (;;) {
|
|
|
+ const createFn = info[`create${i}`];
|
|
|
+ if (!createFn) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ const shapeElem = addDiv(base, 'shape');
|
|
|
+ makeExample(shapeElem, createFn, info[`src${i}`]);
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function createDiagram(base) {
|
|
@@ -511,13 +726,25 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
if (!info) {
|
|
|
throw new Error(`no primitive ${name}`);
|
|
|
}
|
|
|
- return createLiveImage(base, info);
|
|
|
+ return createLiveImage(base, info, name);
|
|
|
}
|
|
|
|
|
|
- function createLiveImage(elem, info) {
|
|
|
- const geometry = info.create();
|
|
|
+ const whiteLineMaterial = new THREE.LineBasicMaterial({
|
|
|
+ color: 0xffffff,
|
|
|
+ transparent: true,
|
|
|
+ opacity: 0.5,
|
|
|
+ });
|
|
|
+ const blackLineMaterial = new THREE.LineBasicMaterial({
|
|
|
+ color: 0x000000,
|
|
|
+ transparent: true,
|
|
|
+ opacity: 0.5,
|
|
|
+ });
|
|
|
+
|
|
|
+ function addGeometry(root, info, args = []) {
|
|
|
+ const geometry = info.create(...args);
|
|
|
const promise = (geometry instanceof Promise) ? geometry : Promise.resolve(geometry);
|
|
|
- promise.then((geometryInfo) => {
|
|
|
+
|
|
|
+ return promise.then((geometryInfo) => {
|
|
|
if (geometryInfo instanceof THREE.BufferGeometry ||
|
|
|
geometryInfo instanceof THREE.Geometry) {
|
|
|
const geometry = geometryInfo;
|
|
@@ -526,41 +753,148 @@ import {threejsLessonUtils} from './threejs-lesson-utils.js';
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- const root = new THREE.Object3D();
|
|
|
-
|
|
|
- if (geometry.mesh) {
|
|
|
- root.add(geometry.mesh);
|
|
|
- } else {
|
|
|
- const boxGeometry = geometryInfo.geometry || geometryInfo.lineGeometry;
|
|
|
- boxGeometry.computeBoundingBox();
|
|
|
- const centerOffset = new THREE.Vector3();
|
|
|
- boxGeometry.boundingBox.getCenter(centerOffset).multiplyScalar(-1);
|
|
|
+ const boxGeometry = geometryInfo.geometry || geometryInfo.lineGeometry;
|
|
|
+ boxGeometry.computeBoundingBox();
|
|
|
+ const centerOffset = new THREE.Vector3();
|
|
|
+ boxGeometry.boundingBox.getCenter(centerOffset).multiplyScalar(-1);
|
|
|
|
|
|
- if (geometryInfo.geometry) {
|
|
|
+ if (geometryInfo.geometry) {
|
|
|
+ if (!info.material) {
|
|
|
const material = new THREE.MeshPhongMaterial({
|
|
|
flatShading: info.flatShading === false ? false : true,
|
|
|
side: THREE.DoubleSide,
|
|
|
});
|
|
|
material.color.setHSL(Math.random(), .5, .5);
|
|
|
- const mesh = new THREE.Mesh(geometryInfo.geometry, material);
|
|
|
- mesh.position.copy(centerOffset);
|
|
|
- root.add(mesh);
|
|
|
- }
|
|
|
-
|
|
|
- if (info.showLines !== false) {
|
|
|
- const lineMesh = new THREE.LineSegments(
|
|
|
- geometryInfo.lineGeometry || geometryInfo.geometry,
|
|
|
- new THREE.LineBasicMaterial({
|
|
|
- color: geometryInfo.geometry ? 0xffffff : colors.lines,
|
|
|
- transparent: true,
|
|
|
- opacity: 0.5,
|
|
|
- }));
|
|
|
- lineMesh.position.copy(centerOffset);
|
|
|
- root.add(lineMesh);
|
|
|
+ info.material = material;
|
|
|
}
|
|
|
+ const mesh = new THREE.Mesh(geometryInfo.geometry, info.material);
|
|
|
+ mesh.position.copy(centerOffset);
|
|
|
+ root.add(mesh);
|
|
|
}
|
|
|
+ if (info.showLines !== false) {
|
|
|
+ const lineMesh = new THREE.LineSegments(
|
|
|
+ geometryInfo.lineGeometry || geometryInfo.geometry,
|
|
|
+ geometryInfo.geometry ? whiteLineMaterial : blackLineMaterial);
|
|
|
+ lineMesh.position.copy(centerOffset);
|
|
|
+ root.add(lineMesh);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateGeometry(root, info, params) {
|
|
|
+ const oldChildren = root.children.slice();
|
|
|
+ addGeometry(root, info, Object.values(params)).then(() => {
|
|
|
+ oldChildren.forEach((child) => {
|
|
|
+ root.remove(child);
|
|
|
+ child.geometry.dispose();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function createLiveImage(elem, info, name) {
|
|
|
+ const root = new THREE.Object3D();
|
|
|
+
|
|
|
+ primitives[name] = primitives[name] || [];
|
|
|
+ primitives[name].push({
|
|
|
+ root,
|
|
|
+ info,
|
|
|
+ });
|
|
|
|
|
|
+ addGeometry(root, info).then(() => {
|
|
|
threejsLessonUtils.addDiagram(elem, {create: () => root});
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+ function getValueElem(commentElem) {
|
|
|
+ return commentElem.previousElementSibling &&
|
|
|
+ commentElem.previousElementSibling.previousElementSibling &&
|
|
|
+ commentElem.previousElementSibling.previousElementSibling.previousElementSibling;
|
|
|
+ }
|
|
|
+
|
|
|
+ threejsLessonUtils.onAfterPrettify(() => {
|
|
|
+ document.querySelectorAll('[data-primitive]').forEach((base) => {
|
|
|
+ const primitiveName = base.dataset.primitive;
|
|
|
+ const infos = primitives[primitiveName];
|
|
|
+ base.querySelectorAll('pre.prettyprint').forEach((shape, ndx) => {
|
|
|
+ const {root, info} = infos[ndx];
|
|
|
+ const params = {};
|
|
|
+ [...shape.querySelectorAll('span.com')]
|
|
|
+ .filter(span => span.textContent.indexOf('// ui:') >= 0)
|
|
|
+ .forEach((span) => {
|
|
|
+ const nameRE = /ui: ([a-zA-Z0-9_]+) *$/;
|
|
|
+ const name = nameRE.exec(span.textContent)[1];
|
|
|
+ span.textContent = '';
|
|
|
+ if (!info.ui) {
|
|
|
+ console.error(`no ui for ${primitiveName}:${ndx}`); // eslint-disable-line
|
|
|
+ return;
|
|
|
+ // throw new Error(`no ui for ${primitiveName}:${ndx}`);
|
|
|
+ }
|
|
|
+ const ui = info.ui[name];
|
|
|
+ if (!ui) {
|
|
|
+ throw new Error(`no ui for ${primitiveName}:${ndx} param: ${name}`);
|
|
|
+ }
|
|
|
+ const valueElem = getValueElem(span);
|
|
|
+ if (!valueElem) {
|
|
|
+ console.error(`no value element for ${primitiveName}:${ndx} param: ${name}`); // eslint-disable-line
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ switch (ui.type) {
|
|
|
+ case 'range': {
|
|
|
+ const valueRange = ui.max - ui.min;
|
|
|
+ const input = document.createElement('input');
|
|
|
+ const inputMax = 100;
|
|
|
+ input.type = 'range';
|
|
|
+ input.min = 0;
|
|
|
+ input.max = inputMax;
|
|
|
+ const value = parseFloat(valueElem.textContent);
|
|
|
+ params[name] = value * (ui.mult || 1);
|
|
|
+ input.value = (value - ui.min) / valueRange * inputMax;
|
|
|
+ span.appendChild(input);
|
|
|
+ const precision = ui.precision === undefined ? (valueRange > 4 ? 0 : 2) : ui.precision;
|
|
|
+ const padding = ui.max.toFixed(precision).length;
|
|
|
+ input.addEventListener('input', () => {
|
|
|
+ let newValue = input.value * valueRange / inputMax + ui.min;
|
|
|
+ if (precision === 0) {
|
|
|
+ newValue = Math.round(newValue);
|
|
|
+ }
|
|
|
+ params[name] = newValue * (ui.mult || 1);
|
|
|
+ valueElem.textContent = newValue.toFixed(precision).padStart(padding, ' ');
|
|
|
+ updateGeometry(root, info, params);
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'bool': {
|
|
|
+ const input = document.createElement('input');
|
|
|
+ input.type = 'checkbox';
|
|
|
+ params[name] = valueElem.textContent === 'true';
|
|
|
+ input.checked = params[name];
|
|
|
+ span.appendChild(input);
|
|
|
+ input.addEventListener('change', () => {
|
|
|
+ params[name] = input.checked;
|
|
|
+ valueElem.textContent = params[name] ? 'true' : 'false';
|
|
|
+ updateGeometry(root, info, params);
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 'text': {
|
|
|
+ const input = document.createElement('input');
|
|
|
+ input.type = 'text';
|
|
|
+ params[name] = valueElem.textContent.slice(1, -1);
|
|
|
+ input.value = params[name];
|
|
|
+ input.maxlength = ui.maxLength || 50;
|
|
|
+ span.appendChild(input);
|
|
|
+ input.addEventListener('input', () => {
|
|
|
+ params[name] = input.value;
|
|
|
+ valueElem.textContent = `'${input.value.replace(/'/g, '\'')}'`;
|
|
|
+ updateGeometry(root, info, params);
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ throw new Error(`unknonw type for ${primitiveName}:${ndx} param: ${name}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
}
|