withinBounds.test.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import type { Bounds } from "../excalidraw/element/bounds";
  2. import { API } from "../excalidraw/tests/helpers/api";
  3. import {
  4. elementPartiallyOverlapsWithOrContainsBBox,
  5. elementsOverlappingBBox,
  6. isElementInsideBBox,
  7. } from "./withinBounds";
  8. const makeElement = (x: number, y: number, width: number, height: number) =>
  9. API.createElement({
  10. type: "rectangle",
  11. x,
  12. y,
  13. width,
  14. height,
  15. });
  16. const makeBBox = (
  17. minX: number,
  18. minY: number,
  19. maxX: number,
  20. maxY: number,
  21. ): Bounds => [minX, minY, maxX, maxY];
  22. describe("isElementInsideBBox()", () => {
  23. it("should return true if element is fully inside", () => {
  24. const bbox = makeBBox(0, 0, 100, 100);
  25. // bbox contains element
  26. expect(isElementInsideBBox(makeElement(0, 0, 100, 100), bbox)).toBe(true);
  27. expect(isElementInsideBBox(makeElement(10, 10, 90, 90), bbox)).toBe(true);
  28. });
  29. it("should return false if element is only partially overlapping", () => {
  30. const bbox = makeBBox(0, 0, 100, 100);
  31. // element contains bbox
  32. expect(isElementInsideBBox(makeElement(-10, -10, 110, 110), bbox)).toBe(
  33. false,
  34. );
  35. // element overlaps bbox from top-left
  36. expect(isElementInsideBBox(makeElement(-10, -10, 100, 100), bbox)).toBe(
  37. false,
  38. );
  39. // element overlaps bbox from top-right
  40. expect(isElementInsideBBox(makeElement(90, -10, 100, 100), bbox)).toBe(
  41. false,
  42. );
  43. // element overlaps bbox from bottom-left
  44. expect(isElementInsideBBox(makeElement(-10, 90, 100, 100), bbox)).toBe(
  45. false,
  46. );
  47. // element overlaps bbox from bottom-right
  48. expect(isElementInsideBBox(makeElement(90, 90, 100, 100), bbox)).toBe(
  49. false,
  50. );
  51. });
  52. it("should return false if element outside", () => {
  53. const bbox = makeBBox(0, 0, 100, 100);
  54. // outside diagonally
  55. expect(isElementInsideBBox(makeElement(110, 110, 100, 100), bbox)).toBe(
  56. false,
  57. );
  58. // outside on the left
  59. expect(isElementInsideBBox(makeElement(-110, 10, 50, 50), bbox)).toBe(
  60. false,
  61. );
  62. // outside on the right
  63. expect(isElementInsideBBox(makeElement(110, 10, 50, 50), bbox)).toBe(false);
  64. // outside on the top
  65. expect(isElementInsideBBox(makeElement(10, -110, 50, 50), bbox)).toBe(
  66. false,
  67. );
  68. // outside on the bottom
  69. expect(isElementInsideBBox(makeElement(10, 110, 50, 50), bbox)).toBe(false);
  70. });
  71. it("should return true if bbox contains element and flag enabled", () => {
  72. const bbox = makeBBox(0, 0, 100, 100);
  73. // element contains bbox
  74. expect(
  75. isElementInsideBBox(makeElement(-10, -10, 110, 110), bbox, true),
  76. ).toBe(true);
  77. // bbox contains element
  78. expect(isElementInsideBBox(makeElement(0, 0, 100, 100), bbox)).toBe(true);
  79. expect(isElementInsideBBox(makeElement(10, 10, 90, 90), bbox)).toBe(true);
  80. });
  81. });
  82. describe("elementPartiallyOverlapsWithOrContainsBBox()", () => {
  83. it("should return true if element overlaps, is inside, or contains", () => {
  84. const bbox = makeBBox(0, 0, 100, 100);
  85. // bbox contains element
  86. expect(
  87. elementPartiallyOverlapsWithOrContainsBBox(
  88. makeElement(0, 0, 100, 100),
  89. bbox,
  90. ),
  91. ).toBe(true);
  92. expect(
  93. elementPartiallyOverlapsWithOrContainsBBox(
  94. makeElement(10, 10, 90, 90),
  95. bbox,
  96. ),
  97. ).toBe(true);
  98. // element contains bbox
  99. expect(
  100. elementPartiallyOverlapsWithOrContainsBBox(
  101. makeElement(-10, -10, 110, 110),
  102. bbox,
  103. ),
  104. ).toBe(true);
  105. // element overlaps bbox from top-left
  106. expect(
  107. elementPartiallyOverlapsWithOrContainsBBox(
  108. makeElement(-10, -10, 100, 100),
  109. bbox,
  110. ),
  111. ).toBe(true);
  112. // element overlaps bbox from top-right
  113. expect(
  114. elementPartiallyOverlapsWithOrContainsBBox(
  115. makeElement(90, -10, 100, 100),
  116. bbox,
  117. ),
  118. ).toBe(true);
  119. // element overlaps bbox from bottom-left
  120. expect(
  121. elementPartiallyOverlapsWithOrContainsBBox(
  122. makeElement(-10, 90, 100, 100),
  123. bbox,
  124. ),
  125. ).toBe(true);
  126. // element overlaps bbox from bottom-right
  127. expect(
  128. elementPartiallyOverlapsWithOrContainsBBox(
  129. makeElement(90, 90, 100, 100),
  130. bbox,
  131. ),
  132. ).toBe(true);
  133. });
  134. it("should return false if element does not overlap", () => {
  135. const bbox = makeBBox(0, 0, 100, 100);
  136. // outside diagonally
  137. expect(
  138. elementPartiallyOverlapsWithOrContainsBBox(
  139. makeElement(110, 110, 100, 100),
  140. bbox,
  141. ),
  142. ).toBe(false);
  143. // outside on the left
  144. expect(
  145. elementPartiallyOverlapsWithOrContainsBBox(
  146. makeElement(-110, 10, 50, 50),
  147. bbox,
  148. ),
  149. ).toBe(false);
  150. // outside on the right
  151. expect(
  152. elementPartiallyOverlapsWithOrContainsBBox(
  153. makeElement(110, 10, 50, 50),
  154. bbox,
  155. ),
  156. ).toBe(false);
  157. // outside on the top
  158. expect(
  159. elementPartiallyOverlapsWithOrContainsBBox(
  160. makeElement(10, -110, 50, 50),
  161. bbox,
  162. ),
  163. ).toBe(false);
  164. // outside on the bottom
  165. expect(
  166. elementPartiallyOverlapsWithOrContainsBBox(
  167. makeElement(10, 110, 50, 50),
  168. bbox,
  169. ),
  170. ).toBe(false);
  171. });
  172. });
  173. describe("elementsOverlappingBBox()", () => {
  174. it("should return elements that overlap bbox", () => {
  175. const bbox = makeBBox(0, 0, 100, 100);
  176. const rectOutside = makeElement(110, 110, 100, 100);
  177. const rectInside = makeElement(10, 10, 90, 90);
  178. const rectContainingBBox = makeElement(-10, -10, 110, 110);
  179. const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50);
  180. expect(
  181. elementsOverlappingBBox({
  182. bounds: bbox,
  183. type: "overlap",
  184. elements: [
  185. rectOutside,
  186. rectInside,
  187. rectContainingBBox,
  188. rectOverlappingTopLeft,
  189. ],
  190. }),
  191. ).toEqual([rectInside, rectContainingBBox, rectOverlappingTopLeft]);
  192. });
  193. it("should return elements inside/containing bbox", () => {
  194. const bbox = makeBBox(0, 0, 100, 100);
  195. const rectOutside = makeElement(110, 110, 100, 100);
  196. const rectInside = makeElement(10, 10, 90, 90);
  197. const rectContainingBBox = makeElement(-10, -10, 110, 110);
  198. const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50);
  199. expect(
  200. elementsOverlappingBBox({
  201. bounds: bbox,
  202. type: "contain",
  203. elements: [
  204. rectOutside,
  205. rectInside,
  206. rectContainingBBox,
  207. rectOverlappingTopLeft,
  208. ],
  209. }),
  210. ).toEqual([rectInside, rectContainingBBox]);
  211. });
  212. it("should return elements inside bbox", () => {
  213. const bbox = makeBBox(0, 0, 100, 100);
  214. const rectOutside = makeElement(110, 110, 100, 100);
  215. const rectInside = makeElement(10, 10, 90, 90);
  216. const rectContainingBBox = makeElement(-10, -10, 110, 110);
  217. const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50);
  218. expect(
  219. elementsOverlappingBBox({
  220. bounds: bbox,
  221. type: "inside",
  222. elements: [
  223. rectOutside,
  224. rectInside,
  225. rectContainingBBox,
  226. rectOverlappingTopLeft,
  227. ],
  228. }),
  229. ).toEqual([rectInside]);
  230. });
  231. // TODO test linear, freedraw, and diamond element types (+rotated)
  232. });