diff --git a/lib/network/modules/PhysicsEngine.js b/lib/network/modules/PhysicsEngine.js index d4d7c6d9..5c18d42b 100644 --- a/lib/network/modules/PhysicsEngine.js +++ b/lib/network/modules/PhysicsEngine.js @@ -251,20 +251,34 @@ class PhysicsEngine { this.physicsUpdateHandler = (properties) => { if (properties.options.physics !== undefined) { if (properties.options.physics) { - let nodes = {}; - nodes[properties.id] = this.createPhysicsNode(properties.id); + let data = { + nodes: {}, + edges: {} + }; + if (properties.type === 'node') { + data.nodes[properties.id] = this.createPhysicsNode(properties.id); + } else if (properties.type === 'edge') { + data.edges[properties.id] = this.createPhysicsEdge(properties.id); + } else { + console.warn('invalid element type'); + } this.physicsWorker.postMessage({ type: 'addElements', - data: { - nodes: nodes, - edges: {} - } + data: data }); } else { - this.physicsWorker.postMessage({type: 'removeElements', data: { - nodes: [properties.id.toString()], - edges: [] - }}); + let data = { + nodeIds: [], + edgeIds: [] + }; + if (properties.type === 'node') { + data.nodeIds = [properties.id.toString()]; + } else if (properties.type === 'edge') { + data.edgeIds = [properties.id.toString()]; + } else { + console.warn('invalid element type'); + } + this.physicsWorker.postMessage({type: 'removeElements', data: data}); } } else { this.physicsWorker.postMessage({type: 'updateProperties', data: properties}); @@ -505,11 +519,14 @@ class PhysicsEngine { let edge = this.body.edges[edgeId]; if (edge && edge.options.physics === true && edge.connected === true) { let physicsEdge = { - connected: edge.connected, id: edge.id, + connected: edge.connected, edgeType: {}, toId: edge.toId, fromId: edge.fromId, + // TODO when will toId not match to.id given that connected is true + // can we rewrite SpringSolver to only use toId and not to.id + // and remove these parameters to: { id: edge.to.id }, @@ -520,6 +537,7 @@ class PhysicsEngine { length: edge.length } }; + // TODO test/implment dynamic if (edge.edgeType.via) { physicsEdge.edgeType = { via: { diff --git a/lib/network/modules/PhysicsWorker.js b/lib/network/modules/PhysicsWorker.js index f0f5b868..99586d71 100644 --- a/lib/network/modules/PhysicsWorker.js +++ b/lib/network/modules/PhysicsWorker.js @@ -21,8 +21,8 @@ class PhysicsWorker { this.positions = {}; this.timestep = 0.5; this.toRemove = { - nodes: [], - edges: [] + nodeIds: [], + edgeIds: [] }; } @@ -50,8 +50,8 @@ class PhysicsWorker { case 'removeElements': // schedule removal of elements on the next physicsTick // avoids having to defensively check every node read in each physics implementation - this.toRemove.nodes.push.apply(this.toRemove.nodes, msg.data.nodes); - this.toRemove.edges.push.apply(this.toRemove.edges, msg.data.edges); + this.toRemove.nodeIds.push.apply(this.toRemove.nodeIds, msg.data.nodeIds); + this.toRemove.edgeIds.push.apply(this.toRemove.edgeIds, msg.data.edgeIds); break; case 'initPhysicsData': this.initPhysicsData(msg.data); @@ -103,8 +103,8 @@ class PhysicsWorker { this.processRemovals(); this.calculateForces(); this.moveNodes(); - for (let i = 0; i < this.toRemove.nodes.length; i++) { - delete this.positions[this.toRemove.nodes[i]]; + for (let i = 0; i < this.toRemove.nodeIds.length; i++) { + delete this.positions[this.toRemove.nodeIds[i]]; } this.postMessage({ type: 'positions', @@ -116,22 +116,36 @@ class PhysicsWorker { } updateProperties(data) { - 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 (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.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'); } - if (opts.mass !== undefined) { - optionsNode.options.mass = opts.mass; + } 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'); } } else { - console.log('sending property to unknown node'); + console.warn('sending properties to unknown element'); } } @@ -169,8 +183,8 @@ class PhysicsWorker { } processRemovals() { - while (this.toRemove.nodes.length > 0) { - let nodeId = this.toRemove.nodes.pop(); + 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); @@ -180,8 +194,8 @@ class PhysicsWorker { delete this.positions[nodeId]; delete this.body.nodes[nodeId]; } - while (this.toRemove.edges.length > 0) { - let edgeId = this.toRemove.edges.pop(); + 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); diff --git a/lib/network/modules/components/Edge.js b/lib/network/modules/components/Edge.js index 236e10de..2cd057cf 100644 --- a/lib/network/modules/components/Edge.js +++ b/lib/network/modules/components/Edge.js @@ -51,7 +51,10 @@ class Edge { this.labelModule = new Label(this.body, this.options); + // prevents sending connected messages on initial creation as it should be handled by added element + this.sendPhysicsUpdates = false; this.setOptions(options); + this.sendPhysicsUpdates = true; } @@ -86,10 +89,14 @@ class Edge { // A node is connected when it has a from and to node that both exist in the network.body.nodes. this.connect(); + // TODO make changing physics of 1 edge not trigger a complete rebuild of physics processing. if (options.hidden !== undefined || options.physics !== undefined) { dataChanged = true; } + // TODO if edgeType.via.id, toId, fromId, to.id, or from.id changed + // emit _physicsUpdate + return dataChanged; } @@ -252,6 +259,8 @@ class Edge { * Connect an edge to its nodes */ connect() { + let previousConnected = this.connected; + this.disconnect(); this.from = this.body.nodes[this.fromId] || undefined; @@ -272,6 +281,10 @@ class Edge { } this.edgeType.connect(); + + if (this.sendPhysicsUpdates && this.connected !== previousConnected) { + this.body.emitter.emit('_physicsUpdate', {type: 'edge', id: this.id, options: {connected: this.connected}}); + } } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index 3c0d38a1..b4aaee8e 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -106,7 +106,7 @@ class Node { // TODO split out fixed portion? let physOpts = Node.parseOptions(this.options, {fixed: newFixed}); if (Object.keys(physOpts).length > 0) { - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); + this.body.emitter.emit('_physicsUpdate', {type: 'node', id: this.id, options: physOpts}); } } @@ -207,7 +207,7 @@ class Node { } if (Object.keys(physOpts).length > 0) { - this.body.emitter.emit('_physicsUpdate', {id: this.id, options: physOpts}); + this.body.emitter.emit('_physicsUpdate', {type: 'node', id: this.id, options: physOpts}); } // TODO make embedded physics trigger this or handle _physicsUpdate messages