Browse Source

add sliders to primitives

Gregg Tavares 5 years ago
parent
commit
3714df0b23

+ 7 - 1
threejs/lessons/resources/lesson.css

@@ -39,6 +39,7 @@ pre>code {
 pre.prettyprint {
     margin-top: 2em !important;
     margin-bottom: 2em !important;
+    position: relative;
 }
 pre.prettyprint li {
     white-space: pre;
@@ -565,5 +566,10 @@ pre.prettyprint.lighttheme .fun { color: #900; }  /* function name */
   .lesson-comment-notes {
     background: #222;
   }
+  input[type=text] {
+    background: #444;
+    color: white;
+    border: none;
+    padding: 3px;
+  }
 }
-

+ 1 - 1
threejs/lessons/resources/threejs-lesson-utils.js

@@ -217,4 +217,4 @@ export const threejsLessonUtils = {
   },
 };
 
-
+window.threejsLessonUtils = threejsLessonUtils;

+ 38 - 0
threejs/lessons/resources/threejs-primitives.css

@@ -21,12 +21,50 @@ div[data-primitive] .desc {
 div[data-primitive] .desc code {
   white-space: normal;
 }
+
+div[data-primitive] .input {
+  display: inline-block;
+  user-select: none;
+}
+div[data-primitive] .input::before {
+  content:" ";
+}
+div[data-primitive] .input input[type=range] {
+  width: 200px;
+}
+div[data-primitive] .input>div {
+  position: absolute;
+  display: inline-block;
+}
+div[data-primitive] .input input {
+  position: absolute;
+}
+
+@media (max-width: 600px) {
+  div[data-primitive] .input>div {
+    right: 0;
+  }
+  div[data-primitive] .input input {
+      opacity: 0.2;
+      right: 1em;
+  }
+}
+
 @media (max-width: 550px) {
   div[data-primitive] .shape {
       width: 120px;
       height: 120px;
   }
 }
+@media (max-width: 450px) {
+  div[data-primitive] .pair {
+    flex-direction: column-reverse;
+  }
+  div[data-primitive] .shape {
+    margin: 0 auto;
+    width: 100%;
+  }
+}
 div[data-primitive] .desc {
   flex: 1 1 auto;
 }

+ 113 - 99
threejs/lessons/resources/threejs-primitives.js

@@ -189,15 +189,15 @@ const geometry = THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
         for (let i = 0; i < 10; ++i) {
           points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * .8));
         }
-        const geometry = new THREE.LatheBufferGeometry(points);
-        return geometry;
+        return new THREE.LatheBufferGeometry(points);
       },
       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);
+        return new THREE.LatheBufferGeometry(
+            points, segments, phiStart, phiLength);
       },
     },
     OctahedronBufferGeometry: {
@@ -266,7 +266,8 @@ const geometry = THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
           target.set(x, y, z).multiplyScalar(0.75);
         }
 
