LabelLayoutUtil.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import {
  2. getDistancePointPoint,
  3. rotateVector,
  4. getAngle
  5. } from './GeometricUtil';
  6. import {
  7. getAttachment
  8. } from './LineAttachmentUtil';
  9. import {
  10. roundPoint
  11. } from 'diagram-js/lib/layout/LayoutUtil';
  12. export function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) {
  13. var index = attachment.segmentIndex;
  14. var offset = newWaypoints.length - oldWaypoints.length;
  15. // segmentMove happened
  16. if (hints.segmentMove) {
  17. var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex,
  18. newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex;
  19. // if label was on moved segment return new segment index
  20. if (index === oldSegmentStartIndex) {
  21. return newSegmentStartIndex;
  22. }
  23. // label is after new segment index
  24. if (index >= newSegmentStartIndex) {
  25. return (index+offset < newSegmentStartIndex) ? newSegmentStartIndex : index+offset;
  26. }
  27. // if label is before new segment index
  28. return index;
  29. }
  30. // bendpointMove happened
  31. if (hints.bendpointMove) {
  32. var insert = hints.bendpointMove.insert,
  33. bendpointIndex = hints.bendpointMove.bendpointIndex,
  34. newIndex;
  35. // waypoints length didnt change
  36. if (offset === 0) {
  37. return index;
  38. }
  39. // label behind new/removed bendpoint
  40. if (index >= bendpointIndex) {
  41. newIndex = insert ? index + 1 : index - 1;
  42. }
  43. // label before new/removed bendpoint
  44. if (index < bendpointIndex) {
  45. newIndex = index;
  46. // decide label should take right or left segment
  47. if (insert && attachment.type !== 'bendpoint' && bendpointIndex-1 === index) {
  48. var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex);
  49. if (rel < attachment.relativeLocation) {
  50. newIndex++;
  51. }
  52. }
  53. }
  54. return newIndex;
  55. }
  56. // start/end changed
  57. if (offset === 0) {
  58. return index;
  59. }
  60. if (hints.connectionStart) {
  61. return (index === 0) ? 0 : null;
  62. }
  63. if (hints.connectionEnd) {
  64. return (index === oldWaypoints.length - 2) ? newWaypoints.length - 2 : null;
  65. }
  66. // if nothing fits, return null
  67. return null;
  68. }
  69. /**
  70. * Calculate the required adjustment (move delta) for the given label
  71. * after the connection waypoints got updated.
  72. *
  73. * @param {djs.model.Label} label
  74. * @param {Array<Point>} newWaypoints
  75. * @param {Array<Point>} oldWaypoints
  76. * @param {Object} hints
  77. *
  78. * @return {Point} delta
  79. */
  80. export function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) {
  81. var x = 0,
  82. y = 0;
  83. var labelPosition = getLabelMid(label);
  84. // get closest attachment
  85. var attachment = getAttachment(labelPosition, oldWaypoints),
  86. oldLabelLineIndex = attachment.segmentIndex,
  87. newLabelLineIndex = findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints);
  88. if (newLabelLineIndex === null) {
  89. return { x: x, y: y };
  90. }
  91. // should never happen
  92. // TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored
  93. if (newLabelLineIndex < 0 ||
  94. newLabelLineIndex > newWaypoints.length - 2) {
  95. return { x: x, y: y };
  96. }
  97. var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex),
  98. newLabelLine = getLine(newWaypoints, newLabelLineIndex),
  99. oldFoot = attachment.position;
  100. var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot),
  101. angleDelta = getAngleDelta(oldLabelLine, newLabelLine);
  102. // special rule if label on bendpoint
  103. if (attachment.type === 'bendpoint') {
  104. var offset = newWaypoints.length - oldWaypoints.length,
  105. oldBendpointIndex = attachment.bendpointIndex,
  106. oldBendpoint = oldWaypoints[oldBendpointIndex];
  107. // bendpoint position hasn't changed, return same position
  108. if (newWaypoints.indexOf(oldBendpoint) !== -1) {
  109. return { x: x, y: y };
  110. }
  111. // new bendpoint and old bendpoint have same index, then just return the offset
  112. if (offset === 0) {
  113. var newBendpoint = newWaypoints[oldBendpointIndex];
  114. return {
  115. x: newBendpoint.x - attachment.position.x,
  116. y: newBendpoint.y - attachment.position.y
  117. };
  118. }
  119. // if bendpoints get removed
  120. if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) {
  121. relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex);
  122. }
  123. }
  124. var newFoot = {
  125. x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x,
  126. y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y
  127. };
  128. // the rotated vector to label
  129. var newLabelVector = rotateVector({
  130. x: labelPosition.x - oldFoot.x,
  131. y: labelPosition.y - oldFoot.y
  132. }, angleDelta);
  133. // the new relative position
  134. x = newFoot.x + newLabelVector.x - labelPosition.x;
  135. y = newFoot.y + newLabelVector.y - labelPosition.y;
  136. return roundPoint({
  137. x: x,
  138. y: y
  139. });
  140. }
  141. // HELPERS //////////////////////
  142. function relativePositionMidWaypoint(waypoints, idx) {
  143. var distanceSegment1 = getDistancePointPoint(waypoints[idx-1], waypoints[idx]),
  144. distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx+1]);
  145. var relativePosition = distanceSegment1 / (distanceSegment1 + distanceSegment2);
  146. return relativePosition;
  147. }
  148. function getLabelMid(label) {
  149. return {
  150. x: label.x + label.width / 2,
  151. y: label.y + label.height / 2
  152. };
  153. }
  154. function getAngleDelta(l1, l2) {
  155. var a1 = getAngle(l1),
  156. a2 = getAngle(l2);
  157. return a2 - a1;
  158. }
  159. function getLine(waypoints, idx) {
  160. return [ waypoints[idx], waypoints[idx+1] ];
  161. }
  162. function getRelativeFootPosition(line, foot) {
  163. var length = getDistancePointPoint(line[0], line[1]),
  164. lengthToFoot = getDistancePointPoint(line[0], foot);
  165. return length === 0 ? 0 : lengthToFoot / length;
  166. }