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.

161 lines
4.5 KiB

  1. import EdgeBase from './EdgeBase'
  2. /**
  3. * The Base Class for all Bezier edges. Bezier curves are used to model smooth
  4. * gradual curves in paths between nodes.
  5. *
  6. * @extends EdgeBase
  7. */
  8. class BezierEdgeBase extends EdgeBase {
  9. /**
  10. * @param {Object} options
  11. * @param {Object} body
  12. * @param {Label} labelModule
  13. */
  14. constructor(options, body, labelModule) {
  15. super(options, body, labelModule);
  16. }
  17. /**
  18. * This function uses binary search to look for the point where the bezier curve crosses the border of the node.
  19. *
  20. * @param {Node} nearNode
  21. * @param {CanvasRenderingContext2D} ctx
  22. * @param {Node} viaNode
  23. * @returns {*}
  24. * @private
  25. */
  26. _findBorderPositionBezier(nearNode, ctx, viaNode = this._getViaCoordinates()) {
  27. var maxIterations = 10;
  28. var iteration = 0;
  29. var low = 0;
  30. var high = 1;
  31. var pos, angle, distanceToBorder, distanceToPoint, difference;
  32. var threshold = 0.2;
  33. var node = this.to;
  34. var from = false;
  35. if (nearNode.id === this.from.id) {
  36. node = this.from;
  37. from = true;
  38. }
  39. while (low <= high && iteration < maxIterations) {
  40. var middle = (low + high) * 0.5;
  41. pos = this.getPoint(middle, viaNode);
  42. angle = Math.atan2((node.y - pos.y), (node.x - pos.x));
  43. distanceToBorder = node.distanceToBorder(ctx, angle);
  44. distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
  45. difference = distanceToBorder - distanceToPoint;
  46. if (Math.abs(difference) < threshold) {
  47. break; // found
  48. }
  49. else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
  50. if (from === false) {
  51. low = middle;
  52. }
  53. else {
  54. high = middle;
  55. }
  56. }
  57. else {
  58. if (from === false) {
  59. high = middle;
  60. }
  61. else {
  62. low = middle;
  63. }
  64. }
  65. iteration++;
  66. }
  67. pos.t = middle;
  68. return pos;
  69. }
  70. /**
  71. * Calculate the distance between a point (x3,y3) and a line segment from
  72. * (x1,y1) to (x2,y2).
  73. * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
  74. * @param {number} x1 from x
  75. * @param {number} y1 from y
  76. * @param {number} x2 to x
  77. * @param {number} y2 to y
  78. * @param {number} x3 point to check x
  79. * @param {number} y3 point to check y
  80. * @param {Node} via
  81. * @returns {number}
  82. * @private
  83. */
  84. _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point
  85. let minDistance = 1e9;
  86. let distance;
  87. let i, t, x, y;
  88. let lastX = x1;
  89. let lastY = y1;
  90. for (i = 1; i < 10; i++) {
  91. t = 0.1 * i;
  92. x = Math.pow(1 - t, 2) * x1 + (2 * t * (1 - t)) * via.x + Math.pow(t, 2) * x2;
  93. y = Math.pow(1 - t, 2) * y1 + (2 * t * (1 - t)) * via.y + Math.pow(t, 2) * y2;
  94. if (i > 0) {
  95. distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3);
  96. minDistance = distance < minDistance ? distance : minDistance;
  97. }
  98. lastX = x;
  99. lastY = y;
  100. }
  101. return minDistance;
  102. }
  103. /**
  104. * Draw a bezier curve between two nodes
  105. *
  106. * The method accepts zero, one or two control points.
  107. * Passing zero control points just draws a straight line
  108. *
  109. * @param {CanvasRenderingContext2D} ctx
  110. * @param {Object} values | options for shadow drawing
  111. * @param {Object|undefined} viaNode1 | first control point for curve drawing
  112. * @param {Object|undefined} viaNode2 | second control point for curve drawing
  113. *
  114. * @protected
  115. */
  116. _bezierCurve(ctx, values, viaNode1, viaNode2) {
  117. var hasNode1 = (viaNode1 !== undefined && viaNode1.x !== undefined);
  118. var hasNode2 = (viaNode2 !== undefined && viaNode2.x !== undefined);
  119. ctx.beginPath();
  120. ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
  121. if (hasNode1 && hasNode2) {
  122. ctx.bezierCurveTo(viaNode1.x, viaNode1.y, viaNode2.x, viaNode2.y, this.toPoint.x, this.toPoint.y);
  123. } else if (hasNode1) {
  124. ctx.quadraticCurveTo(viaNode1.x, viaNode1.y, this.toPoint.x, this.toPoint.y);
  125. } else {
  126. // fallback to normal straight edge
  127. ctx.lineTo(this.toPoint.x, this.toPoint.y);
  128. }
  129. // draw a background
  130. this.drawBackground(ctx, values);
  131. // draw shadow if enabled
  132. this.enableShadow(ctx, values);
  133. ctx.stroke();
  134. this.disableShadow(ctx, values);
  135. }
  136. /**
  137. *
  138. * @returns {*|{x, y}|{x: undefined, y: undefined}}
  139. */
  140. getViaNode() {
  141. return this._getViaCoordinates();
  142. }
  143. }
  144. export default BezierEdgeBase;