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.

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