diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/utils/withinBounds.test.ts | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/utils/withinBounds.test.ts')
| -rw-r--r-- | packages/utils/withinBounds.test.ts | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/packages/utils/withinBounds.test.ts b/packages/utils/withinBounds.test.ts new file mode 100644 index 0000000..85354d7 --- /dev/null +++ b/packages/utils/withinBounds.test.ts @@ -0,0 +1,262 @@ +import type { Bounds } from "@excalidraw/excalidraw/element/bounds"; +import { API } from "@excalidraw/excalidraw/tests/helpers/api"; +import { + elementPartiallyOverlapsWithOrContainsBBox, + elementsOverlappingBBox, + isElementInsideBBox, +} from "./withinBounds"; + +const makeElement = (x: number, y: number, width: number, height: number) => + API.createElement({ + type: "rectangle", + x, + y, + width, + height, + }); + +const makeBBox = ( + minX: number, + minY: number, + maxX: number, + maxY: number, +): Bounds => [minX, minY, maxX, maxY]; + +describe("isElementInsideBBox()", () => { + it("should return true if element is fully inside", () => { + const bbox = makeBBox(0, 0, 100, 100); + + // bbox contains element + expect(isElementInsideBBox(makeElement(0, 0, 100, 100), bbox)).toBe(true); + expect(isElementInsideBBox(makeElement(10, 10, 90, 90), bbox)).toBe(true); + }); + + it("should return false if element is only partially overlapping", () => { + const bbox = makeBBox(0, 0, 100, 100); + + // element contains bbox + expect(isElementInsideBBox(makeElement(-10, -10, 110, 110), bbox)).toBe( + false, + ); + + // element overlaps bbox from top-left + expect(isElementInsideBBox(makeElement(-10, -10, 100, 100), bbox)).toBe( + false, + ); + // element overlaps bbox from top-right + expect(isElementInsideBBox(makeElement(90, -10, 100, 100), bbox)).toBe( + false, + ); + // element overlaps bbox from bottom-left + expect(isElementInsideBBox(makeElement(-10, 90, 100, 100), bbox)).toBe( + false, + ); + // element overlaps bbox from bottom-right + expect(isElementInsideBBox(makeElement(90, 90, 100, 100), bbox)).toBe( + false, + ); + }); + + it("should return false if element outside", () => { + const bbox = makeBBox(0, 0, 100, 100); + + // outside diagonally + expect(isElementInsideBBox(makeElement(110, 110, 100, 100), bbox)).toBe( + false, + ); + + // outside on the left + expect(isElementInsideBBox(makeElement(-110, 10, 50, 50), bbox)).toBe( + false, + ); + // outside on the right + expect(isElementInsideBBox(makeElement(110, 10, 50, 50), bbox)).toBe(false); + // outside on the top + expect(isElementInsideBBox(makeElement(10, -110, 50, 50), bbox)).toBe( + false, + ); + // outside on the bottom + expect(isElementInsideBBox(makeElement(10, 110, 50, 50), bbox)).toBe(false); + }); + + it("should return true if bbox contains element and flag enabled", () => { + const bbox = makeBBox(0, 0, 100, 100); + + // element contains bbox + expect( + isElementInsideBBox(makeElement(-10, -10, 110, 110), bbox, true), + ).toBe(true); + + // bbox contains element + expect(isElementInsideBBox(makeElement(0, 0, 100, 100), bbox)).toBe(true); + expect(isElementInsideBBox(makeElement(10, 10, 90, 90), bbox)).toBe(true); + }); +}); + +describe("elementPartiallyOverlapsWithOrContainsBBox()", () => { + it("should return true if element overlaps, is inside, or contains", () => { + const bbox = makeBBox(0, 0, 100, 100); + + // bbox contains element + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(0, 0, 100, 100), + bbox, + ), + ).toBe(true); + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(10, 10, 90, 90), + bbox, + ), + ).toBe(true); + + // element contains bbox + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(-10, -10, 110, 110), + bbox, + ), + ).toBe(true); + + // element overlaps bbox from top-left + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(-10, -10, 100, 100), + bbox, + ), + ).toBe(true); + // element overlaps bbox from top-right + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(90, -10, 100, 100), + bbox, + ), + ).toBe(true); + // element overlaps bbox from bottom-left + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(-10, 90, 100, 100), + bbox, + ), + ).toBe(true); + // element overlaps bbox from bottom-right + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(90, 90, 100, 100), + bbox, + ), + ).toBe(true); + }); + + it("should return false if element does not overlap", () => { + const bbox = makeBBox(0, 0, 100, 100); + + // outside diagonally + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(110, 110, 100, 100), + bbox, + ), + ).toBe(false); + + // outside on the left + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(-110, 10, 50, 50), + bbox, + ), + ).toBe(false); + // outside on the right + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(110, 10, 50, 50), + bbox, + ), + ).toBe(false); + // outside on the top + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(10, -110, 50, 50), + bbox, + ), + ).toBe(false); + // outside on the bottom + expect( + elementPartiallyOverlapsWithOrContainsBBox( + makeElement(10, 110, 50, 50), + bbox, + ), + ).toBe(false); + }); +}); + +describe("elementsOverlappingBBox()", () => { + it("should return elements that overlap bbox", () => { + const bbox = makeBBox(0, 0, 100, 100); + + const rectOutside = makeElement(110, 110, 100, 100); + const rectInside = makeElement(10, 10, 90, 90); + const rectContainingBBox = makeElement(-10, -10, 110, 110); + const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50); + + expect( + elementsOverlappingBBox({ + bounds: bbox, + type: "overlap", + elements: [ + rectOutside, + rectInside, + rectContainingBBox, + rectOverlappingTopLeft, + ], + }), + ).toEqual([rectInside, rectContainingBBox, rectOverlappingTopLeft]); + }); + + it("should return elements inside/containing bbox", () => { + const bbox = makeBBox(0, 0, 100, 100); + + const rectOutside = makeElement(110, 110, 100, 100); + const rectInside = makeElement(10, 10, 90, 90); + const rectContainingBBox = makeElement(-10, -10, 110, 110); + const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50); + + expect( + elementsOverlappingBBox({ + bounds: bbox, + type: "contain", + elements: [ + rectOutside, + rectInside, + rectContainingBBox, + rectOverlappingTopLeft, + ], + }), + ).toEqual([rectInside, rectContainingBBox]); + }); + + it("should return elements inside bbox", () => { + const bbox = makeBBox(0, 0, 100, 100); + + const rectOutside = makeElement(110, 110, 100, 100); + const rectInside = makeElement(10, 10, 90, 90); + const rectContainingBBox = makeElement(-10, -10, 110, 110); + const rectOverlappingTopLeft = makeElement(-10, -10, 50, 50); + + expect( + elementsOverlappingBBox({ + bounds: bbox, + type: "inside", + elements: [ + rectOutside, + rectInside, + rectContainingBBox, + rectOverlappingTopLeft, + ], + }), + ).toEqual([rectInside]); + }); + + // TODO test linear, freedraw, and diamond element types (+rotated) +}); |
