atlas.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. (function () {
  2. Renderer = function (canvas) {
  3. canvas = $(canvas).get(0)
  4. var ctx = canvas.getContext("2d")
  5. var particleSystem = null
  6. var palette = {
  7. "Africa": "#D68300",
  8. "Asia": "#4D7A00",
  9. "Europe": "#6D87CF",
  10. "North America": "#D4E200",
  11. "Oceania": "#4F2170",
  12. "South America": "#CD2900"
  13. }
  14. var that = {
  15. init: function (system) {
  16. particleSystem = system
  17. particleSystem.screen({
  18. padding: [100, 60, 60, 60], // leave some space at the bottom for the param sliders
  19. step: .02
  20. }) // have the ‘camera’ zoom somewhat slowly as the graph unfolds
  21. $(window).resize(that.resize)
  22. that.resize()
  23. that.initMouseHandling()
  24. },
  25. redraw: function () {
  26. if (particleSystem === null) return
  27. ctx.clearRect(0, 0, canvas.width, canvas.height)
  28. ctx.strokeStyle = "#d3d3d3"
  29. ctx.lineWidth = 1
  30. ctx.beginPath()
  31. particleSystem.eachEdge(function (edge, pt1, pt2) {
  32. // edge: {source:Node, target:Node, length:#, data:{}}
  33. // pt1: {x:#, y:#} source position in screen coords
  34. // pt2: {x:#, y:#} target position in screen coords
  35. var weight = null // Math.max(1,edge.data.border/100)
  36. var color = null // edge.data.color
  37. if (!color || ("" + color).match(/^[ \t]*$/)) color = null
  38. if (color !== undefined || weight !== undefined) {
  39. ctx.save()
  40. ctx.beginPath()
  41. if (!isNaN(weight)) ctx.lineWidth = weight
  42. if (edge.source.data.region == edge.target.data.region) {
  43. ctx.strokeStyle = palette[edge.source.data.region]
  44. }
  45. // if (color) ctx.strokeStyle = color
  46. ctx.fillStyle = null
  47. ctx.moveTo(pt1.x, pt1.y)
  48. ctx.lineTo(pt2.x, pt2.y)
  49. ctx.stroke()
  50. ctx.restore()
  51. } else {
  52. // draw a line from pt1 to pt2
  53. ctx.moveTo(pt1.x, pt1.y)
  54. ctx.lineTo(pt2.x, pt2.y)
  55. }
  56. })
  57. ctx.stroke()
  58. particleSystem.eachNode(function (node, pt) {
  59. // node: {mass:#, p:{x,y}, name:"", data:{}}
  60. // pt: {x:#, y:#} node position in screen coords
  61. // determine the box size and round off the coords if we'll be
  62. // drawing a text label (awful alignment jitter otherwise...)
  63. var w = ctx.measureText(node.data.label || "").width + 6
  64. var label = node.data.label
  65. if (!(label || "").match(/^[ \t]*$/)) {
  66. pt.x = Math.floor(pt.x)
  67. pt.y = Math.floor(pt.y)
  68. } else {
  69. label = null
  70. }
  71. // clear any edges below the text label
  72. // ctx.fillStyle = 'rgba(255,255,255,.6)'
  73. // ctx.fillRect(pt.x-w/2, pt.y-7, w,14)
  74. ctx.clearRect(pt.x - w / 2, pt.y - 7, w, 14)
  75. // draw the text
  76. if (label) {
  77. ctx.font = "bold 11px Arial"
  78. ctx.textAlign = "center"
  79. // if (node.data.region) ctx.fillStyle = palette[node.data.region]
  80. // else ctx.fillStyle = "#888888"
  81. ctx.fillStyle = "#888888"
  82. // ctx.fillText(label||"", pt.x, pt.y+4)
  83. ctx.fillText(label || "", pt.x, pt.y + 4)
  84. }
  85. })
  86. },
  87. resize: function () {
  88. var w = $(window).width(),
  89. h = $(window).height();
  90. canvas.width = w;
  91. canvas.height = h // resize the canvas element to fill the screen
  92. particleSystem.screenSize(w, h) // inform the system so it can map coords for us
  93. that.redraw()
  94. },
  95. initMouseHandling: function () {
  96. // no-nonsense drag and drop (thanks springy.js)
  97. selected = null;
  98. nearest = null;
  99. var dragged = null;
  100. var oldmass = 1
  101. $(canvas).mousedown(function (e) {
  102. var pos = $(this).offset();
  103. var p = {x: e.pageX - pos.left, y: e.pageY - pos.top}
  104. selected = nearest = dragged = particleSystem.nearest(p);
  105. if (selected.node !== null) {
  106. // dragged.node.tempMass = 10000
  107. dragged.node.fixed = true
  108. }
  109. return false
  110. });
  111. $(canvas).mousemove(function (e) {
  112. var old_nearest = nearest && nearest.node._id
  113. var pos = $(this).offset();
  114. var s = {x: e.pageX - pos.left, y: e.pageY - pos.top};
  115. nearest = particleSystem.nearest(s);
  116. if (!nearest) return
  117. if (dragged !== null && dragged.node !== null) {
  118. var p = particleSystem.fromScreen(s)
  119. dragged.node.p = {x: p.x, y: p.y}
  120. // dragged.tempMass = 10000
  121. }
  122. return false
  123. });
  124. $(window).bind('mouseup', function (e) {
  125. if (dragged === null || dragged.node === undefined) return
  126. dragged.node.fixed = false
  127. dragged.node.tempMass = 100
  128. dragged = null;
  129. selected = null
  130. return false
  131. });
  132. },
  133. }
  134. return that
  135. }
  136. var Maps = function (elt) {
  137. var sys = arbor.ParticleSystem(4000, 500, 0.5, 55)
  138. sys.renderer = Renderer("#viewport") // our newly created renderer will have its .init() method called shortly by sys...
  139. var dom = $(elt)
  140. var _links = dom.find('ul')
  141. var _sources = {
  142. nations: 'Derived from Wikipedia’s <a target="_blank" href="http://en.wikipedia.org/wiki/List_of_countries_and_territories_by_land_borders">List of countries and territories by land borders</a>',
  143. states: 'Derived from <a target="_blank" href="http://www.statemaster.com/graph/geo_lan_bou_bor_cou-geography-land-borders">Land borders by state</a>',
  144. risk: 'Derived from Garrett Robinson’s <a target="_blank" href="http://web.mit.edu/sp.268/www/risk.pdf">The Strategy of Risk</a>'
  145. }
  146. var _maps = {
  147. usofa: {title: "United States", p: {stiffness: 600}, source: _sources.states},
  148. africa: {title: "Africa", p: {stiffness: 300}, source: _sources.nations},
  149. asia: {title: "Asia", p: {stiffness: 500}, source: _sources.nations},
  150. europe: {title: "Europe", p: {stiffness: 300}, source: _sources.nations},
  151. mideast: {title: "Middle East", p: {stiffness: 500}, source: _sources.nations},
  152. risk: {title: "Risk", p: {stiffness: 400}, source: _sources.risk}
  153. }
  154. var that = {
  155. init: function () {
  156. $.each(_maps, function (stub, map) {
  157. _links.append("<li><a href='#/" + stub + "' class='" + stub + "'>" + map.title + "</a></li>")
  158. })
  159. _links.find('li > a').click(that.mapClick)
  160. _links.find('.usofa').click()
  161. return that
  162. },
  163. mapClick: function (e) {
  164. var selected = $(e.target)
  165. var newMap = selected.attr('class')
  166. if (newMap in _maps) that.selectMap(newMap)
  167. _links.find('li > a').removeClass('active')
  168. selected.addClass('active')
  169. return false
  170. },
  171. selectMap: function (map_id) {
  172. $.getJSON("maps/" + map_id + ".json", function (data) {
  173. // load the raw data into the particle system as is (since it's already formatted correctly for .merge)
  174. var nodes = data.nodes
  175. $.each(nodes, function (name, info) {
  176. info.label = name.replace(/(people's )?republic of /i, '').replace(/ and /g, ' & ')
  177. })
  178. sys.merge({nodes: nodes, edges: data.edges})
  179. sys.parameters(_maps[map_id].p)
  180. $("#dataset").html(_maps[map_id].source)
  181. })
  182. }
  183. }
  184. return that.init()
  185. }
  186. $(document).ready(function () {
  187. var mcp = Maps("#maps")
  188. })
  189. })()