From 5f812b1932948716aef7ae3cf6c15fe064662512 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Thu, 18 Dec 2014 20:14:36 -0800 Subject: [PATCH 01/17] Draft finger-projected XY movement --- src/InteractablePlane.js | 39 +++++++++++++++++++-------------------- src/leap.proximity.js | 39 +++++++++++++++++++++++++++------------ 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index d4ab734..980f99b 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -41,10 +41,6 @@ window.InteractablePlane = function(planeMesh, controller, options){ // Todo - it would be great to have a "bouncy constraint" option, which would act like the scroll limits on OSX this.movementConstraints = {}; - // If this is ever increased above one, that initial finger can not be counted when averaging position - // otherwise, it causes jumpyness. - this.fingersRequiredForMove = 1; - this.tempVec3 = new THREE.Vector3; this.density = 1; @@ -167,35 +163,37 @@ window.InteractablePlane.prototype = { // Returns the position of the mesh intersected // If position is passed in, sets it. getPosition: function(position){ - var newPosition = position || new THREE.Vector3, intersectionCount = 0; + var sumDelta = (position || new THREE.Vector3).set(0,0,0), intersectionCount = 0; + var n = new THREE.Vector3; + for ( var intersectionKey in this.intersections ){ if( this.intersections.hasOwnProperty(intersectionKey) ){ intersectionCount++; - newPosition.add( - this.moveProximity.intersectionPoints[intersectionKey].clone().sub( - this.intersections[intersectionKey] - ) - ) - - } - } + n.subVectors( + this.moveProximity.intersectingLines[intersectionKey][1], + this.moveProximity.intersectingLines[intersectionKey][0] + ).normalize(); - // todo - experiment with spring physics - if ( intersectionCount < this.fingersRequiredForMove) { + // TODO this should always return something. + var delta = this.moveProximity.delta(intersectionKey); - newPosition.copy(this.mesh.position); - - } else { + if ( delta ){ + sumDelta.add( + delta.projectOnPlane(n) + ); + } - newPosition.divideScalar(intersectionCount); + } } + // becomes averageDelta + if ( intersectionCount > 1 ) sumDelta.divideScalar(intersectionCount); - return newPosition; + return sumDelta.add(this.mesh.position); }, // Adds a spring @@ -579,6 +577,7 @@ window.InteractablePlane.prototype = { var overlap, isHovered; + // "Not optimized: ForInStatement is not fast case" for (var key in this.previousOverlap){ overlap = this.previousOverlap[key]; diff --git a/src/leap.proximity.js b/src/leap.proximity.js index 18491e6..0077f72 100644 --- a/src/leap.proximity.js +++ b/src/leap.proximity.js @@ -123,13 +123,6 @@ Leap.plugin('proximity', function(scope){ // mode: var Proximity = function(mesh, handPoints, options){ - setTimeout( // pop out of angular scope. - function(){ - testIntersectionPointBetweenLines() - }, - 0 - ); - options || (options = {}); this.options = options; @@ -139,6 +132,8 @@ Leap.plugin('proximity', function(scope){ // These are both keyed by the string: hand.id + handPointIndex this.states = {}; this.intersectionPoints = {}; // checkLines: one for each handPoint. Position in world space. + this.lastIntersectionPoints = {}; + this.intersectingLines = {}; // Similar to above, but also includes point on the plane, but not on the plane segment. // This is used for responding to between-frame motion @@ -173,6 +168,7 @@ Leap.plugin('proximity', function(scope){ return this }, + // todo this is kind of a dumb method and should be architected out check: function(hand){ // Handles Spheres. Planes. Boxes? other shapes? custom shapes? @@ -195,16 +191,19 @@ Leap.plugin('proximity', function(scope){ // Todo - this loop could be split in to smaller methods for JIT compiler optimization. checkLines: function(hand, lines){ - var mesh = this.mesh, state, intersectionPoint, key; + var mesh = this.mesh, state, intersectionPoint, key, line; var worldPosition = (new THREE.Vector3).setFromMatrixPosition( this.mesh.matrixWorld ); + this.intersectingLines = {}; + // j because this is inside a loop for every hand - for (var j = 0; j < lines.length; j++){ + for (var i = 0; i < lines.length; i++){ - key = hand.id + '-' + j; + line = lines[i]; + key = hand.id + '-' + i; - intersectionPoint = mesh.intersectedByLine(lines[j][0], lines[j][1], worldPosition); + intersectionPoint = mesh.intersectedByLine(line[0], line[1], worldPosition); var lastIntersectionPoint = this.possibleIntersectionPoints[key]; @@ -279,7 +278,9 @@ Leap.plugin('proximity', function(scope){ if (intersectionPoint){ + this.lastIntersectionPoints[key] = this.intersectionPoints[key]; this.intersectionPoints[key] = intersectionPoint; + this.intersectingLines[key] = line; } else if (this.intersectionPoints[key]) { @@ -300,7 +301,7 @@ Leap.plugin('proximity', function(scope){ state = intersectionPoint ? 'in' : 'out'; if ( (state == 'in' && this.states[key] !== 'in') || (state == 'out' && this.states[key] === 'in')){ // this logic prevents initial `out` events. - this.emit(state, hand, intersectionPoint, key, j); // todo - could include intersection displacement vector here (!) + this.emit(state, hand, intersectionPoint, key, i); // todo - could include intersection displacement vector here (!) this.states[key] = state; } @@ -308,6 +309,20 @@ Leap.plugin('proximity', function(scope){ }, + // Gets the change in position between this frame and last for the given line key + // This function maybe should be merged with various high-speed logic in checkLines + // When it comes to entering and existing the mesh. + delta: function(key){ + + if (!this.lastIntersectionPoints[key] || !this.intersectionPoints[key]) return; + + return (new THREE.Vector3).subVectors( + this.intersectionPoints[key], + this.lastIntersectionPoints[key] + ) + + }, + checkPoints: function(hand, handPoints){ var mesh = this.mesh, length, state, handPoint, meshWorldPosition = new THREE.Vector3, From da895dc1296b4c08a3b5500ef3accff4c3bad4bd Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Mon, 5 Jan 2015 14:23:57 -0800 Subject: [PATCH 02/17] Add metacarpal intersection, rename interactiveEndBones to interactiveBones --- src/InteractablePlane.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 980f99b..f49fe47 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -420,7 +420,7 @@ window.InteractablePlane.prototype = { // todo - rename to something that's not a mozilla method var proximity = this.moveProximity = this.controller.watch( this.mesh, - this.interactiveEndBones + this.interactiveBones ); // this ties InteractablePlane to boneHand plugin - probably should have callbacks pushed out to scene. @@ -564,7 +564,6 @@ window.InteractablePlane.prototype = { } - // note - include moveZ here when implemented. if ( moveX || moveY || moveZ ) this.emit( 'travel', this, this.mesh ); if (this.options.hoverBounds) this.emitHoverEvents(); @@ -690,7 +689,8 @@ window.InteractablePlane.prototype = { // Order matters for our own use in this class // returns a collection of lines to be tested against // could be optimized to reuse vectors between frames - interactiveEndBones: function(hand){ + interactiveBones: function(hand){ + var out = [], finger; for (var i = 0; i < 5; i++){ @@ -699,13 +699,17 @@ window.InteractablePlane.prototype = { if (i > 0){ // no thumb proximal out.push( [ - (new THREE.Vector3).fromArray(finger.proximal.nextJoint), - (new THREE.Vector3).fromArray(finger.proximal.prevJoint) + (new THREE.Vector3).fromArray(finger.metacarpal.nextJoint), + (new THREE.Vector3).fromArray(finger.metacarpal.prevJoint) ] ); } out.push( + [ + (new THREE.Vector3).fromArray(finger.proximal.nextJoint), + (new THREE.Vector3).fromArray(finger.proximal.prevJoint) + ], [ (new THREE.Vector3).fromArray(finger.medial.nextJoint), (new THREE.Vector3).fromArray(finger.medial.prevJoint) From e38c1e9214cd2820e565e4fd2ed5dfddb8195e24 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Mon, 5 Jan 2015 19:03:21 -0800 Subject: [PATCH 03/17] Draft z-slide from x Todos.. - y - multi parallel bone test - multi serial bone test - multi conflicting bone test - circular buttons - re-integrate in to existing code, etc --- src/InteractablePlane.js | 90 ++++++++++++--- src/leap.proximity.js | 22 ++-- test/slideZ.html | 233 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+), 27 deletions(-) create mode 100644 test/slideZ.html diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index f49fe47..dcb54dc 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -163,37 +163,95 @@ window.InteractablePlane.prototype = { // Returns the position of the mesh intersected // If position is passed in, sets it. getPosition: function(position){ - var sumDelta = (position || new THREE.Vector3).set(0,0,0), intersectionCount = 0; + var newPosition = (position || new THREE.Vector3).set(0,0,0); var n = new THREE.Vector3; + // todo: + // if two conflicting Z directions... take the part that works with both? + // ... take the strongest, (which will be the one that moves the least?) + // ... z strength is the complement of z distance + // ... only matters when comparing two things of strength infinity + // ... first, catch a test that case (if z1 sign != z2 sign, debugger) + // find the point where two lines cross, that is where the plane should z + // that is they solve for the same point. + // if multiple agreeing Z directions.. take the largest - for ( var intersectionKey in this.intersections ){ - if( this.intersections.hasOwnProperty(intersectionKey) ){ + // todo: factor back in Y movement and officially scrap this.intersections - intersectionCount++; + var zs = [], maxi, i = 0, ns = [], z, p1, p2, intersectionPoint; - n.subVectors( - this.moveProximity.intersectingLines[intersectionKey][1], - this.moveProximity.intersectingLines[intersectionKey][0] - ).normalize(); + for ( var intersectionKey in this.moveProximity.intersectionPoints ) { + if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; // TODO this should always return something. var delta = this.moveProximity.delta(intersectionKey); + p1 = this.moveProximity.intersectingLines[intersectionKey][0]; // line beginning - if ( delta ){ - sumDelta.add( - delta.projectOnPlane(n) - ); - } + n.subVectors(p2, p1); + + ns.push( n.clone() ); + + var delta = this.moveProximity.positionChange(intersectionKey); + if (!delta) continue; + + // todo - "play" factor? Allow a smidgen of xy and maybe some rotation at strong z angles, indicating bend + + // only x and z + // var scale = delta.x / n.x; + // sumDelta = n.z * scale + // will work for movement to the right right, but not up + // let's try + // for y - maybe take max of two + // y = mx + b + // m = rise/run = n.x / n.z + // z = n.z / n.x * delta.x + z = n.z / n.x * delta.x * -1; + // Don't move farther than bone end + if ( z > 0 ) { + z = Math.min(z, p2.z - intersectionPoint.z); + } else { + z = Math.max(z, p1.z - intersectionPoint.z); } + + zs.push(z); + + // find the bone which causes the most movement. + if ( maxi === undefined ){ + maxi = 0; + } else { + if (zs[i] * zs[maxi] < 0) debugger; // unsupported conflicting directions + + if (zs[i] > 0 && zs[i] > zs[maxi]) maxi = i; + if (zs[i] < 0 && zs[i] < zs[maxi]) maxi = i; + } + + i++; } - // becomes averageDelta - if ( intersectionCount > 1 ) sumDelta.divideScalar(intersectionCount); + newPosition.copy(this.mesh.position); + + if ( maxi !== undefined ){ + + // what happens when this combines with movement constraints? + // todo - check y + var intersectionOffset = ns[maxi].multiplyScalar(zs[maxi] / ns[maxi].z); + + for ( var intersectionKey in this.moveProximity.intersectionPoints ) { + if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; + + this.moveProximity.intersectionPoints[intersectionKey].add(intersectionOffset); + + } + + newPosition.z += zs[maxi]; + + //console.assert( zs[maxi] == intersectionOffset.z); + + } - return sumDelta.add(this.mesh.position); + return newPosition }, // Adds a spring diff --git a/src/leap.proximity.js b/src/leap.proximity.js index 0077f72..0786f30 100644 --- a/src/leap.proximity.js +++ b/src/leap.proximity.js @@ -189,7 +189,7 @@ Leap.plugin('proximity', function(scope){ }, - // Todo - this loop could be split in to smaller methods for JIT compiler optimization. + // todo - circles support checkLines: function(hand, lines){ var mesh = this.mesh, state, intersectionPoint, key, line; @@ -197,7 +197,6 @@ Leap.plugin('proximity', function(scope){ this.intersectingLines = {}; - // j because this is inside a loop for every hand for (var i = 0; i < lines.length; i++){ line = lines[i]; @@ -207,6 +206,7 @@ Leap.plugin('proximity', function(scope){ var lastIntersectionPoint = this.possibleIntersectionPoints[key]; + // handle incoming fast bone // 1: store lastIntersectionPoint at all times // 2: only return values for good intersectionpoints from mesh.intersectedByLine // 3: use it to tune intersectionpoint. @@ -216,7 +216,7 @@ Leap.plugin('proximity', function(scope){ // In that case, the foremost line should push the image, but what happens here and in InteractablePlane#getPosition // is the lines are averaged and then move the image // InteractablePlane should be aware of this adjustment (perhaps doing so itself) - if ( this.states[key] === 'out' && intersectionPoint && lastIntersectionPoint ){ + if ( this.states[key] !== 'in' && intersectionPoint && lastIntersectionPoint ){ // check all four edges, // take the one that actually has a cross @@ -228,11 +228,11 @@ Leap.plugin('proximity', function(scope){ var minLenSq = Infinity; var closestEdgeIntersectionPoint = null; - for (var i = 0; i < 4; i++){ + for (var j = 0; j < 4; j++){ var point = intersectionPointBetweenLines( - corners[i], - corners[(i+1) % 4], + corners[j], + corners[(j+1) % 4], lastIntersectionPoint, intersectionPoint ); @@ -265,6 +265,7 @@ Leap.plugin('proximity', function(scope){ } + // handle outgoing fast bone // if there already was a valid intersection point, // And the new one is valid in z but off in x and y, // don't emit an out event. @@ -292,6 +293,7 @@ Leap.plugin('proximity', function(scope){ this.possibleIntersectionPoints[key] = mesh.intersectionPoint; // mesh.intersectionPoint may be on plane, but not segment. + } else { delete this.possibleIntersectionPoints[key]; @@ -310,11 +312,8 @@ Leap.plugin('proximity', function(scope){ }, // Gets the change in position between this frame and last for the given line key - // This function maybe should be merged with various high-speed logic in checkLines - // When it comes to entering and existing the mesh. - delta: function(key){ - - if (!this.lastIntersectionPoints[key] || !this.intersectionPoints[key]) return; + positionChange: function(key){ + if ( !this.lastIntersectionPoints[key] || !this.intersectionPoints[key] ) return; return (new THREE.Vector3).subVectors( this.intersectionPoints[key], @@ -373,6 +372,7 @@ Leap.plugin('proximity', function(scope){ delete this.states[key]; delete this.intersectionPoints[key]; + delete this.lastIntersectionPoints[key]; delete this.lengths[key]; delete this.distances[key]; this.emit('out', hand, null, key, parseInt(key.split('-')[1],10) ); diff --git a/test/slideZ.html b/test/slideZ.html new file mode 100644 index 0000000..f426538 --- /dev/null +++ b/test/slideZ.html @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + +View Source + +

+ Insert exactly one hand to run loop. Type `moveLineX(0.001)` in to console to experiment. +

+

+ +

+ + + + + + \ No newline at end of file From a2a5b5f34c9fc6aa8fb6827450982d4e28eedc28 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Mon, 5 Jan 2015 19:51:38 -0800 Subject: [PATCH 04/17] Allow line to back out again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Todo - prevent 1-frame “in” flicker --- src/InteractablePlane.js | 14 ++--- test/slideZ.html | 110 ++++++++++++++++++++++++++++++--------- 2 files changed, 91 insertions(+), 33 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index dcb54dc..b33922c 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -183,9 +183,9 @@ window.InteractablePlane.prototype = { for ( var intersectionKey in this.moveProximity.intersectionPoints ) { if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; - // TODO this should always return something. - var delta = this.moveProximity.delta(intersectionKey); p1 = this.moveProximity.intersectingLines[intersectionKey][0]; // line beginning + p2 = this.moveProximity.intersectingLines[intersectionKey][1]; // line end + intersectionPoint = this.moveProximity.intersectionPoints[intersectionKey]; n.subVectors(p2, p1); @@ -236,19 +236,15 @@ window.InteractablePlane.prototype = { // what happens when this combines with movement constraints? // todo - check y - var intersectionOffset = ns[maxi].multiplyScalar(zs[maxi] / ns[maxi].z); + // lol jk: we were never intersecting. rly. + // todo: this may not be valid to do, but we assume that nothing is intersecting for ( var intersectionKey in this.moveProximity.intersectionPoints ) { if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; - - this.moveProximity.intersectionPoints[intersectionKey].add(intersectionOffset); - + delete this.moveProximity.intersectionPoints[intersectionKey]; } newPosition.z += zs[maxi]; - - //console.assert( zs[maxi] == intersectionOffset.z); - } return newPosition diff --git a/test/slideZ.html b/test/slideZ.html index f426538..145931b 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -95,7 +95,8 @@ plane.moveProximity.handPoints = function(){ return [ - [v1, v2] + [v1, v2], + [v3, v4] ] }; @@ -112,6 +113,7 @@ window.currentTest = null; + window.currentTestStep = 0; window.lastTestIndex = null; window.run = function (testName) { window.lastTestIndex = window.testNames.indexOf(testName); @@ -121,27 +123,53 @@ // plane.cleanupHandData(); tests[testName][0](); window.currentTest = testName; + window.currentTestStep = 1; print('press a key'); }; document.addEventListener('keydown', function (e) { console.log('keyDown'); - if (!window.currentTest) { - // advance to next test - var test = window.testNames[window.lastTestIndex+1]; - if (!test) { - console.log('all tests run'); - return - } - window.run(test); - return - } - tests[currentTest][1](); - window.currentTest = null; +// if (!window.currentTest) { +// // advance to next test +// var test = window.testNames[window.lastTestIndex+1]; +// if (!test) { +// console.log('all tests run'); +// return +// } +// window.run(test); +// return +// } + var step = tests[currentTest][currentTestStep]; + if (!step) return; + step(); + currentTestStep++; +// window.currentTest = null; }); var v1 = new THREE.Vector3(0, 0.1,-0.15); var v2 = new THREE.Vector3(0.05,0.1,0.05); + var v3 = new THREE.Vector3(0, 0.1,-0.15); + var v4 = new THREE.Vector3(0.05,0.1,0.05); + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( v1, v2 ); + + var line = new THREE.Line( + lineGeometry, + new THREE.LineBasicMaterial({ color: 0x0000ff }) + ); + + scene.add(line); + + var lineGeometry2 = new THREE.Geometry(); + lineGeometry2.vertices.push( v3, v4 ); + + var line2 = new THREE.Line( + lineGeometry2, + new THREE.LineBasicMaterial({ color: 0x00ee33 }) + ); + + scene.add(line2); // tests: // move line in √ @@ -163,6 +191,13 @@ line.geometry.verticesNeedUpdate = true } + window.moveLine2X = function(amt){ + amt || (amt = 0.01); + v3.x += amt; + v4.x += amt; + line2.geometry.verticesNeedUpdate = true + } + window.moveLineY = function(amt){ amt || (amt = 0.01); v1.y += amt; @@ -175,6 +210,15 @@ offset || (offset = 0.05); v1.setX(pos); v2.setX(pos + offset); + line.geometry.verticesNeedUpdate = true + } + + window.setLine2X = function(pos, offset){ + pos || (pos = 0); + offset || (offset = 0.07); + v3.setX(pos); + v4.setX(pos + offset); + line2.geometry.verticesNeedUpdate = true } var tests = {}; @@ -185,6 +229,21 @@ }, function(){ moveLineX(0.04) + } , + function(){ + moveLineX(-0.01) + } + ]; + + tests.moveLineInTwice = [ + function(){ + setLineX(-0.05); + }, + function(){ + moveLineX(0.04) + } , + function(){ + moveLineX(0.01) } ]; @@ -206,6 +265,17 @@ } ]; + tests.twoLinesDissimilarSlopes = [ + function(){ + setLineX(-0.05, 0.05); + setLine2X(-0.05, 0.07); + }, + function(){ + moveLineX(0.04); + moveLine2X(0.04); + } + ]; + var testNames = []; for (var key in tests){ if (tests.hasOwnProperty(key)){ @@ -213,20 +283,12 @@ } } + setLineX(-0.05, 0.05); + setLine2X(-0.05, 0.07); - window.run('moveLineInSlopedForwards'); - - - - var lineGeometry = new THREE.Geometry(); - lineGeometry.vertices.push( v1, v2 ); - var line = new THREE.Line( - lineGeometry, - new THREE.LineBasicMaterial({ color: 0x0000ff }) - ); + window.run('moveLineIn'); - scene.add(line); From e822a2f307613405b722c7025dafedd9782956bb Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Mon, 5 Jan 2015 21:07:12 -0800 Subject: [PATCH 05/17] Fix frame-lag, 1-frame press event, and flicker-loop on bone-end. --- src/InteractablePlane.js | 37 +++++++++++++++++++++---------------- src/leap.proximity.js | 28 ++++++++++++++++------------ test/slideZ.html | 7 +++++-- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index b33922c..541676b 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -117,7 +117,6 @@ window.InteractablePlane.prototype = { this.touch(function(){ if (!this.interactable) return; - this.highlight(true); }.bind(this)); @@ -210,9 +209,9 @@ window.InteractablePlane.prototype = { // Don't move farther than bone end if ( z > 0 ) { - z = Math.min(z, p2.z - intersectionPoint.z); + z = Math.min(z, p2.z - intersectionPoint.z) + 1e-8; } else { - z = Math.max(z, p1.z - intersectionPoint.z); + z = Math.max(z, p1.z - intersectionPoint.z) - 1e-8; } zs.push(z); @@ -239,6 +238,7 @@ window.InteractablePlane.prototype = { // lol jk: we were never intersecting. rly. // todo: this may not be valid to do, but we assume that nothing is intersecting + // check multiple hands, etc. for ( var intersectionKey in this.moveProximity.intersectionPoints ) { if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; delete this.moveProximity.intersectionPoints[intersectionKey]; @@ -490,12 +490,6 @@ window.InteractablePlane.prototype = { this.intersections[key] = intersectionPoint.clone().sub(this.mesh.position); - if (!this.touched) { - this.touched = true; -// console.log('touch', this.mesh.name); - this.emit('touch', this); - } - }.bind(this) ); proximity.out( function(hand, intersectionPoint, key, index){ @@ -513,13 +507,6 @@ window.InteractablePlane.prototype = { } - // not sure why, but sometimes getting multiple 0 proximity release events - if (proximity.intersectionCount() == 0 && this.touched) { - this.touched = false; -// console.log('release', this.mesh.name, proximity.intersectionCount()); - this.emit('release', this); - } - }.bind(this) ); }, @@ -620,9 +607,27 @@ window.InteractablePlane.prototype = { if ( moveX || moveY || moveZ ) this.emit( 'travel', this, this.mesh ); + this.emitTouchEvents(); // This happens after getPosition has a chance to mutate intersectionPoints. if (this.options.hoverBounds) this.emitHoverEvents(); }, + emitTouchEvents: function(){ + + var intersectionCount = this.moveProximity.intersectionCount(); + + if (intersectionCount > 0 && !this.touched) { + this.touched = true; + this.emit('touch', this); + } + + // not sure why, but sometimes getting multiple 0 proximity release events + if (intersectionCount === 0 && this.touched) { + this.touched = false; + this.emit('release', this); + } + + }, + // Takes the previousOverlap calculated earlier in this frame. // If any within range, emits an event. // note - could also emit an event on that fingertip? diff --git a/src/leap.proximity.js b/src/leap.proximity.js index 0786f30..ef3dbb8 100644 --- a/src/leap.proximity.js +++ b/src/leap.proximity.js @@ -204,7 +204,7 @@ Leap.plugin('proximity', function(scope){ intersectionPoint = mesh.intersectedByLine(line[0], line[1], worldPosition); - var lastIntersectionPoint = this.possibleIntersectionPoints[key]; + var possibleIntersectionPoint = this.possibleIntersectionPoints[key]; // a somewhat terrible name indicating a point on the plane outside of the plane segment. // handle incoming fast bone // 1: store lastIntersectionPoint at all times @@ -216,7 +216,10 @@ Leap.plugin('proximity', function(scope){ // In that case, the foremost line should push the image, but what happens here and in InteractablePlane#getPosition // is the lines are averaged and then move the image // InteractablePlane should be aware of this adjustment (perhaps doing so itself) - if ( this.states[key] !== 'in' && intersectionPoint && lastIntersectionPoint ){ + // This is somewhat edgy (no pun intended): + // we overwrite lastIntersection point to be on the edge. although there was never actually a frame emitted + // with it as an intersection point. + if ( this.states[key] !== 'in' && intersectionPoint && possibleIntersectionPoint ){ // check all four edges, // take the one that actually has a cross @@ -233,7 +236,7 @@ Leap.plugin('proximity', function(scope){ var point = intersectionPointBetweenLines( corners[j], corners[(j+1) % 4], - lastIntersectionPoint, + possibleIntersectionPoint, intersectionPoint ); @@ -244,7 +247,7 @@ Leap.plugin('proximity', function(scope){ //console.assert(!isNaN(point.y)); //console.assert(!isNaN(point.z)); - var lengthSq = (new THREE.Vector3).subVectors(point, lastIntersectionPoint).lengthSq(); + var lengthSq = (new THREE.Vector3).subVectors(point, possibleIntersectionPoint).lengthSq(); // console.log('edge #:', i, 'line #:', j, "distance:", Math.sqrt(lengthSq) ); @@ -255,14 +258,6 @@ Leap.plugin('proximity', function(scope){ } - if (closestEdgeIntersectionPoint) { - - //console.log('edge intersection', closestEdgeIntersectionPoint, "between", intersectionPoint, "and", lastIntersectionPoint); - - intersectionPoint = closestEdgeIntersectionPoint; - - } - } // handle outgoing fast bone @@ -279,6 +274,15 @@ Leap.plugin('proximity', function(scope){ if (intersectionPoint){ + if (closestEdgeIntersectionPoint) { + + //console.log('edge intersection', closestEdgeIntersectionPoint, "between", intersectionPoint, "and", lastIntersectionPoint); + + // actually becomes this.lastIntersectionPoints[key]: + this.intersectionPoints[key] = closestEdgeIntersectionPoint; + + } + this.lastIntersectionPoints[key] = this.intersectionPoints[key]; this.intersectionPoints[key] = intersectionPoint; this.intersectingLines[key] = line; diff --git a/test/slideZ.html b/test/slideZ.html index 145931b..7029235 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -223,7 +223,7 @@ var tests = {}; - tests.moveLineIn = [ + tests.moveLineInOut = [ function(){ setLineX(-0.05); }, @@ -260,6 +260,9 @@ function(){ setLineX(-0.01, -0.05); }, + function(){ + moveLineX(0.04) + }, function(){ moveLineX(0.04) } @@ -287,7 +290,7 @@ setLine2X(-0.05, 0.07); - window.run('moveLineIn'); + window.run('moveLineInSlopedForwards'); From a6a1581b1fde452d4602f9943b231dd51dee8e78 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Mon, 5 Jan 2015 21:48:30 -0800 Subject: [PATCH 06/17] Fix bug where inverted line direction would move the wrong direction --- src/InteractablePlane.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 541676b..ab4ba1e 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -208,10 +208,19 @@ window.InteractablePlane.prototype = { z = n.z / n.x * delta.x * -1; // Don't move farther than bone end + // note: there's got to be a better way of doing this :-/ if ( z > 0 ) { - z = Math.min(z, p2.z - intersectionPoint.z) + 1e-8; + if (p2.z > p1.z) { + z = Math.min(z, p1.z - intersectionPoint.z) + 1e-8; + } else { + z = Math.min(z, p2.z - intersectionPoint.z) + 1e-8; + } } else { - z = Math.max(z, p1.z - intersectionPoint.z) - 1e-8; + if (p2.z > p1.z) { + z = Math.max(z, p1.z - intersectionPoint.z) - 1e-8; + } else { + z = Math.max(z, p2.z - intersectionPoint.z) - 1e-8; + } } zs.push(z); From 6cc87e15ef79c6aaa0cba583b886cdf3689736ab Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 13:03:02 -0800 Subject: [PATCH 07/17] Improve support for two lines on-end --- src/InteractablePlane.js | 2 +- test/slideZ.html | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index ab4ba1e..e95794f 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -191,7 +191,7 @@ window.InteractablePlane.prototype = { ns.push( n.clone() ); var delta = this.moveProximity.positionChange(intersectionKey); - if (!delta) continue; + if ( !delta || ( delta.x === 0 && delta.y === 0 && delta.z === 0) ) continue; // todo - "play" factor? Allow a smidgen of xy and maybe some rotation at strong z angles, indicating bend diff --git a/test/slideZ.html b/test/slideZ.html index 7029235..dd8cd71 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -279,6 +279,25 @@ } ]; + tests.twoLinesOnEnd = [ + function(){ + v1.set(0, 0.1, -0.05); + v2.set(-0.01, 0.1, -0.09); + v3.copy(v2); + v4.set(-0.03, 0.1, -0.15); + line.geometry.verticesNeedUpdate = true + line2.geometry.verticesNeedUpdate = true + }, + function(){ + moveLineX(0.02); + moveLine2X(0.02); + }, + function(){ + moveLineX(0.01); + moveLine2X(0.01); + } + ]; + var testNames = []; for (var key in tests){ if (tests.hasOwnProperty(key)){ @@ -290,7 +309,7 @@ setLine2X(-0.05, 0.07); - window.run('moveLineInSlopedForwards'); + window.run('twoLinesOnEnd'); From 5d95fe2b36f7bd6449f39902a9db96b507fb521d Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 14:10:49 -0800 Subject: [PATCH 08/17] Fix issue where sequential frames would have slip --- src/InteractablePlane.js | 16 +++++++++------- test/slideZ.html | 38 +++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index e95794f..80e02ca 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -191,7 +191,7 @@ window.InteractablePlane.prototype = { ns.push( n.clone() ); var delta = this.moveProximity.positionChange(intersectionKey); - if ( !delta || ( delta.x === 0 && delta.y === 0 && delta.z === 0) ) continue; + if ( !delta || ( Math.abs(delta.x) < 1e-8 && Math.abs(delta.y) < 1e-8 && Math.abs(delta.z) < 1e-8 ) ) continue; // todo - "play" factor? Allow a smidgen of xy and maybe some rotation at strong z angles, indicating bend @@ -211,9 +211,9 @@ window.InteractablePlane.prototype = { // note: there's got to be a better way of doing this :-/ if ( z > 0 ) { if (p2.z > p1.z) { - z = Math.min(z, p1.z - intersectionPoint.z) + 1e-8; - } else { z = Math.min(z, p2.z - intersectionPoint.z) + 1e-8; + } else { + z = Math.min(z, p1.z - intersectionPoint.z) + 1e-8; } } else { if (p2.z > p1.z) { @@ -245,12 +245,14 @@ window.InteractablePlane.prototype = { // what happens when this combines with movement constraints? // todo - check y - // lol jk: we were never intersecting. rly. - // todo: this may not be valid to do, but we assume that nothing is intersecting - // check multiple hands, etc. + i = 0; for ( var intersectionKey in this.moveProximity.intersectionPoints ) { if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; - delete this.moveProximity.intersectionPoints[intersectionKey]; + + var intersectionOffset = ns[i].multiplyScalar(zs[maxi] / ns[i].z); + + this.moveProximity.intersectionPoints[intersectionKey].add(intersectionOffset); + i++; } newPosition.z += zs[maxi]; diff --git a/test/slideZ.html b/test/slideZ.html index dd8cd71..b0c709c 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -54,8 +54,7 @@ targetEl: document.body, jointColor: new THREE.Color(0xffffff), rendererOps: {antialias: true} - }) - .use('proximity'); + }); // Set up scene @@ -129,21 +128,10 @@ document.addEventListener('keydown', function (e) { console.log('keyDown'); -// if (!window.currentTest) { -// // advance to next test -// var test = window.testNames[window.lastTestIndex+1]; -// if (!test) { -// console.log('all tests run'); -// return -// } -// window.run(test); -// return -// } var step = tests[currentTest][currentTestStep]; if (!step) return; step(); currentTestStep++; -// window.currentTest = null; }); var v1 = new THREE.Vector3(0, 0.1,-0.15); @@ -241,7 +229,7 @@ }, function(){ moveLineX(0.04) - } , + }, function(){ moveLineX(0.01) } @@ -276,6 +264,10 @@ function(){ moveLineX(0.04); moveLine2X(0.04); + }, + function(){ + v2.setX(0.1); + line.geometry.verticesNeedUpdate = true; } ]; @@ -298,6 +290,22 @@ } ]; + tests.sequentialFrames = [ + function(){ + setLineX(-0.025); + }, + function(){ + console.log('x'); + var i = 0; + Leap.loopController.on('frame', function(){ + i++; + if (i > 100) return; + console.log('y'); + moveLineX(0.0004) + }) + } + ] + var testNames = []; for (var key in tests){ if (tests.hasOwnProperty(key)){ @@ -309,7 +317,7 @@ setLine2X(-0.05, 0.07); - window.run('twoLinesOnEnd'); + window.run('moveLineInTwice'); From a46948acba2c3dd0b272c9a923b219f21c4f6179 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 15:39:43 -0800 Subject: [PATCH 09/17] Y slide support --- src/InteractablePlane.js | 17 ++++++++++++++++- src/leap.proximity.js | 2 ++ test/slideZ.html | 38 ++++++++++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 80e02ca..643d58d 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -205,7 +205,22 @@ window.InteractablePlane.prototype = { // y = mx + b // m = rise/run = n.x / n.z // z = n.z / n.x * delta.x - z = n.z / n.x * delta.x * -1; + z = 0; + if (n.x !==0) z += n.z / n.x * delta.x * -1; + if (n.y !==0) z += n.z / n.y * delta.y * -1; + + // add to that: y + // for edge cases on the x edge, there will naturally be no delta y, yay. + // - if the previous point is projected horizonally + // - but that's probably not it - instead, it's projected directly between two points + + // for edge conditions: + // the above only looks at z from x, which happens to work due to the geo arrangement of our tests + // We also need to look at y + // and actually rotate the line vector be be square to the edge. + // and then always compare against, say, its x dimension. + // to work with squares, circles, etc. + // we only care about delta in the normal, and line slope in the normal. // Don't move farther than bone end // note: there's got to be a better way of doing this :-/ diff --git a/src/leap.proximity.js b/src/leap.proximity.js index ef3dbb8..e3cdf66 100644 --- a/src/leap.proximity.js +++ b/src/leap.proximity.js @@ -233,6 +233,8 @@ Leap.plugin('proximity', function(scope){ for (var j = 0; j < 4; j++){ + // todo - this doesn't work with circles.. + // maybe: who cares? var point = intersectionPointBetweenLines( corners[j], corners[(j+1) % 4], diff --git a/test/slideZ.html b/test/slideZ.html index b0c709c..18215c9 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -179,6 +179,14 @@ line.geometry.verticesNeedUpdate = true } + window.moveLineXY = function(x,y){ + v1.x += x; + v2.x += x; + v1.y += y; + v2.y += y; + line.geometry.verticesNeedUpdate = true + } + window.moveLine2X = function(amt){ amt || (amt = 0.01); v3.x += amt; @@ -277,8 +285,8 @@ v2.set(-0.01, 0.1, -0.09); v3.copy(v2); v4.set(-0.03, 0.1, -0.15); - line.geometry.verticesNeedUpdate = true - line2.geometry.verticesNeedUpdate = true + line.geometry.verticesNeedUpdate = true; + line2.geometry.verticesNeedUpdate = true; }, function(){ moveLineX(0.02); @@ -304,7 +312,29 @@ moveLineX(0.0004) }) } - ] + ]; + + tests.verticalMotion = [ + function(){ + v1.set(0.02,0.25,0.05); + v2.set(0.04,0.22,-0.15); + line.geometry.verticesNeedUpdate = true; + }, + function(){ + moveLineY(-0.04) + } + ]; + + tests.diagonalMotion = [ + function(){ + v1.set(0.02,0.15,0.05); + v2.set(0.02,0.12,-0.15); + line.geometry.verticesNeedUpdate = true; + }, + function(){ + moveLineXY(0.005,0.005); + } + ]; var testNames = []; for (var key in tests){ @@ -317,7 +347,7 @@ setLine2X(-0.05, 0.07); - window.run('moveLineInTwice'); + window.run('diagonalMotion'); From 95d9da5b27fd6578390e9c5764c9a06a9cc1d510 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 16:50:05 -0800 Subject: [PATCH 10/17] Handle conflicting slope directions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit note - there’s still jumps, sometimes, for unknown reasons --- src/InteractablePlane.js | 44 ++++++++++++++++++---------------------- test/slideZ.html | 21 +++++++++++-------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 643d58d..91358ae 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -177,7 +177,7 @@ window.InteractablePlane.prototype = { // todo: factor back in Y movement and officially scrap this.intersections - var zs = [], maxi, i = 0, ns = [], z, p1, p2, intersectionPoint; + var i = 0, ns = [], z, p1, p2, intersectionPoint, maxZ = 0, minZ = 0; for ( var intersectionKey in this.moveProximity.intersectionPoints ) { if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; @@ -192,6 +192,7 @@ window.InteractablePlane.prototype = { var delta = this.moveProximity.positionChange(intersectionKey); if ( !delta || ( Math.abs(delta.x) < 1e-8 && Math.abs(delta.y) < 1e-8 && Math.abs(delta.z) < 1e-8 ) ) continue; + // ^^ small (e-9) values seem to get introduced sometimes, perhaps from the intersectionOffset. // todo - "play" factor? Allow a smidgen of xy and maybe some rotation at strong z angles, indicating bend @@ -230,49 +231,44 @@ window.InteractablePlane.prototype = { } else { z = Math.min(z, p1.z - intersectionPoint.z) + 1e-8; } + + if (z > maxZ) maxZ = z; } else { if (p2.z > p1.z) { z = Math.max(z, p1.z - intersectionPoint.z) - 1e-8; } else { z = Math.max(z, p2.z - intersectionPoint.z) - 1e-8; } - } - - zs.push(z); - // find the bone which causes the most movement. - if ( maxi === undefined ){ - maxi = 0; - } else { - if (zs[i] * zs[maxi] < 0) debugger; // unsupported conflicting directions - - if (zs[i] > 0 && zs[i] > zs[maxi]) maxi = i; - if (zs[i] < 0 && zs[i] < zs[maxi]) maxi = i; + if (z < minZ) minZ = z; } i++; } - newPosition.copy(this.mesh.position); + // add the max positive to the max negative + // this means that equals will be near 0 + // but unlike average, a small negative won't halve the positive :-{ + // TODO: this appears jumpy still sometimes. + z = maxZ + minZ; - if ( maxi !== undefined ){ + newPosition.copy(this.mesh.position); - // what happens when this combines with movement constraints? - // todo - check y - i = 0; - for ( var intersectionKey in this.moveProximity.intersectionPoints ) { - if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; + // what happens when this combines with movement constraints? - var intersectionOffset = ns[i].multiplyScalar(zs[maxi] / ns[i].z); + i = 0; + for ( var intersectionKey in this.moveProximity.intersectionPoints ) { + if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; - this.moveProximity.intersectionPoints[intersectionKey].add(intersectionOffset); - i++; - } + var intersectionOffset = ns[i].multiplyScalar(z / ns[i].z); - newPosition.z += zs[maxi]; + this.moveProximity.intersectionPoints[intersectionKey].add(intersectionOffset); + i++; } + newPosition.z += z; + return newPosition }, diff --git a/test/slideZ.html b/test/slideZ.html index 18215c9..05e0ddb 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -71,6 +71,7 @@ var planeGeo = new THREE.PlaneGeometry(0.1, 0.2); +// var planeGeo = new THREE.CircleGeometry(0.1); var material = new THREE.MeshPhongMaterial({side: THREE.DoubleSide, color: 0xff0000}); var planeMesh = new THREE.Mesh(planeGeo, material); planeMesh.position.setX(0.05); @@ -92,13 +93,6 @@ var plane = new InteractablePlane(planeMesh, Leap.loopController, {moveX: true, moveY: true}); - plane.moveProximity.handPoints = function(){ - return [ - [v1, v2], - [v3, v4] - ] - }; - document.getElementById('view-source').href = "view-source:" + window.location.href; document.getElementById('view-source').target = "_blank"; @@ -115,6 +109,14 @@ window.currentTestStep = 0; window.lastTestIndex = null; window.run = function (testName) { + + plane.moveProximity.handPoints = function(hand){ + return [ + [v1, v2], + [v3, v4] + ] + }; + window.lastTestIndex = window.testNames.indexOf(testName); clear(); print('running ' + testName); @@ -127,6 +129,7 @@ }; document.addEventListener('keydown', function (e) { + if (!currentTest) return; console.log('keyDown'); var step = tests[currentTest][currentTestStep]; if (!step) return; @@ -301,6 +304,8 @@ tests.sequentialFrames = [ function(){ setLineX(-0.025); + v1.y += 0.04; + line.geometry.verticesNeedUpdate = true; }, function(){ console.log('x'); @@ -347,7 +352,7 @@ setLine2X(-0.05, 0.07); - window.run('diagonalMotion'); +// window.run('sequentialFrames'); From 0200d1d3bd69d8bb4013b2b74db23d781c7194ab Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 17:08:13 -0800 Subject: [PATCH 11/17] Fix issue where plane would jump to bone ends all the time --- src/InteractablePlane.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 91358ae..3265856 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -207,12 +207,16 @@ window.InteractablePlane.prototype = { // m = rise/run = n.x / n.z // z = n.z / n.x * delta.x z = 0; - if (n.x !==0) z += n.z / n.x * delta.x * -1; - if (n.y !==0) z += n.z / n.y * delta.y * -1; + if (n.x !==0 && Math.abs(n.z / n.x) < 10 ) z += n.z / n.x * delta.x * -1; + if (n.y !==0 && Math.abs(n.z / n.y) < 10 ) z += n.z / n.y * delta.y * -1; + //if ( !(Math.abs(n.z / n.y) < 10) ) { + // console.log('slope too slight', n.z / n.y); + //} + // add to that: y // for edge cases on the x edge, there will naturally be no delta y, yay. - // - if the previous point is projected horizonally + // - if the previous point is projected horizontally // - but that's probably not it - instead, it's projected directly between two points // for edge conditions: From cce1ed109f3607c37bd218e1ffb48e78ff4dd542 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 17:13:48 -0800 Subject: [PATCH 12/17] remove old comments --- src/InteractablePlane.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 3265856..d27e8de 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -165,16 +165,6 @@ window.InteractablePlane.prototype = { var newPosition = (position || new THREE.Vector3).set(0,0,0); var n = new THREE.Vector3; - // todo: - // if two conflicting Z directions... take the part that works with both? - // ... take the strongest, (which will be the one that moves the least?) - // ... z strength is the complement of z distance - // ... only matters when comparing two things of strength infinity - // ... first, catch a test that case (if z1 sign != z2 sign, debugger) - // find the point where two lines cross, that is where the plane should z - // that is they solve for the same point. - // if multiple agreeing Z directions.. take the largest - // todo: factor back in Y movement and officially scrap this.intersections var i = 0, ns = [], z, p1, p2, intersectionPoint, maxZ = 0, minZ = 0; @@ -196,13 +186,6 @@ window.InteractablePlane.prototype = { // todo - "play" factor? Allow a smidgen of xy and maybe some rotation at strong z angles, indicating bend - // only x and z - // var scale = delta.x / n.x; - // sumDelta = n.z * scale - // will work for movement to the right right, but not up - // let's try - // for y - maybe take max of two - // y = mx + b // m = rise/run = n.x / n.z // z = n.z / n.x * delta.x @@ -253,7 +236,6 @@ window.InteractablePlane.prototype = { // add the max positive to the max negative // this means that equals will be near 0 // but unlike average, a small negative won't halve the positive :-{ - // TODO: this appears jumpy still sometimes. z = maxZ + minZ; newPosition.copy(this.mesh.position); From 4dbd051f39953f96ee03c3e367f54b81e0407f85 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 17:32:25 -0800 Subject: [PATCH 13/17] Restore setBoneMeshColor to its former glory --- src/InteractablePlane.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index d27e8de..497c5e6 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -462,18 +462,22 @@ window.InteractablePlane.prototype = { // for every 2 index, we want to add (4 - 2). That will equal the boneMesh index. // not sure if there is a clever formula for the following array: - var indexToBoneMeshIndex = [0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3]; + var indexToBoneMeshIndex = [ + [0,5],[0,3],[0,1], + [1,7],[1,5],[1,3],[1,1], + [2,7],[2,5],[2,3],[2,1], + [3,7],[3,5],[3,3],[3,1], + [4,7],[4,5],[4,3],[4,1] + ]; var setBoneMeshColor = function(hand, index, color){ // In `index / 2`, `2` is the number of joints per hand we're looking at. - var meshes = hand.fingers[ Math.floor(index / 4) ].data('boneMeshes'); + if (!hand.data('handMesh')) return; + var meshes = hand.data('handMesh').fingerMeshes; + var x = indexToBoneMeshIndex[index]; - if (!meshes) return; - - meshes[ - indexToBoneMeshIndex[index] - ].material.color.setHex(color) + meshes[x[0]][x[1]].material.color.setHex(color) }; @@ -494,7 +498,7 @@ window.InteractablePlane.prototype = { // This doesn't allow intersections to count if I'm already pinching // So if they want to move after a pinch, they have to take hand out of picture and re-place. if (hand.data('resizing')) return; - setBoneMeshColor(hand, index, 0xffffff); + setBoneMeshColor(hand, index, 0x00ff00); this.intersections[key] = intersectionPoint.clone().sub(this.mesh.position); From aa727459a3b4e0d111ba6d8040ba8fd105a93956 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Tue, 6 Jan 2015 19:09:16 -0800 Subject: [PATCH 14/17] Fix issue where hand would be holding on outside of box MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .. by adding proximity’s xyRetain option, which is to be used --- src/InteractablePlane.js | 2 ++ src/leap.proximity.js | 12 +++++++++--- test/slideZ.html | 19 ++++++++++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 497c5e6..e65d374 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -166,6 +166,8 @@ window.InteractablePlane.prototype = { var n = new THREE.Vector3; // todo: factor back in Y movement and officially scrap this.intersections + // this may need a proper place (perhaps with getters and setters on moveX and moveY) + this.moveProximity.options.xyRetain = false; var i = 0, ns = [], z, p1, p2, intersectionPoint, maxZ = 0, minZ = 0; diff --git a/src/leap.proximity.js b/src/leap.proximity.js index e3cdf66..2e46770 100644 --- a/src/leap.proximity.js +++ b/src/leap.proximity.js @@ -126,6 +126,10 @@ Leap.plugin('proximity', function(scope){ options || (options = {}); this.options = options; + // This is to be used when the object is mobile on the XY plane, meaning that one wouldn't usually expect this to be let go + // Todo - this could be refactored with something smarter - more constraint-aware. + this.options.xyRetain !== undefined || (this.options.xyRetain = true); + this.mesh = mesh; this.handPoints = handPoints; @@ -267,7 +271,10 @@ Leap.plugin('proximity', function(scope){ // And the new one is valid in z but off in x and y, // don't emit an out event. // This allows high-speed motions out. - if ( !intersectionPoint && this.intersectionPoints[key] && mesh.intersectionPoint ) { + // when there's one bone being dragged out + // and there's no complimenting z motion, this shouldn't fire pretty much.. + // we provide the xyRetain option as a quick fix to disable this feature + if ( this.options.xyRetain && !intersectionPoint && this.intersectionPoints[key] && mesh.intersectionPoint ) { //console.log('found newly lost intersection point'); intersectionPoint = mesh.intersectionPoint @@ -299,7 +306,6 @@ Leap.plugin('proximity', function(scope){ this.possibleIntersectionPoints[key] = mesh.intersectionPoint; // mesh.intersectionPoint may be on plane, but not segment. - } else { delete this.possibleIntersectionPoints[key]; @@ -309,7 +315,7 @@ Leap.plugin('proximity', function(scope){ state = intersectionPoint ? 'in' : 'out'; if ( (state == 'in' && this.states[key] !== 'in') || (state == 'out' && this.states[key] === 'in')){ // this logic prevents initial `out` events. - this.emit(state, hand, intersectionPoint, key, i); // todo - could include intersection displacement vector here (!) + this.emit(state, hand, intersectionPoint, key, i); this.states[key] = state; } diff --git a/test/slideZ.html b/test/slideZ.html index 05e0ddb..77bf1b1 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -171,8 +171,8 @@ // past a bone end √ // past a bone end and on to another bone // rotation in world space - // bone in y - // bone in x and y + // bone in y √ + // bone in x and y √ window.moveLineX = function(amt){ @@ -341,6 +341,19 @@ } ]; + // Due to proximity's smartness, it tries not to let go of bones. As demonstrated: + // highlight should be gone after the test. + tests.removeLine = [ + function(){ + v1.set(0.02,0.1,0.05); + v2.set(0.02,0.1,-0.15); + line.geometry.verticesNeedUpdate = true; + }, + function(){ + moveLineX(-0.05); + } + ] + var testNames = []; for (var key in tests){ if (tests.hasOwnProperty(key)){ @@ -352,7 +365,7 @@ setLine2X(-0.05, 0.07); -// window.run('sequentialFrames'); +// window.run('moveLineInOut'); From e01255d95bd4de25c23e45fdc4848d6fb3a70a91 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Wed, 7 Jan 2015 14:22:45 -0800 Subject: [PATCH 15/17] Disable proximity eager possible intersection, break out minSlideAngle to its own conf option This is important - the eager intersectionPoints were causing massive jumps to bone end. They were designed for xy planar movement, you know. --- src/InteractablePlane.js | 10 ++++++++-- src/leap.proximity.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index e65d374..0f204d7 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -12,6 +12,12 @@ window.InteractablePlane = function(planeMesh, controller, options){ this.options.damping !== undefined || (this.options.damping = 0.12); // this can be configured through this.highlightMesh this.options.hoverBounds !== undefined || (this.options.hoverBounds = [0, 0.32]); // react to hover within 3cm. + // For use with a plane constrained to the z. This configures the threshold with which a hand can push in the z + // By moving with a sloped hand. The number is the slope of the hand (m = rise/run = x/z or y/z). e.g., 1 = 1/1 = 45° + // Turn it up (like to 100) for a more responsive, but jumpy, feel + // Turn it down (like to 5) for something more controllable. + this.options.minSlideAngle !== undefined || (this.options.minSlideAngle = 10); // react to hover within 3cm. + this.mesh = planeMesh; if (!(controller instanceof Leap.Controller)) { @@ -192,10 +198,10 @@ window.InteractablePlane.prototype = { // m = rise/run = n.x / n.z // z = n.z / n.x * delta.x z = 0; - if (n.x !==0 && Math.abs(n.z / n.x) < 10 ) z += n.z / n.x * delta.x * -1; - if (n.y !==0 && Math.abs(n.z / n.y) < 10 ) z += n.z / n.y * delta.y * -1; //if ( !(Math.abs(n.z / n.y) < 10) ) { // console.log('slope too slight', n.z / n.y); + if (n.x !==0 && Math.abs(n.z / n.x) < this.options.minSlideAngle ) z += n.z / n.x * delta.x * -1; + if (n.y !==0 && Math.abs(n.z / n.y) < this.options.minSlideAngle ) z += n.z / n.y * delta.y * -1; //} diff --git a/src/leap.proximity.js b/src/leap.proximity.js index 2e46770..255a723 100644 --- a/src/leap.proximity.js +++ b/src/leap.proximity.js @@ -223,7 +223,7 @@ Leap.plugin('proximity', function(scope){ // This is somewhat edgy (no pun intended): // we overwrite lastIntersection point to be on the edge. although there was never actually a frame emitted // with it as an intersection point. - if ( this.states[key] !== 'in' && intersectionPoint && possibleIntersectionPoint ){ + if ( this.options.xyRetain && this.states[key] !== 'in' && intersectionPoint && possibleIntersectionPoint ){ // check all four edges, // take the one that actually has a cross From 141c1f5a4b3edeed93e9deabf38547003f35150c Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Wed, 7 Jan 2015 14:48:16 -0800 Subject: [PATCH 16/17] Average z travels, rather than taking the most extreme --- src/InteractablePlane.js | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/InteractablePlane.js b/src/InteractablePlane.js index 0f204d7..9d7affc 100644 --- a/src/InteractablePlane.js +++ b/src/InteractablePlane.js @@ -171,11 +171,13 @@ window.InteractablePlane.prototype = { var newPosition = (position || new THREE.Vector3).set(0,0,0); var n = new THREE.Vector3; - // todo: factor back in Y movement and officially scrap this.intersections + // todo: factor back in XY movement and officially scrap this.intersections // this may need a proper place (perhaps with getters and setters on moveX and moveY) this.moveProximity.options.xyRetain = false; - var i = 0, ns = [], z, p1, p2, intersectionPoint, maxZ = 0, minZ = 0; + var sumZ = 0; + + var i = 0, ns = [], z = 0, p1, p2, intersectionPoint; for ( var intersectionKey in this.moveProximity.intersectionPoints ) { if( !this.moveProximity.intersectionPoints.hasOwnProperty(intersectionKey) ) continue; @@ -198,53 +200,31 @@ window.InteractablePlane.prototype = { // m = rise/run = n.x / n.z // z = n.z / n.x * delta.x z = 0; - //if ( !(Math.abs(n.z / n.y) < 10) ) { - // console.log('slope too slight', n.z / n.y); if (n.x !==0 && Math.abs(n.z / n.x) < this.options.minSlideAngle ) z += n.z / n.x * delta.x * -1; if (n.y !==0 && Math.abs(n.z / n.y) < this.options.minSlideAngle ) z += n.z / n.y * delta.y * -1; - //} - - - // add to that: y - // for edge cases on the x edge, there will naturally be no delta y, yay. - // - if the previous point is projected horizontally - // - but that's probably not it - instead, it's projected directly between two points - - // for edge conditions: - // the above only looks at z from x, which happens to work due to the geo arrangement of our tests - // We also need to look at y - // and actually rotate the line vector be be square to the edge. - // and then always compare against, say, its x dimension. - // to work with squares, circles, etc. - // we only care about delta in the normal, and line slope in the normal. // Don't move farther than bone end - // note: there's got to be a better way of doing this :-/ + // note: there's got to be a way to clean this up a little if ( z > 0 ) { if (p2.z > p1.z) { z = Math.min(z, p2.z - intersectionPoint.z) + 1e-8; } else { z = Math.min(z, p1.z - intersectionPoint.z) + 1e-8; } - - if (z > maxZ) maxZ = z; } else { if (p2.z > p1.z) { z = Math.max(z, p1.z - intersectionPoint.z) - 1e-8; } else { z = Math.max(z, p2.z - intersectionPoint.z) - 1e-8; } - - if (z < minZ) minZ = z; } + sumZ += z; i++; } - // add the max positive to the max negative - // this means that equals will be near 0 - // but unlike average, a small negative won't halve the positive :-{ - z = maxZ + minZ; + // average the inputs + if (i > 0) z = sumZ / i; newPosition.copy(this.mesh.position); From 1d11989c7100437e517ebf309e02f1c811ecf2f2 Mon Sep 17 00:00:00 2001 From: Peter Ehrlich Date: Wed, 7 Jan 2015 14:53:26 -0800 Subject: [PATCH 17/17] Add quick circleGeo test --- test/slideZ.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/slideZ.html b/test/slideZ.html index 77bf1b1..173ab9f 100644 --- a/test/slideZ.html +++ b/test/slideZ.html @@ -71,7 +71,7 @@ var planeGeo = new THREE.PlaneGeometry(0.1, 0.2); -// var planeGeo = new THREE.CircleGeometry(0.1); +// var planeGeo = new THREE.CircleGeometry(0.1, 32); var material = new THREE.MeshPhongMaterial({side: THREE.DoubleSide, color: 0xff0000}); var planeMesh = new THREE.Mesh(planeGeo, material); planeMesh.position.setX(0.05); @@ -82,6 +82,7 @@ var base = new THREE.Mesh(new THREE.BoxGeometry(0.1, longThrow, longThrow), new THREE.MeshPhongMaterial({color: 0x222222})); base.position.set(0.05, 0.05, -0.1); + // (Skip this positioning for circlegeo) planeMesh.position.set( 0, planeMesh.geometry.parameters.height / 2 - longThrow / 2,