vis.js is a dynamic, browser-based visualization library
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.

881 lines
25 KiB

10 years ago
10 years ago
  1. var util = require('../../../../util');
  2. import Label from '../unified/label.js'
  3. /**
  4. * @class Node
  5. * A node. A node can be connected to other nodes via one or multiple edges.
  6. * @param {object} options An object containing options for the node. All
  7. * options are optional, except for the id.
  8. * {number} id Id of the node. Required
  9. * {string} label Text label for the node
  10. * {number} x Horizontal position of the node
  11. * {number} y Vertical position of the node
  12. * {string} shape Node shape, available:
  13. * "database", "circle", "ellipse",
  14. * "box", "image", "text", "dot",
  15. * "star", "triangle", "triangleDown",
  16. * "square", "icon"
  17. * {string} image An image url
  18. * {string} title An title text, can be HTML
  19. * {anytype} group A group name or number
  20. * @param {Network.Images} imagelist A list with images. Only needed
  21. * when the node has an image
  22. * @param {Network.Groups} grouplist A list with groups. Needed for
  23. * retrieving group options
  24. * @param {Object} constants An object with default values for
  25. * example for the color
  26. *
  27. */
  28. class Node {
  29. constructor(options, body, imagelist, grouplist, globalOptions) {
  30. this.options = util.bridgeObject(globalOptions);
  31. this.selected = false;
  32. this.hover = false;
  33. this.edges = []; // all edges connected to this node
  34. // set defaults for the options
  35. this.id = undefined;
  36. this.allowedToMoveX = false;
  37. this.allowedToMoveY = false;
  38. this.xFixed = false;
  39. this.yFixed = false;
  40. this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0};
  41. this.imagelist = imagelist;
  42. this.grouplist = grouplist;
  43. // physics options
  44. this.x = null;
  45. this.y = null;
  46. this.predefinedPosition = false; // used to check if initial zoomExtent should just take the range or approximate
  47. this.fixedData = {x: null, y: null};
  48. this.labelModule = new Label(body, this.options);
  49. this.setOptions(options);
  50. }
  51. /**
  52. * Attach a edge to the node
  53. * @param {Edge} edge
  54. */
  55. attachEdge(edge) {
  56. if (this.edges.indexOf(edge) == -1) {
  57. this.edges.push(edge);
  58. }
  59. }
  60. /**
  61. * Detach a edge from the node
  62. * @param {Edge} edge
  63. */
  64. detachEdge(edge) {
  65. var index = this.edges.indexOf(edge);
  66. if (index != -1) {
  67. this.edges.splice(index, 1);
  68. }
  69. }
  70. /**
  71. * Set or overwrite options for the node
  72. * @param {Object} options an object with options
  73. * @param {Object} constants and object with default, global options
  74. */
  75. setOptions(options) {
  76. if (!options) {
  77. return;
  78. }
  79. var fields = [
  80. 'borderWidth',
  81. 'borderWidthSelected',
  82. 'shape',
  83. 'image',
  84. 'brokenImage',
  85. 'size',
  86. 'label',
  87. 'customScalingFunction',
  88. 'icon',
  89. 'value',
  90. 'hidden',
  91. 'physics'
  92. ];
  93. util.selectiveDeepExtend(fields, this.options, options);
  94. // basic options
  95. if (options.id !== undefined) {
  96. this.id = options.id;
  97. }
  98. if (options.title !== undefined) {
  99. this.title = options.title;
  100. }
  101. if (options.x !== undefined) {
  102. this.x = options.x;
  103. this.predefinedPosition = true;
  104. }
  105. if (options.y !== undefined) {
  106. this.y = options.y;
  107. this.predefinedPosition = true;
  108. }
  109. if (options.value !== undefined) {
  110. this.value = options.value;
  111. }
  112. if (options.level !== undefined) {
  113. this.level = options.level;
  114. this.preassignedLevel = true;
  115. }
  116. if (options.triggerFunction !== undefined) {
  117. this.triggerFunction = options.triggerFunction;
  118. }
  119. if (this.id === undefined) {
  120. throw "Node must have an id";
  121. }
  122. // copy group options
  123. if (typeof options.group === 'number' || (typeof options.group === 'string' && options.group != '')) {
  124. var groupObj = this.grouplist.get(options.group);
  125. util.deepExtend(this.options, groupObj);
  126. // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case.
  127. this.options.color = util.parseColor(this.options.color);
  128. }
  129. // individual shape options
  130. if (options.color !== undefined) {
  131. this.options.color = util.parseColor(options.color);
  132. }
  133. if (this.options.image !== undefined && this.options.image != "") {
  134. if (this.imagelist) {
  135. this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage);
  136. }
  137. else {
  138. throw "No imagelist provided";
  139. }
  140. }
  141. if (options.allowedToMoveX !== undefined) {
  142. this.xFixed = !options.allowedToMoveX;
  143. this.allowedToMoveX = options.allowedToMoveX;
  144. }
  145. else if (options.x !== undefined && this.allowedToMoveX == false) {
  146. this.xFixed = true;
  147. }
  148. if (options.allowedToMoveY !== undefined) {
  149. this.yFixed = !options.allowedToMoveY;
  150. this.allowedToMoveY = options.allowedToMoveY;
  151. }
  152. else if (options.y !== undefined && this.allowedToMoveY == false) {
  153. this.yFixed = true;
  154. }
  155. // choose draw method depending on the shape
  156. switch (this.options.shape) {
  157. case 'database':
  158. this.draw = this._drawDatabase;
  159. this.resize = this._resizeDatabase;
  160. break;
  161. case 'box':
  162. this.draw = this._drawBox;
  163. this.resize = this._resizeBox;
  164. break;
  165. case 'circle':
  166. this.draw = this._drawCircle;
  167. this.resize = this._resizeCircle;
  168. break;
  169. case 'ellipse':
  170. this.draw = this._drawEllipse;
  171. this.resize = this._resizeEllipse;
  172. break;
  173. // TODO: add diamond shape
  174. case 'image':
  175. this.draw = this._drawImage;
  176. this.resize = this._resizeImage;
  177. break;
  178. case 'circularImage':
  179. this.draw = this._drawCircularImage;
  180. this.resize = this._resizeCircularImage;
  181. break;
  182. case 'text':
  183. this.draw = this._drawText;
  184. this.resize = this._resizeText;
  185. break;
  186. case 'dot':
  187. this.draw = this._drawDot;
  188. this.resize = this._resizeShape;
  189. break;
  190. case 'square':
  191. this.draw = this._drawSquare;
  192. this.resize = this._resizeShape;
  193. break;
  194. case 'triangle':
  195. this.draw = this._drawTriangle;
  196. this.resize = this._resizeShape;
  197. break;
  198. case 'triangleDown':
  199. this.draw = this._drawTriangleDown;
  200. this.resize = this._resizeShape;
  201. break;
  202. case 'star':
  203. this.draw = this._drawStar;
  204. this.resize = this._resizeShape;
  205. break;
  206. case 'icon':
  207. this.draw = this._drawIcon;
  208. this.resize = this._resizeIcon;
  209. break;
  210. default:
  211. this.draw = this._drawEllipse;
  212. this.resize = this._resizeEllipse;
  213. break;
  214. }
  215. this.labelModule.setOptions(this.options);
  216. // reset the size of the node, this can be changed
  217. this._reset();
  218. }
  219. /**
  220. * select this node
  221. */
  222. select() {
  223. this.selected = true;
  224. this._reset();
  225. }
  226. /**
  227. * unselect this node
  228. */
  229. unselect() {
  230. this.selected = false;
  231. this._reset();
  232. }
  233. /**
  234. * Reset the calculated size of the node, forces it to recalculate its size
  235. * @private
  236. */
  237. _reset() {
  238. this.width = undefined;
  239. this.height = undefined;
  240. }
  241. /**
  242. * get the title of this node.
  243. * @return {string} title The title of the node, or undefined when no title
  244. * has been set.
  245. */
  246. getTitle() {
  247. return typeof this.title === "function" ? this.title() : this.title;
  248. }
  249. /**
  250. * Calculate the distance to the border of the Node
  251. * @param {CanvasRenderingContext2D} ctx
  252. * @param {Number} angle Angle in radians
  253. * @returns {number} distance Distance to the border in pixels
  254. */
  255. distanceToBorder(ctx, angle) {
  256. var borderWidth = 1;
  257. if (!this.width) {
  258. this.resize(ctx);
  259. }
  260. switch (this.options.shape) {
  261. case 'circle':
  262. case 'dot':
  263. return this.options.size + borderWidth;
  264. case 'ellipse':
  265. var a = this.width / 2;
  266. var b = this.height / 2;
  267. var w = (Math.sin(angle) * a);
  268. var h = (Math.cos(angle) * b);
  269. return a * b / Math.sqrt(w * w + h * h);
  270. // TODO: implement distanceToBorder for database
  271. // TODO: implement distanceToBorder for triangle
  272. // TODO: implement distanceToBorder for triangleDown
  273. case 'box':
  274. case 'image':
  275. case 'text':
  276. default:
  277. if (this.width) {
  278. return Math.min(
  279. Math.abs(this.width / 2 / Math.cos(angle)),
  280. Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
  281. // TODO: reckon with border size too in case of box
  282. }
  283. else {
  284. return 0;
  285. }
  286. }
  287. // TODO: implement calculation of distance to border for all shapes
  288. }
  289. /**
  290. * Check if this node has a fixed x and y position
  291. * @return {boolean} true if fixed, false if not
  292. */
  293. isFixed() {
  294. return (this.xFixed && this.yFixed);
  295. }
  296. /**
  297. * check if this node is selecte
  298. * @return {boolean} selected True if node is selected, else false
  299. */
  300. isSelected() {
  301. return this.selected;
  302. }
  303. /**
  304. * Retrieve the value of the node. Can be undefined
  305. * @return {Number} value
  306. */
  307. getValue() {
  308. return this.value;
  309. }
  310. /**
  311. * Adjust the value range of the node. The node will adjust it's size
  312. * based on its value.
  313. * @param {Number} min
  314. * @param {Number} max
  315. */
  316. setValueRange(min, max, total) {
  317. if (this.value !== undefined) {
  318. var scale = this.options.scaling.customScalingFunction(min, max, total, this.value);
  319. var sizeDiff = this.options.scaling.max - this.options.scaling.min;
  320. if (this.options.scaling.label.enabled == true) {
  321. var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min;
  322. this.options.font.size = this.options.scaling.label.min + scale * fontDiff;
  323. }
  324. this.options.size = this.options.scaling.min + scale * sizeDiff;
  325. }
  326. }
  327. /**
  328. * Draw this node in the given canvas
  329. * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
  330. * @param {CanvasRenderingContext2D} ctx
  331. */
  332. draw(ctx) {
  333. throw "Draw method not initialized for node";
  334. }
  335. /**
  336. * Recalculate the size of this node in the given canvas
  337. * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
  338. * @param {CanvasRenderingContext2D} ctx
  339. */
  340. resize(ctx) {
  341. throw "Resize method not initialized for node";
  342. }
  343. /**
  344. * Check if this object is overlapping with the provided object
  345. * @param {Object} obj an object with parameters left, top, right, bottom
  346. * @return {boolean} True if location is located on node
  347. */
  348. isOverlappingWith(obj) {
  349. return (this.left < obj.right &&
  350. this.left + this.width > obj.left &&
  351. this.top < obj.bottom &&
  352. this.top + this.height > obj.top);
  353. }
  354. _resizeImage(ctx) {
  355. // TODO: pre calculate the image size
  356. if (!this.width || !this.height) { // undefined or 0
  357. var width, height;
  358. if (this.value) {
  359. var scale = this.imageObj.height / this.imageObj.width;
  360. if (scale !== undefined) {
  361. width = this.options.size || this.imageObj.width;
  362. height = this.options.size * scale || this.imageObj.height;
  363. }
  364. else {
  365. width = 0;
  366. height = 0;
  367. }
  368. }
  369. else {
  370. width = this.imageObj.width;
  371. height = this.imageObj.height;
  372. }
  373. this.width = width;
  374. this.height = height;
  375. }
  376. }
  377. _drawImageAtPosition(ctx) {
  378. if (this.imageObj.width != 0) {
  379. // draw the image
  380. ctx.globalAlpha = 1.0;
  381. ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
  382. }
  383. }
  384. _drawImageLabel(ctx) {
  385. var yLabel;
  386. var offset = 0;
  387. if (this.height) {
  388. offset = this.height / 2;
  389. var labelDimensions = this.labelModule.labelDimensions;
  390. if (labelDimensions.lineCount >= 1) {
  391. offset += labelDimensions.height / 2;
  392. offset += 3;
  393. }
  394. }
  395. yLabel = this.y + offset;
  396. this.labelModule.draw(ctx, this.x, yLabel);
  397. }
  398. _drawImage(ctx) {
  399. this._resizeImage(ctx);
  400. this.left = this.x - this.width / 2;
  401. this.top = this.y - this.height / 2;
  402. this._drawImageAtPosition(ctx);
  403. this.boundingBox.top = this.top;
  404. this.boundingBox.left = this.left;
  405. this.boundingBox.right = this.left + this.width;
  406. this.boundingBox.bottom = this.top + this.height;
  407. this._drawImageLabel(ctx);
  408. this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
  409. this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
  410. this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height);
  411. }
  412. _resizeCircularImage(ctx) {
  413. if (!this.imageObj.src || !this.imageObj.width || !this.imageObj.height) {
  414. if (!this.width) {
  415. var diameter = this.options.size * 2;
  416. this.width = diameter;
  417. this.height = diameter;
  418. this._swapToImageResizeWhenImageLoaded = true;
  419. }
  420. }
  421. else {
  422. if (this._swapToImageResizeWhenImageLoaded) {
  423. this.width = 0;
  424. this.height = 0;
  425. delete this._swapToImageResizeWhenImageLoaded;
  426. }
  427. this._resizeImage(ctx);
  428. }
  429. }
  430. _drawCircularImage(ctx) {
  431. this._resizeCircularImage(ctx);
  432. this.left = this.x - this.width / 2;
  433. this.top = this.y - this.height / 2;
  434. var centerX = this.left + (this.width / 2);
  435. var centerY = this.top + (this.height / 2);
  436. var size = Math.abs(this.height / 2);
  437. this._drawRawCircle(ctx, size);
  438. ctx.save();
  439. ctx.circle(this.x, this.y, size);
  440. ctx.stroke();
  441. ctx.clip();
  442. this._drawImageAtPosition(ctx);
  443. ctx.restore();
  444. this.boundingBox.top = this.y - this.options.size;
  445. this.boundingBox.left = this.x - this.options.size;
  446. this.boundingBox.right = this.x + this.options.size;
  447. this.boundingBox.bottom = this.y + this.options.size;
  448. this._drawImageLabel(ctx);
  449. this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
  450. this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
  451. this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height);
  452. }
  453. _resizeBox(ctx) {
  454. if (!this.width) {
  455. var margin = 5;
  456. var textSize = this.labelModule.getTextSize(ctx,this.selected);
  457. this.width = textSize.width + 2 * margin;
  458. this.height = textSize.height + 2 * margin;
  459. }
  460. }
  461. _drawBox(ctx) {
  462. this._resizeBox(ctx);
  463. this.left = this.x - this.width / 2;
  464. this.top = this.y - this.height / 2;
  465. var borderWidth = this.options.borderWidth;
  466. var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
  467. ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
  468. ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
  469. ctx.lineWidth *= this.networkScaleInv;
  470. ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
  471. ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
  472. ctx.roundRect(this.left, this.top, this.width, this.height, this.options.size);
  473. ctx.fill();
  474. ctx.stroke();
  475. this.boundingBox.top = this.top;
  476. this.boundingBox.left = this.left;
  477. this.boundingBox.right = this.left + this.width;
  478. this.boundingBox.bottom = this.top + this.height;
  479. this.labelModule.draw(ctx, this.x, this.y);
  480. }
  481. _resizeDatabase(ctx) {
  482. if (!this.width) {
  483. var margin = 5;
  484. var textSize = this.labelModule.getTextSize(ctx,this.selected);
  485. var size = textSize.width + 2 * margin;
  486. this.width = size;
  487. this.height = size;
  488. }
  489. }
  490. _drawDatabase(ctx) {
  491. this._resizeDatabase(ctx);
  492. this.left = this.x - this.width / 2;
  493. this.top = this.y - this.height / 2;
  494. var borderWidth = this.options.borderWidth;
  495. var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
  496. ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
  497. ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
  498. ctx.lineWidth *= this.networkScaleInv;
  499. ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
  500. ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
  501. ctx.database(this.x - this.width / 2, this.y - this.height * 0.5, this.width, this.height);
  502. ctx.fill();
  503. ctx.stroke();
  504. this.boundingBox.top = this.top;
  505. this.boundingBox.left = this.left;
  506. this.boundingBox.right = this.left + this.width;
  507. this.boundingBox.bottom = this.top + this.height;
  508. this.labelModule.draw(ctx, this.x, this.y);
  509. }
  510. _resizeCircle(ctx) {
  511. if (!this.width) {
  512. var margin = 5;
  513. var textSize = this.labelModule.getTextSize(ctx,this.selected);
  514. var diameter = Math.max(textSize.width, textSize.height) + 2 * margin;
  515. this.options.size = diameter / 2;
  516. this.width = diameter;
  517. this.height = diameter;
  518. }
  519. }
  520. _drawRawCircle(ctx, size) {
  521. var borderWidth = this.options.borderWidth;
  522. var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
  523. ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
  524. ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
  525. ctx.lineWidth *= this.networkScaleInv;
  526. ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
  527. ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
  528. ctx.circle(this.x, this.y, size);
  529. ctx.fill();
  530. ctx.stroke();
  531. }
  532. _drawCircle(ctx) {
  533. this._resizeCircle(ctx);
  534. this.left = this.x - this.width / 2;
  535. this.top = this.y - this.height / 2;
  536. this._drawRawCircle(ctx, this.options.size);
  537. this.boundingBox.top = this.y - this.options.size;
  538. this.boundingBox.left = this.x - this.options.size;
  539. this.boundingBox.right = this.x + this.options.size;
  540. this.boundingBox.bottom = this.y + this.options.size;
  541. this.labelModule.draw(ctx, this.x, this.y);
  542. }
  543. _resizeEllipse(ctx) {
  544. if (this.width === undefined) {
  545. var textSize = this.labelModule.getTextSize(ctx,this.selected);
  546. this.width = textSize.width * 1.5;
  547. this.height = textSize.height * 2;
  548. if (this.width < this.height) {
  549. this.width = this.height;
  550. }
  551. }
  552. }
  553. _drawEllipse(ctx) {
  554. this._resizeEllipse(ctx);
  555. this.left = this.x - this.width / 2;
  556. this.top = this.y - this.height / 2;
  557. var borderWidth = this.options.borderWidth;
  558. var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
  559. ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
  560. ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
  561. ctx.lineWidth *= this.networkScaleInv;
  562. ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
  563. ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
  564. ctx.ellipse(this.left, this.top, this.width, this.height);
  565. ctx.fill();
  566. ctx.stroke();
  567. this.boundingBox.top = this.top;
  568. this.boundingBox.left = this.left;
  569. this.boundingBox.right = this.left + this.width;
  570. this.boundingBox.bottom = this.top + this.height;
  571. this.labelModule.draw(ctx, this.x, this.y, this.selected);
  572. }
  573. _drawDot(ctx) {
  574. this._drawShape(ctx, 'circle');
  575. }
  576. _drawTriangle(ctx) {
  577. this._drawShape(ctx, 'triangle');
  578. }
  579. _drawTriangleDown(ctx) {
  580. this._drawShape(ctx, 'triangleDown');
  581. }
  582. _drawSquare(ctx) {
  583. this._drawShape(ctx, 'square');
  584. }
  585. _drawStar(ctx) {
  586. this._drawShape(ctx, 'star');
  587. }
  588. _resizeShape(ctx) {
  589. if (!this.width) {
  590. var size = 2 * this.options.size;
  591. this.width = size;
  592. this.height = size;
  593. }
  594. }
  595. _drawShape(ctx, shape) {
  596. this._resizeShape(ctx);
  597. this.left = this.x - this.width / 2;
  598. this.top = this.y - this.height / 2;
  599. var borderWidth = this.options.borderWidth;
  600. var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
  601. var sizeMultiplier = 2;
  602. // choose draw method depending on the shape
  603. switch (shape) {
  604. case 'dot':
  605. sizeMultiplier = 2;
  606. break;
  607. case 'square':
  608. sizeMultiplier = 2;
  609. break;
  610. case 'triangle':
  611. sizeMultiplier = 3;
  612. break;
  613. case 'triangleDown':
  614. sizeMultiplier = 3;
  615. break;
  616. case 'star':
  617. sizeMultiplier = 4;
  618. break;
  619. }
  620. ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
  621. ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth);
  622. ctx.lineWidth *= this.networkScaleInv;
  623. ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
  624. ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
  625. ctx[shape](this.x, this.y, this.options.size);
  626. ctx.fill();
  627. ctx.stroke();
  628. this.boundingBox.top = this.y - this.options.size;
  629. this.boundingBox.left = this.x - this.options.size;
  630. this.boundingBox.right = this.x + this.options.size;
  631. this.boundingBox.bottom = this.y + this.options.size;
  632. if (this.options.label!== undefined) {
  633. this.labelModule.draw(ctx, this.x, this.y + (this.height + this.labelModule.size.height)*0.5, this.selected, 'hanging');
  634. this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
  635. this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
  636. this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height);
  637. }
  638. }
  639. _resizeText(ctx) {
  640. if (!this.width) {
  641. var margin = 5;
  642. var textSize = this.labelModule.getTextSize(ctx,this.selected);
  643. this.width = textSize.width + 2 * margin;
  644. this.height = textSize.height + 2 * margin;
  645. }
  646. }
  647. _drawText(ctx) {
  648. this._resizeText(ctx);
  649. this.left = this.x - this.width / 2;
  650. this.top = this.y - this.height / 2;
  651. this.labelModule.draw(ctx, this.x, this.y);
  652. this.boundingBox.top = this.top;
  653. this.boundingBox.left = this.left;
  654. this.boundingBox.right = this.left + this.width;
  655. this.boundingBox.bottom = this.top + this.height;
  656. }
  657. _resizeIcon(ctx) {
  658. if (!this.width) {
  659. var margin = 5;
  660. var iconSize =
  661. {
  662. width: Number(this.options.icon.iconSize),
  663. height: Number(this.options.icon.iconSize)
  664. };
  665. this.width = iconSize.width + 2 * margin;
  666. this.height = iconSize.height + 2 * margin;
  667. }
  668. }
  669. _drawIcon(ctx) {
  670. this._resizeIcon(ctx);
  671. this.options.icon.iconSize = this.options.icon.iconSize || 50;
  672. this.left = this.x - this.width / 2;
  673. this.top = this.y - this.height / 2;
  674. this._icon(ctx);
  675. this.boundingBox.top = this.y - this.options.icon.iconSize / 2;
  676. this.boundingBox.left = this.x - this.options.icon.iconSize / 2;
  677. this.boundingBox.right = this.x + this.options.icon.iconSize / 2;
  678. this.boundingBox.bottom = this.y + this.options.icon.iconSize / 2;
  679. if (this.options.label!== unde) {
  680. var iconTextSpacing = 5;
  681. this.labelModule.draw(ctx, this.x, this.y + this.height / 2 + iconTextSpacing);
  682. this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
  683. this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
  684. this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height);
  685. }
  686. }
  687. _icon(ctx) {
  688. var relativeIconSize = Number(this.options.icon.iconSize) * this.networkScale;
  689. if (this.options.icon.code && relativeIconSize > this.options.scaling.label.drawThreshold - 1) {
  690. var iconSize = Number(this.options.icon.iconSize);
  691. ctx.font = (this.selected ? "bold " : "") + iconSize + "px " + this.options.icon.iconFontFace;
  692. // draw icon
  693. ctx.fillStyle = this.options.icon.iconColor || "black";
  694. ctx.textAlign = "center";
  695. ctx.textBaseline = "middle";
  696. ctx.fillText(this.options.icon.code, this.x, this.y);
  697. }
  698. }
  699. }
  700. export default Node;