From cffb0be38487e0a38f83dd5a8081eef0d0efd011 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Aug 2015 22:49:14 -0700 Subject: [PATCH] Removing edge finding calculations --- examples/toys/magSticks.js | 2 +- examples/toys/magSticks/graph.js | 7 + examples/toys/magSticks/handController.js | 32 +- examples/toys/magSticks/highlighter.js | 7 + examples/toys/magSticks/magBalls.js | 376 +------------------- examples/toys/magSticks/springEdgeEntity.js | 34 +- examples/toys/magSticks/utils.js | 7 + 7 files changed, 47 insertions(+), 418 deletions(-) diff --git a/examples/toys/magSticks.js b/examples/toys/magSticks.js index 9461ffd047..1c51d1d0da 100644 --- a/examples/toys/magSticks.js +++ b/examples/toys/magSticks.js @@ -16,7 +16,7 @@ Script.include("magSticks/handController.js"); var magBalls = new MagBalls(); // Clear any previous balls -magBalls.clear(); +// magBalls.clear(); // How close do we need to be to a ball to select it.... radius + 10% var BALL_SELECTION_RADIUS = BALL_SIZE / 2.0 * 1.5; diff --git a/examples/toys/magSticks/graph.js b/examples/toys/magSticks/graph.js index 8a5c00451d..8ea9f97fc3 100644 --- a/examples/toys/magSticks/graph.js +++ b/examples/toys/magSticks/graph.js @@ -1,3 +1,10 @@ +// +// Created by Bradley Austin Davis on 2015/08/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// // A collection of nodes and edges connecting them. Graph = function() { diff --git a/examples/toys/magSticks/handController.js b/examples/toys/magSticks/handController.js index cf3ef96b4a..57bef9be4a 100644 --- a/examples/toys/magSticks/handController.js +++ b/examples/toys/magSticks/handController.js @@ -1,28 +1,14 @@ - +// +// Created by Bradley Austin Davis on 2015/08/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// LEFT_CONTROLLER = 0; RIGHT_CONTROLLER = 1; -WAND_LIFETIME = 30; - -var WAND_PROPERTIES = { - type: "Model", - modelURL: "file:///f:/Downloads/wand.FBX", - ignoreForCollisions: true, - dimensions: { - x: 0.1, - y: 0.1, - z: 0.1 - }, - lifetime: 30 -}; - -if (!Date.now) { - Date.now = function now() { - return new Date().getTime(); - }; -} - - +// FIXME add a customizable wand model and a mechanism to switch between wands HandController = function(side) { this.side = side; this.palm = 2 * side; @@ -38,9 +24,7 @@ HandController = function(side) { solid: true, visible: false, }); - //this.wand = Entities.addEntity(WAND_PROPERTIES); - // Connect to desired events var _this = this; Controller.actionEvent.connect(function(action, state) { diff --git a/examples/toys/magSticks/highlighter.js b/examples/toys/magSticks/highlighter.js index 7316549c84..149d9ec5b7 100644 --- a/examples/toys/magSticks/highlighter.js +++ b/examples/toys/magSticks/highlighter.js @@ -1,3 +1,10 @@ +// +// Created by Bradley Austin Davis on 2015/08/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// var SELECTION_OVERLAY = { position: { x: 0, diff --git a/examples/toys/magSticks/magBalls.js b/examples/toys/magSticks/magBalls.js index 9a0465c582..ca843fc2ef 100644 --- a/examples/toys/magSticks/magBalls.js +++ b/examples/toys/magSticks/magBalls.js @@ -1,3 +1,10 @@ +// +// Created by Bradley Austin Davis on 2015/08/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// var BALL_NAME = "MagBall" var EDGE_NAME = "MagStick" @@ -70,14 +77,7 @@ var EDGE_PROTOTYPE = LINE_PROTOTYPE; // A collection of balls and edges connecting them. MagBalls = function() { this.selectedNodes = {}; - Graph.call(this); - - var _this = this; - Script.update.connect(function(deltaTime) { - _this.onUpdate(deltaTime); - }); - Script.scriptEnding.connect(function() { _this.onCleanup(); }); @@ -94,10 +94,10 @@ MagBalls.prototype.createNodeEntity = function(customProperies) { return Entities.addEntity(mergeObjects(BALL_PROTOTYPE, customProperies)); } -MagBalls.prototype.getEdgeProperties = function(nodeIdA, nodeIdB) { +MagBalls.prototype.createEdgeEntity = function(nodeIdA, nodeIdB) { var apos = this.getNodePosition(nodeIdA); var bpos = this.getNodePosition(nodeIdB); - return { + return Entities.addEntity(mergeObjects(EDGE_PROTOTYPE, { position: apos, linePoints: [ ZERO_VECTOR, Vec3.subtract(bpos, apos) ], userData: JSON.stringify({ @@ -107,12 +107,7 @@ MagBalls.prototype.getEdgeProperties = function(nodeIdA, nodeIdB) { length: BALL_DISTANCE } }) - }; -} - -MagBalls.prototype.createEdgeEntity = function(nodeIdA, nodeIdB) { - var customProperties = this.getEdgeProperties(nodeIdA, nodeIdB) - return Entities.addEntity(mergeObjects(EDGE_PROTOTYPE, customProperties)); + })); } MagBalls.prototype.findPotentialEdges = function(nodeId) { @@ -178,340 +173,10 @@ MagBalls.prototype.releaseBall = function(releasedBall) { for (var otherBallId in targets) { this.createEdge(otherBallId, releasedBall); } - this.clean(); - return; - - // sort other balls by distance from the stick length - var createdEdge = false; - while (true) { - // Get the list of candidate connections - var variances = this.findPotentialEdges(releasedBall); - // sort them by the difference from an ideal distance - var targetBalls = Object.keys(variances); - if (!targetBalls.length) { - break; - } - - // special case when there are 2 potential matches, - // try to create a ring on a plane - if (targetBalls.length == 2 && this.tryCreateRing(targetBalls, releasedBall)) { - createdEdge = true; - break; - } - - // special case when there are 3 potential matches, - // try create a fan - if (targetBalls.length == 3 && this.tryCreateFan(targetBalls, releasedBall)) { - createdEdge = true; - break; - } - - targetBalls.sort(function(a, b){ - return variances[a] - variances[b]; - }); - - // find the nearest matching unconnected ball and create an edge to it - // if possible - // note that createEdge will preferentially move the second entity, the - // released ball - // in order to create a fit - var str = "Attempt to create edge between " + targetBalls[0] + " and " + releasedBall; - if (!this.tryCreateEdge(targetBalls[0], releasedBall)) { - logDebug(str + "failed"); - var nodeDistance = this.getNodeDistance(targetBalls[0], releasedBall); - var variance = this.getVariance(nodeDistance); - logDebug("Distance was " + (nodeDistance * 100).toFixed(2) + "cm with a variance of " + variance); - break; - } - logDebug(str + " succeeded"); - - releasePosition = this.getNodePosition(releasedBall); - - // Record that we created at least one edge - createdEdge = true; - } - - if (createdEdge) { - // FIXME play a snap sound - } - this.clean(); this.validate(); } -MagBalls.prototype.tryCreateEdge = function(from, to) { - var fromPos = this.getNodePosition(from); - var toPos = this.getNodePosition(to); - var vector = Vec3.subtract(toPos, fromPos); - var originalLength = Vec3.length(vector); - - var variance = this.getVariance(originalLength); - // if they're already at a close enough distance, just create the edge - if (variance < EPSILON) { - logDebug("Length " + originalLength + " with variance of " + (variance * 100).toFixed(2) + " is within epislon " + EPSILON) ; - // close enough for government work - this.createEdge(from, to); - return true; - } - - // FIXME find the constraints on `from` and `to` and determine if there is a - // new positiong - // for 'to' that keeps it's current connections and connects with 'from' - // Do only first order scanning for now, unless we can expose a mechanism - // for interacting - // to reach equilibrium via Bullet - // * a ball zero edges is fully free... - // * a ball with one edge free to move on a sphere section - // * a ball with two edges is free to move in a circle - // * a ball with more than two edges is not free to move - - // Zero edges for the destination - var existingEdges = Object.keys(this.nodes[to]); - var edgeCount = existingEdges.length; - if (!edgeCount) { - // Easy case 1: unconnected ball - // Move the ball along it's current path to match the desired distance - vector = Vec3.multiply(BALL_DISTANCE, Vec3.normalize(vector)); - // Add the vector to the starting position to find the new position - var newPosition = Vec3.sum(vector, fromPos); - // update the entity - Entities.editEntity(to, { position: newPosition }); - moved = true; - } else if (edgeCount > 2) { - // Easy case 2: locked position ball - // FIXME should check the target ball to see if it can be moved. - // Possible easy solution is to recurse into this.createEdge and swap - // the parameters, - // but need to prevert infinite recursion - // for now... - return false; - } else { - var connectedBalls = this.getConnectedNodes(to); - // find the other balls connected, will be either 1 or 2 - var origin = { x: 0, y: 0, z: 0 }; - for (var nodeId in connectedBalls) { - origin = Vec3.sum(origin, this.getNodePosition(nodeId)); - } - - if (edgeCount > 1) { - origin = Vec3.multiply(origin, 1 / edgeCount); - } - // logDebug("Using origin " + vec3toStr(origin)); - - if (edgeCount == 1) { - // vectors from the temp origin to the two balls. - var v1 = Vec3.subtract(toPos, origin); - var v2 = Vec3.subtract(fromPos, origin); - - // ortogonal to the solution plane - var o1 = Vec3.normalize(Vec3.cross(Vec3.normalize(v2), Vec3.normalize(v1))); - // addLine(origin, o1, COLORS.RED); // debugging - - // orthogonal to o1, lying on the solution plane - var o2 = Vec3.normalize(Vec3.cross(o1, Vec3.normalize(v2))); - // addLine(origin, o2, COLORS.YELLOW); // debugging - - // The adjacent side of a right triangle containg the - // solution as one of the points - var v3 = Vec3.multiply(0.5, v2); - - // The length of the adjacent side of the triangle - var l1 = Vec3.length(v3); - // The length of the hypotenuse - var r = BALL_DISTANCE; - - // No connection possible - if (l1 > r) { - return false; - } - - // The length of the opposite side - var l2 = Math.sqrt(r * r - l1 * l1); - - // vector with the length and direction of the opposite side - var v4 = Vec3.multiply(l2, Vec3.normalize(o2)); - - // Add the adjacent vector and the opposite vector to get the - // hypotenuse vector - var result = Vec3.sum(v3, v4); - // move back into world space - result = Vec3.sum(origin, result); - // update the entity - Entities.editEntity(to, { position: result }); - } else { - // Has a bug of some kind... validation fails after this - - // Debugging marker - //Entities.addEntity(mergeObjects(BALL_PROTOTYPE, { - // position: origin, - // color: COLORS.YELLOW, - // dimensions: Vec3.multiply(0.4, BALL_DIMENSIONS) - //})); - - var v1 = Vec3.subtract(fromPos, origin); - // addLine(origin, v1, COLORS.RED); // debugging - - - var v2 = Vec3.subtract(toPos, origin); - // addLine(origin, v2, COLORS.GREEN); // debugging - - // the lengths of v1 and v2 represent the lengths of two sides - // of the triangle we need to build. - var l1 = Vec3.length(v1); - var l2 = Vec3.length(v2); - // The remaining side is the edge we are trying to create, so - // it will be of length BALL_DISTANCE - var l3 = BALL_DISTANCE; - // given this triangle, we want to know the angle between l1 and l2 - // (this is NOT the same as the angle between v1 and v2, because we - // are trying to rotate v2 around o1 to find a solution - // Use law of cosines to find the angle: cos A = (b^2 + c^2 − a^2) / - // 2bc - var cosA = (l1 * l1 + l2 * l2 - l3 * l3) / (2.0 * l1 * l2); - - // Having this angle gives us all three angles of the right triangle - // containing - // the solution, along with the length of the hypotenuse, which is - // l2 - var hyp = l2; - // We need to find the length of the adjacent and opposite sides - // since cos(A) = adjacent / hypotenuse, then adjacent = hypotenuse - // * cos(A) - var adj = hyp * cosA; - // Pythagoras gives us the opposite side length - var opp = Math.sqrt(hyp * hyp - adj * adj); - - // v1 is the direction vector we need for the adjacent side, so - // resize it to - // the proper length - v1 = Vec3.multiply(adj, Vec3.normalize(v1)); - // addLine(origin, v1, COLORS.GREEN); // debugging - - // FIXME, these are not the right normals, because the ball needs to rotate around the origin - - // This is the normal to the plane on which our solution lies - var o1 = Vec3.cross(v1, v2); - - // Our final side is a normal to the plane defined by o1 and v1 - // and is of length opp - var o2 = Vec3.multiply(opp, Vec3.normalize(Vec3.cross(o1, v1))); - - // Our final result is the sum of v1 and o2 (opposite side vector + - // adjacent side vector) - var result = Vec3.sum(v1, o2); - // Move back into world space - result = Vec3.sum(origin, result); - // update the entity - Entities.editEntity(to, { position: result }); - } - } - - // Fixup existing edges if we moved the ball - for (var edgeId in this.nodes[to]) { - this.fixupEdge(edgeId); - } - - this.createEdge(from, to); - return true; -} - -MagBalls.prototype.tryCreateRing = function(fromBalls, to) { - // FIXME, if the user tries to connect two points, attempt to - // walk the graph and see if they're creating a ring of 4 or - // more vertices, if so and they're within N percent of lying - // on a plane, then adjust them all so they lie on a plance - return false; -} - -function pausecomp(millis) { - var date = new Date(); - var curDate = null; - do { curDate = new Date(); } while(curDate-date < millis); -} - -// find a normal between three points -function findNormal(a, b, c) { - var aa = Vec3.subtract(a, b); - var cc = Vec3.subtract(c, b); - return Vec3.cross(aa, cc); -} - -MagBalls.prototype.tryCreateFan = function(fromBalls, to) { - logDebug("Attempting to create fan"); - // if the user tries to connect three points, attempt to - // walk the graph and see if they're creating fan, adjust all the - // points to lie on a plane an equidistant from the shared vertex - - // A fan may exist if given three potential connections, two of the connection - // share and edge with the third - var a = fromBalls[0]; - var b = fromBalls[1]; - var c = fromBalls[2]; - var ab = this.areConnected(a, b); - var bc = this.areConnected(b, c); - var ca = this.areConnected(c, a); - if (ab && bc && ca) { - // tetrahedron, let the generic code handle it - return false; - } - var crux = null; - var left = null; - var right = null; - if (ab && bc) { - crux = b; - left = a; - right = c; - } else if (bc && ca) { - crux = a; - left = b; - right = a; - } else if (ca && ab) { - crux = a; - left = c; - right = b; - } - if (crux == null) { - // we don't have two nodes which share edges with the third, so fail - return false; - } - var loop = this.findShortestPath(left, right, { exclude: crux }); - if (!loop) { - return false; - } - - // find the normal to the target plane - var origin = this.getNodePosition(crux); - var normals = []; - var averageNormal = ZERO_VECTOR; - for (var i = 0; i < loop.length - 2; ++i) { - var a = loop[i]; - var b = loop[i + 1]; - var c = loop[i + 2]; - var apos = this.getNodePosition(a); - var bpos = this.getNodePosition(b); - var cpos = this.getNodePosition(c); - var normal = Vec3.normalize(findNormal(apos, bpos, cpos)); - averageNormal = Vec3.sum(averageNormal, normal); - addLine(bpos, normal, COLORS.YELLOW); - normals.push(normal); - } - averageNormal = Vec3.normalize(Vec3.multiply(1 / normals.length, averageNormal)); - - addLine(origin, averageNormal, COLORS.RED); - - // FIXME need to account for locked nodes... if there are 3 locked nodes on the loop, - // then find their cross product - // if there are more than 3 locked nodes on the loop, check if they have matching cross - // products, otherwise fail - - return false; -} - -MagBalls.prototype.fixupEdge = function(edgeId) { - var ballsInEdge = Object.keys(this.edges[edgeId]); - var customProperties = this.getEdgeProperties(ballsInEdge[0], ballsInEdge[1]); - Entities.editEntity(edgeId, customProperties); -} MagBalls.prototype.getVariance = function(distance) { // Given two points, how big is the difference between their distance @@ -550,24 +215,3 @@ MagBalls.prototype.clear = function() { }, this); } } - -// Override to check lengths as well as connection consistency -MagBalls.prototype.validate = function() { - var error = Graph.prototype.validate.call(this); - - if (!error) { - for (edgeId in this.edges) { - var length = this.getEdgeLength(edgeId); - var variance = this.getVariance(length); - if (variance > EPSILON) { - Entities.editEntity(edgeId, { color: COLORS.RED }); - - logDebug("Edge " + edgeId + " length " + (length * 100).toFixed(2) + " cm variance " + (variance * 100).toFixed(3) + "%"); - error = true; - } - } - if (error) { - logDebug(EPSILON); - } - } -} diff --git a/examples/toys/magSticks/springEdgeEntity.js b/examples/toys/magSticks/springEdgeEntity.js index 9e0ebd2115..56ef55e5d2 100644 --- a/examples/toys/magSticks/springEdgeEntity.js +++ b/examples/toys/magSticks/springEdgeEntity.js @@ -6,7 +6,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - (function(){ this.preload = function(entityId) { this.MIN_CHECK_INTERVAL = 0.05; @@ -18,26 +17,22 @@ var userData = JSON.parse(properties.userData); this.start = userData.magBalls.start; this.end = userData.magBalls.end; - this.originalColor = properties.color; this.desiredLength = userData.magBalls.length; this.timeSinceLastUpdate = 0; this.nextCheckInterval = this.MIN_CHECK_INTERVAL; - print("preload("+entityId+") " + this.start + " -> " + this.end + " " + this.desiredLength); - + // FIXME do I really need to do this nonsense? var _this = this; this.updateWrapper = function(deltaTime) { _this.onUpdate(deltaTime); }; Script.update.connect(this.updateWrapper); + Script.scriptEnding.connect(function() { _this.onCleanup(); }); - Entities.deletingEntity.connect(function(entityId) { - if (_this.entityId == entityId) { - _this.onCleanup(); - } + _this.onCleanup(); }); }; @@ -57,20 +52,12 @@ return; } this.decrementCheckInterval(); - print("Length is wrong: " + (length * 100).toFixed(1) + "cm variance " + variance); - var adjustmentVector = Vec3.multiply(variance / 4, this.vector); var newPosition = Vec3.sum(Vec3.multiply(-1, adjustmentVector), this.position); var newVector = Vec3.sum(Vec3.multiply(2, adjustmentVector), this.vector); var newLength = Vec3.length(newVector); var newVariance = this.getVariance(newLength); - var color = { color: this.originalColor } - if (Math.abs(newVariance) > this.MAX_VARIANCE) { - color = { red: 255, green: 0, blue: 0 }; - } - print("Updating entity to new variance " + newVariance); Entities.editEntity(this.entityId, { - color: color, position: newPosition, linePoints: [ this.ZERO_VECTOR, newVector ] }); @@ -80,7 +67,6 @@ Entities.editEntity(this.end, { position: Vec3.sum(newPosition, newVector) }); - } } @@ -93,8 +79,10 @@ } this.onCleanup = function() { - print("Stopping spring script"); - Script.update.disconnect(this.updateWrapper); + if (this.updateWrapper) { + Script.update.disconnect(this.updateWrapper); + delete this.updateWrapper; + } } this.getVariance = function(length) { @@ -132,12 +120,4 @@ } this.position = startPos; } - - this.enterEntity = function(entityId) { - print("enterEntity("+entityId+")"); - }; - - this.leaveEntity = function(entityId) { - print("leaveEntity("+entityId+")"); - }; }); diff --git a/examples/toys/magSticks/utils.js b/examples/toys/magSticks/utils.js index 5eaf1791fb..8a522ac41f 100644 --- a/examples/toys/magSticks/utils.js +++ b/examples/toys/magSticks/utils.js @@ -1,3 +1,10 @@ +// +// Created by Bradley Austin Davis on 2015/08/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// vec3toStr = function (v, digits) { if (!digits) { digits = 3; }