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