(function(){ Renderer = function(canvas){ canvas = $(canvas).get(0) var ctx = canvas.getContext("2d") var particleSystem = null var palette = { "Africa": "#D68300", "Asia": "#4D7A00", "Europe": "#6D87CF", "North America": "#D4E200", "Oceania": "#4F2170", "South America": "#CD2900" } var that = { init:function(system){ particleSystem = system particleSystem.screen({padding:[100, 60, 60, 60], // leave some space at the bottom for the param sliders step:.02}) // have the ‘camera’ zoom somewhat slowly as the graph unfolds $(window).resize(that.resize) that.resize() that.initMouseHandling() }, redraw:function(){ if (particleSystem===null) return ctx.clearRect(0,0, canvas.width, canvas.height) ctx.strokeStyle = "#d3d3d3" ctx.lineWidth = 1 ctx.beginPath() particleSystem.eachEdge(function(edge, pt1, pt2){ // edge: {source:Node, target:Node, length:#, data:{}} // pt1: {x:#, y:#} source position in screen coords // pt2: {x:#, y:#} target position in screen coords var weight = null // Math.max(1,edge.data.border/100) var color = null // edge.data.color if (!color || (""+color).match(/^[ \t]*$/)) color = null if (color!==undefined || weight!==undefined){ ctx.save() ctx.beginPath() if (!isNaN(weight)) ctx.lineWidth = weight if (edge.source.data.region==edge.target.data.region){ ctx.strokeStyle = palette[edge.source.data.region] } // if (color) ctx.strokeStyle = color ctx.fillStyle = null ctx.moveTo(pt1.x, pt1.y) ctx.lineTo(pt2.x, pt2.y) ctx.stroke() ctx.restore() }else{ // draw a line from pt1 to pt2 ctx.moveTo(pt1.x, pt1.y) ctx.lineTo(pt2.x, pt2.y) } }) ctx.stroke() particleSystem.eachNode(function(node, pt){ // node: {mass:#, p:{x,y}, name:"", data:{}} // pt: {x:#, y:#} node position in screen coords // determine the box size and round off the coords if we'll be // drawing a text label (awful alignment jitter otherwise...) var w = ctx.measureText(node.data.label||"").width + 6 var label = node.data.label if (!(label||"").match(/^[ \t]*$/)){ pt.x = Math.floor(pt.x) pt.y = Math.floor(pt.y) }else{ label = null } // clear any edges below the text label // ctx.fillStyle = 'rgba(255,255,255,.6)' // ctx.fillRect(pt.x-w/2, pt.y-7, w,14) ctx.clearRect(pt.x-w/2, pt.y-7, w,14) // draw the text if (label){ ctx.font = "bold 11px Arial" ctx.textAlign = "center" // if (node.data.region) ctx.fillStyle = palette[node.data.region] // else ctx.fillStyle = "#888888" ctx.fillStyle = "#888888" // ctx.fillText(label||"", pt.x, pt.y+4) ctx.fillText(label||"", pt.x, pt.y+4) } }) }, resize:function(){ var w = $(window).width(), h = $(window).height(); canvas.width = w; canvas.height = h // resize the canvas element to fill the screen particleSystem.screenSize(w,h) // inform the system so it can map coords for us that.redraw() }, initMouseHandling:function(){ // no-nonsense drag and drop (thanks springy.js) selected = null; nearest = null; var dragged = null; var oldmass = 1 $(canvas).mousedown(function(e){ var pos = $(this).offset(); var p = {x:e.pageX-pos.left, y:e.pageY-pos.top} selected = nearest = dragged = particleSystem.nearest(p); if (selected.node !== null){ // dragged.node.tempMass = 10000 dragged.node.fixed = true } return false }); $(canvas).mousemove(function(e){ var old_nearest = nearest && nearest.node._id var pos = $(this).offset(); var s = {x:e.pageX-pos.left, y:e.pageY-pos.top}; nearest = particleSystem.nearest(s); if (!nearest) return if (dragged !== null && dragged.node !== null){ var p = particleSystem.fromScreen(s) dragged.node.p = {x:p.x, y:p.y} // dragged.tempMass = 10000 } return false }); $(window).bind('mouseup',function(e){ if (dragged===null || dragged.node===undefined) return dragged.node.fixed = false dragged.node.tempMass = 100 dragged = null; selected = null return false }); }, } return that } var Maps = function(elt){ var sys = arbor.ParticleSystem(4000, 500, 0.5, 55) sys.renderer = Renderer("#viewport") // our newly created renderer will have its .init() method called shortly by sys... var dom = $(elt) var _links = dom.find('ul') var _sources = { nations:'Derived from Wikipedia’s List of countries and territories by land borders', states:'Derived from Land borders by state', risk:'Derived from Garrett Robinson’s The Strategy of Risk' } var _maps = { usofa:{title:"United States", p:{stiffness:600}, source:_sources.states}, africa:{title:"Africa", p:{stiffness:300}, source:_sources.nations}, asia:{title:"Asia", p:{stiffness:500}, source:_sources.nations}, europe:{title:"Europe", p:{stiffness:300}, source:_sources.nations}, mideast:{title:"Middle East", p:{stiffness:500}, source:_sources.nations}, risk:{title:"Risk", p:{stiffness:400}, source:_sources.risk} } var that = { init:function(){ $.each(_maps, function(stub, map){ _links.append("
  • "+map.title+"
  • ") }) _links.find('li > a').click(that.mapClick) _links.find('.usofa').click() return that }, mapClick:function(e){ var selected = $(e.target) var newMap = selected.attr('class') if (newMap in _maps) that.selectMap(newMap) _links.find('li > a').removeClass('active') selected.addClass('active') return false }, selectMap:function(map_id){ $.getJSON("maps/"+map_id+".json",function(data){ // load the raw data into the particle system as is (since it's already formatted correctly for .merge) var nodes = data.nodes $.each(nodes, function(name, info){ info.label=name.replace(/(people's )?republic of /i,'').replace(/ and /g,' & ') }) sys.merge({nodes:nodes, edges:data.edges}) sys.parameters(_maps[map_id].p) $("#dataset").html(_maps[map_id].source) }) } } return that.init() } $(document).ready(function(){ var mcp = Maps("#maps") }) })()