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.

145 lines
4.1 KiB

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