metamaps--metamaps/frontend/src/Metamaps/Visualize.js

233 lines
7.7 KiB
JavaScript
Raw Normal View History

/* global Metamaps, $ */
2016-09-22 09:36:47 +00:00
2016-09-22 15:51:33 +00:00
import _ from 'lodash'
import $jit from '../patched/JIT'
2016-09-22 09:36:47 +00:00
import Active from './Active'
import JIT from './JIT'
import Loading from './Loading'
2016-09-22 10:31:56 +00:00
import Router from './Router'
import TopicCard from './TopicCard'
2016-09-22 09:36:47 +00:00
/*
* Metamaps.Visualize
*
* Dependencies:
* - Metamaps.Metacodes
* - Metamaps.Synapses
* - Metamaps.Topics
*/
const Visualize = {
mGraph: null, // a reference to the graph object.
cameraPosition: null, // stores the camera position when using a 3D visualization
type: 'ForceDirected', // the type of graph we're building, could be "RGraph", "ForceDirected", or "ForceDirected3D"
loadLater: false, // indicates whether there is JSON that should be loaded right in the offset, or whether to wait till the first topic is created
2016-09-22 09:14:34 +00:00
touchDragNode: null,
init: function () {
var self = Visualize
// disable awkward dragging of the canvas element that would sometimes happen
$('#infovis-canvas').on('dragstart', function (event) {
event.preventDefault()
})
// prevent touch events on the canvas from default behaviour
$('#infovis-canvas').bind('touchstart', function (event) {
event.preventDefault()
self.mGraph.events.touched = true
})
// prevent touch events on the canvas from default behaviour
$('#infovis-canvas').bind('touchmove', function (event) {
2016-09-22 09:36:47 +00:00
// JIT.touchPanZoomHandler(event)
})
// prevent touch events on the canvas from default behaviour
$('#infovis-canvas').bind('touchend touchcancel', function (event) {
lastDist = 0
2016-09-22 10:31:56 +00:00
if (!self.mGraph.events.touchMoved && !Visualize.touchDragNode) TopicCard.hideCurrentCard()
self.mGraph.events.touched = self.mGraph.events.touchMoved = false
2016-09-22 09:14:34 +00:00
Visualize.touchDragNode = false
})
},
computePositions: function () {
var self = Visualize,
mapping
if (self.type == 'RGraph') {
var i, l, startPos, endPos, topic, synapse
self.mGraph.graph.eachNode(function (n) {
topic = Metamaps.Topics.get(n.id)
topic.set({ node: n }, { silent: true })
topic.updateNode()
n.eachAdjacency(function (edge) {
if (!edge.getData('init')) {
edge.setData('init', true)
l = edge.getData('synapseIDs').length
for (i = 0; i < l; i++) {
synapse = Metamaps.Synapses.get(edge.getData('synapseIDs')[i])
synapse.set({ edge: edge }, { silent: true })
synapse.updateEdge()
}
}
})
var pos = n.getPos()
pos.setc(-200, -200)
})
self.mGraph.compute('end')
} else if (self.type == 'ForceDirected') {
var i, l, startPos, endPos, topic, synapse
self.mGraph.graph.eachNode(function (n) {
topic = Metamaps.Topics.get(n.id)
topic.set({ node: n }, { silent: true })
topic.updateNode()
mapping = topic.getMapping()
n.eachAdjacency(function (edge) {
if (!edge.getData('init')) {
edge.setData('init', true)
l = edge.getData('synapseIDs').length
for (i = 0; i < l; i++) {
synapse = Metamaps.Synapses.get(edge.getData('synapseIDs')[i])
synapse.set({ edge: edge }, { silent: true })
synapse.updateEdge()
}
}
})
startPos = new $jit.Complex(0, 0)
endPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc'))
n.setPos(startPos, 'start')
n.setPos(endPos, 'end')
})
} else if (self.type == 'ForceDirected3D') {
self.mGraph.compute()
}
},
/**
* render does the heavy lifting of creating the engine that renders the graph with the properties we desire
*
*/
render: function () {
var self = Visualize, RGraphSettings, FDSettings
2016-09-29 02:36:53 +00:00
if (self.type == 'RGraph') {
// clear the previous canvas from #infovis
$('#infovis').empty()
2016-09-22 09:36:47 +00:00
RGraphSettings = $.extend(true, {}, JIT.ForceDirected.graphSettings)
2016-09-22 09:36:47 +00:00
$jit.RGraph.Plot.NodeTypes.implement(JIT.ForceDirected.nodeSettings)
$jit.RGraph.Plot.EdgeTypes.implement(JIT.ForceDirected.edgeSettings)
RGraphSettings.width = $(document).width()
RGraphSettings.height = $(document).height()
2016-09-22 09:36:47 +00:00
RGraphSettings.background = JIT.RGraph.background
RGraphSettings.levelDistance = JIT.RGraph.levelDistance
self.mGraph = new $jit.RGraph(RGraphSettings)
2016-09-29 02:36:53 +00:00
} else if (self.type == 'ForceDirected') {
// clear the previous canvas from #infovis
$('#infovis').empty()
2016-09-22 09:36:47 +00:00
FDSettings = $.extend(true, {}, JIT.ForceDirected.graphSettings)
2016-09-22 09:36:47 +00:00
$jit.ForceDirected.Plot.NodeTypes.implement(JIT.ForceDirected.nodeSettings)
$jit.ForceDirected.Plot.EdgeTypes.implement(JIT.ForceDirected.edgeSettings)
FDSettings.width = $('body').width()
FDSettings.height = $('body').height()
self.mGraph = new $jit.ForceDirected(FDSettings)
} else if (self.type == 'ForceDirected3D' && !self.mGraph) {
// clear the previous canvas from #infovis
$('#infovis').empty()
// init ForceDirected3D
2016-09-22 09:36:47 +00:00
self.mGraph = new $jit.ForceDirected3D(JIT.ForceDirected3D.graphSettings)
self.cameraPosition = self.mGraph.canvas.canvases[0].camera.position
} else {
self.mGraph.graph.empty()
}
2016-04-26 12:08:12 +00:00
2016-09-22 09:36:47 +00:00
if (self.type == 'ForceDirected' && Active.Mapper) $.post('/maps/' + Active.Map.id + '/events/user_presence')
2016-04-26 12:08:12 +00:00
function runAnimation () {
Loading.hide()
// load JSON data, if it's not empty
if (!self.loadLater) {
// load JSON data.
var rootIndex = 0
2016-09-22 09:36:47 +00:00
if (Active.Topic) {
var node = _.find(JIT.vizData, function (node) {
return node.id === Active.Topic.id
})
2016-09-22 09:36:47 +00:00
rootIndex = _.indexOf(JIT.vizData, node)
}
2016-09-22 09:36:47 +00:00
self.mGraph.loadJSON(JIT.vizData, rootIndex)
// compute positions and plot.
self.computePositions()
self.mGraph.busy = true
if (self.type == 'RGraph') {
2016-09-22 09:36:47 +00:00
self.mGraph.fx.animate(JIT.RGraph.animate)
} else if (self.type == 'ForceDirected') {
2016-09-22 09:36:47 +00:00
self.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
} else if (self.type == 'ForceDirected3D') {
2016-09-22 09:36:47 +00:00
self.mGraph.animate(JIT.ForceDirected.animateFDLayout)
}
}
}
// hold until all the needed metacode images are loaded
// hold for a maximum of 80 passes, or 4 seconds of waiting time
var tries = 0
function hold () {
var unique = _.uniq(Metamaps.Topics.models, function (metacode) { return metacode.get('metacode_id'); }),
requiredMetacodes = _.map(unique, function (metacode) { return metacode.get('metacode_id'); }),
loadedCount = 0
_.each(requiredMetacodes, function (metacode_id) {
var metacode = Metamaps.Metacodes.get(metacode_id),
img = metacode ? metacode.get('image') : false
if (img && (img.complete || (typeof img.naturalWidth !== 'undefined' && img.naturalWidth !== 0))) {
loadedCount += 1
}
})
if (loadedCount === requiredMetacodes.length || tries > 80) runAnimation()
else setTimeout(function () { tries++; hold() }, 50)
}
hold()
// update the url now that the map is ready
2016-09-22 10:31:56 +00:00
clearTimeout(Router.timeoutId)
Router.timeoutId = setTimeout(function () {
2016-09-22 09:36:47 +00:00
var m = Active.Map
var t = Active.Topic
if (m && window.location.pathname !== '/maps/' + m.id) {
2016-09-22 10:31:56 +00:00
Router.navigate('/maps/' + m.id)
}
else if (t && window.location.pathname !== '/topics/' + t.id) {
2016-09-22 10:31:56 +00:00
Router.navigate('/topics/' + t.id)
}
}, 800)
},
clearVisualization: function() {
Visualize.mGraph.graph.empty()
Visualize.mGraph.plot()
JIT.centerMap(Visualize.mGraph.canvas)
$('#infovis').empty()
},
}
export default Visualize