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.

267 lines
13 KiB

  1. /**
  2. * The Base class for all Nodes.
  3. *
  4. * @class NodeBase
  5. */
  6. class NodeBase {
  7. /**
  8. * @param {Object} options
  9. * @param {Object} body
  10. * @param {Label} labelModule
  11. * @constructor NodeBase
  12. */
  13. constructor(options, body, labelModule) {
  14. this.body = body;
  15. this.labelModule = labelModule;
  16. this.setOptions(options);
  17. this.top = undefined;
  18. this.left = undefined;
  19. this.height = undefined;
  20. this.width = undefined;
  21. this.radius = undefined;
  22. this.margin = undefined;
  23. this.refreshNeeded = true;
  24. this.boundingBox = {top: 0, left: 0, right: 0, bottom: 0};
  25. }
  26. /**
  27. *
  28. * @param {Object} options
  29. */
  30. setOptions(options) {
  31. this.options = options;
  32. }
  33. /**
  34. *
  35. * @param {Label} labelModule
  36. * @private
  37. */
  38. _setMargins(labelModule) {
  39. this.margin = {};
  40. if (this.options.margin) {
  41. if (typeof this.options.margin == 'object') {
  42. this.margin.top = this.options.margin.top;
  43. this.margin.right = this.options.margin.right;
  44. this.margin.bottom = this.options.margin.bottom;
  45. this.margin.left = this.options.margin.left;
  46. } else {
  47. this.margin.top = this.options.margin;
  48. this.margin.right = this.options.margin;
  49. this.margin.bottom = this.options.margin;
  50. this.margin.left = this.options.margin;
  51. }
  52. }
  53. labelModule.adjustSizes(this.margin)
  54. }
  55. /**
  56. *
  57. * @param {CanvasRenderingContext2D} ctx
  58. * @param {Number} angle
  59. * @returns {Number}
  60. * @private
  61. */
  62. _distanceToBorder(ctx,angle) {
  63. var borderWidth = this.options.borderWidth;
  64. this.resize(ctx);
  65. return Math.min(
  66. Math.abs(this.width / 2 / Math.cos(angle)),
  67. Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
  68. }
  69. /**
  70. *
  71. * @param {CanvasRenderingContext2D} ctx
  72. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  73. */
  74. enableShadow(ctx, values) {
  75. if (values.shadow) {
  76. ctx.shadowColor = values.shadowColor;
  77. ctx.shadowBlur = values.shadowSize;
  78. ctx.shadowOffsetX = values.shadowX;
  79. ctx.shadowOffsetY = values.shadowY;
  80. }
  81. }
  82. /**
  83. *
  84. * @param {CanvasRenderingContext2D} ctx
  85. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  86. */
  87. disableShadow(ctx, values) {
  88. if (values.shadow) {
  89. ctx.shadowColor = 'rgba(0,0,0,0)';
  90. ctx.shadowBlur = 0;
  91. ctx.shadowOffsetX = 0;
  92. ctx.shadowOffsetY = 0;
  93. }
  94. }
  95. /**
  96. *
  97. * @param {CanvasRenderingContext2D} ctx
  98. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  99. */
  100. enableBorderDashes(ctx, values) {
  101. if (values.borderDashes !== false) {
  102. if (ctx.setLineDash !== undefined) {
  103. let dashes = values.borderDashes;
  104. if (dashes === true) {
  105. dashes = [5,15]
  106. }
  107. ctx.setLineDash(dashes);
  108. }
  109. else {
  110. console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used.");
  111. this.options.shapeProperties.borderDashes = false;
  112. values.borderDashes = false;
  113. }
  114. }
  115. }
  116. /**
  117. *
  118. * @param {CanvasRenderingContext2D} ctx
  119. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  120. */
  121. disableBorderDashes(ctx, values) {
  122. if (values.borderDashes !== false) {
  123. if (ctx.setLineDash !== undefined) {
  124. ctx.setLineDash([0]);
  125. }
  126. else {
  127. console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used.");
  128. this.options.shapeProperties.borderDashes = false;
  129. values.borderDashes = false;
  130. }
  131. }
  132. }
  133. /**
  134. * Determine if the shape of a node needs to be recalculated.
  135. *
  136. * @param {boolean} selected
  137. * @param {boolean} hover
  138. * @returns {boolean}
  139. * @protected
  140. */
  141. needsRefresh(selected, hover) {
  142. if (this.refreshNeeded === true) {
  143. // This is probably not the best location to reset this member.
  144. // However, in the current logic, it is the most convenient one.
  145. this.refreshNeeded = false;
  146. return true;
  147. }
  148. return (this.width === undefined) || (this.labelModule.differentState(selected, hover));
  149. }
  150. /**
  151. *
  152. * @param {CanvasRenderingContext2D} ctx
  153. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  154. */
  155. initContextForDraw(ctx, values) {
  156. var borderWidth = values.borderWidth / this.body.view.scale;
  157. ctx.lineWidth = Math.min(this.width, borderWidth);
  158. ctx.strokeStyle = values.borderColor;
  159. ctx.fillStyle = values.color;
  160. }
  161. /**
  162. *
  163. * @param {CanvasRenderingContext2D} ctx
  164. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  165. */
  166. performStroke(ctx, values) {
  167. var borderWidth = values.borderWidth / this.body.view.scale;
  168. //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
  169. ctx.save();
  170. // if borders are zero width, they will be drawn with width 1 by default. This prevents that
  171. if (borderWidth > 0) {
  172. this.enableBorderDashes(ctx, values);
  173. //draw the border
  174. ctx.stroke();
  175. //disable dashed border for other elements
  176. this.disableBorderDashes(ctx, values);
  177. }
  178. ctx.restore();
  179. }
  180. /**
  181. *
  182. * @param {CanvasRenderingContext2D} ctx
  183. * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
  184. */
  185. performFill(ctx, values) {
  186. // draw shadow if enabled
  187. this.enableShadow(ctx, values);
  188. // draw the background
  189. ctx.fill();
  190. // disable shadows for other elements.
  191. this.disableShadow(ctx, values);
  192. this.performStroke(ctx, values);
  193. }
  194. /**
  195. *
  196. * @param {Number} margin
  197. * @private
  198. */
  199. _addBoundingBoxMargin(margin) {
  200. this.boundingBox.left -= margin;
  201. this.boundingBox.top -= margin;
  202. this.boundingBox.bottom += margin;
  203. this.boundingBox.right += margin;
  204. }
  205. /**
  206. * Actual implementation of this method call.
  207. *
  208. * Doing it like this makes it easier to override
  209. * in the child classes.
  210. *
  211. * @param {number} x width
  212. * @param {number} y height
  213. * @param {CanvasRenderingContext2D} ctx
  214. * @param {boolean} selected
  215. * @param {boolean} hover
  216. * @private
  217. */
  218. _updateBoundingBox(x, y, ctx, selected, hover) {
  219. if (ctx !== undefined) {
  220. this.resize(ctx, selected, hover);
  221. }
  222. this.left = x - this.width / 2;
  223. this.top = y - this.height/ 2;
  224. this.boundingBox.left = this.left;
  225. this.boundingBox.top = this.top;
  226. this.boundingBox.bottom = this.top + this.height;
  227. this.boundingBox.right = this.left + this.width;
  228. }
  229. /**
  230. * Default implementation of this method call.
  231. * This acts as a stub which can be overridden.
  232. *
  233. * @param {number} x width
  234. * @param {number} y height
  235. * @param {CanvasRenderingContext2D} ctx
  236. * @param {boolean} selected
  237. * @param {boolean} hover
  238. */
  239. updateBoundingBox(x, y, ctx, selected, hover) {
  240. this._updateBoundingBox(x, y, ctx, selected, hover);
  241. }
  242. }
  243. export default NodeBase;