You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

313 lines
6.9 KiB

  1. define([
  2. 'js/constants',
  3. 'js/events'
  4. ], function (
  5. constants,
  6. events
  7. ) {
  8. return {
  9. canvas: null,
  10. ctx: null,
  11. areaSelectOrigin: null,
  12. panOrigin: null,
  13. currentZoom: 1,
  14. screen: {
  15. w: 0,
  16. h: 0
  17. },
  18. pos: {
  19. x: 0,
  20. y: 0
  21. },
  22. oldPos: null,
  23. mouse: {
  24. x: 0,
  25. y: 0
  26. },
  27. dirty: true,
  28. init: function () {
  29. this.canvas = $('.canvas')[0];
  30. this.screen.w = this.canvas.width = $('body').width();
  31. this.screen.h = this.canvas.height = $('body').height();
  32. this.ctx = this.canvas.getContext('2d');
  33. this.ctx.lineWidth = constants.lineWidth;
  34. $(this.canvas)
  35. .on('contextmenu', function () {
  36. return false;
  37. });
  38. events.on('onMouseMove', this.events.onMouseMove.bind(this));
  39. events.on('onStartAreaSelect', this.events.onStartAreaSelect.bind(this));
  40. events.on('onEndAreaSelect', this.events.onEndAreaSelect.bind(this));
  41. events.on('onSelectGroup', this.events.onSelectGroup.bind(this));
  42. },
  43. center: function (node) {
  44. if (!node)
  45. return;
  46. this.pos.x = ~~(node.pos.x * constants.gridSize) + (constants.blockSize / 2) - (this.screen.w / 2);
  47. this.pos.y = ~~(node.pos.y * constants.gridSize) + (constants.blockSize / 2) - (this.screen.h / 2);
  48. this.makeDirty();
  49. },
  50. pan: function (e, event) {
  51. var action = ({
  52. down: 'onPanStart',
  53. up: 'onPanEnd',
  54. move: 'onPan'
  55. })[event];
  56. this.events[action].call(this, e);
  57. },
  58. makeDirty: function () {
  59. this.dirty = true;
  60. },
  61. renderNodes: function (nodes, links) {
  62. links.forEach(function (l) {
  63. var linked = (
  64. nodes.find(n => (n == l.from)).selected &&
  65. nodes.find(n => (n == l.to)).selected
  66. );
  67. this.renderers.line.call(this, l.from, l.to, linked);
  68. }, this);
  69. nodes.forEach(function (n) {
  70. this.renderers.node.call(this, n, n.pos.x, n.pos.y);
  71. }, this);
  72. },
  73. render: function (nodes, links) {
  74. this.dirty = false;
  75. this.renderers.clear.call(this);
  76. this.renderers.grid.call(this);
  77. this.renderNodes(nodes, links);
  78. if (this.areaSelectOrigin)
  79. this.renderers.selectArea.call(this);
  80. },
  81. zoom: function (delta, zoom) {
  82. delta = delta ? -1 : 1;
  83. this.renderers.clear.call(this);
  84. this.ctx.restore();
  85. var newZoom = zoom || (this.currentZoom + (delta * 0.2));
  86. this.currentZoom = newZoom;
  87. if (this.currentZoom < 0.4)
  88. this.currentZoom = 0.4;
  89. else if (this.currentZoom > 3)
  90. this.currentZoom = 3;
  91. this.screen.w = $('body').width() / this.currentZoom;
  92. this.screen.h = $('body').height() / this.currentZoom;
  93. this.ctx.save();
  94. this.ctx.scale(this.currentZoom, this.currentZoom);
  95. },
  96. renderers: {
  97. clear: function () {
  98. var pos = this.oldPos || this.pos;
  99. this.ctx.clearRect(0, 0, this.screen.w, this.screen.h);
  100. delete this.oldPos;
  101. },
  102. selectArea: function () {
  103. var ctx = this.ctx;
  104. var area = this.areaSelectOrigin;
  105. var mouse = this.mouse;
  106. ctx.fillStyle = '#51fc9a';
  107. ctx.globalAlpha = 0.1;
  108. var lowX = (Math.min(area.x, mouse.x) * constants.gridSize) - this.pos.x;
  109. var lowY = (Math.min(area.y, mouse.y) * constants.gridSize) - this.pos.y;
  110. var highX = (Math.max(area.x, mouse.x) * constants.gridSize) - this.pos.x;
  111. var highY = (Math.max(area.y, mouse.y) * constants.gridSize) - this.pos.y;
  112. ctx.fillRect(lowX, lowY, highX - lowX, highY - lowY);
  113. ctx.globalAlpha = 1;
  114. },
  115. grid: function () {
  116. var gridSize = constants.gridSize;
  117. var ctx = this.ctx;
  118. var mouse = this.mouse;
  119. var gapSize = (constants.blockSize - 4) / 2;
  120. var x = ~~(this.pos.x / gridSize) - (this.pos.x / gridSize);
  121. var y = ~~(this.pos.y / gridSize) - (this.pos.y / gridSize);
  122. w = ~~(this.screen.w / gridSize);
  123. h = ~~(this.screen.h / gridSize);
  124. ctx.fillStyle = '#3c3f4c';
  125. for (var i = x; i < w; i++) {
  126. for (var j = y; j < h; j++) {
  127. ctx.fillRect((i * gridSize) + gapSize, (j * gridSize) + gapSize, 4, 4);
  128. }
  129. }
  130. ctx.strokeStyle = '#44cb95';
  131. ctx.strokeRect(
  132. (this.mouse.x * constants.gridSize) - this.pos.x + (gapSize / 1),
  133. (this.mouse.y * constants.gridSize) - this.pos.y + (gapSize / 1),
  134. 8,
  135. 8
  136. );
  137. },
  138. node: function (node) {
  139. var color = (node.color >= 0) ? (node.color + 1) : -1;
  140. if ((!node.stats) || (Object.keys(node.stats).length == 0))
  141. color = 0;
  142. this.ctx.fillStyle = ([
  143. '#69696e',
  144. '#c0c3cf',
  145. '#3fa7dd',
  146. '#4ac441',
  147. '#d43346',
  148. '#a24eff',
  149. '#faac45',
  150. '#44cb95'
  151. ])[color];
  152. var size = ([
  153. constants.blockSize,
  154. constants.blockSize * 2,
  155. constants.blockSize * 3
  156. ])[node.size];
  157. var x = (node.pos.x * constants.gridSize) - ((size - constants.blockSize) / 2) - this.pos.x;
  158. var y = (node.pos.y * constants.gridSize) - ((size - constants.blockSize) / 2) - this.pos.y;
  159. this.ctx.fillRect(x, y, size, size);
  160. this.ctx.strokeStyle = ([
  161. '#69696e',
  162. '#69696e',
  163. '#42548d',
  164. '#386646',
  165. '#763b3b',
  166. '#533399',
  167. '#d07840',
  168. '#3f8d6d'
  169. ])[color];
  170. this.ctx.strokeRect(x, y, size, size);
  171. if (node.selected) {
  172. this.ctx.strokeStyle = '#fafcfc';
  173. this.ctx.strokeRect(x, y, size, size);
  174. }
  175. },
  176. line: function (fromNode, toNode, linked) {
  177. var ctx = this.ctx;
  178. var halfSize = constants.blockSize / 2;
  179. var fromX = (fromNode.pos.x * constants.gridSize) + halfSize - this.pos.x;
  180. var fromY = (fromNode.pos.y * constants.gridSize) + halfSize - this.pos.y;
  181. var toX = (toNode.pos.x * constants.gridSize) + halfSize - this.pos.x;
  182. var toY = (toNode.pos.y * constants.gridSize) + halfSize - this.pos.y;
  183. ctx.strokeStyle = linked ? '#fafcfc' : '#69696e';
  184. ctx.beginPath();
  185. ctx.moveTo(fromX, fromY);
  186. ctx.lineTo(toX, toY);
  187. ctx.closePath();
  188. ctx.stroke();
  189. }
  190. },
  191. events: {
  192. onMouseMove: function (pos) {
  193. if ((this.mouse.x == pos.x) && (this.mouse.y == pos.y))
  194. return;
  195. this.mouse = {
  196. x: pos.x,
  197. y: pos.y
  198. };
  199. this.makeDirty();
  200. },
  201. onPanStart: function (e) {
  202. this.panOrigin = {
  203. x: e.clientX,
  204. y: e.clientY
  205. };
  206. },
  207. onPan: function (e) {
  208. if (!this.panOrigin)
  209. return;
  210. if (!this.oldPos) {
  211. this.oldPos = {
  212. x: this.pos.x,
  213. y: this.pos.y
  214. };
  215. }
  216. var zoomPanMultiplier = this.currentZoom;
  217. var scrollSpeed = constants.scrollSpeed / zoomPanMultiplier;
  218. this.pos.x += (this.panOrigin.x - e.clientX) * scrollSpeed;
  219. this.pos.y += (this.panOrigin.y - e.clientY) * scrollSpeed;
  220. this.panOrigin = {
  221. x: e.clientX,
  222. y: e.clientY
  223. };
  224. this.makeDirty();
  225. },
  226. onPanEnd: function (e) {
  227. this.panOrigin = null;
  228. },
  229. onStartAreaSelect: function (e) {
  230. this.areaSelectOrigin = {
  231. x: e.x,
  232. y: e.y
  233. };
  234. },
  235. onEndAreaSelect: function (e) {
  236. events.emit('onAreaSelect', this.areaSelectOrigin, e);
  237. this.areaSelectOrigin = null;
  238. },
  239. onSelectGroup: function (pos) {
  240. this.pos.x = pos.x;
  241. this.pos.y = pos.y;
  242. this.makeDirty();
  243. }
  244. }
  245. };
  246. });