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.

207 lines
6.6 KiB

  1. import BezierEdgeBase from './util/BezierEdgeBase'
  2. /**
  3. * A Static Bezier Edge. Bezier curves are used to model smooth gradual
  4. * curves in paths between nodes.
  5. *
  6. * @extends BezierEdgeBase
  7. */
  8. class BezierEdgeStatic extends BezierEdgeBase {
  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. * Draw a line between two nodes
  19. * @param {CanvasRenderingContext2D} ctx
  20. * @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
  21. * @param {Node} viaNode
  22. * @private
  23. */
  24. _line(ctx, values, viaNode) {
  25. this._bezierCurve(ctx, values, viaNode);
  26. }
  27. /**
  28. *
  29. * @returns {Array.<{x: number, y: number}>}
  30. */
  31. getViaNode() {
  32. return this._getViaCoordinates();
  33. }
  34. /**
  35. * We do not use the to and fromPoints here to make the via nodes the same as edges without arrows.
  36. * @returns {{x: undefined, y: undefined}}
  37. * @private
  38. */
  39. _getViaCoordinates() {
  40. // Assumption: x/y coordinates in from/to always defined
  41. let xVia = undefined;
  42. let yVia = undefined;
  43. let factor = this.options.smooth.roundness;
  44. let type = this.options.smooth.type;
  45. let dx = Math.abs(this.from.x - this.to.x);
  46. let dy = Math.abs(this.from.y - this.to.y);
  47. if (type === 'discrete' || type === 'diagonalCross') {
  48. let stepX;
  49. let stepY;
  50. if (dx <= dy) {
  51. stepX = stepY = factor * dy;
  52. } else {
  53. stepX = stepY = factor * dx;
  54. }
  55. if (this.from.x > this.to.x) stepX = -stepX;
  56. if (this.from.y >= this.to.y) stepY = -stepY;
  57. xVia = this.from.x + stepX;
  58. yVia = this.from.y + stepY;
  59. if (type === "discrete") {
  60. if (dx <= dy) {
  61. xVia = dx < factor * dy ? this.from.x : xVia;
  62. } else {
  63. yVia = dy < factor * dx ? this.from.y : yVia;
  64. }
  65. }
  66. }
  67. else if (type === "straightCross") {
  68. let stepX = (1 - factor) * dx;
  69. let stepY = (1 - factor) * dy;
  70. if (dx <= dy) { // up - down
  71. stepX = 0;
  72. if (this.from.y < this.to.y) stepY = -stepY;
  73. }
  74. else { // left - right
  75. if (this.from.x < this.to.x) stepX = -stepX;
  76. stepY = 0;
  77. }
  78. xVia = this.to.x + stepX;
  79. yVia = this.to.y + stepY;
  80. }
  81. else if (type === 'horizontal') {
  82. let stepX = (1 - factor) * dx;
  83. if (this.from.x < this.to.x) stepX = -stepX;
  84. xVia = this.to.x + stepX;
  85. yVia = this.from.y;
  86. }
  87. else if (type === 'vertical') {
  88. let stepY = (1 - factor) * dy;
  89. if (this.from.y < this.to.y) stepY = -stepY;
  90. xVia = this.from.x;
  91. yVia = this.to.y + stepY;
  92. }
  93. else if (type === 'curvedCW') {
  94. dx = this.to.x - this.from.x;
  95. dy = this.from.y - this.to.y;
  96. let radius = Math.sqrt(dx * dx + dy * dy);
  97. let pi = Math.PI;
  98. let originalAngle = Math.atan2(dy, dx);
  99. let myAngle = (originalAngle + ((factor * 0.5) + 0.5) * pi) % (2 * pi);
  100. xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle);
  101. yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle);
  102. }
  103. else if (type === 'curvedCCW') {
  104. dx = this.to.x - this.from.x;
  105. dy = this.from.y - this.to.y;
  106. let radius = Math.sqrt(dx * dx + dy * dy);
  107. let pi = Math.PI;
  108. let originalAngle = Math.atan2(dy, dx);
  109. let myAngle = (originalAngle + ((-factor * 0.5) + 0.5) * pi) % (2 * pi);
  110. xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle);
  111. yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle);
  112. }
  113. else { // continuous
  114. let stepX;
  115. let stepY;
  116. if (dx <= dy) {
  117. stepX = stepY = factor * dy;
  118. } else {
  119. stepX = stepY = factor * dx;
  120. }
  121. if (this.from.x > this.to.x) stepX = -stepX;
  122. if (this.from.y >= this.to.y) stepY = -stepY;
  123. xVia = this.from.x + stepX;
  124. yVia = this.from.y + stepY;
  125. if (dx <= dy) {
  126. if (this.from.x <= this.to.x) {
  127. xVia = this.to.x < xVia ? this.to.x : xVia;
  128. }
  129. else {
  130. xVia = this.to.x > xVia ? this.to.x : xVia;
  131. }
  132. }
  133. else {
  134. if (this.from.y >= this.to.y) {
  135. yVia = this.to.y > yVia ? this.to.y : yVia;
  136. } else {
  137. yVia = this.to.y < yVia ? this.to.y : yVia;
  138. }
  139. }
  140. }
  141. return {x: xVia, y: yVia};
  142. }
  143. /**
  144. *
  145. * @param {Node} nearNode
  146. * @param {CanvasRenderingContext2D} ctx
  147. * @param {Object} options
  148. * @returns {*}
  149. * @private
  150. */
  151. _findBorderPosition(nearNode, ctx, options = {}) {
  152. return this._findBorderPositionBezier(nearNode, ctx, options.via);
  153. }
  154. /**
  155. *
  156. * @param {number} x1
  157. * @param {number} y1
  158. * @param {number} x2
  159. * @param {number} y2
  160. * @param {number} x3
  161. * @param {number} y3
  162. * @param {Node} viaNode
  163. * @returns {number}
  164. * @private
  165. */
  166. _getDistanceToEdge(x1, y1, x2, y2, x3, y3, viaNode = this._getViaCoordinates()) { // x3,y3 is the point
  167. return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode);
  168. }
  169. /**
  170. * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
  171. * @param {number} percentage
  172. * @param {Node} viaNode
  173. * @returns {{x: number, y: number}}
  174. * @private
  175. */
  176. getPoint(percentage, viaNode = this._getViaCoordinates()) {
  177. var t = percentage;
  178. var x = Math.pow(1 - t, 2) * this.fromPoint.x + (2 * t * (1 - t)) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
  179. var y = Math.pow(1 - t, 2) * this.fromPoint.y + (2 * t * (1 - t)) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
  180. return {x: x, y: y};
  181. }
  182. }
  183. export default BezierEdgeStatic;