BendpointSnapping.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import {
  2. assign,
  3. forEach,
  4. isArray
  5. } from 'min-dash';
  6. import { setSnapped } from '../snapping/SnapUtil';
  7. import { getClosestPointOnConnection } from './BendpointUtil';
  8. var abs = Math.abs,
  9. round = Math.round;
  10. var TOLERANCE = 10;
  11. export default function BendpointSnapping(eventBus) {
  12. function snapTo(values, value) {
  13. if (isArray(values)) {
  14. var i = values.length;
  15. while (i--) if (abs(values[i] - value) <= TOLERANCE) {
  16. return values[i];
  17. }
  18. } else {
  19. values = +values;
  20. var rem = value % values;
  21. if (rem < TOLERANCE) {
  22. return value - rem;
  23. }
  24. if (rem > values - TOLERANCE) {
  25. return value - rem + values;
  26. }
  27. }
  28. return value;
  29. }
  30. function getSnapPoint(element, event) {
  31. if (element.waypoints) {
  32. return getClosestPointOnConnection(event, element);
  33. }
  34. if (element.width) {
  35. return {
  36. x: round(element.width / 2 + element.x),
  37. y: round(element.height / 2 + element.y)
  38. };
  39. }
  40. }
  41. // connection segment snapping //////////////////////
  42. function getConnectionSegmentSnaps(event) {
  43. var context = event.context,
  44. snapPoints = context.snapPoints,
  45. connection = context.connection,
  46. waypoints = connection.waypoints,
  47. segmentStart = context.segmentStart,
  48. segmentStartIndex = context.segmentStartIndex,
  49. segmentEnd = context.segmentEnd,
  50. segmentEndIndex = context.segmentEndIndex,
  51. axis = context.axis;
  52. if (snapPoints) {
  53. return snapPoints;
  54. }
  55. var referenceWaypoints = [
  56. waypoints[segmentStartIndex - 1],
  57. segmentStart,
  58. segmentEnd,
  59. waypoints[segmentEndIndex + 1]
  60. ];
  61. if (segmentStartIndex < 2) {
  62. referenceWaypoints.unshift(getSnapPoint(connection.source, event));
  63. }
  64. if (segmentEndIndex > waypoints.length - 3) {
  65. referenceWaypoints.unshift(getSnapPoint(connection.target, event));
  66. }
  67. context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
  68. forEach(referenceWaypoints, function(p) {
  69. // we snap on existing bendpoints only,
  70. // not placeholders that are inserted during add
  71. if (p) {
  72. p = p.original || p;
  73. if (axis === 'y') {
  74. snapPoints.horizontal.push(p.y);
  75. }
  76. if (axis === 'x') {
  77. snapPoints.vertical.push(p.x);
  78. }
  79. }
  80. });
  81. return snapPoints;
  82. }
  83. eventBus.on('connectionSegment.move.move', 1500, function(event) {
  84. var snapPoints = getConnectionSegmentSnaps(event),
  85. x = event.x,
  86. y = event.y,
  87. sx, sy;
  88. if (!snapPoints) {
  89. return;
  90. }
  91. // snap
  92. sx = snapTo(snapPoints.vertical, x);
  93. sy = snapTo(snapPoints.horizontal, y);
  94. // correction x/y
  95. var cx = (x - sx),
  96. cy = (y - sy);
  97. // update delta
  98. assign(event, {
  99. dx: event.dx - cx,
  100. dy: event.dy - cy,
  101. x: sx,
  102. y: sy
  103. });
  104. // only set snapped if actually snapped
  105. if (cx || snapPoints.vertical.indexOf(x) !== -1) {
  106. setSnapped(event, 'x', sx);
  107. }
  108. if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
  109. setSnapped(event, 'y', sy);
  110. }
  111. });
  112. // bendpoint snapping //////////////////////
  113. function getBendpointSnaps(context) {
  114. var snapPoints = context.snapPoints,
  115. waypoints = context.connection.waypoints,
  116. bendpointIndex = context.bendpointIndex;
  117. if (snapPoints) {
  118. return snapPoints;
  119. }
  120. var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];
  121. context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
  122. forEach(referenceWaypoints, function(p) {
  123. // we snap on existing bendpoints only,
  124. // not placeholders that are inserted during add
  125. if (p) {
  126. p = p.original || p;
  127. snapPoints.horizontal.push(p.y);
  128. snapPoints.vertical.push(p.x);
  129. }
  130. });
  131. return snapPoints;
  132. }
  133. // Snap Endpoint of new connection
  134. eventBus.on([
  135. 'connect.hover',
  136. 'connect.move',
  137. 'connect.end'
  138. ], 1500, function(event) {
  139. var context = event.context,
  140. hover = context.hover,
  141. hoverMid = hover && getSnapPoint(hover, event);
  142. // only snap on connections, elements can have multiple connect endpoints
  143. if (!isConnection(hover) || !hoverMid || !hoverMid.x || !hoverMid.y) {
  144. return;
  145. }
  146. setSnapped(event, 'x', hoverMid.x);
  147. setSnapped(event, 'y', hoverMid.y);
  148. });
  149. eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) {
  150. var context = event.context,
  151. snapPoints = getBendpointSnaps(context),
  152. hover = context.hover,
  153. hoverMid = hover && getSnapPoint(hover, event),
  154. x = event.x,
  155. y = event.y,
  156. sx, sy;
  157. if (!snapPoints) {
  158. return;
  159. }
  160. // snap to hover mid
  161. sx = snapTo(hoverMid ? snapPoints.vertical.concat([ hoverMid.x ]) : snapPoints.vertical, x);
  162. sy = snapTo(hoverMid ? snapPoints.horizontal.concat([ hoverMid.y ]) : snapPoints.horizontal, y);
  163. // correction x/y
  164. var cx = (x - sx),
  165. cy = (y - sy);
  166. // update delta
  167. assign(event, {
  168. dx: event.dx - cx,
  169. dy: event.dy - cy,
  170. x: event.x - cx,
  171. y: event.y - cy
  172. });
  173. // only set snapped if actually snapped
  174. if (cx || snapPoints.vertical.indexOf(x) !== -1) {
  175. setSnapped(event, 'x', sx);
  176. }
  177. if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
  178. setSnapped(event, 'y', sy);
  179. }
  180. });
  181. }
  182. BendpointSnapping.$inject = [ 'eventBus' ];
  183. // helpers //////////////////////
  184. function isConnection(element) {
  185. return element && !!element.waypoints;
  186. }