GridSnappingLayoutConnectionBehavior.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import inherits from 'inherits-browser';
  2. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  3. import { pointsAligned } from 'diagram-js/lib/util/Geometry';
  4. import {
  5. assign
  6. } from 'min-dash';
  7. /**
  8. * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
  9. * @typedef {import('diagram-js/lib/features/grid-snapping/GridSnapping').default} GridSnapping
  10. * @typedef {import('../../modeling/Modeling').default} Modeling
  11. *
  12. * @typedef {import('diagram-js/lib/util/Types').Point} Point
  13. */
  14. var HIGH_PRIORITY = 3000;
  15. /**
  16. * Snaps connections with Manhattan layout.
  17. *
  18. * @param {EventBus} eventBus
  19. * @param {GridSnapping} gridSnapping
  20. * @param {Modeling} modeling
  21. */
  22. export default function GridSnappingLayoutConnectionBehavior(eventBus, gridSnapping, modeling) {
  23. CommandInterceptor.call(this, eventBus);
  24. this._gridSnapping = gridSnapping;
  25. var self = this;
  26. this.postExecuted([
  27. 'connection.create',
  28. 'connection.layout'
  29. ], HIGH_PRIORITY, function(event) {
  30. var context = event.context,
  31. connection = context.connection,
  32. hints = context.hints || {},
  33. waypoints = connection.waypoints;
  34. if (hints.connectionStart || hints.connectionEnd || hints.createElementsBehavior === false) {
  35. return;
  36. }
  37. if (!hasMiddleSegments(waypoints)) {
  38. return;
  39. }
  40. modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
  41. });
  42. }
  43. GridSnappingLayoutConnectionBehavior.$inject = [
  44. 'eventBus',
  45. 'gridSnapping',
  46. 'modeling'
  47. ];
  48. inherits(GridSnappingLayoutConnectionBehavior, CommandInterceptor);
  49. /**
  50. * Snap middle segments of a given connection.
  51. *
  52. * @param {Point[]} waypoints
  53. *
  54. * @return {Point[]}
  55. */
  56. GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) {
  57. var gridSnapping = this._gridSnapping,
  58. snapped;
  59. waypoints = waypoints.slice();
  60. for (var i = 1; i < waypoints.length - 2; i++) {
  61. snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
  62. waypoints[i] = snapped[0];
  63. waypoints[i + 1] = snapped[1];
  64. }
  65. return waypoints;
  66. };
  67. // helpers //////////
  68. /**
  69. * Check whether a connection has a middle segments.
  70. *
  71. * @param {Point[]} waypoints
  72. *
  73. * @return {boolean}
  74. */
  75. function hasMiddleSegments(waypoints) {
  76. return waypoints.length > 3;
  77. }
  78. /**
  79. * Check whether an alignment is horizontal.
  80. *
  81. * @param {string} aligned
  82. *
  83. * @return {boolean}
  84. */
  85. function horizontallyAligned(aligned) {
  86. return aligned === 'h';
  87. }
  88. /**
  89. * Check whether an alignment is vertical.
  90. *
  91. * @param {string} aligned
  92. *
  93. * @return {boolean}
  94. */
  95. function verticallyAligned(aligned) {
  96. return aligned === 'v';
  97. }
  98. /**
  99. * Get middle segments from a given connection.
  100. *
  101. * @param {Point[]} waypoints
  102. *
  103. * @return {Point[]}
  104. */
  105. function snapSegment(gridSnapping, segmentStart, segmentEnd) {
  106. var aligned = pointsAligned(segmentStart, segmentEnd);
  107. var snapped = {};
  108. if (horizontallyAligned(aligned)) {
  109. // snap horizontally
  110. snapped.y = gridSnapping.snapValue(segmentStart.y);
  111. }
  112. if (verticallyAligned(aligned)) {
  113. // snap vertically
  114. snapped.x = gridSnapping.snapValue(segmentStart.x);
  115. }
  116. if ('x' in snapped || 'y' in snapped) {
  117. segmentStart = assign({}, segmentStart, snapped);
  118. segmentEnd = assign({}, segmentEnd, snapped);
  119. }
  120. return [ segmentStart, segmentEnd ];
  121. }