|
@ -120,15 +120,13 @@ class PhysicsEngine { |
|
|
this.body.emitter.on('_positionUpdate', (properties) => this.positionUpdateHandler(properties)); |
|
|
this.body.emitter.on('_positionUpdate', (properties) => this.positionUpdateHandler(properties)); |
|
|
this.body.emitter.on('_physicsUpdate', (properties) => this.physicsUpdateHandler(properties)); |
|
|
this.body.emitter.on('_physicsUpdate', (properties) => this.physicsUpdateHandler(properties)); |
|
|
// For identifying which nodes to send to worker thread
|
|
|
// For identifying which nodes to send to worker thread
|
|
|
this.body.emitter.on('dragStart', (properties) => {this.draggingNodes = properties.nodes;}); |
|
|
|
|
|
this.body.emitter.on('dragEnd', () => { |
|
|
|
|
|
// need one last update to handle the case where a drag happens
|
|
|
|
|
|
// and the user holds the node clicked at the final position
|
|
|
|
|
|
// for a time prior to releasing
|
|
|
|
|
|
this.updateWorkerPositions(); |
|
|
|
|
|
|
|
|
this.body.emitter.on('dragStart', (properties) => { |
|
|
|
|
|
this.draggingNodes = properties.nodes; |
|
|
|
|
|
}); |
|
|
|
|
|
this.body.emitter.on('dragEnd', () => { |
|
|
this.draggingNodes = []; |
|
|
this.draggingNodes = []; |
|
|
}); |
|
|
}); |
|
|
this.body.emitter.on('destroy', () => { |
|
|
|
|
|
|
|
|
this.body.emitter.on('destroy', () => { |
|
|
if (this.physicsWorker) { |
|
|
if (this.physicsWorker) { |
|
|
this.physicsWorker.terminate(); |
|
|
this.physicsWorker.terminate(); |
|
|
this.physicsWorker = undefined; |
|
|
this.physicsWorker = undefined; |
|
@ -185,7 +183,7 @@ class PhysicsEngine { |
|
|
this.options.useWorker = false; |
|
|
this.options.useWorker = false; |
|
|
this.physicsWorker.terminate(); |
|
|
this.physicsWorker.terminate(); |
|
|
this.physicsWorker = undefined; |
|
|
this.physicsWorker = undefined; |
|
|
this.updatePhysicsData(); |
|
|
|
|
|
|
|
|
this.initPhysicsData(); |
|
|
} |
|
|
} |
|
|
var options; |
|
|
var options; |
|
|
if (this.options.solver === 'forceAtlas2Based') { |
|
|
if (this.options.solver === 'forceAtlas2Based') { |
|
@ -253,12 +251,18 @@ class PhysicsEngine { |
|
|
this.physicsUpdateHandler = (properties) => { |
|
|
this.physicsUpdateHandler = (properties) => { |
|
|
if (properties.options.physics !== undefined) { |
|
|
if (properties.options.physics !== undefined) { |
|
|
if (properties.options.physics) { |
|
|
if (properties.options.physics) { |
|
|
this.physicsWorker.postMessage({type: 'addElements', data: 'TODO: createNode'}); |
|
|
|
|
|
|
|
|
this.physicsWorker.postMessage({ |
|
|
|
|
|
type: 'addElements', |
|
|
|
|
|
data: this.createPhysicsNode(properties.id) |
|
|
|
|
|
}); |
|
|
} else { |
|
|
} else { |
|
|
this.physicsWorker.postMessage({type: 'removeElements', data: properties.id}); |
|
|
|
|
|
|
|
|
this.physicsWorker.postMessage({type: 'removeElements', data: { |
|
|
|
|
|
nodes: [properties.id.toString()], |
|
|
|
|
|
edges: [] |
|
|
|
|
|
}}); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
this.physicsWorker.postMessage({type: 'updateProperty', data: properties}); |
|
|
|
|
|
|
|
|
this.physicsWorker.postMessage({type: 'updateProperties', data: properties}); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
@ -315,7 +319,6 @@ class PhysicsEngine { |
|
|
*/ |
|
|
*/ |
|
|
startSimulation() { |
|
|
startSimulation() { |
|
|
if (this.physicsEnabled === true && this.options.enabled === true) { |
|
|
if (this.physicsEnabled === true && this.options.enabled === true) { |
|
|
this.updateWorkerPositions(); |
|
|
|
|
|
this.stabilized = false; |
|
|
this.stabilized = false; |
|
|
|
|
|
|
|
|
// when visible, adaptivity is disabled.
|
|
|
// when visible, adaptivity is disabled.
|
|
@ -404,7 +407,6 @@ class PhysicsEngine { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.stabilized === false) { |
|
|
if (this.stabilized === false) { |
|
|
this.updateWorkerFixed(); |
|
|
|
|
|
// adaptivity means the timestep adapts to the situation, only applicable for stabilization
|
|
|
// adaptivity means the timestep adapts to the situation, only applicable for stabilization
|
|
|
if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { |
|
|
if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { |
|
|
// this is the factor for increasing the timestep on success.
|
|
|
// this is the factor for increasing the timestep on success.
|
|
@ -460,7 +462,7 @@ class PhysicsEngine { |
|
|
this.timestep = this.options.timestep; |
|
|
this.timestep = this.options.timestep; |
|
|
if (this.physicsWorker) { |
|
|
if (this.physicsWorker) { |
|
|
// console.log('asking working to do a physics iteration');
|
|
|
// console.log('asking working to do a physics iteration');
|
|
|
this.physicsWorker.postMessage({type: 'calculateForces'}); |
|
|
|
|
|
|
|
|
this.physicsWorker.postMessage({type: 'physicsTick'}); |
|
|
} else { |
|
|
} else { |
|
|
this.calculateForces(); |
|
|
this.calculateForces(); |
|
|
this.moveNodes(); |
|
|
this.moveNodes(); |
|
@ -476,12 +478,32 @@ class PhysicsEngine { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
createPhysicsNode(nodeId) { |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
if (node && node.options.physics === true) { |
|
|
|
|
|
// for updating fixed later
|
|
|
|
|
|
this.physicsBody.physicsNodeIndices.push(nodeId); |
|
|
|
|
|
return { |
|
|
|
|
|
id: node.id.toString(), |
|
|
|
|
|
x: node.x, |
|
|
|
|
|
y: node.y, |
|
|
|
|
|
options: { |
|
|
|
|
|
fixed: { |
|
|
|
|
|
x: node.options.fixed.x, |
|
|
|
|
|
y: node.options.fixed.y |
|
|
|
|
|
}, |
|
|
|
|
|
mass: node.options.mass |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. |
|
|
* Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. |
|
|
* |
|
|
* |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
updatePhysicsData() { |
|
|
|
|
|
|
|
|
initPhysicsData() { |
|
|
let nodes = this.body.nodes; |
|
|
let nodes = this.body.nodes; |
|
|
let edges = this.body.edges; |
|
|
let edges = this.body.edges; |
|
|
|
|
|
|
|
@ -490,28 +512,12 @@ class PhysicsEngine { |
|
|
this.physicsBody.physicsEdgeIndices = []; |
|
|
this.physicsBody.physicsEdgeIndices = []; |
|
|
|
|
|
|
|
|
if (this.physicsWorker) { |
|
|
if (this.physicsWorker) { |
|
|
this.physicsWorkerNodes = {}; |
|
|
|
|
|
var physicsWorkerEdges = {}; |
|
|
|
|
|
|
|
|
let physicsWorkerNodes = {}; |
|
|
|
|
|
let physicsWorkerEdges = {}; |
|
|
|
|
|
|
|
|
for (let nodeId in nodes) { |
|
|
for (let nodeId in nodes) { |
|
|
if (nodes.hasOwnProperty(nodeId)) { |
|
|
if (nodes.hasOwnProperty(nodeId)) { |
|
|
let node = nodes[nodeId]; |
|
|
|
|
|
if (node.options.physics === true) { |
|
|
|
|
|
// for updating fixed later
|
|
|
|
|
|
this.physicsBody.physicsNodeIndices.push(nodeId); |
|
|
|
|
|
this.physicsWorkerNodes[nodeId] = { |
|
|
|
|
|
id: node.id, |
|
|
|
|
|
x: node.x, |
|
|
|
|
|
y: node.y, |
|
|
|
|
|
options: { |
|
|
|
|
|
fixed: { |
|
|
|
|
|
x: node.options.fixed.x, |
|
|
|
|
|
y: node.options.fixed.y |
|
|
|
|
|
}, |
|
|
|
|
|
mass: node.options.mass |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
physicsWorkerNodes[nodeId] = this.createPhysicsNode(nodeId); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -549,7 +555,7 @@ class PhysicsEngine { |
|
|
this.physicsWorker.postMessage({ |
|
|
this.physicsWorker.postMessage({ |
|
|
type: 'physicsObjects', |
|
|
type: 'physicsObjects', |
|
|
data: { |
|
|
data: { |
|
|
nodes: this.physicsWorkerNodes, |
|
|
|
|
|
|
|
|
nodes: physicsWorkerNodes, |
|
|
edges: physicsWorkerEdges |
|
|
edges: physicsWorkerEdges |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
@ -592,52 +598,6 @@ class PhysicsEngine { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
updateWorkerPositions() { |
|
|
|
|
|
if (this.physicsWorker) { |
|
|
|
|
|
for (let i = 0; i < this.draggingNodes.length; i++) { |
|
|
|
|
|
let nodeId = this.draggingNodes[i]; |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
this.physicsWorker.postMessage({ |
|
|
|
|
|
type: 'updatePositions', |
|
|
|
|
|
data: { |
|
|
|
|
|
id: nodeId, |
|
|
|
|
|
x: node.x, |
|
|
|
|
|
y: node.y |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
updateWorkerFixed() { |
|
|
|
|
|
if (this.physicsWorker) { |
|
|
|
|
|
for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { |
|
|
|
|
|
let nodeId = this.physicsBody.physicsNodeIndices[i]; |
|
|
|
|
|
let physicsNode = this.physicsWorkerNodes[nodeId]; |
|
|
|
|
|
let node = this.body.nodes[nodeId]; |
|
|
|
|
|
if (physicsNode.options.fixed.x !== node.options.fixed.x || |
|
|
|
|
|
physicsNode.options.fixed.y !== node.options.fixed.y) |
|
|
|
|
|
{ |
|
|
|
|
|
let fixed = { |
|
|
|
|
|
x: node.options.fixed.x, |
|
|
|
|
|
y: node.options.fixed.y |
|
|
|
|
|
}; |
|
|
|
|
|
physicsNode.options.fixed.x = fixed.x; |
|
|
|
|
|
physicsNode.options.fixed.y = fixed.y; |
|
|
|
|
|
this.physicsWorker.postMessage({ |
|
|
|
|
|
type: 'updateFixed', |
|
|
|
|
|
data: { |
|
|
|
|
|
id: nodeId, |
|
|
|
|
|
x: node.x, |
|
|
|
|
|
y: node.y, |
|
|
|
|
|
fixed: fixed |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. |
|
|
* Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. |
|
|
*/ |
|
|
*/ |
|
@ -786,8 +746,7 @@ class PhysicsEngine { |
|
|
if (nodes.hasOwnProperty(id)) { |
|
|
if (nodes.hasOwnProperty(id)) { |
|
|
if (nodes[id].x && nodes[id].y) { |
|
|
if (nodes[id].x && nodes[id].y) { |
|
|
this.freezeCache[id] = {x:nodes[id].options.fixed.x,y:nodes[id].options.fixed.y}; |
|
|
this.freezeCache[id] = {x:nodes[id].options.fixed.x,y:nodes[id].options.fixed.y}; |
|
|
nodes[id].options.fixed.x = true; |
|
|
|
|
|
nodes[id].options.fixed.y = true; |
|
|
|
|
|
|
|
|
nodes[id].setFixed(true); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -803,8 +762,7 @@ class PhysicsEngine { |
|
|
for (var id in nodes) { |
|
|
for (var id in nodes) { |
|
|
if (nodes.hasOwnProperty(id)) { |
|
|
if (nodes.hasOwnProperty(id)) { |
|
|
if (this.freezeCache[id] !== undefined) { |
|
|
if (this.freezeCache[id] !== undefined) { |
|
|
nodes[id].options.fixed.x = this.freezeCache[id].x; |
|
|
|
|
|
nodes[id].options.fixed.y = this.freezeCache[id].y; |
|
|
|
|
|
|
|
|
nodes[id].setFixed({x: this.freezeCache[id].x, y: this.freezeCache[id].y}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|