|
|
- import PhysicsBase from './PhysicsBase';
-
- class PhysicsWorker extends PhysicsBase {
- constructor(postMessage) {
- super();
- this.body = {
- nodes: {},
- edges: {}
- };
- this.postMessage = postMessage;
- this.previousStates = {};
- this.toRemove = {
- nodeIds: [],
- edgeIds: []
- };
- this.physicsTimeout = null;
- this.isWorker = true;
- this.emit = (event, data) => {this.postMessage({type: 'emit', data: {event: event, data: data}})};
- }
-
- handleMessage(event) {
- var msg = event.data;
- switch (msg.type) {
- case 'physicsTick':
- this.physicsTick();
- this.sendPositions();
- break;
- case 'updatePositions':
- this.receivePositions(msg.data);
- break;
- case 'updateProperties':
- this.updateProperties(msg.data);
- break;
- case 'addElements':
- this.addElements(msg.data);
- break;
- case 'removeElements':
- this.removeElements(msg.data);
- break;
- case 'stabilization':
- this.stabilize(msg.data);
- break;
- case 'initPhysicsData':
- console.debug('init physics data');
- this.initPhysicsData(msg.data);
- break;
- case 'options':
- this.options = msg.data;
- this.timestep = this.options.timestep;
- this.initPhysicsSolvers();
- break;
- default:
- console.warn('unknown message from PhysicsEngine', msg);
- }
- }
-
- // physicsTick() {
- // if(this.physicsTimeout) {
- // // cancel any outstanding requests to prevent manipulation of data while iterating
- // // and we're going to handle it next anyways.
- // clearTimeout(this.physicsTimeout);
- // }
- // this.processRemovals();
- // if (this.options.enabled) {
- // this.calculateForces();
- // this.moveNodes();
- // // Handle the case where physics was enabled, data was removed
- // // but this physics tick was longer than the timeout and during that delta
- // // physics was disabled.
- // this.processRemovals();
- // }
- // this.stabilizationIterations++;
- // }
-
- sendPositions() {
- let nodeIndices = this.physicsBody.physicsNodeIndices;
- let positions = {};
- for (let i = 0; i < nodeIndices.length; i++) {
- let nodeId = nodeIndices[i];
- let node = this.body.nodes[nodeId];
- positions[nodeId] = {x:node.x, y:node.y};
- }
-
- this.postMessage({
- type: 'positions',
- data: {
- positions: positions,
- stabilized: this.stabilized
- }
- });
- }
-
- receivePositions(data) {
- let updatedNode = this.body.nodes[data.id];
- if (updatedNode) {
- updatedNode.x = data.x;
- updatedNode.y = data.y;
- this.physicsBody.forces[updatedNode.id] = {x: 0, y: 0};
- this.physicsBody.velocities[updatedNode.id] = {x: 0, y: 0};
- }
- }
-
- stabilize(data) {
- this.stabilized = false;
- this.targetIterations = data.targetIterations;
- this.stabilizationIterations = 0;
- setTimeout(() => this._stabilizationBatch(), 0);
- }
-
- updateProperties(data) {
- if (data.type === 'node') {
- let optionsNode = this.body.nodes[data.id];
- if (optionsNode) {
- let opts = data.options;
- 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;
- }
- } else {
- console.warn('sending properties to unknown node', data.id, data.options);
- }
- } else if (data.type === 'edge') {
- let edge = this.body.edges[data.id];
- if (edge) {
- let opts = data.options;
- if (opts.connected) {
- edge.connected = opts.connected;
- }
- } else {
- console.warn('sending properties to unknown edge', data.id, data.options);
- }
- } else {
- console.warn('sending properties to unknown element', data.id, data.options);
- }
- }
-
- addElements(data, replaceElements = true) {
- let nodeIds = Object.keys(data.nodes);
- for (let i = 0; i < nodeIds.length; i++) {
- let nodeId = nodeIds[i];
- let newNode = data.nodes[nodeId];
- if (replaceElements) {
- this.body.nodes[nodeId] = newNode;
- }
- this.physicsBody.forces[nodeId] = {x: 0, y: 0};
- // forces can be reset because they are recalculated. Velocities have to persist.
- if (this.physicsBody.velocities[nodeId] === undefined) {
- this.physicsBody.velocities[nodeId] = {x: 0, y: 0};
- }
- if (this.physicsBody.physicsNodeIndices.indexOf(nodeId) === -1) {
- this.physicsBody.physicsNodeIndices.push(nodeId);
- }
- }
- let edgeIds = Object.keys(data.edges);
- for (let i = 0; i < edgeIds.length; i++) {
- let edgeId = edgeIds[i];
- if (replaceElements) {
- this.body.edges[edgeId] = data.edges[edgeId];
- }
- if (this.physicsBody.physicsEdgeIndices.indexOf(edgeId) === -1) {
- this.physicsBody.physicsEdgeIndices.push(edgeId);
- }
- }
- }
-
- removeElements(data) {
- // schedule removal of elements on the next physicsTick
- // avoids having to defensively check every node read in each physics implementation
- this.toRemove.nodeIds.push.apply(this.toRemove.nodeIds, data.nodeIds);
- this.toRemove.edgeIds.push.apply(this.toRemove.edgeIds, data.edgeIds);
- // Handle case where physics is disabled.
- if(this.physicsTimeout) {
- // don't schedule more than one physicsTick
- clearTimeout(this.physicsTimeout);
- }
- this.physicsTimeout = setTimeout(()=> {
- // if physics is still enabled, the next tick will handle removeElements
- if (!this.options.enabled) {
- this.physicsTimeout = null;
- this.physicsTick();
- }
- }, 250);
- }
-
- processRemovals() {
- while (this.toRemove.nodeIds.length > 0) {
- let nodeId = this.toRemove.nodeIds.pop();
- let index = this.physicsBody.physicsNodeIndices.indexOf(nodeId);
- if (index > -1) {
- this.physicsBody.physicsNodeIndices.splice(index,1);
- }
- delete this.physicsBody.forces[nodeId];
- delete this.physicsBody.velocities[nodeId];
- delete this.body.nodes[nodeId];
- }
- while (this.toRemove.edgeIds.length > 0) {
- let edgeId = this.toRemove.edgeIds.pop();
- let index = this.physicsBody.physicsEdgeIndices.indexOf(edgeId);
- if (index > -1) {
- this.physicsBody.physicsEdgeIndices.splice(index,1);
- }
- delete this.body.edges[edgeId];
- }
- }
-
- initPhysicsData(data) {
- this.physicsBody.forces = {};
- this.physicsBody.physicsNodeIndices = [];
- this.physicsBody.physicsEdgeIndices = [];
-
- this.body.nodes = data.nodes;
- this.body.edges = data.edges;
- this.addElements(data, false);
-
- // clean deleted nodes from the velocity vector
- for (let nodeId in this.physicsBody.velocities) {
- if (this.body.nodes[nodeId] === undefined) {
- delete this.physicsBody.velocities[nodeId];
- }
- }
- }
-
- /**
- * Perform the actual step
- *
- * @param nodeId
- * @param maxVelocity
- * @returns {number}
- * @private
- */
- _performStep(nodeId,maxVelocity) {
- let node = this.body.nodes[nodeId];
- let timestep = this.timestep;
- let forces = this.physicsBody.forces;
- let velocities = this.physicsBody.velocities;
-
- // store the state so we can revert
- this.previousStates[nodeId] = {x:node.x, y:node.y, vx:velocities[nodeId].x, vy:velocities[nodeId].y};
-
- if (node.options.fixed.x === false) {
- let dx = this.modelOptions.damping * velocities[nodeId].x; // damping force
- 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
- }
- else {
- forces[nodeId].x = 0;
- velocities[nodeId].x = 0;
- }
-
- if (node.options.fixed.y === false) {
- let dy = this.modelOptions.damping * velocities[nodeId].y; // damping force
- 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
- }
- else {
- forces[nodeId].y = 0;
- velocities[nodeId].y = 0;
- }
-
- let totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x,2) + Math.pow(velocities[nodeId].y,2));
- return totalVelocity;
- }
-
- _finalizeStabilization() {
- this.sendPositions();
- this.postMessage({
- type: 'finalizeStabilization',
- data: {
- stabilizationIterations: this.stabilizationIterations
- }
- });
- }
- }
-
- export default PhysicsWorker;
|