-        return new THREE.ParametricBufferGeometry(klein, slices, stacks);
+        return new THREE.ParametricBufferGeometry(
+            klein, slices, stacks);
       },
     },
     PlaneBufferGeometry: {
@@ -317,7 +318,8 @@ const geometry = THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
         thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
       },
       create(innerRadius = 2, outerRadius = 7, thetaSegments = 18) {
-        return new THREE.RingBufferGeometry(innerRadius, outerRadius, thetaSegments);
+        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(
@@ -341,8 +343,7 @@ const geometry = THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
         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 geometry = new THREE.ShapeBufferGeometry(shape);
-        return geometry;
+        return new THREE.ShapeBufferGeometry(shape);
       },
       create2(curveSegments = 5) {
         const shape = new THREE.Shape();
@@ -405,24 +406,20 @@ const geometry = THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
         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) {
-          return new Promise((resolve, reject) => {
-            loader.load(url, resolve, undefined, reject);
+      create(text = 'three.js', size = 3, height = 0.2, curveSegments = 12, bevelEnabled = true, bevelThickness = 0.15, bevelSize = 0.3, bevelSegments = 5) {
+        return new Promise((resolve) => {
+          fontPromise.then((font) => {
+            resolve(new THREE.TextBufferGeometry(text, {
+              font: font,
+              size,
+              height,
+              curveSegments,
+              bevelEnabled,
+              bevelThickness,
+              bevelSize,
+              bevelSegments,
+            }));
           });
-        }
-        const font = await loadFont('/threejs/resources/threejs/fonts/helvetiker_regular.typeface.json');
-        return new THREE.TextBufferGeometry(text, {
-          font: font,
-          size,
-          height,
-          curveSegments,
-          bevelEnabled,
-          bevelThickness,
-          bevelSize,
-          bevelSegments,
         });
       },
       src: `
@@ -499,25 +496,40 @@ loader.load('../resources/threejs/fonts/helvetiker_regular.typeface.json', (font
     },
     EdgesGeometry: {
       ui: {
-        widthSegments: { type: 'range', min: 1, max: 10, },
-        heightSegments: { type: 'range', min: 1, max: 10, },
-        depthSegments: { type: 'range', min: 1, max: 10, },
+        thresholdAngle: { type: 'range', min: 1, max: 180, },
       },
-      create(widthSegments = 2, heightSegments = 2, depthSegments = 2) {
-        const size = 8;
+      create() {
         return {
-          lineGeometry: new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(
-            size, size, size,
-            widthSegments, heightSegments, depthSegments)),
+          lineGeometry: new THREE.EdgesGeometry(
+            new THREE.BoxBufferGeometry(8, 8, 8)),
+        };
+      },
+      create2(thresholdAngle = 1) {
+        return {
+          lineGeometry: new THREE.EdgesGeometry(
+            new THREE.SphereBufferGeometry(7, 6, 3), thresholdAngle),
         };
       },
       nonBuffer: false,
+      addConstCode: false,
       src: `
 const size = 8;
-const geometry = new THREE.EdgesGeometry(
-    new THREE.BoxBufferGeometry(
-      size, size, size,
-      widthSegments, heightSegments, depthSegments));
+const widthSegments = 2;
+const heightSegments = 2;
+const depthSegments = 2;
+const boxGeometry = new THREE.BoxBufferGeometry(
+    size, size, size,
+    widthSegments, heightSegments, depthSegments);
+const geometry = new THREE.EdgesGeometry(boxGeometry);
+`,
+      src2: `
+const radius = 7;
+const widthSegments = 6;
+const heightSegments = 3;
+const sphereGeometry = new THREE.SphereBufferGeometry(
+    radius, widthSegments, heightSegments);
+const thresholdAngle = 1;  // ui: thresholdAngle
+const geometry = new THREE.EdgesGeometry(sphereGeometry, thresholdAngle);
 `,
     },
     WireframeGeometry: {
@@ -535,8 +547,12 @@ const geometry = new THREE.EdgesGeometry(
         };
       },
       nonBuffer: false,
+      addConstCode: false,
       src: `
 const size = 8;
+const widthSegments = 2;  // ui: widthSegments
+const heightSegments = 2;  // ui: heightSegments
+const depthSegments = 2;  // ui: depthSegments
 const geometry = new THREE.WireframeGeometry(
     new THREE.BoxBufferGeometry(
       size, size, size,
@@ -557,6 +573,7 @@ const geometry = new THREE.WireframeGeometry(
         return {
           showLines: false,
           mesh: points,
+          geometry,
         };
       },
     },
@@ -575,6 +592,7 @@ const geometry = new THREE.WireframeGeometry(
         return {
           showLines: false,
           mesh: points,
+          geometry,
         };
       },
     },
@@ -650,10 +668,6 @@ const geometry = new THREE.WireframeGeometry(
     return addElem(parent, 'div', className);
   }
 
-  const primitives = {};
-  document.querySelectorAll('[data-diagram]').forEach(createDiagram);
-  document.querySelectorAll('[data-primitive]').forEach(createPrimitiveDOM);
-
   function createPrimitiveDOM(base) {
     const name = base.dataset.primitive;
     const info = diagrams[name];
@@ -726,72 +740,65 @@ const geometry = new THREE.WireframeGeometry(
     if (!info) {
       throw new Error(`no primitive ${name}`);
     }
-    return createLiveImage(base, info, name);
+    createLiveImage(base, info, name);
   }
 
-  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 = []) {
+  async function addGeometry(root, info, args = []) {
     const geometry = info.create(...args);
     const promise = (geometry instanceof Promise) ? geometry : Promise.resolve(geometry);
 
-    return promise.then((geometryInfo) => {
-      if (geometryInfo instanceof THREE.BufferGeometry ||
-          geometryInfo instanceof THREE.Geometry) {
-        const geometry = geometryInfo;
-        geometryInfo = {
-          geometry,
-        };
-      }
+    let geometryInfo = await promise;
+    if (geometryInfo instanceof THREE.BufferGeometry ||
+        geometryInfo instanceof THREE.Geometry) {
+      const geometry = geometryInfo;
+      geometryInfo = {
+        geometry,
+      };
+    }
 
-      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 (!info.material) {
-          const material = new THREE.MeshPhongMaterial({
-            flatShading: info.flatShading === false ? false : true,
-            side: THREE.DoubleSide,
-          });
-          material.color.setHSL(Math.random(), .5, .5);
-          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);
+    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);
+        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,
+        new THREE.LineBasicMaterial({
+          color: geometryInfo.geometry ? 0xffffff : colors.lines,
+          transparent: true,
+          opacity: 0.5,
+        }));
+      lineMesh.position.copy(centerOffset);
+      root.add(lineMesh);
+    }
   }
 
-  function updateGeometry(root, info, params) {
+  async 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();
-      });
+    await addGeometry(root, info, Object.values(params));
+    oldChildren.forEach((child) => {
+      root.remove(child);
+      child.geometry.dispose();
     });
   }
 
-  function createLiveImage(elem, info, name) {
+  const primitives = {};
+
+  async function createLiveImage(elem, info, name) {
     const root = new THREE.Object3D();
 
     primitives[name] = primitives[name] || [];
@@ -800,9 +807,8 @@ const geometry = new THREE.WireframeGeometry(
       info,
     });
 
-    addGeometry(root, info).then(() => {
-      threejsLessonUtils.addDiagram(elem, {create: () => root});
-    });
+    await addGeometry(root, info);
+    threejsLessonUtils.addDiagram(elem, {create: () => root});
   }
 
   function getValueElem(commentElem) {
@@ -838,6 +844,11 @@ const geometry = new THREE.WireframeGeometry(
             console.error(`no value element for ${primitiveName}:${ndx} param: ${name}`);  // eslint-disable-line
             return;
           }
+          const inputHolderHolder = document.createElement('div');
+          inputHolderHolder.className = 'input';
+          const inputHolder = document.createElement('div');
+          span.appendChild(inputHolderHolder);
+          inputHolderHolder.appendChild(inputHolder);
           switch (ui.type) {
             case 'range': {
               const valueRange = ui.max - ui.min;
@@ -849,7 +860,7 @@ const geometry = new THREE.WireframeGeometry(
               const value = parseFloat(valueElem.textContent);
               params[name] = value * (ui.mult || 1);
               input.value = (value - ui.min) / valueRange * inputMax;
-              span.appendChild(input);
+              inputHolder.appendChild(input);
               const precision = ui.precision === undefined ? (valueRange > 4 ? 0 : 2) : ui.precision;
               const padding = ui.max.toFixed(precision).length;
               input.addEventListener('input', () => {
@@ -868,7 +879,7 @@ const geometry = new THREE.WireframeGeometry(
               input.type = 'checkbox';
               params[name] = valueElem.textContent === 'true';
               input.checked = params[name];
-              span.appendChild(input);
+              inputHolder.appendChild(input);
               input.addEventListener('change', () => {
                 params[name] = input.checked;
                 valueElem.textContent = params[name] ? 'true' : 'false';
@@ -882,7 +893,7 @@ const geometry = new THREE.WireframeGeometry(
               params[name] = valueElem.textContent.slice(1, -1);
               input.value = params[name];
               input.maxlength = ui.maxLength || 50;
-              span.appendChild(input);
+              inputHolder.appendChild(input);
               input.addEventListener('input', () => {
                 params[name] = input.value;
                 valueElem.textContent = `'${input.value.replace(/'/g, '\'')}'`;
@@ -891,10 +902,13 @@ const geometry = new THREE.WireframeGeometry(
               break;
             }
             default:
-              throw new Error(`unknonw type for ${primitiveName}:${ndx} param: ${name}`);
+              throw new Error(`unknown type for ${primitiveName}:${ndx} param: ${name}`);
           }
         });
       });
     });
   });
+
+  document.querySelectorAll('[data-diagram]').forEach(createDiagram);
+  document.querySelectorAll('[data-primitive]').forEach(createPrimitiveDOM);
 }

+ 2 - 2
threejs/lessons/threejs-primitives.md

@@ -42,7 +42,7 @@ for <code>TextBufferGeometry</code> and <code>TextGeometry</code> respectively.<
 <div data-primitive="TorusBufferGeometry">A torus (donut)</div>
 <div data-primitive="TorusKnotBufferGeometry">A torus knot</div>
 <div data-primitive="TubeBufferGeometry">A circle traced down a path</div>
-<div data-primitive="EdgesGeometry">A helper object that takes another geometry as input and generates edges only if the angle between faces is greater than some threshold. For example if you look at the box at the top it shows a line going through each face showing every triangle that makes the box. Using an <code>EdgesGeometry</code> instead the middle lines are removed.</div>
+<div data-primitive="EdgesGeometry">A helper object that takes another geometry as input and generates edges only if the angle between faces is greater than some threshold. For example if you look at the box at the top it shows a line going through each face showing every triangle that makes the box. Using an <code>EdgesGeometry</code> instead the middle lines are removed. Adjust the thresholdAngle below and you'll see the edges below that threshold disappear.</div>
 <div data-primitive="WireframeGeometry">Generates geometry that contains one line segment (2 points) per edge in the given geometry. Without this you'd often be missing edges or get extra edges since WebGL generally requires 2 points per line segment. For example if all you had was a single triangle there would only be 3 points. If you tried to draw it using a material with <code>wireframe: true</code> you would only get a single line. Passing that triangle geometry to a <code>WireframeGeometry</code> will generate a new geometry that has 3 lines segments using 6 points..</div>
 
 You might notice of most of them come in pairs of `Geometry`
@@ -75,7 +75,7 @@ can not have new vertices easily added. The number of vertices used is
 decided at creation time, storage is created, and then data for vertices
 are filled in. Whereas for `Geometry` you can add vertices as you go.
 
-We'll go over creating custom geometry in another article. For now
+We'll go over creating custom geometry in [another article](threejs-custom-geometry.html). For now
 let's make an example creating each type of primitive. We'll start
 with the [examples from the previous article](threejs-responsive.html).