ResizeUtil.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. var newBounds = {
  44. x: bounds.x,
  45. y: bounds.y,
  46. width: bounds.width,
  47. height: bounds.height
  48. };
  49. if (direction.indexOf('n') !== -1) {
  50. newBounds.y = bounds.y + dy;
  51. newBounds.height = bounds.height - dy;
  52. } else if (direction.indexOf('s') !== -1) {
  53. newBounds.height = bounds.height + dy;
  54. }
  55. if (direction.indexOf('e') !== -1) {
  56. newBounds.width = bounds.width + dx;
  57. } else if (direction.indexOf('w') !== -1) {
  58. newBounds.x = bounds.x + dx;
  59. newBounds.width = bounds.width - dx;
  60. }
  61. return newBounds;
  62. }
  63. /**
  64. * Resize the given bounds by applying the passed
  65. * { top, right, bottom, left } delta.
  66. *
  67. * @param {Bounds} bounds
  68. * @param {TRBL} trblResize
  69. *
  70. * @return {Bounds}
  71. */
  72. export function resizeTRBL(bounds, resize) {
  73. return {
  74. x: bounds.x + (resize.left || 0),
  75. y: bounds.y + (resize.top || 0),
  76. width: bounds.width - (resize.left || 0) + (resize.right || 0),
  77. height: bounds.height - (resize.top || 0) + (resize.bottom || 0)
  78. };
  79. }
  80. export function reattachPoint(bounds, newBounds, point) {
  81. var sx = bounds.width / newBounds.width,
  82. sy = bounds.height / newBounds.height;
  83. return {
  84. x: Math.round((newBounds.x + newBounds.width / 2)) - Math.floor(((bounds.x + bounds.width / 2) - point.x) / sx),
  85. y: Math.round((newBounds.y + newBounds.height / 2)) - Math.floor(((bounds.y + bounds.height / 2) - point.y) / sy)
  86. };
  87. }
  88. function applyConstraints(attr, trbl, resizeConstraints) {
  89. var value = trbl[attr],
  90. minValue = resizeConstraints.min && resizeConstraints.min[attr],
  91. maxValue = resizeConstraints.max && resizeConstraints.max[attr];
  92. if (isNumber(minValue)) {
  93. value = (/top|left/.test(attr) ? min : max)(value, minValue);
  94. }
  95. if (isNumber(maxValue)) {
  96. value = (/top|left/.test(attr) ? max : min)(value, maxValue);
  97. }
  98. return value;
  99. }
  100. export function ensureConstraints(currentBounds, resizeConstraints) {
  101. if (!resizeConstraints) {
  102. return currentBounds;
  103. }
  104. var currentTrbl = asTRBL(currentBounds);
  105. return asBounds({
  106. top: applyConstraints('top', currentTrbl, resizeConstraints),
  107. right: applyConstraints('right', currentTrbl, resizeConstraints),
  108. bottom: applyConstraints('bottom', currentTrbl, resizeConstraints),
  109. left: applyConstraints('left', currentTrbl, resizeConstraints)
  110. });
  111. }
  112. export function getMinResizeBounds(direction, currentBounds, minDimensions, childrenBounds) {
  113. var currentBox = asTRBL(currentBounds);
  114. var minBox = {
  115. top: /n/.test(direction) ? currentBox.bottom - minDimensions.height : currentBox.top,
  116. left: /w/.test(direction) ? currentBox.right - minDimensions.width : currentBox.left,
  117. bottom: /s/.test(direction) ? currentBox.top + minDimensions.height : currentBox.bottom,
  118. right: /e/.test(direction) ? currentBox.left + minDimensions.width : currentBox.right
  119. };
  120. var childrenBox = childrenBounds ? asTRBL(childrenBounds) : minBox;
  121. var combinedBox = {
  122. top: min(minBox.top, childrenBox.top),
  123. left: min(minBox.left, childrenBox.left),
  124. bottom: max(minBox.bottom, childrenBox.bottom),
  125. right: max(minBox.right, childrenBox.right)
  126. };
  127. return asBounds(combinedBox);
  128. }
  129. function asPadding(mayBePadding, defaultValue) {
  130. if (typeof mayBePadding !== 'undefined') {
  131. return mayBePadding;
  132. } else {
  133. return DEFAULT_CHILD_BOX_PADDING;
  134. }
  135. }
  136. export function addPadding(bbox, padding) {
  137. var left, right, top, bottom;
  138. if (typeof padding === 'object') {
  139. left = asPadding(padding.left);
  140. right = asPadding(padding.right);
  141. top = asPadding(padding.top);
  142. bottom = asPadding(padding.bottom);
  143. } else {
  144. left = right = top = bottom = asPadding(padding);
  145. }
  146. return {
  147. x: bbox.x - left,
  148. y: bbox.y - top,
  149. width: bbox.width + left + right,
  150. height: bbox.height + top + bottom
  151. };
  152. }
  153. /**
  154. * Is the given element part of the resize
  155. * targets min boundary box?
  156. *
  157. * This is the default implementation which excludes
  158. * connections and labels.
  159. *
  160. * @param {djs.model.Base} element
  161. */
  162. function isBBoxChild(element) {
  163. // exclude connections
  164. if (element.waypoints) {
  165. return false;
  166. }
  167. // exclude labels
  168. if (element.type === 'label') {
  169. return false;
  170. }
  171. return true;
  172. }
  173. /**
  174. * Return children bounding computed from a shapes children
  175. * or a list of prefiltered children.
  176. *
  177. * @param {djs.model.Shape|Array<djs.model.Shape>} shapeOrChildren
  178. * @param {number|Object} padding
  179. *
  180. * @return {Bounds}
  181. */
  182. export function computeChildrenBBox(shapeOrChildren, padding) {
  183. var elements;
  184. // compute based on shape
  185. if (shapeOrChildren.length === undefined) {
  186. // grab all the children that are part of the
  187. // parents children box
  188. elements = filter(shapeOrChildren.children, isBBoxChild);
  189. } else {
  190. elements = shapeOrChildren;
  191. }
  192. if (elements.length) {
  193. return addPadding(getBBox(elements), padding);
  194. }
  195. }