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.

194 lines
6.0 KiB

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