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.

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