diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.test.ts b/invokeai/frontend/web/src/features/controlLayers/konva/util.test.ts new file mode 100644 index 0000000000..a7db030ae4 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/konva/util.test.ts @@ -0,0 +1,47 @@ +import { getPrefixedId, getRectUnion } from 'features/controlLayers/konva/util'; +import { describe, expect, it } from 'vitest'; + +describe('util', () => { + describe('getPrefixedId', () => { + it('should return a prefixed id', () => { + expect(getPrefixedId('foo').split(':')[0]).toBe('foo'); + }); + }); + + describe('getRectUnion', () => { + it('should return the union of rects (2 rects)', () => { + const rect1 = { x: 0, y: 0, width: 10, height: 10 }; + const rect2 = { x: 5, y: 5, width: 10, height: 10 }; + const union = getRectUnion(rect1, rect2); + expect(union).toEqual({ x: 0, y: 0, width: 15, height: 15 }); + }); + it('should return the union of rects (3 rects)', () => { + const rect1 = { x: 0, y: 0, width: 10, height: 10 }; + const rect2 = { x: 5, y: 5, width: 10, height: 10 }; + const rect3 = { x: 10, y: 10, width: 10, height: 10 }; + const union = getRectUnion(rect1, rect2, rect3); + expect(union).toEqual({ x: 0, y: 0, width: 20, height: 20 }); + }); + it('should return the union of rects (2 rects none from zero)', () => { + const rect1 = { x: 5, y: 5, width: 10, height: 10 }; + const rect2 = { x: 10, y: 10, width: 10, height: 10 }; + const union = getRectUnion(rect1, rect2); + expect(union).toEqual({ x: 5, y: 5, width: 15, height: 15 }); + }); + it('should return the union of rects (2 rects with negative x/y)', () => { + const rect1 = { x: -5, y: -5, width: 10, height: 10 }; + const rect2 = { x: 0, y: 0, width: 10, height: 10 }; + const union = getRectUnion(rect1, rect2); + expect(union).toEqual({ x: -5, y: -5, width: 15, height: 15 }); + }); + it('should return the union of the first rect if only one rect is provided', () => { + const rect = { x: 0, y: 0, width: 10, height: 10 }; + const union = getRectUnion(rect); + expect(union).toEqual(rect); + }); + it('should fall back on an empty rect if no rects are provided', () => { + const union = getRectUnion(); + expect(union).toEqual({ x: 0, y: 0, width: 0, height: 0 }); + }); + }); +}); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts index eea14e02ba..c6e42aca27 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts @@ -418,19 +418,25 @@ export function snapToNearest(value: number, candidateValues: number[], threshol } /** - * Gets the union of two rects - * @param rect1 The first rect - * @param rect2 The second rect + * Gets the union of any number of rects. + * @params rects The rects to union * @returns The union of the two rects */ export const getRectUnion = (...rects: Rect[]): Rect => { + const firstRect = rects.shift(); + + if (!firstRect) { + return getEmptyRect(); + } + const rect = rects.reduce((acc, r) => { const x = Math.min(acc.x, r.x); const y = Math.min(acc.y, r.y); const width = Math.max(acc.x + acc.width, r.x + r.width) - x; const height = Math.max(acc.y + acc.height, r.y + r.height) - y; return { x, y, width, height }; - }, getEmptyRect()); + }, firstRect); + return rect; };