diff --git a/frontend/src/Metamaps/Control.js b/frontend/src/Metamaps/Control.js index b4dd67f2..77462447 100644 --- a/frontend/src/Metamaps/Control.js +++ b/frontend/src/Metamaps/Control.js @@ -5,6 +5,7 @@ import outdent from 'outdent' import Active from './Active' import DataModel from './DataModel' +import Engine from './Engine' import Filter from './Filter' import GlobalUI from './GlobalUI' import JIT from './JIT' @@ -22,6 +23,7 @@ const Control = { node.selected = true node.setData('dim', 30, 'current') Selected.Nodes.push(node) + Engine.setNodeSleeping(node.getData('body_id'), true) }, deselectAllNodes: function() { var l = Selected.Nodes.length @@ -38,6 +40,7 @@ const Control = { // remove the node Selected.Nodes.splice( Selected.Nodes.indexOf(node), 1) + Engine.setNodeSleeping(node.getData('body_id'), false) }, deleteSelected: function() { if (!Active.Map) return diff --git a/frontend/src/Metamaps/Create.js b/frontend/src/Metamaps/Create.js index 177e951f..20545181 100644 --- a/frontend/src/Metamaps/Create.js +++ b/frontend/src/Metamaps/Create.js @@ -5,6 +5,7 @@ import Mouse from './Mouse' import Selected from './Selected' import Synapse from './Synapse' import Topic from './Topic' +import Util from './Util' import Visualize from './Visualize' import GlobalUI from './GlobalUI' @@ -356,8 +357,35 @@ const Create = { Create.newTopic.addSynapse = false Create.newSynapse.topic1id = 0 Create.newSynapse.topic2id = 0 + Create.newSynapse.node1 = null + Create.newSynapse.node2 = null Mouse.synapseStartCoordinates = [] + Mouse.synapseEndCoordinates = null if (Visualize.mGraph) Visualize.mGraph.plot() + }, + updateForm: function() { + // set the draw synapse start positions + Mouse.synapseStartCoordinates = [] + for (let i = Selected.Nodes.length - 1; i >= 0; i -= 1) { + const n = Selected.Nodes[i] + Mouse.synapseStartCoordinates.push({ + x: n.pos.getc().x, + y: n.pos.getc().y + }) + } + let pixelPos, midpoint = {} + if (Create.newSynapse.beingCreated) { + Mouse.synapseEndCoordinates = { + x: Create.newSynapse.node2.pos.getc().x, + y: Create.newSynapse.node2.pos.getc().y + } + // position the form + midpoint.x = Create.newSynapse.node1.pos.getc().x + (Create.newSynapse.node2.pos.getc().x - Create.newSynapse.node1.pos.getc().x) / 2 + midpoint.y = Create.newSynapse.node1.pos.getc().y + (Create.newSynapse.node2.pos.getc().y - Create.newSynapse.node1.pos.getc().y) / 2 + pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint) + $('#new_synapse').css('left', pixelPos.x + 'px') + $('#new_synapse').css('top', pixelPos.y + 'px') + } } } } diff --git a/frontend/src/Metamaps/DataModel/Synapse.js b/frontend/src/Metamaps/DataModel/Synapse.js index dd229c6f..dea7923c 100644 --- a/frontend/src/Metamaps/DataModel/Synapse.js +++ b/frontend/src/Metamaps/DataModel/Synapse.js @@ -88,8 +88,8 @@ const Synapse = Backbone.Model.extend({ } } - if (Active.Map) { - mapping = providedMapping || this.getMapping() + if (Active.Map && providedMapping) { + mapping = providedMapping mappingID = mapping.isNew() ? mapping.cid : mapping.id edge.data.$mappings = [] edge.data.$mappingIDs = [mappingID] @@ -100,10 +100,12 @@ const Synapse = Backbone.Model.extend({ updateEdge: function() { var mapping var edge = this.get('edge') + edge.data.$synapses = edge.data.$synapses || [] edge.getData('synapses').push(this) if (Active.Map) { mapping = this.getMapping() + edge.data.$mappings = edge.data.$mappings || [] edge.getData('mappings').push(mapping) } diff --git a/frontend/src/Metamaps/DataModel/index.js b/frontend/src/Metamaps/DataModel/index.js index eb40ab37..24def309 100644 --- a/frontend/src/Metamaps/DataModel/index.js +++ b/frontend/src/Metamaps/DataModel/index.js @@ -117,10 +117,6 @@ const DataModel = { Filter.checkMetacodes() Filter.checkMappers() }) - DataModel.Mappings.on('remove', function(mapping) { - console.log('removed', mapping) - if (mapping.get('mappable_type') === 'Topic') Engine.removeTopic(mapping) - }) } } diff --git a/frontend/src/Metamaps/Engine.js b/frontend/src/Metamaps/Engine.js index 567e4749..b1b96069 100644 --- a/frontend/src/Metamaps/Engine.js +++ b/frontend/src/Metamaps/Engine.js @@ -1,8 +1,10 @@ -import Matter, { World, Composite, Runner, Common, Body, Bodies, Events } from 'matter-js' +import Matter, { Vector, Sleeping, World, Constraint, Composite, Runner, Common, Body, Bodies, Events } from 'matter-js' import $jit from '../patched/JIT' +import Create from './Create' import DataModel from './DataModel' +import JIT from './JIT' import Visualize from './Visualize' const Engine = { @@ -12,32 +14,63 @@ const Engine = { Engine.engine.world.gravity.scale = 0 }, run: init => { - if (init) DataModel.Mappings.each(Engine.addTopic) + if (init) { + Visualize.mGraph.graph.eachNode(Engine.addNode) + DataModel.Synapses.each(s => Engine.addEdge(s.get('edge'))) + } Engine.runner = Matter.Runner.run(Engine.engine) }, endActiveMap: () => { Runner.stop(Engine.runner) Matter.Engine.clear(Engine.engine) }, - setTopicPos: (id, x, y) => { + setNodePos: (id, x, y) => { const body = Composite.get(Engine.engine.world, id, 'body') Body.setPosition(body, { x, y }) + Body.setVelocity(body, Vector.create(0, 0)) + Body.setAngularVelocity(body, 0) + Body.setAngle(body, 0) }, - addTopic: mapping => { - let body = Bodies.circle(mapping.get('xloc'), mapping.get('yloc'), 25) - body.topic_id = mapping.get('mappable_id') - DataModel.Topics.get(body.topic_id).get('node').setData('body_id', body.id) + setNodeSleeping: (id, isSleeping) => { + const body = Composite.get(Engine.engine.world, id, 'body') + Sleeping.set(body, isSleeping) + if (!isSleeping) { + Body.setVelocity(body, Vector.create(0, 0)) + Body.setAngularVelocity(body, 0) + Body.setAngle(body, 0) + } + }, + addNode: node => { + let body = Bodies.circle(node.pos.x, node.pos.y, 100) + body.node_id = node.id + node.setData('body_id', body.id) World.addBody(Engine.engine.world, body) }, - removeTopic: mapping => { + removeNode: node => { + + }, + addEdge: edge => { + const bodyA = Composite.get(Engine.engine.world, edge.nodeFrom.getData('body_id'), 'body') + const bodyB = Composite.get(Engine.engine.world, edge.nodeTo.getData('body_id'), 'body') + let constraint = Constraint.create({ + bodyA, + bodyB, + length: JIT.ForceDirected.graphSettings.levelDistance, + stiffness: 0.2 + }) + edge.setData('constraint_id', constraint.id) + World.addConstraint(Engine.engine.world, constraint) + }, + removeEdge: synapse => { }, callUpdate: () => { Engine.engine.world.bodies.forEach(b => { - const node = DataModel.Topics.get(b.topic_id).get('node') + const node = Visualize.mGraph.graph.getNode(b.node_id) const newPos = new $jit.Complex(b.position.x, b.position.y) - node.setPos(newPos, 'current') + node && node.setPos(newPos, 'current') }) + Create.newSynapse.updateForm() Visualize.mGraph.plot() } } diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 1625111f..7add4ea9 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -393,7 +393,6 @@ const JIT = { Visualize.mGraph.busy = false Mouse.boxEndCoordinates = eventInfo.getPos() JIT.selectWithBox(e) - return } } @@ -853,22 +852,6 @@ const JIT = { Create.newTopic.hide() Create.newSynapse.hide() - // set the draw synapse start positions - var l = Selected.Nodes.length - if (l > 0) { - for (let i = l - 1; i >= 0; i -= 1) { - const n = Selected.Nodes[i] - Mouse.synapseStartCoordinates.push({ - x: n.pos.getc().x, - y: n.pos.getc().y - }) - } - } else { - Mouse.synapseStartCoordinates = [{ - x: JIT.tempNode.pos.getc().x, - y: JIT.tempNode.pos.getc().y - }] - } Mouse.synapseEndCoordinates = { x: pos.x, y: pos.y @@ -954,6 +937,8 @@ const JIT = { Create.newTopic.addSynapse = false Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id Create.newSynapse.topic2id = JIT.tempNode2.getData('topic').id + Create.newSynapse.node1 = JIT.tempNode + Create.newSynapse.node2 = JIT.tempNode2 JIT.tempNode2.setData('dim', 25, 'current') Visualize.mGraph.plot() midpoint.x = JIT.tempNode.pos.getc().x + (JIT.tempNode2.pos.getc().x - JIT.tempNode.pos.getc().x) / 2 @@ -961,6 +946,7 @@ const JIT = { pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint) $('#new_synapse').css('left', pixelPos.x + 'px') $('#new_synapse').css('top', pixelPos.y + 'px') + Create.newSynapse.alreadyAdded = false Create.newSynapse.open() JIT.tempNode = null JIT.tempNode2 = null @@ -1068,7 +1054,7 @@ const JIT = { n.pos.setp(theta, rho) } else { n.pos.setc(x, y) - Engine.setTopicPos(n.getData('body_id'), x, y) + Engine.setNodePos(n.getData('body_id'), x, y) } if (Active.Map) { @@ -1274,26 +1260,6 @@ const JIT = { Mouse.boxEndCoordinates = false Visualize.mGraph.plot() }, // selectWithBox - drawSelectBox: function(eventInfo, e) { - const ctx = Visualize.mGraph.canvas.getCtx() - - const startX = Mouse.boxStartCoordinates.x - const startY = Mouse.boxStartCoordinates.y - const currX = eventInfo.getPos().x - const currY = eventInfo.getPos().y - - Visualize.mGraph.canvas.clear() - Visualize.mGraph.plot() - - ctx.beginPath() - ctx.moveTo(startX, startY) - ctx.lineTo(startX, currY) - ctx.lineTo(currX, currY) - ctx.lineTo(currX, startY) - ctx.lineTo(startX, startY) - ctx.strokeStyle = 'black' - ctx.stroke() - }, // drawSelectBox selectNodeOnClickHandler: function(node, e) { if (Visualize.mGraph.busy) return diff --git a/frontend/src/Metamaps/Synapse.js b/frontend/src/Metamaps/Synapse.js index f4091659..90d98df4 100644 --- a/frontend/src/Metamaps/Synapse.js +++ b/frontend/src/Metamaps/Synapse.js @@ -4,6 +4,7 @@ import Active from './Active' import Control from './Control' import Create from './Create' import DataModel from './DataModel' +import Engine from './Engine' import JIT from './JIT' import Map from './Map' import Selected from './Selected' @@ -28,13 +29,18 @@ const Synapse = { } else callback(DataModel.Synapses.get(id)) }, - renderSynapse: function(mapping, synapse, node1, node2, createNewInDB) { + renderSynapse: function(mapping, synapse, node1, node2, createNewInDB, alreadyAdded) { var edgeOnViz + var newedge - var newedge = synapse.createEdge(mapping) - - Visualize.mGraph.graph.addAdjacence(node1, node2, newedge.data) + if (!alreadyAdded) { + newedge = synapse.createEdge(mapping) + Visualize.mGraph.graph.addAdjacence(node1, node2, newedge.data) + } edgeOnViz = Visualize.mGraph.graph.getAdjacence(node1.id, node2.id) + if (!alreadyAdded) { + Engine.addEdge(edgeOnViz) + } synapse.set('edge', edgeOnViz) synapse.updateEdge() // links the synapse and the mapping to the edge @@ -109,7 +115,7 @@ const Synapse = { DataModel.Mappings.add(mapping) // this function also includes the creation of the synapse in the database - self.renderSynapse(mapping, synapse, node1, node2, true) + self.renderSynapse(mapping, synapse, node1, node2, true, Create.newSynapse.alreadyAdded) } // for each in synapsesToCreate Create.newSynapse.hide() diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 8c70e6eb..e753867e 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -11,9 +11,11 @@ import Filter from './Filter' import GlobalUI from './GlobalUI' import JIT from './JIT' import Map from './Map' +import Mouse from './Mouse' import Router from './Router' import Selected from './Selected' import Settings from './Settings' +import Synapse from './Synapse' import SynapseCard from './SynapseCard' import TopicCard from './TopicCard' import Util from './Util' @@ -170,15 +172,26 @@ const Topic = { // TODO: move createNewInDB and permitCreateSynapseAfter into opts renderTopic: function(mapping, topic, createNewInDB, permitCreateSynapseAfter, opts = {}) { var nodeOnViz, tempPos - var newnode = topic.createNode() - var midpoint = {} var pixelPos if (!$.isEmptyObject(Visualize.mGraph.graph.nodes)) { - Visualize.mGraph.graph.addNode(newnode) + if (Create.newTopic.addSynapse && permitCreateSynapseAfter) { + Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id + Create.newSynapse.node1 = JIT.tempNode + // this will also add a new node if the node doesn't exist + Visualize.mGraph.graph.addAdjacence(JIT.tempNode, newnode) + Create.newSynapse.alreadyAdded = true + Mouse.synapseEndCoordinates = null + } + else Visualize.mGraph.graph.addNode(newnode) nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id) + if (Create.newTopic.addSynapse && permitCreateSynapseAfter) Create.newSynapse.node2 = nodeOnViz + Engine.addNode(nodeOnViz) + if (Create.newTopic.addSynapse && permitCreateSynapseAfter) { + Engine.addEdge(Visualize.mGraph.graph.getAdjacence(JIT.tempNode.id, nodeOnViz.id)) + } topic.set('node', nodeOnViz, {silent: true}) topic.updateNode() // links the topic and the mapping to the node @@ -196,16 +209,8 @@ const Topic = { nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'end') } if (Create.newTopic.addSynapse && permitCreateSynapseAfter) { - Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id - - // position the form - midpoint.x = JIT.tempNode.pos.getc().x + (nodeOnViz.pos.getc().x - JIT.tempNode.pos.getc().x) / 2 - midpoint.y = JIT.tempNode.pos.getc().y + (nodeOnViz.pos.getc().y - JIT.tempNode.pos.getc().y) / 2 - pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint) - $('#new_synapse').css('left', pixelPos.x + 'px') - $('#new_synapse').css('top', pixelPos.y + 'px') // show the form - Create.newSynapse.open() + //Create.newSynapse.open() Visualize.mGraph.fx.animate({ modes: ['node-property:dim'], duration: 500, @@ -227,6 +232,7 @@ const Topic = { Engine.run() Visualize.mGraph.loadJSON(newnode) nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id) + Engine.addNode(nodeOnViz) topic.set('node', nodeOnViz, {silent: true}) topic.updateNode() // links the topic and the mapping to the node @@ -251,7 +257,6 @@ const Topic = { } var topicSuccessCallback = function(topicModel, response) { if (Active.Map) { - Engine.addTopic(mapping) mapping.save({ mappable_id: topicModel.id }, { success: function(model, response) { mappingSuccessCallback(model, response, topicModel) @@ -264,6 +269,7 @@ const Topic = { if (Create.newTopic.addSynapse) { Create.newSynapse.topic2id = topicModel.id + Synapse.createSynapseLocally() } } diff --git a/frontend/src/patched/JIT.js b/frontend/src/patched/JIT.js index e780604e..052d1789 100644 --- a/frontend/src/patched/JIT.js +++ b/frontend/src/patched/JIT.js @@ -2560,7 +2560,10 @@ Extras.Classes.Navigation = new Class({ } if (Metamaps.Mouse.boxStartCoordinates && ((e.button == 0 && e.shiftKey) || (e.button == 0 && e.ctrlKey) || rightClick)) { Metamaps.Visualize.mGraph.busy = true; - Metamaps.JIT.drawSelectBox(eventInfo,e); + Metamaps.Mouse.boxEndCoordinates = { + x: eventInfo.getPos().x, + y: eventInfo.getPos().y + } //console.log('mouse move'); return; } @@ -2606,9 +2609,7 @@ Extras.Classes.Navigation = new Class({ this.pressed = false; // START METAMAPS CODE - if (Metamaps.Mouse.didPan) Metamaps.JIT.SmoothPanning(); - - + if (Metamaps.Mouse.didPan) Metamaps.JIT.SmoothPanning(); // END METAMAPS CODE }, @@ -2651,7 +2652,10 @@ Extras.Classes.Navigation = new Class({ } if (Metamaps.Mouse.boxStartCoordinates && ((e.button == 0 && e.shiftKey) || (e.button == 0 && e.ctrlKey) || rightClick)) { Metamaps.Visualize.mGraph.busy = true; - Metamaps.JIT.drawSelectBox(eventInfo,e); + Metamaps.Mouse.boxEndCoordinates = { + x: eventInfo.getPos().x, + y: eventInfo.getPos().y + } return; } if (rightClick){ @@ -7225,7 +7229,7 @@ Graph.Plot = { var T = !!root.visited; //START METAMAPS CODE - if (Metamaps.Mouse.synapseStartCoordinates.length > 0) { + if (Metamaps.Mouse.synapseStartCoordinates.length > 0 && Metamaps.Mouse.synapseEndCoordinates) { ctx.save(); var start; var end = Metamaps.Mouse.synapseEndCoordinates; @@ -7238,6 +7242,19 @@ Graph.Plot = { } ctx.restore(); } + + if (Metamaps.Mouse.boxStartCoordinates && Metamaps.Mouse.boxEndCoordinates) { + ctx.save(); + ctx.beginPath() + ctx.moveTo(Metamaps.Mouse.boxStartCoordinates.x, Metamaps.Mouse.boxStartCoordinates.y) + ctx.lineTo(Metamaps.Mouse.boxStartCoordinates.x, Metamaps.Mouse.boxEndCoordinates.y) + ctx.lineTo(Metamaps.Mouse.boxEndCoordinates.x, Metamaps.Mouse.boxEndCoordinates.y) + ctx.lineTo(Metamaps.Mouse.boxEndCoordinates.x, Metamaps.Mouse.boxStartCoordinates.y) + ctx.lineTo(Metamaps.Mouse.boxStartCoordinates.x, Metamaps.Mouse.boxStartCoordinates.y) + ctx.strokeStyle = 'black' + ctx.stroke() + ctx.restore() + } //END METAMAPS CODE aGraph.eachNode(function(node) {