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.

170 lines
4.6 KiB

  1. import BezierEdgeBase from './util/BezierEdgeBase'
  2. class BezierEdgeDynamic extends BezierEdgeBase {
  3. constructor(options, body, labelModule) {
  4. //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked.
  5. super(options, body, labelModule); // --> this calls the setOptions below
  6. this._boundFunction = () => {this.positionBezierNode();};
  7. this.body.emitter.on("_repositionBezierNodes", this._boundFunction);
  8. }
  9. setOptions(options) {
  10. // check if the physics has changed.
  11. let physicsChange = false;
  12. if (this.options.physics !== options.physics) {
  13. physicsChange = true;
  14. }
  15. // set the options and the to and from nodes
  16. this.options = options;
  17. this.id = this.options.id;
  18. this.from = this.body.nodes[this.options.from];
  19. this.to = this.body.nodes[this.options.to];
  20. // setup the support node and connect
  21. this.setupSupportNode();
  22. this.connect();
  23. // when we change the physics state of the edge, we reposition the support node.
  24. if (physicsChange === true) {
  25. this.via.setOptions({physics: this.options.physics})
  26. this.positionBezierNode();
  27. }
  28. }
  29. connect() {
  30. this.from = this.body.nodes[this.options.from];
  31. this.to = this.body.nodes[this.options.to];
  32. if (this.from === undefined || this.to === undefined || this.options.physics === false) {
  33. this.via.setOptions({physics:false})
  34. }
  35. else {
  36. // fix weird behaviour where a self referencing node has physics enabled
  37. if (this.from.id === this.to.id) {
  38. this.via.setOptions({physics: false})
  39. }
  40. else {
  41. this.via.setOptions({physics: true})
  42. }
  43. }
  44. }
  45. /**
  46. * remove the support nodes
  47. * @returns {boolean}
  48. */
  49. cleanup() {
  50. this.body.emitter.off("_repositionBezierNodes", this._boundFunction);
  51. if (this.via !== undefined) {
  52. delete this.body.nodes[this.via.id];
  53. this.via = undefined;
  54. return true;
  55. }
  56. return false;
  57. }
  58. /**
  59. * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but
  60. * are used for the force calculation.
  61. *
  62. * The changed data is not called, if needed, it is returned by the main edge constructor.
  63. * @private
  64. */
  65. setupSupportNode() {
  66. if (this.via === undefined) {
  67. var nodeId = "edgeId:" + this.id;
  68. var node = this.body.functions.createNode({
  69. id: nodeId,
  70. shape: 'circle',
  71. physics:true,
  72. hidden:true
  73. });
  74. this.body.nodes[nodeId] = node;
  75. this.via = node;
  76. this.via.parentEdgeId = this.id;
  77. this.positionBezierNode();
  78. }
  79. }
  80. positionBezierNode() {
  81. if (this.via !== undefined && this.from !== undefined && this.to !== undefined) {
  82. this.via.x = 0.5 * (this.from.x + this.to.x);
  83. this.via.y = 0.5 * (this.from.y + this.to.y);
  84. }
  85. else if (this.via !== undefined) {
  86. this.via.x = 0;
  87. this.via.y = 0;
  88. }
  89. }
  90. /**
  91. * Draw a line between two nodes
  92. * @param {CanvasRenderingContext2D} ctx
  93. * @param {Array<Object>} values
  94. * @param {vis.Node} viaNode
  95. * @private
  96. */
  97. _line(ctx, values, viaNode) {
  98. this._bezierCurve(ctx, values, viaNode);
  99. }
  100. getViaNode() {
  101. return this.via;
  102. }
  103. /**
  104. * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
  105. *
  106. * @param {number} percentage
  107. * @param {vis.Node} viaNode
  108. * @returns {{x: number, y: number}}
  109. * @private
  110. */
  111. getPoint(percentage, viaNode = this.via) {
  112. let t = percentage;
  113. let x, y;
  114. if (this.from === this.to){
  115. let [cx,cy,cr] = this._getCircleData(this.from)
  116. let a = 2 * Math.PI * (1 - t);
  117. x = cx + cr * Math.sin(a);
  118. y = cy + cr - cr * (1 - Math.cos(a));
  119. } else {
  120. x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
  121. y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
  122. }
  123. return {x: x, y: y};
  124. }
  125. /**
  126. *
  127. * @param {vis.Node} nearNode
  128. * @param {CanvasRenderingContext2D} ctx
  129. * @returns {*}
  130. * @private
  131. */
  132. _findBorderPosition(nearNode, ctx) {
  133. return this._findBorderPositionBezier(nearNode, ctx, this.via);
  134. }
  135. /**
  136. *
  137. * @param {number} x1
  138. * @param {number} y1
  139. * @param {number} x2
  140. * @param {number} y2
  141. * @param {number} x3
  142. * @param {number} y3
  143. * @returns {number}
  144. * @private
  145. */
  146. _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { // x3,y3 is the point
  147. return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via);
  148. }
  149. }
  150. export default BezierEdgeDynamic;