Browse Source

start on node changes based pushes to physicsWorker

webworkersNetwork^2^2
Eric VanDever 9 years ago
parent
commit
0586985726
3 changed files with 123 additions and 29 deletions
  1. +25
    -5
      lib/network/modules/PhysicsEngine.js
  2. +37
    -15
      lib/network/modules/PhysicsWorker.js
  3. +61
    -9
      lib/network/modules/components/Node.js

+ 25
- 5
lib/network/modules/PhysicsEngine.js View File

@ -90,6 +90,8 @@ class PhysicsEngine {
this.timestep = 0.5;
this.layoutFailed = false;
this.draggingNodes = [];
this.positionUpdateHandler = () => {};
this.physicsUpdateHandler = () => {};
this.bindEventListeners();
}
@ -115,6 +117,8 @@ class PhysicsEngine {
this.stopSimulation(false);
this.body.emitter.off();
});
this.body.emitter.on('_positionUpdate', (properties) => this.positionUpdateHandler(properties));
this.body.emitter.on('_physicsUpdate', (properties) => this.physicsUpdateHandler(properties));
// For identifying which nodes to send to worker thread
this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;});
this.body.emitter.on('dragEnd', () => {
@ -175,6 +179,8 @@ class PhysicsEngine {
* configure the engine.
*/
initEmbeddedPhysics() {
this.positionUpdateHandler = () => {};
this.physicsUpdateHandler = () => {};
if (this.physicsWorker) {
this.options.useWorker = false;
this.physicsWorker.terminate();
@ -237,10 +243,24 @@ class PhysicsEngine {
this.physicsWorkerMessageHandler(event);
});
this.physicsWorker.onerror = (event) => {
console.error('Falling back to embedded physics engine');
console.error('Falling back to embedded physics engine', event);
this.initEmbeddedPhysics();
// throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")");
};
this.positionUpdateHandler = (positions) => {
this.physicsWorker.postMessage({type: 'updatePositions', data: positions});
};
this.physicsUpdateHandler = (properties) => {
if (properties.options.physics !== undefined) {
if (properties.options.physics) {
this.physicsWorker.postMessage({type: 'addElements', data: 'TODO: createNode'});
} else {
this.physicsWorker.postMessage({type: 'removeElements', data: properties.id});
}
} else {
this.physicsWorker.postMessage({type: 'updateProperty', data: properties});
}
};
}
}
@ -259,8 +279,8 @@ class PhysicsEngine {
let node = this.body.nodes[nodeId];
// handle case where we get a positions from an old physicsObject
if (node) {
node.x = positions[nodeId].x;
node.y = positions[nodeId].y;
node.setX(positions[nodeId].x);
node.setY(positions[nodeId].y);
}
}
break;
@ -719,7 +739,7 @@ class PhysicsEngine {
let ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration
velocities[nodeId].x += ax * timestep; // velocity
velocities[nodeId].x = (Math.abs(velocities[nodeId].x) > maxVelocity) ? ((velocities[nodeId].x > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].x;
node.x += velocities[nodeId].x * timestep; // position
node.setX(node.x + velocities[nodeId].x * timestep); // position
}
else {
forces[nodeId].x = 0;
@ -731,7 +751,7 @@ class PhysicsEngine {
let ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration
velocities[nodeId].y += ay * timestep; // velocity
velocities[nodeId].y = (Math.abs(velocities[nodeId].y) > maxVelocity) ? ((velocities[nodeId].y > 0) ? maxVelocity : -maxVelocity) : velocities[nodeId].y;
node.y += velocities[nodeId].y * timestep; // position
node.setY(node.y + velocities[nodeId].y * timestep); // position
}
else {
forces[nodeId].y = 0;

+ 37
- 15
lib/network/modules/PhysicsWorker.js View File

@ -9,7 +9,10 @@ import ForceAtlas2BasedCentralGravitySolver from './components/physics/FA2BasedC
class PhysicsWorker {
constructor(postMessage) {
this.body = {};
this.body = {
nodes: {},
edges: {}
};
this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}};
this.postMessage = postMessage;
this.options = {};
@ -35,28 +38,47 @@ class PhysicsWorker {
break;
case 'updatePositions':
let updatedNode = this.body.nodes[msg.data.id];
updatedNode.x = msg.data.x;
updatedNode.y = msg.data.y;
this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0};
this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0};
if (updatedNode) {
updatedNode.x = msg.data.x;
updatedNode.y = msg.data.y;
this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0};
this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0};
}
break;
case 'updateFixed':
let fixedNode = this.body.nodes[msg.data.id];
fixedNode.x = msg.data.x;
fixedNode.y = msg.data.y;
fixedNode.options.fixed.x = msg.data.fixed.x;
fixedNode.options.fixed.y = msg.data.fixed.y;
case 'updateProperty':
let optionsNode = this.body.nodes[msg.data.id];
if (optionsNode) {
let opts = msg.data.options;
// TODO see if we need a position with fixed
if (opts.fixed) {
if (opts.fixed.x !== undefined) {
optionsNode.options.fixed.x = opts.fixed.x;
}
if (opts.fixed.y !== undefined) {
optionsNode.options.fixed.y = opts.fixed.y;
}
}
if (opts.mass !== undefined) {
optionsNode.options.mass = opts.mass;
}
}
break;
case 'options':
this.options = msg.data;
this.timestep = this.options.timestep;
this.init();
case 'addElements':
console.log('add');
break;
case 'removeElements':
console.log('remove');
break;
case 'physicsObjects':
this.body.nodes = msg.data.nodes;
this.body.edges = msg.data.edges;
this.updatePhysicsData();
break;
case 'options':
this.options = msg.data;
this.timestep = this.options.timestep;
this.init();
break;
default:
console.warn('unknown message from PhysicsEngine', msg);
}

+ 61
- 9
lib/network/modules/components/Node.js View File

@ -59,8 +59,8 @@ class Node {
this.grouplist = grouplist;
// state options
this.x = undefined;
this.y = undefined;
this._x = undefined;
this._y = undefined;
this.baseSize = this.options.size;
this.baseFontSize = this.options.font.size;
this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate
@ -71,6 +71,39 @@ class Node {
this.setOptions(options);
}
get x() {
return this._x;
}
set x(newX) {
this._x = newX;
this.body.emitter.emit('_positionUpdate', {id: this.id, x: this._x, y: this._y});
}
/**
* Non emitting version for use by physics engine so we don't create infinite loops.
* @param newX
*/
setX(newX) {
this._x = newX;
}
get y() {
return this._y;
}
set y(newY) {
this._y = newY;
this.body.emitter.emit('_positionUpdate', {id: this.id, x: this._x, y: this._y});
}
/**
* Non emitting version for use by physics engine so we don't create infinite loops.
* @param newY
*/
setY(newY) {
this._y = newY;
}
/**
* Attach a edge to the node
@ -136,7 +169,7 @@ class Node {
}
// this transforms all shorthands into fully defined options
Node.parseOptions(this.options, options, true, this.globalOptions);
this.parseOptions(this.options, options, true, this.globalOptions);
// load the images
if (this.options.image !== undefined) {
@ -151,8 +184,16 @@ class Node {
this.updateLabelModule();
this.updateShape(currentShape);
if (options.mass !== undefined) {
this.options.mass = options.mass;
this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {mass: options.mass}});
}
if (options.physics !== undefined) {
this.options.physics = options.physics;
this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {physics: options.physics}});
}
if (options.hidden !== undefined || options.physics !== undefined) {
if (options.hidden !== undefined) {
return true;
}
return false;
@ -165,7 +206,7 @@ class Node {
* @param parentOptions
* @param newOptions
*/
static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) {
parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) {
var fields = [
'color',
'font',
@ -189,15 +230,26 @@ class Node {
// handle the fixed options
if (newOptions.fixed !== undefined && newOptions.fixed !== null) {
if (typeof newOptions.fixed === 'boolean') {
parentOptions.fixed.x = newOptions.fixed;
parentOptions.fixed.y = newOptions.fixed;
if (parentOptions.fixed.x !== newOptions.fixed || parentOptions.fixed.y !== newOptions.fixed) {
parentOptions.fixed.x = newOptions.fixed;
parentOptions.fixed.y = newOptions.fixed;
this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {x: newOptions.fixed, y: newOptions.fixed}}});
}
}
else {
if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') {
if (newOptions.fixed.x !== undefined &&
typeof newOptions.fixed.x === 'boolean' &&
parentOptions.fixed.x !== newOptions.fixed.x)
{
parentOptions.fixed.x = newOptions.fixed.x;
this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {x: newOptions.fixed.x}}});
}
if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') {
if (newOptions.fixed.y !== undefined &&
typeof newOptions.fixed.y === 'boolean' &&
parentOptions.fixed.y !== newOptions.fixed.y)
{
parentOptions.fixed.y = newOptions.fixed.y;
this.body.emitter.emit('_physicsUpdate', {id: this.id, options: {fixed: {y: newOptions.fixed.y}}});
}
}
}

Loading…
Cancel
Save