Browse Source

add missing diagram

Gregg Tavares 6 years ago
parent
commit
a338becfb6

+ 0 - 0
threejs/resources/images/label-sorting-issue.png → threejs/lessons/resources/images/label-sorting-issue.png


+ 285 - 0
threejs/lessons/resources/threejs-align-html-elements-to-3d.js

@@ -0,0 +1,285 @@
+'use strict';
+
+/* global dat */
+{
+  function outlineText(ctx, msg, x, y) {
+    ctx.strokeText(msg, x, y);
+    ctx.fillText(msg, x, y);
+  }
+
+  function arrow(ctx, x1, y1, x2, y2, start, end, size) {
+    size = size || 1;
+    const dx = x1 - x2;
+    const dy = y1 - y2;
+    const rot = -Math.atan2(dx, dy);
+    const len = Math.sqrt(dx * dx + dy * dy);
+    ctx.save();
+    {
+      ctx.translate(x1, y1);
+      ctx.rotate(rot);
+      ctx.beginPath();
+      ctx.moveTo(0, 0);
+      ctx.lineTo(0, -(len - 10 * size));
+      ctx.stroke();
+    }
+    ctx.restore();
+    if (start) {
+      arrowHead(ctx, x1, y1, rot, size);
+    }
+    if (end) {
+      arrowHead(ctx, x2, y2, rot + Math.PI, size);
+    }
+  }
+
+  function arrowHead(ctx, x, y, rot, size) {
+    ctx.save();
+    {
+      ctx.translate(x, y);
+      ctx.rotate(rot);
+      ctx.scale(size, size);
+      ctx.translate(0, -10);
+      ctx.beginPath();
+      ctx.moveTo(0, 0);
+      ctx.lineTo(-5, -2);
+      ctx.lineTo(0,  10);
+      ctx.lineTo(5, -2);
+      ctx.closePath();
+      ctx.fill();
+    }
+    ctx.restore();
+  }
+
+  const THREE = {
+    Math: {
+      radToDeg(rad) {
+        return rad * 180 / Math.PI;
+      },
+      degToRad(deg) {
+        return deg * Math.PI / 180;
+      },
+    },
+  };
+
+  class DegRadHelper {
+    constructor(obj, prop) {
+      this.obj = obj;
+      this.prop = prop;
+    }
+    get value() {
+      return THREE.Math.radToDeg(this.obj[this.prop]);
+    }
+    set value(v) {
+      this.obj[this.prop] = THREE.Math.degToRad(v);
+    }
+  }
+
+  function dot(x1, y1, x2, y2) {
+    return x1 * x2 + y1 * y2;
+  }
+
+  function distance(x1, y1, x2, y2) {
+    const dx = x1 - x2;
+    const dy = y1 - y2;
+    return Math.sqrt(dx * dx + dy * dy);
+  }
+
+  function normalize(x, y) {
+    const l = distance(0, 0, x, y);
+    if (l > 0.00001) {
+      return [x / l, y / l];
+    } else {
+      return [0, 0];
+    }
+  }
+
+  function resizeCanvasToDisplaySize(canvas, pixelRatio = 1) {
+    const width  = canvas.clientWidth  * pixelRatio | 0;
+    const height = canvas.clientHeight * pixelRatio | 0;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      canvas.width = width;
+      canvas.height = height;
+    }
+    return needResize;
+  }
+
+  const diagrams = {
+    dotProduct: {
+      create(info) {
+        const {elem} = info;
+        const div = document.createElement('div');
+        div.style.position = 'relative';
+        div.style.width = '100%';
+        div.style.height = '100%';
+        elem.appendChild(div);
+
+        const ctx = document.createElement('canvas').getContext('2d');
+        div.appendChild(ctx.canvas);
+        const settings = {
+          rotation: 0.3,
+        };
+
+        const gui = new dat.GUI({autoPlace: false});
+        gui.add(new DegRadHelper(settings, 'rotation'), 'value', -180, 180).name('rotation').onChange(render);
+        gui.domElement.style.position = 'absolute';
+        gui.domElement.style.top = '0';
+        gui.domElement.style.right = '0';
+        div.appendChild(gui.domElement);
+
+        const darkColors = {
+          globe: 'green',
+          camera: '#AAA',
+          base: '#DDD',
+          label: '#0FF',
+        };
+        const lightColors = {
+          globe: '#0C0',
+          camera: 'black',
+          base: '#000',
+          label: 'blue',
+        };
+
+        const darkMatcher = window.matchMedia('(prefers-color-scheme: dark)');
+        darkMatcher.addEventListener('change', render);
+
+        function render() {
+          const {rotation} = settings;
+          const isDarkMode = darkMatcher.matches;
+          const colors = isDarkMode ? darkColors : lightColors;
+
+          const pixelRatio = window.devicePixelRatio;
+          resizeCanvasToDisplaySize(ctx.canvas, pixelRatio);
+
+          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+          ctx.save();
+          {
+            const width = ctx.canvas.width / pixelRatio;
+            const height = ctx.canvas.height / pixelRatio;
+            const min = Math.min(width, height);
+            const half = min / 2;
+
+            const r = half * 0.4;
+            const x = r * Math.sin(-rotation);
+            const y = r * Math.cos(-rotation);
+
+            const camDX = x - 0;
+            const camDY = y - (half - 40);
+
+            const labelDir = normalize(x, y);
+            const camToLabelDir = normalize(camDX, camDY);
+
+            const dp = dot(...camToLabelDir, ...labelDir);
+
+            ctx.scale(pixelRatio, pixelRatio);
+            ctx.save();
+            {
+              {
+                ctx.translate(width / 2, height / 2);
+                ctx.beginPath();
+                ctx.arc(0, 0, half * 0.4, 0, Math.PI * 2);
+                ctx.fillStyle = colors.globe;
+                ctx.fill();
+
+                ctx.save();
+                {
+                  ctx.fillStyle = colors.camera;
+                  ctx.translate(0, half);
+                  ctx.fillRect(-15, -30, 30, 30);
+                  ctx.beginPath();
+                  ctx.moveTo(0, -25);
+                  ctx.lineTo(-25, -50);
+                  ctx.lineTo( 25, -50);
+                  ctx.closePath();
+                  ctx.fill();
+                }
+                ctx.restore();
+
+                ctx.save();
+                {
+                  ctx.lineWidth = 4;
+                  ctx.strokeStyle = colors.camera;
+                  ctx.fillStyle = colors.camera;
+                  arrow(ctx, 0, half - 40, x, y, false, true, 2);
+
+                  ctx.save();
+                  {
+                    ctx.strokeStyle = colors.label;
+                    ctx.fillStyle = colors.label;
+                    arrow(ctx, 0, 0, x, y, false, true, 2);
+                  }
+                  ctx.restore();
+
+                  {
+                    ctx.lineWidth = 3;
+                    ctx.strokeStyle = 'black';
+                    ctx.fillStyle = dp < 0 ? 'white' : 'red';
+                    ctx.font = '20px sans-serif';
+                    ctx.textAlign = 'center';
+                    ctx.textBaseline = 'middle';
+                    outlineText(ctx, 'label', x, y);
+                  }
+                }
+                ctx.restore();
+
+              }
+              ctx.restore();
+            }
+
+            ctx.lineWidth = 3;
+            ctx.font = '24px sans-serif';
+            ctx.strokeStyle = 'black';
+            ctx.textAlign = 'left';
+            ctx.textBaseline = 'middle';
+            ctx.save();
+            {
+              ctx.translate(width / 4, 80);
+              const textColor = dp < 0 ? colors.base : 'red';
+              advanceText(ctx, textColor, 'dot( ');
+              ctx.save();
+              {
+                ctx.fillStyle = colors.camera;
+                ctx.strokeStyle = colors.camera;
+                ctx.rotate(Math.atan2(camDY, camDX));
+                arrow(ctx, -8, 0, 8, 0, false, true, 1);
+              }
+              ctx.restore();
+              advanceText(ctx, textColor, ' ,  ');
+              ctx.save();
+              {
+                ctx.fillStyle = colors.label;
+                ctx.strokeStyle = colors.label;
+                ctx.rotate(rotation + Math.PI * 0.5);
+                arrow(ctx, -8, 0, 8, 0, false, true, 1);
+              }
+              ctx.restore();
+              advanceText(ctx, textColor, ` ) = ${dp.toFixed(2)}`);
+            }
+            ctx.restore();
+          }
+          ctx.restore();
+        }
+        render();
+        window.addEventListener('resize', render);
+      },
+    },
+  };
+
+  function advanceText(ctx, color, str) {
+    ctx.fillStyle = color;
+    ctx.fillText(str, 0, 0);
+    ctx.translate(ctx.measureText(str).width, 0);
+  }
+
+  [...document.querySelectorAll('[data-diagram]')].forEach(createDiagram);
+
+  function createDiagram(base) {
+    const name = base.dataset.diagram;
+    const info = diagrams[name];
+    if (!info) {
+      throw new Error(`no diagram ${name}`);
+    }
+    info.create({elem:base});
+  }
+}
+
+

+ 2 - 1
threejs/lessons/threejs-align-html-elements-to-3d.md

@@ -791,4 +791,5 @@ div[data-diagram] canvas {
   display: block;
 }
 </style>
-<script src="resources/threejs-align-html-elements-to-3d.js"></script>
+<script src="/3rdparty/dat.gui.min.js"></script>
+<script src="resources/threejs-align-html-elements-to-3d.js"></script>