mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 09:24:00 +02:00
Magnetic sticks and balls
This commit is contained in:
parent
08986dcb17
commit
38b62108af
6 changed files with 767 additions and 0 deletions
92
examples/toys/magSticks.js
Normal file
92
examples/toys/magSticks.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/25
|
||||
// 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
|
||||
//
|
||||
|
||||
Script.include("magSticks/utils.js");
|
||||
Script.include("magSticks/constants.js");
|
||||
Script.include("magSticks/magBalls.js");
|
||||
Script.include("magSticks/highlighter.js");
|
||||
Script.include("magSticks/handController.js");
|
||||
|
||||
var magBalls = new MagBalls();
|
||||
|
||||
// Clear any previous balls
|
||||
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.1;
|
||||
|
||||
BallController = function(side) {
|
||||
HandController.call(this, side);
|
||||
this.highlighter = new Highlighter();
|
||||
this.highlighter.setSize(BALL_SIZE);
|
||||
this.ghostEdges = {};
|
||||
}
|
||||
|
||||
BallController.prototype = Object.create( HandController.prototype );
|
||||
|
||||
BallController.prototype.onUpdate = function(deltaTime) {
|
||||
HandController.prototype.updateControllerState.call(this, deltaTime);
|
||||
if (!this.selected) {
|
||||
// Find the highlight target and set it.
|
||||
var target = magBalls.findNearestBall(this.tipPosition, BALL_SELECTION_RADIUS);
|
||||
this.highlighter.highlight(target);
|
||||
return;
|
||||
}
|
||||
this.highlighter.highlight(null);
|
||||
Entities.editEntity(this.selected, { position: this.tipPosition });
|
||||
var targetBalls = magBalls.findMatches(this.selected);
|
||||
for (var ballId in targetBalls) {
|
||||
if (!this.ghostEdges[ballId]) {
|
||||
// create the ovleray
|
||||
this.ghostEdges[ballId] = Overlays.addOverlay("line3d", {
|
||||
start: magBalls.getBallPosition(ballId),
|
||||
end: this.tipPosition,
|
||||
color: { red: 255, green: 0, blue: 0},
|
||||
alpha: 1,
|
||||
lineWidth: 5,
|
||||
visible: true,
|
||||
});
|
||||
} else {
|
||||
Overlays.editOverlay(this.ghostEdges[ballId], {
|
||||
end: this.tipPosition,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (var ballId in this.ghostEdges) {
|
||||
if (!targetBalls[ballId]) {
|
||||
Overlays.deleteOverlay(this.ghostEdges[ballId]);
|
||||
delete this.ghostEdges[ballId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BallController.prototype.onClick = function() {
|
||||
this.selected = magBalls.grabBall(this.tipPosition, BALL_SELECTION_RADIUS);
|
||||
this.highlighter.highlight(null);
|
||||
}
|
||||
|
||||
BallController.prototype.onRelease = function() {
|
||||
this.clearGhostEdges();
|
||||
magBalls.releaseBall(this.selected);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
BallController.prototype.clearGhostEdges = function() {
|
||||
for(var ballId in this.ghostEdges) {
|
||||
Overlays.deleteOverlay(this.ghostEdges[ballId]);
|
||||
delete this.ghostEdges[ballId];
|
||||
}
|
||||
}
|
||||
|
||||
BallController.prototype.onCleanup = function() {
|
||||
HandController.prototype.updateControllerState.call(this);
|
||||
this.clearGhostEdges();
|
||||
}
|
||||
|
||||
// FIXME resolve some of the issues with dual controllers before allowing both controllers active
|
||||
var handControllers = [new BallController(LEFT_CONTROLLER)]; //, new HandController(RIGHT) ];
|
16
examples/toys/magSticks/constants.js
Normal file
16
examples/toys/magSticks/constants.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/27
|
||||
// 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
|
||||
//
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
STICK_URL = HIFI_PUBLIC_BUCKET + "models/props/geo_stick.fbx";
|
||||
|
||||
// FIXME make this editable through some script UI, so the user can customize the size of the structure built
|
||||
SCALE = 0.5;
|
||||
BALL_SIZE = 0.08 * SCALE;
|
||||
STICK_LENGTH = 0.24 * SCALE;
|
||||
|
94
examples/toys/magSticks/handController.js
Normal file
94
examples/toys/magSticks/handController.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
|
||||
LEFT_CONTROLLER = 0;
|
||||
RIGHT_CONTROLLER = 1;
|
||||
|
||||
|
||||
HandController = function(side) {
|
||||
this.side = side;
|
||||
this.palm = 2 * side;
|
||||
this.tip = 2 * side + 1;
|
||||
this.action = findAction(side ? "ACTION2" : "ACTION1");
|
||||
this.active = false;
|
||||
this.pointer = Overlays.addOverlay("sphere", {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
size: 0.01,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 0
|
||||
},
|
||||
alpha: 1.0,
|
||||
solid: true,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
// Connect to desired events
|
||||
var _this = this;
|
||||
Controller.actionEvent.connect(function(action, state) {
|
||||
_this.onActionEvent(action, state);
|
||||
});
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
_this.onUpdate(deltaTime);
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
_this.onCleanup();
|
||||
});
|
||||
}
|
||||
|
||||
HandController.prototype.onActionEvent = function(action, state) {
|
||||
if (action == this.action) {
|
||||
if (state) {
|
||||
this.onClick();
|
||||
} else {
|
||||
this.onRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandController.prototype.setActive = function(active) {
|
||||
if (active == this.active) {
|
||||
return;
|
||||
}
|
||||
debugPrint("Setting active: " + active);
|
||||
this.active = active;
|
||||
Overlays.editOverlay(this.pointer, {
|
||||
position: this.tipPosition,
|
||||
visible: this.active
|
||||
});
|
||||
}
|
||||
|
||||
HandController.prototype.updateControllerState = function() {
|
||||
var palmPos = Controller.getSpatialControlPosition(this.palm);
|
||||
// When on the base hydras report a position of 0
|
||||
this.setActive(Vec3.length(palmPos) > 0.001);
|
||||
if (!this.active) {
|
||||
return;
|
||||
}
|
||||
var tipPos = Controller.getSpatialControlPosition(this.tip);
|
||||
this.tipPosition = scaleLine(palmPos, tipPos, 1.4);
|
||||
Overlays.editOverlay(this.pointer, {
|
||||
position: this.tipPosition
|
||||
});
|
||||
}
|
||||
|
||||
HandController.prototype.onCleanup = function() {
|
||||
Overlays.deleteOverlay(this.pointer);
|
||||
}
|
||||
|
||||
HandController.prototype.onUpdate = function(deltaTime) {
|
||||
this.updateControllerState();
|
||||
}
|
||||
|
||||
HandController.prototype.onClick = function() {
|
||||
debugPrint("Base hand controller does nothing on click");
|
||||
}
|
||||
|
||||
HandController.prototype.onRelease = function() {
|
||||
debugPrint("Base hand controller does nothing on release");
|
||||
}
|
63
examples/toys/magSticks/highlighter.js
Normal file
63
examples/toys/magSticks/highlighter.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
var SELECTION_OVERLAY = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 0
|
||||
},
|
||||
alpha: 1,
|
||||
size: 1.0,
|
||||
solid: false,
|
||||
//colorPulse: 1.0,
|
||||
//pulseMin: 0.5,
|
||||
//pulseMax: 1.0,
|
||||
visible: false,
|
||||
lineWidth: 1.0,
|
||||
borderSize: 1.4,
|
||||
};
|
||||
|
||||
Highlighter = function() {
|
||||
this.highlightCube = Overlays.addOverlay("cube", this.SELECTION_OVERLAY);
|
||||
this.hightlighted = null;
|
||||
var _this = this;
|
||||
Script.scriptEnding.connect(function() {
|
||||
_this.onCleanup();
|
||||
});
|
||||
};
|
||||
|
||||
Highlighter.prototype.onCleanup = function() {
|
||||
Overlays.deleteOverlay(this.highlightCube);
|
||||
}
|
||||
|
||||
Highlighter.prototype.highlight = function(entityId) {
|
||||
if (entityId != this.hightlighted) {
|
||||
this.hightlighted = entityId;
|
||||
this.updateHighlight();
|
||||
}
|
||||
}
|
||||
|
||||
Highlighter.prototype.setSize = function(newSize) {
|
||||
Overlays.editOverlay(this.highlightCube, {
|
||||
size: newSize
|
||||
});
|
||||
}
|
||||
|
||||
Highlighter.prototype.updateHighlight = function() {
|
||||
if (this.hightlighted) {
|
||||
var properties = Entities.getEntityProperties(this.hightlighted);
|
||||
// logDebug("Making highlight " + this.highlightCube + " visible @ " + vec3toStr(properties.position));
|
||||
Overlays.editOverlay(this.highlightCube, {
|
||||
position: properties.position,
|
||||
visible: true
|
||||
});
|
||||
} else {
|
||||
// logDebug("Making highlight invisible");
|
||||
Overlays.editOverlay(this.highlightCube, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}
|
454
examples/toys/magSticks/magBalls.js
Normal file
454
examples/toys/magSticks/magBalls.js
Normal file
|
@ -0,0 +1,454 @@
|
|||
var BALL_NAME = "MagBall"
|
||||
|
||||
var EDGE_NAME = "MagStick"
|
||||
|
||||
var BALL_DIMENSIONS = {
|
||||
x: BALL_SIZE,
|
||||
y: BALL_SIZE,
|
||||
z: BALL_SIZE
|
||||
};
|
||||
|
||||
var BALL_COLOR = {
|
||||
red: 128,
|
||||
green: 128,
|
||||
blue: 128
|
||||
};
|
||||
|
||||
var STICK_DIMENSIONS = {
|
||||
x: STICK_LENGTH / 6,
|
||||
y: STICK_LENGTH / 6,
|
||||
z: STICK_LENGTH
|
||||
};
|
||||
|
||||
var BALL_DISTANCE = STICK_LENGTH + BALL_SIZE;
|
||||
|
||||
var BALL_PROTOTYPE = {
|
||||
type: "Sphere",
|
||||
name: BALL_NAME,
|
||||
dimensions: BALL_DIMENSIONS,
|
||||
color: BALL_COLOR,
|
||||
ignoreCollisions: true,
|
||||
collisionsWillMove: false
|
||||
};
|
||||
|
||||
// A collection of balls and edges connecting them.
|
||||
MagBalls = function() {
|
||||
/*
|
||||
this.balls: {
|
||||
ballId1: {
|
||||
edgeId1: true
|
||||
}
|
||||
ballId2: {
|
||||
edgeId1: true
|
||||
},
|
||||
ballId3: {
|
||||
edgeId2: true
|
||||
edgeId3: true
|
||||
edgeId4: true
|
||||
edgeId5: true
|
||||
},
|
||||
...
|
||||
}
|
||||
*/
|
||||
this.balls = {};
|
||||
/*
|
||||
this.edges: {
|
||||
edgeId1: {
|
||||
ballId1: true
|
||||
ballId2: true
|
||||
},
|
||||
edgeId2: {
|
||||
ballId3: true
|
||||
ballId4: true
|
||||
},
|
||||
...
|
||||
}
|
||||
*/
|
||||
|
||||
// FIXME initialize from nearby entities
|
||||
this.edges = {};
|
||||
this.selectedBalls = {};
|
||||
|
||||
var _this = this;
|
||||
Script.update.connect(function(deltaTime) {
|
||||
_this.onUpdate(deltaTime);
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
_this.onCleanup();
|
||||
});
|
||||
}
|
||||
|
||||
MagBalls.prototype.findNearestBall = function(position, maxDist) {
|
||||
var resultId = null;
|
||||
var resultDist = 0;
|
||||
for (var id in this.balls) {
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
var curDist = Vec3.distance(properties.position, position);
|
||||
if (!maxDist || curDist <= maxDist) {
|
||||
if (!resultId || curDist < resultDist) {
|
||||
resultId = id;
|
||||
resultDist = curDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultId;
|
||||
}
|
||||
|
||||
// FIXME move to a physics based implementation as soon as bullet
|
||||
// is exposed to entities
|
||||
MagBalls.prototype.onUpdate = function(deltaTime) {
|
||||
}
|
||||
|
||||
function mergeObjects(proto, custom) {
|
||||
var result = {};
|
||||
for (var attrname in proto) {
|
||||
result[attrname] = proto[attrname];
|
||||
}
|
||||
for (var attrname in custom) {
|
||||
result[attrname] = custom[attrname];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MagBalls.prototype.createBall = function(customProperies) {
|
||||
var ballId = Entities.addEntity(mergeObjects(BALL_PROTOTYPE, customProperies));
|
||||
this.balls[ballId] = {};
|
||||
this.validate();
|
||||
return ballId;
|
||||
}
|
||||
|
||||
MagBalls.prototype.grabBall = function(position, maxDist) {
|
||||
var selected = this.findNearestBall(position, maxDist);
|
||||
if (!selected) {
|
||||
selected = this.createBall({ position: position });
|
||||
}
|
||||
if (selected) {
|
||||
this.breakEdges(selected);
|
||||
this.selectedBalls[selected] = true;
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
MagBalls.prototype.findMatches = function(ballId) {
|
||||
var variances = {};
|
||||
for (var otherBallId in this.balls) {
|
||||
if (otherBallId == ballId || this.areConnected(otherBallId, ballId)) {
|
||||
// can't self connect or doubly connect
|
||||
continue;
|
||||
}
|
||||
var variance = this.getStickLengthVariance(ballId, otherBallId);
|
||||
if (variance > BALL_DISTANCE / 4) {
|
||||
continue;
|
||||
}
|
||||
variances[otherBallId] = variance;
|
||||
}
|
||||
return variances;
|
||||
}
|
||||
|
||||
MagBalls.prototype.releaseBall = function(releasedBall) {
|
||||
delete this.selectedBalls[releasedBall];
|
||||
debugPrint("Released ball: " + releasedBall);
|
||||
|
||||
// sort other balls by distance from the stick length
|
||||
var edgeTargetBall = null;
|
||||
do {
|
||||
var releasePosition = this.getBallPosition(releasedBall);
|
||||
// Get the list of candidate connections
|
||||
var variances = this.findMatches(releasedBall);
|
||||
// sort them by the difference from an ideal distance
|
||||
var sortedBalls = Object.keys(variances);
|
||||
if (!sortedBalls.length) {
|
||||
return;
|
||||
}
|
||||
sortedBalls.sort(function(a, b){
|
||||
return variances[a] - variances[b];
|
||||
});
|
||||
// find the nearest matching unconnected ball
|
||||
edgeTargetBall = sortedBalls[0];
|
||||
// note that createEdge will preferentially move the second parameter, the target ball
|
||||
} while(this.createEdge(edgeTargetBall, releasedBall))
|
||||
|
||||
this.clean();
|
||||
}
|
||||
|
||||
// FIXME the Quat should be able to do this
|
||||
function findOrientation(from, to) {
|
||||
//float m = sqrt(2.f + 2.f * dot(u, v));
|
||||
//vec3 w = (1.f / m) * cross(u, v);
|
||||
//return quat(0.5f * m, w.x, w.y, w.z);
|
||||
var v2 = Vec3.normalize(Vec3.subtract(to, from));
|
||||
var v1 = { x: 0.0, y: 1.0, z: 0.0 };
|
||||
var m = Math.sqrt(2 + 2 * Vec3.dot(v1, v2));
|
||||
var w = Vec3.multiply(1.0 / m, Vec3.cross(v1, v2));
|
||||
return {
|
||||
w: 0.5 * m,
|
||||
x: w.x,
|
||||
y: w.y,
|
||||
z: w.z
|
||||
};
|
||||
}
|
||||
|
||||
var LINE_DIMENSIONS = 5;
|
||||
|
||||
var EDGE_PROTOTYPE = {
|
||||
type: "Line",
|
||||
name: EDGE_NAME,
|
||||
color: { red: 0, green: 255, blue: 255 },
|
||||
dimensions: {
|
||||
x: LINE_DIMENSIONS,
|
||||
y: LINE_DIMENSIONS,
|
||||
z: LINE_DIMENSIONS
|
||||
},
|
||||
lineWidth: 5,
|
||||
visible: true,
|
||||
ignoreCollisions: true,
|
||||
collisionsWillMove: false
|
||||
}
|
||||
|
||||
var ZERO_VECTOR = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
//var EDGE_PROTOTYPE = {
|
||||
// type: "Sphere",
|
||||
// name: EDGE_NAME,
|
||||
// color: { red: 0, green: 255, blue: 255 },
|
||||
// //dimensions: STICK_DIMENSIONS,
|
||||
// dimensions: { x: 0.02, y: 0.02, z: 0.02 },
|
||||
// rotation: rotation,
|
||||
// visible: true,
|
||||
// ignoreCollisions: true,
|
||||
// collisionsWillMove: false
|
||||
//}
|
||||
|
||||
MagBalls.prototype.createEdge = function(from, to) {
|
||||
// FIXME find the constraints on from an to and determine if there is an intersection.
|
||||
// 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
|
||||
|
||||
var fromPos = this.getBallPosition(from);
|
||||
var toPos = this.getBallPosition(to);
|
||||
var vector = Vec3.subtract(toPos, fromPos);
|
||||
var originalLength = Vec3.length(originalLength);
|
||||
|
||||
// if they're already at a close enough distance, just create the edge
|
||||
if ((originalLength - BALL_DISTANCE) > (BALL_DISTANCE * 0.01)) {
|
||||
//code
|
||||
} else {
|
||||
// Attempt to move the ball to match the distance
|
||||
vector = Vec3.multiply(BALL_DISTANCE, Vec3.normalize(vector));
|
||||
// Zero edges for the destination
|
||||
var edgeCount = Object.keys(this.balls[to]).length;
|
||||
if (!edgeCount) {
|
||||
// update the entity
|
||||
var newPosition = Vec3.sum(vector, fromPos);
|
||||
Entities.editEntity(to, { position: newPosition });
|
||||
} else if (1 == edgeCount) {
|
||||
// FIXME
|
||||
// find the other end of the edge already connected, call it ball2
|
||||
// given two spheres of radius BALL_DISTANCE centered at fromPos and ball2.position,
|
||||
// find the closest point of intersection to toPos
|
||||
// move the ball to toPos
|
||||
} else if (2 == edgeCount) {
|
||||
// FIXME
|
||||
} else {
|
||||
// FIXME check for the ability to move fromPos
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fixup existing edges
|
||||
for (var edgeId in this.balls[to]) {
|
||||
this.fixupEdge(edgeId);
|
||||
}
|
||||
|
||||
// FIXME, find the correct orientation for a box or model between the two balls
|
||||
// for now use a line
|
||||
var newEdge = Entities.addEntity(mergeObjects(EDGE_PROTOTYPE, this.findEdgeParams(from, to)));
|
||||
|
||||
this.edges[newEdge] = {};
|
||||
this.edges[newEdge][from] = true;
|
||||
this.edges[newEdge][to] = true;
|
||||
this.balls[from][newEdge] = true;
|
||||
this.balls[to][newEdge] = true;
|
||||
this.validate();
|
||||
return true;
|
||||
}
|
||||
|
||||
MagBalls.prototype.findEdgeParams = function(startBall, endBall) {
|
||||
var startBallPos = this.getBallPosition(startBall);
|
||||
var endBallPos = this.getBallPosition(endBall);
|
||||
var vector = Vec3.subtract(endBallPos, startBallPos);
|
||||
return {
|
||||
position: startBallPos,
|
||||
linePoints: [ ZERO_VECTOR, vector ]
|
||||
};
|
||||
}
|
||||
|
||||
MagBalls.prototype.fixupEdge = function(edgeId) {
|
||||
var ballsInEdge = Object.keys(this.edges[edgeId]);
|
||||
Entities.editEntity(edgeId, this.findEdgeParams(ballsInEdge[0], ballsInEdge[1]));
|
||||
}
|
||||
|
||||
// Given two balls, how big is the difference between their distances and the stick length
|
||||
MagBalls.prototype.getStickLengthVariance = function(a, b) {
|
||||
var apos = this.getBallPosition(a);
|
||||
var bpos = this.getBallPosition(b);
|
||||
var distance = Vec3.distance(apos, bpos);
|
||||
var variance = Math.abs(distance - BALL_DISTANCE);
|
||||
return variance;
|
||||
}
|
||||
|
||||
// FIXME remove unconnected balls
|
||||
MagBalls.prototype.clean = function() {
|
||||
//var deletedBalls = {};
|
||||
//if (Object.keys(this.balls).length > 1) {
|
||||
// for (var ball in this.balls) {
|
||||
// if (!this.getConnections(ball)) {
|
||||
// deletedBalls[ball] = true;
|
||||
// Entities.deleteEntity(ball);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//for (var ball in deletedBalls) {
|
||||
// delete this.balls[ball];
|
||||
//}
|
||||
}
|
||||
|
||||
MagBalls.prototype.getBallPosition = function(ball) {
|
||||
var properties = Entities.getEntityProperties(ball);
|
||||
return properties.position;
|
||||
}
|
||||
|
||||
MagBalls.prototype.breakEdges = function(ballId) {
|
||||
for (var edgeId in this.balls[ballId]) {
|
||||
this.destroyEdge(edgeId);
|
||||
}
|
||||
// This shouldn't be necessary
|
||||
this.balls[ballId] = {};
|
||||
}
|
||||
|
||||
MagBalls.prototype.destroyEdge = function(edgeId) {
|
||||
logDebug("Deleting edge " + edgeId);
|
||||
// Delete the edge from other balls
|
||||
for (var edgeBallId in this.edges[edgeId]) {
|
||||
delete this.balls[edgeBallId][edgeId];
|
||||
}
|
||||
delete this.edges[edgeId];
|
||||
Entities.deleteEntity(edgeId);
|
||||
this.validate();
|
||||
}
|
||||
|
||||
MagBalls.prototype.destroyBall = function(ballId) {
|
||||
logDebug("Deleting ball " + ballId);
|
||||
breakEdges(ballId);
|
||||
Entities.deleteEntity(ballId);
|
||||
}
|
||||
|
||||
MagBalls.prototype.clear = function() {
|
||||
if (DEBUG_MAGSTICKS) {
|
||||
var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||
var result = [];
|
||||
ids.forEach(function(id) {
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
if (properties.name == BALL_NAME || properties.name == EDGE_NAME) {
|
||||
Entities.deleteEntity(id);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
MagBalls.prototype.areConnected = function(a, b) {
|
||||
for (var edge in this.balls[a]) {
|
||||
// edge already exists
|
||||
if (this.balls[b][edge]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MagBalls.prototype.validate = function() {
|
||||
var error = false;
|
||||
for (ballId in this.balls) {
|
||||
for (edgeId in this.balls[ballId]) {
|
||||
var edge = this.edges[edgeId];
|
||||
if (!edge) {
|
||||
logError("Error: ball " + ballId + " refers to unknown edge " + edgeId);
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (!edge[ballId]) {
|
||||
logError("Error: ball " + ballId + " refers to edge " + edgeId + " but not vice versa");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (edgeId in this.edges) {
|
||||
for (ballId in this.edges[edgeId]) {
|
||||
var ball = this.balls[ballId];
|
||||
if (!ball) {
|
||||
logError("Error: edge " + edgeId + " refers to unknown ball " + ballId);
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (!ball[edgeId]) {
|
||||
logError("Error: edge " + edgeId + " refers to ball " + ballId + " but not vice versa");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
logDebug(JSON.stringify({ edges: this.edges, balls: this.balls }, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME fetch from a subkey of user data to support non-destructive modifications
|
||||
MagBalls.prototype.setUserData = function(id, data) {
|
||||
Entities.editEntity(id, { userData: JSON.stringify(data) });
|
||||
}
|
||||
|
||||
// FIXME do non-destructive modification of the existing user data
|
||||
MagBalls.prototype.getUserData = function(id) {
|
||||
var results = null;
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
if (properties.userData) {
|
||||
results = JSON.parse(this.properties.userData);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
//MagBalls.prototype.findBalls = function() {
|
||||
// var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||
// var result = [];
|
||||
// ids.forEach(function(id) {
|
||||
// var properties = Entities.getEntityProperties(id);
|
||||
// if (properties.name == BALL_NAME) {
|
||||
// result.push(id);
|
||||
// }
|
||||
// }, this);
|
||||
// return result;
|
||||
//};
|
||||
//
|
||||
//MagBalls.prototype.findEdges = function() {
|
||||
// var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||
// var result = [];
|
||||
// ids.forEach(function(id) {
|
||||
// var properties = Entities.getEntityProperties(id);
|
||||
// if (properties.name == EDGE_NAME) {
|
||||
// result.push(id);
|
||||
// }
|
||||
// }, this);
|
||||
// return result;
|
||||
//};
|
48
examples/toys/magSticks/utils.js
Normal file
48
examples/toys/magSticks/utils.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
DEBUG_MAGSTICKS = true;
|
||||
|
||||
debugPrint = function (str) {
|
||||
if (DEBUG_MAGSTICKS) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
vec3toStr = function (v) {
|
||||
return "{ " +
|
||||
(Math.round(v.x*1000)/1000) + ", " +
|
||||
(Math.round(v.y*1000)/1000) + ", " +
|
||||
(Math.round(v.z*1000)/1000) + " }";
|
||||
}
|
||||
|
||||
scaleLine = function (start, end, scale) {
|
||||
var v = Vec3.subtract(end, start);
|
||||
var length = Vec3.length(v);
|
||||
v = Vec3.multiply(scale, v);
|
||||
return Vec3.sum(start, v);
|
||||
}
|
||||
|
||||
findAction = function(name) {
|
||||
var actions = Controller.getAllActions();
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
if (actions[i].actionName == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
logWarn = function(str) {
|
||||
print(str);
|
||||
}
|
||||
|
||||
logError = function(str) {
|
||||
print(str);
|
||||
}
|
||||
|
||||
logInfo = function(str) {
|
||||
print(str);
|
||||
}
|
||||
|
||||
logDebug = function(str) {
|
||||
debugPrint(str);
|
||||
}
|
Loading…
Reference in a new issue