diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/index.js | 111 | ||||
| -rw-r--r-- | src/template.html | 67 |
2 files changed, 156 insertions, 22 deletions
diff --git a/src/index.js b/src/index.js index d86d8ea..7804d94 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import Leap from 'leapjs'; import * as T from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; +import { TransformControls } from 'three/examples/jsm/controls/TransformControls'; import { saveAs } from 'file-saver'; const clamp = (val, min=0, max=1) => Math.min(max, Math.max(min, val)); @@ -12,6 +13,7 @@ const max = (...vecs) => vecs.reduce((last, next) => last.map((v, i) => Math.max const $ = x => document.getElementById(x); const canvas = $('main'); +const renderer = new T.WebGLRenderer({ canvas }); const scene = new T.Scene(); scene.background = new T.Color().setHSL(0.7, .95, .93); @@ -33,43 +35,114 @@ scene.add(dirLight); const camera = new T.PerspectiveCamera(75, 1, 0.1, 1000); camera.position.set(-30, 400, 80); -const controls = new OrbitControls(camera, canvas); -// const context = canvas.getContext('webgl2', { alpha: false }); -const renderer = new T.WebGLRenderer({ canvas }); // , context }); + +const orbit = new OrbitControls(camera, canvas); +const transform = new TransformControls(camera, canvas); +transform.addEventListener('dragging-changed', e => { orbit.enabled = !e.value }); +scene.add(transform); window.onresize = () => { renderer.setSize(canvas.offsetWidth, canvas.offsetHeight); camera.aspect = canvas.offsetWidth / canvas.offsetHeight; camera.updateProjectionMatrix(); - controls.update(); + orbit.update(); }; window.onresize(); class Finger extends T.Group { static tipGeom = new T.SphereBufferGeometry(4, 16, 16); static trackGeom = new T.SphereBufferGeometry(2, 4, 4); + static sliceMaterial = new T.MeshLambertMaterial({ color: 0xffffff, transparent: true, opacity: 0.5 }); static MAX_TRACK = 1 << 10; - constructor(hue) { + constructor(i) { super(); - const color = new T.Color().setHSL(hue || Math.random(), .8, .6); - const meshMaterial = new T.MeshLambertMaterial({ color }); - const lineMaterial = new T.LineBasicMaterial({ color, linewidth: 2 }); + const color = new T.Color().setHSL(i / 5, .8, .6); + this.meshMaterial = new T.MeshLambertMaterial({ color }); + this.lineMaterial = new T.LineBasicMaterial({ color, linewidth: 2 }); - this.tip = new T.Mesh(Finger.tipGeom, meshMaterial); + this.tip = new T.Mesh(Finger.tipGeom, this.meshMaterial); this.add(this.tip); this.lineGeometry = new T.Geometry(); this.lineGeometry.vertices.push(new T.Vector3(), new T.Vector3(), new T.Vector3()); - this.skeleton = new T.Line(this.lineGeometry, lineMaterial); + this.skeleton = new T.Line(this.lineGeometry, this.lineMaterial); this.add(this.skeleton); - this.trackers = new T.InstancedMesh(Finger.trackGeom.clone(), meshMaterial.clone(), Finger.MAX_TRACK); + this.trackers = new T.InstancedMesh(Finger.trackGeom.clone(), this.meshMaterial.clone(), Finger.MAX_TRACK); this.add(this.trackers); this.dataPoints = []; this.track = false; + + $('header').append(this.createControls(i)); + } + + createControls(i) { + const title = document.createElement('span'); + title.innerText = `slice ${i}`; + + const select = document.createElement('button'); + select.innerText = 'select'; + select.onclick = e => { + e.preventDefault(); + this.selectSlice(); + } + + const remove = document.createElement('button'); + remove.innerText = 'remove'; + remove.onclick = e => { + e.preventDefault(); + this.removeSlice(); + } + + const controls = document.createElement('div'); + controls.append(title); + controls.append(select); + controls.append(remove); + return controls; + } + + selectSlice() { + if (!this.slice) { + const average = this.dataPoints.reduce((sum, p) => sum.add(new T.Vector3(...p.dip)), new T.Vector3()); + if (this.dataPoints.length) + average.divideScalar(this.dataPoints.length); + + this.slice = new T.Mesh(new T.BoxGeometry(), Finger.sliceMaterial); + this.slice.scale.set(25, 5, 50); + this.slice.position.copy(average); + this.add(this.slice); + transform.attach(this.slice); + } else if (transform.object === this.slice) { + transform.detach(); + } else { + transform.attach(this.slice); + } + } + + updateSlice() { + const sliceTransform = this.slice.matrixWorld.getInverse(); + const points = this.dataPoints.reduce((list, point) => { + const tip = new T.Vector3(...point.tip); + + tip.applyMatrix4(sliceTransform); + if (Math.abs(tip.x) <= 1 && Math.abs(tip.y) <= 1 && Math.abs(tip.z) <= 1) { + + } + }, []); + } + + removeSlice() { + if (!this.slice) + return; + + if (transform.object === this.slice) + transform.detach(); + + this.remove(this.slice); + this.slice = null; } update(finger) { @@ -91,6 +164,7 @@ class Finger extends T.Group { } clear() { + this.removeSlice(); this.dataPoints = []; for (let i = 0; i < Finger.MAX_TRACK; i++) { this.trackers.setMatrixAt(i, new T.Matrix4()); @@ -121,7 +195,7 @@ const palm = new T.Mesh(new T.BoxBufferGeometry(8, 2, 8), material); scene.add(palm); const fingers = [0,1,2,3,4].map(i => { - const finger = new Finger(i / 5); + const finger = new Finger(i); scene.add(finger); return finger; }); @@ -129,15 +203,12 @@ window.F = fingers; window.onkeydown = (e) => { if (e.key === 'r') { - e.preventDefault(); fingers.forEach(finger => finger.clear()); return; } else if (e.key === 's') { - e.preventDefault(); const data = fingers.map(finger => finger.store()); saveAs(new Blob([JSON.stringify(data)]), 'text/json'); } else if (e.key === 'l') { - e.preventDefault(); const input = document.createElement('input'); input.type = 'file'; input.click(); @@ -150,6 +221,10 @@ window.onkeydown = (e) => { } reader.readAsText(e.target.files[0]); } + } else if (e.key === 'Shift') { + transform.setMode('rotate'); + } else if (e.key === 'Alt') { + transform.setMode('scale'); } const i = +e.key; @@ -163,6 +238,10 @@ window.onkeydown = (e) => { window.onkeyup = (e) => { const i = +e.key; + if (e.key === 'Shift' || e.key === 'Alt') { + transform.setMode('translate'); + } + if (i > -1 && i < 6) { e.preventDefault(); fingers[i % 5].track = false; @@ -191,7 +270,7 @@ const controller = Leap.loop({ }, frame => { update(frame); - controls.update(); + orbit.update(); renderer.render(scene, camera); }); diff --git a/src/template.html b/src/template.html index f071025..401c9da 100644 --- a/src/template.html +++ b/src/template.html @@ -14,29 +14,84 @@ width: 100vw; height: 100vh; + overflow: hidden; + flex-direction: column; + + background: #212121; + color: #696969; + font-family: sans-serif; } header { display: flex; + min-height: 7em; justify-content: space-between; + flex-wrap: wrap; - height: 150px; flex: 0 0 auto; } + header > div { + display: flex; + + flex-direction: column; + margin: 0.5em 1em; + } + + header > div > span { + font-size: 2em; + } + + button { + margin: 0.2em 0; + flex: 1; + + border: 2px solid #696969; + color: #696969; + background: none; + + transition: background 300ms, color 300ms; + } + + button:hover { + background: #696969; + color: #212121; + } + + key { + font-size: 0.8rem; + line-height: 0.8rem; + padding: 0.2rem; + font-family: monospace; + + background: #696969; + color: #212121; + } + + div { + padding: 0 0.5em 0.5em; + } + #main { flex: 1 1 0; + width: auto !important; + height: auto !important; } </style> </head> <body> - <header> - <canvas id="graphs_a"></canvas> - <canvas id="graphs_b"></canvas> - <div id="controls"></div> - </header> + <header id="header"></header> + <div> + <b>hotkeys —</b> + <key>1-5</key> track fingers — + <key>shift</key> rotate slice — + <key>alt</key> scale slice — + <key>S</key> export data — + <key>L</key> import data — + <key>R</key> reset + </div> <canvas id="main"></canvas> </body> </html> |
