ResizeUtil.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import {
  2. filter,
  3. isNumber
  4. } from 'min-dash';
  5. var max = Math.max,
  6. min = Math.min;
  7. var DEFAULT_CHILD_BOX_PADDING = 20;
  8. import {
  9. getBBox
  10. } from '../../util/Elements';
  11. import {
  12. asTRBL,
  13. asBounds
  14. } from '../../layout/LayoutUtil';
  15. /**
  16. * Substract a TRBL from another
  17. *
  18. * @param {TRBL} trblA
  19. * @param {TRBL} trblB
  20. *
  21. * @return {TRBL}
  22. */
  23. export function substractTRBL(trblA, trblB) {
  24. return {
  25. top: trblA.top - trblB.top,
  26. right: trblA.right - trblB.right,
  27. bottom: trblA.bottom - trblB.bottom,
  28. left: trblA.left - trblB.left
  29. };
  30. }
  31. /**
  32. * Resize the given bounds by the specified delta from a given anchor point.
  33. *
  34. * @param {Bounds} bounds the bounding box that should be resized
  35. * @param {String} direction in which the element is resized (nw, ne, se, sw)
  36. * @param {Point} delta of the resize operation
  37. *
  38. * @return {Bounds} resized bounding box
  39. */
  40. export function resizeBounds(bounds, direction, delta) {
  41. var dx = delta.x,
  42. dy = delta.y;
  43. switch (direction) {
  44. case 'nw':
  45. return {
  46. x: bounds.x + dx,
  47. y: bounds.y + dy,
  48. width: bounds.width - dx,
  49. height: bounds.height - dy
  50. };
  51. case 'sw':
  52. return {
  53. x: bounds.x + dx,
  54. y: bounds.y,
  55. width: bounds.width - dx,
  56. height: bounds.height + dy
  57. };
  58. case 'ne':
  59. return {
  60. x: bounds.x,
  61. y: bounds.y + dy,
  62. width: bounds.width + dx,
  63. height: bounds.height - dy
  64. };
  65. case 'se':
  66. return {
  67. x: bounds.x,
  68. y: bounds.y,
  69. width: bounds.width + dx,
  70. height: bounds.height + dy
  71. };
  72. default:
  73. throw new Error('unrecognized direction: ' + direction);
  74. }
  75. }
  76. /**
  77. * Resize the given bounds by applying the passed
  78. * { top, right, bottom, left } delta.
  79. *
  80. * @param {Bounds} bounds
  81. * @param {TRBL} trblResize
  82. *
  83. * @return {Bounds}
  84. */
  85. export function resizeTRBL(bounds, resize) {
  86. return {
  87. x: bounds.x + (resize.left || 0),
  88. y: bounds.y + (resize.top || 0),
  89. width: bounds.width - (resize.left || 0) + (resize.right || 0),
  90. height: bounds.height - (resize.top || 0) + (resize.bottom || 0)
  91. };
  92. }
  93. export function reattachPoint(bounds, newBounds, point) {
  94. var sx = bounds.width / newBounds.width,
  95. sy = bounds.height / newBounds.height;
  96. return {
  97. x: Math.round((newBounds.x + newBounds.width / 2)) - Math.floor(((bounds.x + bounds.width / 2) - point.x) / sx),
  98. y: Math.round((newBounds.y + newBounds.height / 2)) - Math.floor(((bounds.y + bounds.height / 2) - point.y) / sy)
  99. };
  100. }
  101. function applyConstraints(attr, trbl, resizeConstraints) {
  102. var value = trbl[attr],
  103. minValue = resizeConstraints.min && resizeConstraints.min[attr],
  104. maxValue = resizeConstraints.max && resizeConstraints.max[attr];
  105. if (isNumber(minValue)) {
  106. value = (/top|left/.test(attr) ? min : max)(value, minValue);
  107. }
  108. if (isNumber(maxValue)) {
  109. value = (/top|left/.test(attr) ? max : min)(value, maxValue);
  110. }
  111. return value;
  112. }
  113. export function ensureConstraints(currentBounds, resizeConstraints) {
  114. if (!resizeConstraints) {
  115. return currentBounds;
  116. }
  117. var currentTrbl = asTRBL(currentBounds);
  118. return asBounds({
  119. top: applyConstraints('top', currentTrbl, resizeConstraints),
  120. right: applyConstraints('right', currentTrbl, resizeConstraints),
  121. bottom: applyConstraints('bottom', currentTrbl, resizeConstraints),
  122. left: applyConstraints('left', currentTrbl, resizeConstraints)
  123. });
  124. }
  125. export function getMinResizeBounds(direction, currentBounds, minDimensions, childrenBounds) {
  126. var currentBox = asTRBL(currentBounds);
  127. var minBox = {
  128. top: /n/.test(direction) ? currentBox.bottom - minDimensions.height : currentBox.top,
  129. left: /w/.test(direction) ? currentBox.right - minDimensions.width : currentBox.left,
  130. bottom: /s/.test(direction) ? currentBox.top + minDimensions.height : currentBox.bottom,
  131. right: /e/.test(direction) ? currentBox.left + minDimensions.width : currentBox.right
  132. };
  133. var childrenBox = childrenBounds ? asTRBL(childrenBounds) : minBox;
  134. var combinedBox = {
  135. top: min(minBox.top, childrenBox.top),
  136. left: min(minBox.left, childrenBox.left),
  137. bottom: max(minBox.bottom, childrenBox.bottom),
  138. right: max(minBox.right, childrenBox.right)
  139. };
  140. return asBounds(combinedBox);
  141. }
  142. function asPadding(mayBePadding, defaultValue) {
  143. if (typeof mayBePadding !== 'undefined') {
  144. return mayBePadding;
  145. } else {
  146. return DEFAULT_CHILD_BOX_PADDING;
  147. }
  148. }
  149. export function addPadding(bbox, padding) {
  150. var left, right, top, bottom;
  151. if (typeof padding === 'object') {
  152. left = asPadding(padding.left);
  153. right = asPadding(padding.right);
  154. top = asPadding(padding.top);
  155. bottom = asPadding(padding.bottom);
  156. } else {
  157. left = right = top = bottom = asPadding(padding);
  158. }
  159. return {
  160. x: bbox.x - left,
  161. y: bbox.y - top,
  162. width: bbox.width + left + right,
  163. height: bbox.height + top + bottom
  164. };
  165. }
  166. /**
  167. * Is the given element part of the resize
  168. * targets min boundary box?
  169. *
  170. * This is the default implementation which excludes
  171. * connections and labels.
  172. *
  173. * @param {djs.model.Base} element
  174. */
  175. function isBBoxChild(element) {
  176. // exclude connections
  177. if (element.waypoints) {
  178. return false;
  179. }
  180. // exclude labels
  181. if (element.type === 'label') {
  182. return false;
  183. }
  184. return true;
  185. }
  186. /**
  187. * Return children bounding computed from a shapes children
  188. * or a list of prefiltered children.
  189. *
  190. * @param {djs.model.Shape|Array<djs.model.Shape>} shapeOrChildren
  191. * @param {Number|Object} padding
  192. *
  193. * @return {Bounds}
  194. */
  195. export function computeChildrenBBox(shapeOrChildren, padding) {
  196. var elements;
  197. // compute based on shape
  198. if (shapeOrChildren.length === undefined) {
  199. // grab all the children that are part of the
  200. // parents children box
  201. elements = filter(shapeOrChildren.children, isBBoxChild);
  202. } else {
  203. elements = shapeOrChildren;
  204. }
  205. if (elements.length) {
  206. return addPadding(getBBox(elements), padding);
  207. }
  208. }