aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index.js111
-rw-r--r--src/template.html67
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 &mdash;</b>
+ <key>1-5</key> track fingers &mdash;
+ <key>shift</key> rotate slice &mdash;
+ <key>alt</key> scale slice &mdash;
+ <key>S</key> export data &mdash;
+ <key>L</key> import data &mdash;
+ <key>R</key> reset
+ </div>
<canvas id="main"></canvas>
</body>
</html>