2016-10-02 22:28:00 +08:00
|
|
|
/* global $ */
|
2016-09-22 17:36:47 +08:00
|
|
|
|
2016-09-22 23:51:33 +08:00
|
|
|
import _ from 'lodash'
|
|
|
|
|
2016-09-23 11:47:40 +08:00
|
|
|
import $jit from '../patched/JIT'
|
|
|
|
|
2016-09-22 17:36:47 +08:00
|
|
|
import Active from './Active'
|
2016-10-02 22:28:00 +08:00
|
|
|
import DataModel from './DataModel'
|
2016-09-22 17:36:47 +08:00
|
|
|
import JIT from './JIT'
|
2016-10-02 21:10:41 +08:00
|
|
|
import Loading from './Loading'
|
2016-09-22 18:31:56 +08:00
|
|
|
import Router from './Router'
|
|
|
|
import TopicCard from './TopicCard'
|
2016-09-22 17:36:47 +08:00
|
|
|
|
2016-09-22 15:21:59 +08:00
|
|
|
const Visualize = {
|
2016-04-24 11:50:35 -04:00
|
|
|
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 17:14:34 +08:00
|
|
|
touchDragNode: null,
|
2016-11-07 15:25:08 -05:00
|
|
|
init: function(serverData) {
|
2016-09-22 15:21:59 +08:00
|
|
|
var self = Visualize
|
2016-10-02 22:28:00 +08:00
|
|
|
|
|
|
|
if (serverData.VisualizeType) self.type = serverData.VisualizeType
|
|
|
|
|
2016-04-24 11:50:35 -04:00
|
|
|
// disable awkward dragging of the canvas element that would sometimes happen
|
2016-11-07 15:25:08 -05:00
|
|
|
$('#infovis-canvas').on('dragstart', function(event) {
|
2016-04-24 11:50:35 -04:00
|
|
|
event.preventDefault()
|
|
|
|
})
|
|
|
|
|
|
|
|
// prevent touch events on the canvas from default behaviour
|
2016-11-07 15:25:08 -05:00
|
|
|
$('#infovis-canvas').bind('touchstart', function(event) {
|
2016-04-24 11:50:35 -04:00
|
|
|
event.preventDefault()
|
|
|
|
self.mGraph.events.touched = true
|
|
|
|
})
|
|
|
|
|
|
|
|
// prevent touch events on the canvas from default behaviour
|
2016-11-07 15:25:08 -05:00
|
|
|
$('#infovis-canvas').bind('touchmove', function(event) {
|
2016-09-22 17:36:47 +08:00
|
|
|
// JIT.touchPanZoomHandler(event)
|
2016-04-24 11:50:35 -04:00
|
|
|
})
|
|
|
|
|
|
|
|
// prevent touch events on the canvas from default behaviour
|
2016-11-07 15:25:08 -05:00
|
|
|
$('#infovis-canvas').bind('touchend touchcancel', function(event) {
|
2016-09-22 18:31:56 +08:00
|
|
|
if (!self.mGraph.events.touchMoved && !Visualize.touchDragNode) TopicCard.hideCurrentCard()
|
2016-04-24 11:50:35 -04:00
|
|
|
self.mGraph.events.touched = self.mGraph.events.touchMoved = false
|
2016-09-22 17:14:34 +08:00
|
|
|
Visualize.touchDragNode = false
|
2016-04-24 11:50:35 -04:00
|
|
|
})
|
|
|
|
},
|
2016-11-07 15:25:08 -05:00
|
|
|
computePositions: function() {
|
|
|
|
const self = Visualize
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
if (self.type === 'RGraph') {
|
|
|
|
let i
|
|
|
|
let l
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
self.mGraph.graph.eachNode(function(n) {
|
|
|
|
const topic = DataModel.Topics.get(n.id)
|
2016-04-24 11:50:35 -04:00
|
|
|
topic.set({ node: n }, { silent: true })
|
|
|
|
topic.updateNode()
|
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
n.eachAdjacency(function(edge) {
|
2016-04-24 11:50:35 -04:00
|
|
|
if (!edge.getData('init')) {
|
|
|
|
edge.setData('init', true)
|
|
|
|
|
|
|
|
l = edge.getData('synapseIDs').length
|
|
|
|
for (i = 0; i < l; i++) {
|
2016-11-07 15:25:08 -05:00
|
|
|
const synapse = DataModel.Synapses.get(edge.getData('synapseIDs')[i])
|
2016-04-24 11:50:35 -04:00
|
|
|
synapse.set({ edge: edge }, { silent: true })
|
|
|
|
synapse.updateEdge()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
var pos = n.getPos()
|
|
|
|
pos.setc(-200, -200)
|
|
|
|
})
|
|
|
|
self.mGraph.compute('end')
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (self.type === 'ForceDirected') {
|
|
|
|
self.mGraph.graph.eachNode(function(n) {
|
|
|
|
const topic = DataModel.Topics.get(n.id)
|
2016-04-24 11:50:35 -04:00
|
|
|
topic.set({ node: n }, { silent: true })
|
|
|
|
topic.updateNode()
|
2016-11-07 15:25:08 -05:00
|
|
|
const mapping = topic.getMapping()
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
n.eachAdjacency(function(edge) {
|
2016-04-24 11:50:35 -04:00
|
|
|
if (!edge.getData('init')) {
|
|
|
|
edge.setData('init', true)
|
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
const l = edge.getData('synapseIDs').length
|
|
|
|
for (let i = 0; i < l; i++) {
|
|
|
|
const synapse = DataModel.Synapses.get(edge.getData('synapseIDs')[i])
|
2016-04-24 11:50:35 -04:00
|
|
|
synapse.set({ edge: edge }, { silent: true })
|
|
|
|
synapse.updateEdge()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
const startPos = new $jit.Complex(0, 0)
|
|
|
|
const endPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc'))
|
2016-04-24 11:50:35 -04:00
|
|
|
n.setPos(startPos, 'start')
|
|
|
|
n.setPos(endPos, 'end')
|
|
|
|
})
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (self.type === 'ForceDirected3D') {
|
2016-04-24 11:50:35 -04:00
|
|
|
self.mGraph.compute()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* render does the heavy lifting of creating the engine that renders the graph with the properties we desire
|
|
|
|
*
|
|
|
|
*/
|
2016-11-07 15:25:08 -05:00
|
|
|
render: function() {
|
|
|
|
const self = Visualize
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
if (self.type === 'RGraph') {
|
2016-08-10 18:37:26 +00:00
|
|
|
// clear the previous canvas from #infovis
|
|
|
|
$('#infovis').empty()
|
2016-11-07 15:25:08 -05:00
|
|
|
|
|
|
|
const RGraphSettings = $.extend(true, {}, JIT.ForceDirected.graphSettings)
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-09-22 17:36:47 +08:00
|
|
|
$jit.RGraph.Plot.NodeTypes.implement(JIT.ForceDirected.nodeSettings)
|
|
|
|
$jit.RGraph.Plot.EdgeTypes.implement(JIT.ForceDirected.edgeSettings)
|
2016-04-24 11:50:35 -04:00
|
|
|
|
|
|
|
RGraphSettings.width = $(document).width()
|
|
|
|
RGraphSettings.height = $(document).height()
|
2016-09-22 17:36:47 +08:00
|
|
|
RGraphSettings.background = JIT.RGraph.background
|
|
|
|
RGraphSettings.levelDistance = JIT.RGraph.levelDistance
|
2016-04-24 11:50:35 -04:00
|
|
|
|
|
|
|
self.mGraph = new $jit.RGraph(RGraphSettings)
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (self.type === 'ForceDirected') {
|
2016-08-10 18:37:26 +00:00
|
|
|
// clear the previous canvas from #infovis
|
|
|
|
$('#infovis').empty()
|
2016-11-07 15:25:08 -05:00
|
|
|
|
|
|
|
const FDSettings = $.extend(true, {}, JIT.ForceDirected.graphSettings)
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-09-22 17:36:47 +08:00
|
|
|
$jit.ForceDirected.Plot.NodeTypes.implement(JIT.ForceDirected.nodeSettings)
|
|
|
|
$jit.ForceDirected.Plot.EdgeTypes.implement(JIT.ForceDirected.edgeSettings)
|
2016-04-24 11:50:35 -04:00
|
|
|
|
|
|
|
FDSettings.width = $('body').width()
|
|
|
|
FDSettings.height = $('body').height()
|
|
|
|
|
|
|
|
self.mGraph = new $jit.ForceDirected(FDSettings)
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (self.type === 'ForceDirected3D' && !self.mGraph) {
|
2016-08-10 18:37:26 +00:00
|
|
|
// clear the previous canvas from #infovis
|
|
|
|
$('#infovis').empty()
|
2016-11-07 15:25:08 -05:00
|
|
|
|
2016-04-24 11:50:35 -04:00
|
|
|
// init ForceDirected3D
|
2016-09-22 17:36:47 +08:00
|
|
|
self.mGraph = new $jit.ForceDirected3D(JIT.ForceDirected3D.graphSettings)
|
2016-04-24 11:50:35 -04:00
|
|
|
self.cameraPosition = self.mGraph.canvas.canvases[0].camera.position
|
|
|
|
} else {
|
|
|
|
self.mGraph.graph.empty()
|
|
|
|
}
|
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
if (self.type === 'ForceDirected' && Active.Mapper) $.post('/maps/' + Active.Map.id + '/events/user_presence')
|
2016-04-26 08:08:12 -04:00
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
function runAnimation() {
|
2016-10-02 21:10:41 +08:00
|
|
|
Loading.hide()
|
2016-04-24 11:50:35 -04:00
|
|
|
// load JSON data, if it's not empty
|
|
|
|
if (!self.loadLater) {
|
|
|
|
// load JSON data.
|
|
|
|
var rootIndex = 0
|
2016-09-22 17:36:47 +08:00
|
|
|
if (Active.Topic) {
|
2016-11-07 15:25:08 -05:00
|
|
|
var node = _.find(JIT.vizData, function(node) {
|
2016-09-22 17:36:47 +08:00
|
|
|
return node.id === Active.Topic.id
|
2016-04-24 11:50:35 -04:00
|
|
|
})
|
2016-09-22 17:36:47 +08:00
|
|
|
rootIndex = _.indexOf(JIT.vizData, node)
|
2016-04-24 11:50:35 -04:00
|
|
|
}
|
2016-09-22 17:36:47 +08:00
|
|
|
self.mGraph.loadJSON(JIT.vizData, rootIndex)
|
2016-04-24 11:50:35 -04:00
|
|
|
// compute positions and plot.
|
|
|
|
self.computePositions()
|
|
|
|
self.mGraph.busy = true
|
2016-11-07 15:25:08 -05:00
|
|
|
if (self.type === 'RGraph') {
|
2016-09-22 17:36:47 +08:00
|
|
|
self.mGraph.fx.animate(JIT.RGraph.animate)
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (self.type === 'ForceDirected') {
|
2016-09-22 17:36:47 +08:00
|
|
|
self.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (self.type === 'ForceDirected3D') {
|
2016-09-22 17:36:47 +08:00
|
|
|
self.mGraph.animate(JIT.ForceDirected.animateFDLayout)
|
2016-04-24 11:50:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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
|
2016-11-07 15:25:08 -05:00
|
|
|
function hold() {
|
|
|
|
const unique = _.uniq(DataModel.Topics.models, function(metacode) { return metacode.get('metacode_id') })
|
|
|
|
const requiredMetacodes = _.map(unique, function(metacode) { return metacode.get('metacode_id') })
|
|
|
|
let loadedCount = 0
|
2016-04-24 11:50:35 -04:00
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
_.each(requiredMetacodes, function(metacodeId) {
|
|
|
|
const metacode = DataModel.Metacodes.get(metacodeId)
|
|
|
|
const img = metacode ? metacode.get('image') : false
|
2016-04-24 11:50:35 -04:00
|
|
|
|
|
|
|
if (img && (img.complete || (typeof img.naturalWidth !== 'undefined' && img.naturalWidth !== 0))) {
|
|
|
|
loadedCount += 1
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2016-11-07 15:25:08 -05:00
|
|
|
if (loadedCount === requiredMetacodes.length || tries > 80) {
|
|
|
|
runAnimation()
|
|
|
|
} else {
|
|
|
|
setTimeout(function() { tries++; hold() }, 50)
|
|
|
|
}
|
2016-04-24 11:50:35 -04:00
|
|
|
}
|
|
|
|
hold()
|
|
|
|
|
|
|
|
// update the url now that the map is ready
|
2016-09-22 18:31:56 +08:00
|
|
|
clearTimeout(Router.timeoutId)
|
2016-11-07 15:25:08 -05:00
|
|
|
Router.timeoutId = setTimeout(function() {
|
2016-09-22 17:36:47 +08:00
|
|
|
var m = Active.Map
|
|
|
|
var t = Active.Topic
|
2016-04-24 11:50:35 -04:00
|
|
|
|
|
|
|
if (m && window.location.pathname !== '/maps/' + m.id) {
|
2016-09-22 18:31:56 +08:00
|
|
|
Router.navigate('/maps/' + m.id)
|
2016-11-07 15:25:08 -05:00
|
|
|
} else if (t && window.location.pathname !== '/topics/' + t.id) {
|
2016-09-22 18:31:56 +08:00
|
|
|
Router.navigate('/topics/' + t.id)
|
2016-04-24 11:50:35 -04:00
|
|
|
}
|
|
|
|
}, 800)
|
2016-09-25 20:10:18 +08:00
|
|
|
},
|
|
|
|
clearVisualization: function() {
|
|
|
|
Visualize.mGraph.graph.empty()
|
|
|
|
Visualize.mGraph.plot()
|
|
|
|
JIT.centerMap(Visualize.mGraph.canvas)
|
|
|
|
$('#infovis').empty()
|
2016-11-07 15:25:08 -05:00
|
|
|
}
|
2016-09-22 15:21:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export default Visualize
|