BendpointMove.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import {
  2. pointDistance,
  3. pointsOnLine
  4. } from '../../util/Geometry';
  5. import {
  6. addBendpoint
  7. } from './BendpointUtil';
  8. var MARKER_OK = 'connect-ok',
  9. MARKER_NOT_OK = 'connect-not-ok',
  10. MARKER_CONNECT_HOVER = 'connect-hover',
  11. MARKER_CONNECT_UPDATING = 'djs-updating';
  12. var COMMAND_BENDPOINT_UPDATE = 'connection.updateWaypoints',
  13. COMMAND_RECONNECT_START = 'connection.reconnectStart',
  14. COMMAND_RECONNECT_END = 'connection.reconnectEnd';
  15. var round = Math.round;
  16. import {
  17. classes as svgClasses,
  18. remove as svgRemove
  19. } from 'tiny-svg';
  20. import { translate } from '../../util/SvgTransformUtil';
  21. /**
  22. * A component that implements moving of bendpoints
  23. */
  24. export default function BendpointMove(
  25. injector, eventBus, canvas,
  26. dragging, graphicsFactory, rules,
  27. modeling) {
  28. // optional connection docking integration
  29. var connectionDocking = injector.get('connectionDocking', false);
  30. // API
  31. this.start = function(event, connection, bendpointIndex, insert) {
  32. var type,
  33. context,
  34. waypoints = connection.waypoints,
  35. gfx = canvas.getGraphics(connection);
  36. if (!insert && bendpointIndex === 0) {
  37. type = COMMAND_RECONNECT_START;
  38. } else
  39. if (!insert && bendpointIndex === waypoints.length - 1) {
  40. type = COMMAND_RECONNECT_END;
  41. } else {
  42. type = COMMAND_BENDPOINT_UPDATE;
  43. }
  44. context = {
  45. connection: connection,
  46. bendpointIndex: bendpointIndex,
  47. insert: insert,
  48. type: type
  49. };
  50. dragging.init(event, 'bendpoint.move', {
  51. data: {
  52. connection: connection,
  53. connectionGfx: gfx,
  54. context: context
  55. }
  56. });
  57. };
  58. // DRAGGING IMPLEMENTATION
  59. function redrawConnection(data) {
  60. graphicsFactory.update('connection', data.connection, data.connectionGfx);
  61. }
  62. function filterRedundantWaypoints(waypoints) {
  63. // alter copy of waypoints, not original
  64. waypoints = waypoints.slice();
  65. var idx = 0,
  66. point,
  67. previousPoint,
  68. nextPoint;
  69. while (waypoints[idx]) {
  70. point = waypoints[idx];
  71. previousPoint = waypoints[idx - 1];
  72. nextPoint = waypoints[idx + 1];
  73. if (pointDistance(point, nextPoint) === 0 ||
  74. pointsOnLine(previousPoint, nextPoint, point)) {
  75. // remove point, if overlapping with {nextPoint}
  76. // or on line with {previousPoint} -> {point} -> {nextPoint}
  77. waypoints.splice(idx, 1);
  78. } else {
  79. idx++;
  80. }
  81. }
  82. return waypoints;
  83. }
  84. eventBus.on('bendpoint.move.start', function(e) {
  85. var context = e.context,
  86. connection = context.connection,
  87. originalWaypoints = connection.waypoints,
  88. waypoints = originalWaypoints.slice(),
  89. insert = context.insert,
  90. idx = context.bendpointIndex;
  91. context.originalWaypoints = originalWaypoints;
  92. if (insert) {
  93. // insert placeholder for bendpoint to-be-added
  94. waypoints.splice(idx, 0, null);
  95. }
  96. connection.waypoints = waypoints;
  97. // add dragger gfx
  98. context.draggerGfx = addBendpoint(canvas.getLayer('overlays'));
  99. svgClasses(context.draggerGfx).add('djs-dragging');
  100. canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
  101. });
  102. eventBus.on('bendpoint.move.hover', function(e) {
  103. var context = e.context;
  104. context.hover = e.hover;
  105. if (e.hover) {
  106. canvas.addMarker(e.hover, MARKER_CONNECT_HOVER);
  107. // asks whether reconnect / bendpoint move / bendpoint add
  108. // is allowed at the given position
  109. var allowed = context.allowed = rules.allowed(context.type, context);
  110. if (allowed) {
  111. canvas.removeMarker(context.hover, MARKER_NOT_OK);
  112. canvas.addMarker(context.hover, MARKER_OK);
  113. context.target = context.hover;
  114. } else if (allowed === false) {
  115. canvas.removeMarker(context.hover, MARKER_OK);
  116. canvas.addMarker(context.hover, MARKER_NOT_OK);
  117. context.target = null;
  118. }
  119. }
  120. });
  121. eventBus.on([
  122. 'bendpoint.move.out',
  123. 'bendpoint.move.cleanup'
  124. ], function(e) {
  125. // remove connect marker
  126. // if it was added
  127. var hover = e.context.hover;
  128. if (hover) {
  129. canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
  130. canvas.removeMarker(hover, e.context.target ? MARKER_OK : MARKER_NOT_OK);
  131. }
  132. });
  133. eventBus.on('bendpoint.move.move', function(e) {
  134. var context = e.context,
  135. moveType = context.type,
  136. connection = e.connection,
  137. source, target;
  138. connection.waypoints[context.bendpointIndex] = { x: e.x, y: e.y };
  139. if (connectionDocking) {
  140. if (context.hover) {
  141. if (moveType === COMMAND_RECONNECT_START) {
  142. source = context.hover;
  143. }
  144. if (moveType === COMMAND_RECONNECT_END) {
  145. target = context.hover;
  146. }
  147. }
  148. connection.waypoints = connectionDocking.getCroppedWaypoints(connection, source, target);
  149. }
  150. // add dragger gfx
  151. translate(context.draggerGfx, e.x, e.y);
  152. redrawConnection(e);
  153. });
  154. eventBus.on([
  155. 'bendpoint.move.end',
  156. 'bendpoint.move.cancel'
  157. ], function(e) {
  158. var context = e.context,
  159. hover = context.hover,
  160. connection = context.connection;
  161. // remove dragger gfx
  162. svgRemove(context.draggerGfx);
  163. context.newWaypoints = connection.waypoints.slice();
  164. connection.waypoints = context.originalWaypoints;
  165. canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
  166. if (hover) {
  167. canvas.removeMarker(hover, MARKER_OK);
  168. canvas.removeMarker(hover, MARKER_NOT_OK);
  169. }
  170. });
  171. eventBus.on('bendpoint.move.end', function(e) {
  172. var context = e.context,
  173. waypoints = context.newWaypoints,
  174. bendpointIndex = context.bendpointIndex,
  175. bendpoint = waypoints[bendpointIndex],
  176. allowed = context.allowed,
  177. hints;
  178. // ensure we have actual pixel values bendpoint
  179. // coordinates (important when zoom level was > 1 during move)
  180. bendpoint.x = round(bendpoint.x);
  181. bendpoint.y = round(bendpoint.y);
  182. if (allowed && context.type === COMMAND_RECONNECT_START) {
  183. modeling.reconnectStart(context.connection, context.target, bendpoint);
  184. } else
  185. if (allowed && context.type === COMMAND_RECONNECT_END) {
  186. modeling.reconnectEnd(context.connection, context.target, bendpoint);
  187. } else
  188. if (allowed !== false && context.type === COMMAND_BENDPOINT_UPDATE) {
  189. // pass hints on the actual moved bendpoint
  190. // this is useful for connection and label layouting
  191. hints = {
  192. bendpointMove: {
  193. insert: e.context.insert,
  194. bendpointIndex: bendpointIndex
  195. }
  196. };
  197. modeling.updateWaypoints(context.connection, filterRedundantWaypoints(waypoints), hints);
  198. } else {
  199. redrawConnection(e);
  200. return false;
  201. }
  202. });
  203. eventBus.on('bendpoint.move.cancel', function(e) {
  204. redrawConnection(e);
  205. });
  206. }
  207. BendpointMove.$inject = [
  208. 'injector',
  209. 'eventBus',
  210. 'canvas',
  211. 'dragging',
  212. 'graphicsFactory',
  213. 'rules',
  214. 'modeling'
  215. ];