UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1,165 lines (1,083 loc) 33.4 kB
import { describe, it, expect } from 'vitest'; import { StaticCanvas } from '../../canvas/StaticCanvas'; import { FabricObject } from './FabricObject'; import { Point } from '../../Point'; import { Rect } from '../Rect'; import { LEFT, TOP } from '../../constants'; describe('fabric.ObjectGeometry', () => { const canvas = new StaticCanvas(undefined, { enableRetinaScaling: false }); it('intersectsWithRectangle without zoom', () => { const cObj = new FabricObject({ left: 100, top: 100, width: 100, height: 100, }); cObj.setCoords(); expect(cObj.intersectsWithRect).toBeTypeOf('function'); const point1 = new Point(110, 100), point2 = new Point(210, 200), point3 = new Point(0, 0), point4 = new Point(10, 10); expect(cObj.intersectsWithRect(point1, point2)).toBeTruthy(); expect(cObj.intersectsWithRect(point3, point4)).toBeFalsy(); }); it('intersectsWithRectangle with zoom', () => { const cObj = new Rect({ left: 20, top: 20, width: 20, height: 20 }); canvas.add(cObj); canvas.viewportTransform = [2, 0, 0, 2, 0, 0]; cObj.setCoords(); canvas.calcViewportBoundaries(); const point1 = new Point(5, 5), point2 = new Point(15, 15), point3 = new Point(25, 25), point4 = new Point(35, 35); expect( cObj.intersectsWithRect(point1, point2), 'Does intersect also with a 2x zoom', ).toBeTruthy(); expect( cObj.intersectsWithRect(point3, point4), 'Does intersect also with a 2x zoom', ).toBeTruthy(); }); it('intersectsWithObject', () => { const cObj = new FabricObject({ left: 100, top: 100, width: 100, height: 100, }); cObj.setCoords(); expect(cObj.intersectsWithObject).toBeTypeOf('function'); const cObj2 = new FabricObject({ left: -50, top: -50, width: 200, height: 200, }); cObj2.setCoords(); expect( cObj.intersectsWithObject(cObj2), 'cobj2 does intersect with cobj', ).toBeTruthy(); expect( cObj2.intersectsWithObject(cObj), 'cobj2 does intersect with cobj', ).toBeTruthy(); const cObj3 = new FabricObject({ left: 399, top: 356, width: 13, height: 33, }); cObj3.setCoords(); expect( cObj.intersectsWithObject(cObj3), 'cobj3 does not intersect with cobj (external)', ).toBeFalsy(); expect( cObj3.intersectsWithObject(cObj), 'cobj3 does not intersect with cobj (external)', ).toBeFalsy(); const cObj4 = new FabricObject({ left: 100, top: 100, width: 200, height: 200, }); cObj4.setCoords(); expect( cObj4.intersectsWithObject(cObj), 'overlapping objects are considered intersecting', ).toBeTruthy(); expect( cObj.intersectsWithObject(cObj4), 'overlapping objects are considered intersecting', ).toBeTruthy(); }); it('isContainedWithinRect', () => { const cObj = new FabricObject({ left: 25, top: 25, width: 10, height: 10 }); cObj.setCoords(); expect(cObj.isContainedWithinRect).toBeTypeOf('function'); // fully contained expect( cObj.isContainedWithinRect(new Point(10, 10), new Point(100, 100)), ).toBeTruthy(); // only intersects expect( cObj.isContainedWithinRect(new Point(10, 10), new Point(25, 25)), ).toBeFalsy(); // doesn't intersect expect( cObj.isContainedWithinRect(new Point(100, 100), new Point(110, 110)), ).toBeFalsy(); }); it('isContainedWithinRect with zoom', () => { const cObj = new Rect({ left: 25, top: 25, width: 10, height: 10 }); canvas.add(cObj); canvas.viewportTransform = [2, 0, 0, 2, 0, 0]; cObj.setCoords(); canvas.calcViewportBoundaries(); expect(cObj.isContainedWithinRect).toBeTypeOf('function'); // fully contained expect( cObj.isContainedWithinRect(new Point(10, 10), new Point(100, 100)), ).toBeTruthy(); // only intersects expect( cObj.isContainedWithinRect(new Point(10, 10), new Point(25, 25)), ).toBeFalsy(); // doesn't intersect expect( cObj.isContainedWithinRect(new Point(100, 100), new Point(110, 110)), ).toBeFalsy(); }); it('intersectsWithRect', () => { const object = new FabricObject({ left: 20, top: 25, width: 40, height: 50, angle: 160, }); const point1 = new Point(-10, -10); const point2 = new Point(20, 30); const point3 = new Point(10, 15); const point4 = new Point(30, 35); const point5 = new Point(50, 60); const point6 = new Point(70, 80); object.setCoords(); // object and area intersects expect(object.intersectsWithRect(point1, point2)).toBe(true); // area is contained in object (no intersection) expect(object.intersectsWithRect(point3, point4)).toBe(false); // area is outside of object (no intersection) expect(object.intersectsWithRect(point5, point6)).toBe(false); }); it('intersectsWithObject', () => { const object = new FabricObject({ left: 20, top: 30, width: 40, height: 50, angle: 230, strokeWidth: 0, }); const object1 = new FabricObject({ left: 20, top: 30, width: 60, height: 30, angle: 10, strokeWidth: 0, }); const object2 = new FabricObject({ left: 25, top: 35, width: 20, height: 20, angle: 50, strokeWidth: 0, }); const object3 = new FabricObject({ left: 50, top: 50, width: 20, height: 20, angle: 0, strokeWidth: 0, }); object.set({ originX: 'center', originY: 'center' }).setCoords(); object1.set({ originX: 'center', originY: 'center' }).setCoords(); object2.set({ originX: 'center', originY: 'center' }).setCoords(); object3.set({ originX: 'center', originY: 'center' }).setCoords(); expect( object.intersectsWithObject(object1), 'object and object1 intersects', ).toBe(true); expect( object.intersectsWithObject(object2), 'object2 is contained in object', ).toBe(true); expect( object.intersectsWithObject(object3), 'object3 is outside of object (no intersection)', ).toBe(false); }); it('isContainedWithinObject', () => { const object = new FabricObject({ left: 0, top: 0, width: 40, height: 40, angle: 0, }); const object1 = new FabricObject({ left: 1, top: 1, width: 38, height: 38, angle: 0, }); const object2 = new FabricObject({ left: 20, top: 20, width: 40, height: 40, angle: 0, }); const object3 = new FabricObject({ left: 50, top: 50, width: 40, height: 40, angle: 0, }); object.setCoords(); object1.setCoords(); object2.setCoords(); object3.setCoords(); expect( object1.isContainedWithinObject(object), 'object1 is fully contained within object', ).toBe(true); expect( object2.isContainedWithinObject(object), 'object2 intersects object (not fully contained)', ).toBe(false); expect( object3.isContainedWithinObject(object), 'object3 is outside of object (not fully contained)', ).toBe(false); object1.angle = 45; object1.setCoords(); expect( object1.isContainedWithinObject(object), 'object1 rotated is not contained within object', ).toBe(false); const rect1 = new Rect({ width: 50, height: 50, left: 50, top: 50, }); const rect2 = new Rect({ width: 100, height: 100, left: 100, top: 0, angle: 45, }); rect1.setCoords(); rect2.setCoords(); expect( rect1.isContainedWithinObject(rect2), 'rect1 rotated is not contained within rect2', ).toBe(false); }); it('isContainedWithinRect', () => { const object = new FabricObject({ left: 40, top: 40, width: 40, height: 50, angle: 160, }); const point1 = new Point(0, 0); const point2 = new Point(80, 80); const point3 = new Point(0, 0); const point4 = new Point(80, 60); const point5 = new Point(80, 80); const point6 = new Point(90, 90); object.set({ originX: 'center', originY: 'center' }).setCoords(); // area is contained in object (no intersection) expect(object.isContainedWithinRect(point1, point2)).toBe(true); // object and area intersects expect(object.isContainedWithinRect(point3, point4)).toBe(false); // area is outside of object (no intersection) expect(object.isContainedWithinRect(point5, point6)).toBe(false); }); it('containsPoint', () => { const object = new FabricObject({ left: 40, top: 40, width: 40, height: 50, angle: 160, strokeWidth: 0, }); const point1 = new Point(30, 30); const point2 = new Point(60, 30); const point3 = new Point(45, 65); const point4 = new Point(15, 40); const point5 = new Point(30, 15); object.set({ originX: 'center', originY: 'center' }).setCoords(); // point1 is contained in object expect(object.containsPoint(point1)).toBe(true); // point2 is outside of object (right) expect(object.containsPoint(point2)).toBe(false); // point3 is outside of object (bottom) expect(object.containsPoint(point3)).toBe(false); // point4 is outside of object (left) expect(object.containsPoint(point4)).toBe(false); // point5 is outside of object (top) expect(object.containsPoint(point5)).toBe(false); }); it('setCoords', () => { const cObj = new FabricObject({ left: 200, top: 200, width: 100, height: 100, strokeWidth: 0, // @ts-expect-error -- fake canvas canvas: {}, }); expect(cObj.setCoords).toBeTypeOf('function'); cObj.setCoords(); expect(cObj.oCoords.tl.x).toBe(150); expect(cObj.oCoords.tl.y).toBe(150); expect(cObj.oCoords.tr.x).toBe(250); expect(cObj.oCoords.tr.y).toBe(150); expect(cObj.oCoords.bl.x).toBe(150); expect(cObj.oCoords.bl.y).toBe(250); expect(cObj.oCoords.br.x).toBe(250); expect(cObj.oCoords.br.y).toBe(250); expect(cObj.oCoords.mtr.x).toBe(200); expect(cObj.oCoords.mtr.y).toBe(110); cObj.set('left', 300).set('top', 300); // coords should still correspond to initial one, even after invoking `set` expect(cObj.oCoords.tl.x).toBe(150); expect(cObj.oCoords.tl.y).toBe(150); expect(cObj.oCoords.tr.x).toBe(250); expect(cObj.oCoords.tr.y).toBe(150); expect(cObj.oCoords.bl.x).toBe(150); expect(cObj.oCoords.bl.y).toBe(250); expect(cObj.oCoords.br.x).toBe(250); expect(cObj.oCoords.br.y).toBe(250); expect(cObj.oCoords.mtr.x).toBe(200); expect(cObj.oCoords.mtr.y).toBe(110); // recalculate coords cObj.setCoords(); // check that coords are now updated expect(cObj.oCoords.tl.x).toBe(250); expect(cObj.oCoords.tl.y).toBe(250); expect(cObj.oCoords.tr.x).toBe(350); expect(cObj.oCoords.tr.y).toBe(250); expect(cObj.oCoords.bl.x).toBe(250); expect(cObj.oCoords.bl.y).toBe(350); expect(cObj.oCoords.br.x).toBe(350); expect(cObj.oCoords.br.y).toBe(350); expect(cObj.oCoords.mtr.x).toBe(300); expect(cObj.oCoords.mtr.y).toBe(210); cObj.set('padding', 25); cObj.setCoords(); // coords should still correspond to initial one, even after invoking `set` expect(cObj.oCoords.tl.x, 'setCoords tl.x padding').toBe(225); expect(cObj.oCoords.tl.y, 'setCoords tl.y padding').toBe(225); expect(cObj.oCoords.tr.x, 'setCoords tr.x padding').toBe(375); expect(cObj.oCoords.tr.y, 'setCoords tr.y padding').toBe(225); expect(cObj.oCoords.bl.x, 'setCoords bl.x padding').toBe(225); expect(cObj.oCoords.bl.y, 'setCoords bl.y padding').toBe(375); expect(cObj.oCoords.br.x, 'setCoords br.x padding').toBe(375); expect(cObj.oCoords.br.y, 'setCoords br.y padding').toBe(375); expect(cObj.oCoords.mtr.x, 'setCoords mtr.x padding').toBe(300); expect(cObj.oCoords.mtr.y, 'setCoords mtr.y padding').toBe(185); }); it('setCoords and aCoords', () => { const cObj = new FabricObject({ left: 200, top: 200, width: 100, height: 100, strokeWidth: 0, }); // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 0, 0], }; cObj.setCoords(); expect( cObj.oCoords.tl.x, 'oCoords are modified by viewportTransform tl.x', ).toBe(300); expect( cObj.oCoords.tl.y, 'oCoords are modified by viewportTransform tl.y', ).toBe(300); expect( cObj.oCoords.tr.x, 'oCoords are modified by viewportTransform tr.x', ).toBe(500); expect( cObj.oCoords.tr.y, 'oCoords are modified by viewportTransform tr.y', ).toBe(300); expect( cObj.oCoords.bl.x, 'oCoords are modified by viewportTransform bl.x', ).toBe(300); expect( cObj.oCoords.bl.y, 'oCoords are modified by viewportTransform bl.y', ).toBe(500); expect( cObj.oCoords.br.x, 'oCoords are modified by viewportTransform br.x', ).toBe(500); expect( cObj.oCoords.br.y, 'oCoords are modified by viewportTransform br.y', ).toBe(500); expect( cObj.oCoords.mtr.x, 'oCoords are modified by viewportTransform mtr.x', ).toBe(400); expect( cObj.oCoords.mtr.y, 'oCoords are modified by viewportTransform mtr.y', ).toBe(260); expect( cObj.aCoords.tl.x, 'aCoords do not interfere with viewportTransform', ).toBe(150); expect( cObj.aCoords.tl.y, 'aCoords do not interfere with viewportTransform', ).toBe(150); expect( cObj.aCoords.tr.x, 'aCoords do not interfere with viewportTransform', ).toBe(250); expect( cObj.aCoords.tr.y, 'aCoords do not interfere with viewportTransform', ).toBe(150); expect( cObj.aCoords.bl.x, 'aCoords do not interfere with viewportTransform', ).toBe(150); expect( cObj.aCoords.bl.y, 'aCoords do not interfere with viewportTransform', ).toBe(250); expect( cObj.aCoords.br.x, 'aCoords do not interfere with viewportTransform', ).toBe(250); expect( cObj.aCoords.br.y, 'aCoords do not interfere with viewportTransform', ).toBe(250); }); it('isOnScreen', () => { const cObj = new FabricObject({ left: 100, top: 100, width: 100, height: 100, strokeWidth: 0, }); canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; canvas.calcViewportBoundaries(); // @ts-expect-error -- expects Canvas but we are setting StaticCanvas cObj.canvas = canvas; cObj.setCoords(); expect(cObj.isOnScreen()).toBeTruthy(); cObj.top = 1000; expect(cObj.isOnScreen()).toBeTruthy(); cObj.setCoords(); expect(cObj.isOnScreen()).toBeFalsy(); canvas.setZoom(0.1); cObj.setCoords(); expect(cObj.isOnScreen()).toBeTruthy(); }); it('isOnScreen flipped vpt', () => { const cObj = new FabricObject({ left: 0, top: 0, width: 100, height: 100, strokeWidth: 0, }); canvas.viewportTransform = [-1, 0, 0, -1, 0, 0]; canvas.calcViewportBoundaries(); // @ts-expect-error -- expects Canvas but we are setting StaticCanvas cObj.canvas = canvas; cObj.setCoords(); expect(cObj.isOnScreen()).toBeTruthy(); cObj.top = 1000; expect(cObj.isOnScreen()).toBeTruthy(); cObj.setCoords(); expect(cObj.isOnScreen()).toBeFalsy(); canvas.setZoom(0.1); cObj.setCoords(); expect(cObj.isOnScreen()).toBeTruthy(); }); it('transformMatrixKey depends from properties', () => { const cObj = new FabricObject({ left: -10, top: -10, width: 30, height: 40, strokeWidth: 0, }); const key1 = cObj.transformMatrixKey(); cObj.left = 5; const key2 = cObj.transformMatrixKey(); cObj.left = -10; const key3 = cObj.transformMatrixKey(); cObj.width = 5; const key4 = cObj.transformMatrixKey(); expect(key1).not.toEqual(key2); expect(key1).toEqual(key3); expect(key4).not.toEqual(key2); expect(key4).not.toEqual(key1); expect(key4).not.toEqual(key3); }); it('transformMatrixKey depends from originX/originY', () => { const cObj = new FabricObject({ left: -10, top: -10, width: 30, height: 40, strokeWidth: 0, originX: 'left', originY: 'top', }); const key1 = cObj.transformMatrixKey(); cObj.originX = 'center'; const key2 = cObj.transformMatrixKey(); cObj.originY = 'center'; const key3 = cObj.transformMatrixKey(); expect(key1).not.toEqual(key2); expect(key1).not.toEqual(key3); expect(key2).not.toEqual(key3); }); it('isOnScreen with object that include canvas', () => { const cObj = new FabricObject({ left: -10, top: -10, width: canvas.getWidth() + 100, height: canvas.getHeight(), strokeWidth: 0, }); canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; canvas.calcViewportBoundaries(); // @ts-expect-error -- expects Canvas but we are setting StaticCanvas cObj.canvas = canvas; cObj.setCoords(); expect(cObj.isOnScreen()).toBe(true); cObj.top = -1000; cObj.left = -1000; cObj.setCoords(); expect(cObj.isOnScreen()).toBe(false); }); it('isOnScreen with object that is in top left corner of canvas', () => { const cObj = new Rect({ left: -21.56, top: 14.23, width: 50, height: 50, angle: 314.57, }); canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; canvas.calcViewportBoundaries(); // @ts-expect-error -- expects Canvas but we are setting StaticCanvas cObj.canvas = canvas; cObj.setCoords(); expect(cObj.isOnScreen()).toBeTruthy(); cObj.top -= 20; cObj.left -= 20; cObj.setCoords(); expect(cObj.isOnScreen()).toBeFalsy(); }); it('calcTransformMatrix with no group', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 0 }); expect(cObj.calcTransformMatrix).toBeTypeOf('function'); cObj.top = 0; cObj.left = 0; cObj.scaleX = 2; cObj.scaleY = 3; expect(cObj.calcTransformMatrix()).toEqual(cObj.calcOwnMatrix()); }); it('calcOwnMatrix', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 0, originX: LEFT, originY: TOP, }); expect(cObj.calcOwnMatrix).toBeTypeOf('function'); cObj.top = 0; cObj.left = 0; expect(cObj.calcOwnMatrix()).toEqual([1, 0, 0, 1, 5, 7.5]); cObj.scaleX = 2; cObj.scaleY = 3; expect(cObj.calcOwnMatrix()).toEqual([2, 0, 0, 3, 10, 22.5]); cObj.skewX = 45; expect(cObj.calcOwnMatrix()).toEqual([ 2, 0, 1.9999999999999998, 3, 25, 22.5, ]); cObj.skewY = 30; expect(cObj.calcOwnMatrix()).toEqual([ 3.1547005383792515, 1.7320508075688772, 1.9999999999999998, 3, 30.773502691896255, 31.160254037844386, ]); cObj.angle = 38; expect(cObj.calcOwnMatrix()).toEqual([ 1.4195809931249126, 3.3071022498267006, -0.2709629187635314, 3.595355211471482, 5.065683074898075, 43.50067533516962, ]); cObj.flipX = true; expect(cObj.calcOwnMatrix()).toEqual([ -3.552294904178618, -0.5773529255117364, -3.4230059331904186, 1.1327093101688495, 5.065683074898075, 43.50067533516962, ]); cObj.flipY = true; expect(cObj.calcOwnMatrix()).toEqual([ -1.4195809931249126, -3.3071022498267006, 0.2709629187635314, -3.595355211471482, 5.065683074898075, 43.50067533516962, ]); }); it('calcOwnMatrix is cached', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 0, originX: LEFT, originY: TOP, }); cObj.scaleX = 2; cObj.scaleY = 3; const expectedMatrix = cObj.calcOwnMatrix(); expect(expectedMatrix).toEqual([2, 0, 0, 3, 10, 22.5]); expect(cObj.calcOwnMatrix()).toBe(expectedMatrix); }); it('scaleToWidth', () => { const cObj = new FabricObject({ width: 560, strokeWidth: 0 }); expect(cObj.scaleToWidth).toBeTypeOf('function'); cObj.scaleToWidth(100); expect(cObj.getScaledWidth()).toBe(100); expect(cObj.get('scaleX')).toBe(100 / 560); }); it('scaleToWidth with zoom', () => { const cObj = new FabricObject({ width: 560, strokeWidth: 0 }); // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 0, 0], }; cObj.scaleToWidth(100); expect(cObj.getScaledWidth(), 'is not influenced by zoom - width').toBe( 100, ); expect(cObj.get('scaleX')).toBe(100 / 560); }); it('scaleToHeight', () => { const cObj = new FabricObject({ height: 560, strokeWidth: 0 }); expect(cObj.scaleToHeight).toBeTypeOf('function'); cObj.scaleToHeight(100); expect(cObj.getScaledHeight()).toBe(100); expect(cObj.get('scaleY')).toBe(100 / 560); }); it('scaleToHeight with zoom', () => { const cObj = new FabricObject({ height: 560, strokeWidth: 0 }); // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 0, 0], }; cObj.scaleToHeight(100); expect(cObj.getScaledHeight(), 'is not influenced by zoom - height').toBe( 100, ); expect(cObj.get('scaleY')).toBe(100 / 560); }); it('scaleToWidth on rotated object', () => { const obj = new FabricObject({ height: 100, width: 100, strokeWidth: 0 }); obj.rotate(45); obj.scaleToWidth(200); expect(Math.round(obj.getBoundingRect().width)).toBe(200); }); it('scaleToHeight on rotated object', () => { const obj = new FabricObject({ height: 100, width: 100, strokeWidth: 0 }); obj.rotate(45); obj.scaleToHeight(300); expect(Math.round(obj.getBoundingRect().height)).toBe(300); }); it('getBoundingRect with absolute coords', () => { const cObj = new FabricObject({ strokeWidth: 0, width: 10, height: 10, top: 11, left: 10, }); let boundingRect; cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect( boundingRect.left, 'gives the bounding rect left with absolute coords', ).toBe(5); expect( boundingRect.width, 'gives the bounding rect width with absolute coords', ).toBe(10); expect( boundingRect.height, 'gives the bounding rect height with absolute coords', ).toBe(10); // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 0, 0], }; cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect( boundingRect.left, 'gives the bounding rect left with absolute coords, regardless of vpt', ).toBe(5); expect( boundingRect.width, 'gives the bounding rect width with absolute coords, regardless of vpt', ).toBe(10); expect( boundingRect.height, 'gives the bounding rect height with absolute coords, regardless of vpt', ).toBe(10); }); it('getBoundingRect', () => { const cObj = new FabricObject({ strokeWidth: 0 }); let boundingRect; expect(cObj.getBoundingRect).toBeTypeOf('function'); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(boundingRect.left).toBe(0); expect(boundingRect.top).toBe(0); expect(boundingRect.width).toBe(0); expect(boundingRect.height).toBe(0); cObj.set('width', 123).setCoords(); boundingRect = cObj.getBoundingRect(); expect(boundingRect.left).toBe(-61.5); expect(boundingRect.top).toBe(0); expect(boundingRect.width).toBe(123); expect(boundingRect.height).toBe(0); cObj.set('height', 167); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(boundingRect.left).toBe(-61.5); expect(boundingRect.top).toBe(-83.5); expect(boundingRect.width).toBe(123); expect(boundingRect.height).toBe(167); cObj.scale(2); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(boundingRect.left).toBe(-123); expect(boundingRect.top).toBe(-167); expect(boundingRect.width).toBe(246); expect(boundingRect.height).toBe(334); }); it('getBoundingRect with stroke', () => { const cObj = new FabricObject(); let boundingRect; expect(cObj.getBoundingRect).toBeTypeOf('function'); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(Number(boundingRect.left.toFixed(2))).toBe(-0.5); expect(Number(boundingRect.top.toFixed(2))).toBe(-0.5); expect(Number(boundingRect.width.toFixed(2))).toBe(1); expect(Number(boundingRect.height.toFixed(2))).toBe(1); cObj.set('width', 123); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(Number(boundingRect.left.toFixed(2))).toBe(-62); expect(Number(boundingRect.top.toFixed(2))).toBe(-0.5); expect(Number(boundingRect.width.toFixed(2))).toBe(124); expect(Number(boundingRect.height.toFixed(2))).toBe(1); cObj.set('height', 167); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(Number(boundingRect.left.toFixed(2))).toBe(-62); expect(Number(boundingRect.top.toFixed(2))).toBe(-84); expect(Number(boundingRect.width.toFixed(2))).toBe(124); expect(Number(boundingRect.height.toFixed(2))).toBe(168); cObj.scale(2); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); expect(Number(boundingRect.left.toFixed(2))).toBe(-124); expect(Number(boundingRect.top.toFixed(2))).toBe(-168); expect(Number(boundingRect.width.toFixed(2))).toBe(248); expect(Number(boundingRect.height.toFixed(2))).toBe(336); }); it('getScaledWidth', () => { const cObj = new FabricObject(); expect(cObj.getScaledWidth).toBeTypeOf('function'); expect(cObj.getScaledWidth()).toBe(0 + cObj.strokeWidth); cObj.set('width', 123); expect(cObj.getScaledWidth()).toBe(123 + cObj.strokeWidth); cObj.set('scaleX', 2); expect(cObj.getScaledWidth()).toBe(246 + cObj.strokeWidth * 2); }); it('getScaledHeight', () => { const cObj = new FabricObject({ strokeWidth: 0 }); expect(cObj.getScaledHeight()).toBe(0); cObj.set('height', 123); expect(cObj.getScaledHeight()).toBe(123); cObj.set('scaleY', 2); expect(cObj.getScaledHeight()).toBe(246); }); it('scale', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 0 }); expect(cObj.scale).toBeTypeOf('function'); }); it('_constrainScale', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 0 }); expect(cObj._constrainScale).toBeTypeOf('function'); cObj.set('scaleX', 0); expect(cObj.scaleX).toBe(0.0001); cObj.set('scaleY', 0); expect(cObj.scaleY).toBe(0.0001); cObj.minScaleLimit = 3; cObj.set('scaleY', 0); expect(cObj.scaleY).toBe(3); }); it('getCoords return coordinate of object in scene coordinate.', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 2, top: 38.5, left: 46, }); const coords = cObj.getCoords(); expect(coords[0]).toEqual(new Point(40, 30)); expect(coords[1]).toEqual(new Point(52, 30)); expect(coords[2]).toEqual(new Point(52, 47)); expect(coords[3]).toEqual(new Point(40, 47)); cObj.left += 5; const newCoords = cObj.getCoords(); expect(newCoords[0]).toEqual(new Point(40, 30)); expect(newCoords[1]).toEqual(new Point(52, 30)); expect(newCoords[2]).toEqual(new Point(52, 47)); expect(newCoords[3]).toEqual(new Point(40, 47)); cObj.setCoords(); const updatedCoords = cObj.getCoords(); expect(updatedCoords[0]).toEqual(new Point(45, 30)); expect(updatedCoords[1]).toEqual(new Point(57, 30)); expect(updatedCoords[2]).toEqual(new Point(57, 47)); expect(updatedCoords[3]).toEqual(new Point(45, 47)); }); it('getCoords return coordinate of object in scene coordinates and is not affected by viewport', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 2, top: 38.5, left: 46, }); // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 35, 35], }; const coords = cObj.getCoords(); expect(coords[0]).toEqual(new Point(40, 30)); expect(coords[1]).toEqual(new Point(52, 30)); expect(coords[2]).toEqual(new Point(52, 47)); expect(coords[3]).toEqual(new Point(40, 47)); }); it('getCoords with angle', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40, angle: 20, }); // the viewport is non influent. // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 35, 25], }; const coords = cObj.getCoords(); expect(coords).toMatchSnapshot(); }); it('getCoords with skewX', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 49, skewX: 45, }); // the viewport is non influent. // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 35, 25], }; const coords = cObj.getCoords(); expect(coords).toMatchInlineSnapshot(` [ Point { "x": 34.5, "y": 21.5, }, Point { "x": 63.5, "y": 21.5, }, Point { "x": 63.5, "y": 38.5, }, Point { "x": 34.5, "y": 38.5, }, ] `); }); it('getCoords with skewY', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40, skewY: 45, }); // the viewport is non influent. // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 35, 25], }; const coords = cObj.getCoords(); expect(coords).toMatchInlineSnapshot(` [ Point { "x": 34, "y": 15.5, }, Point { "x": 46, "y": 15.5, }, Point { "x": 46, "y": 44.5, }, Point { "x": 34, "y": 44.5, }, ] `); }); it('getCoords with skewY skewX angle', () => { const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 2, top: 30, left: 40, skewY: 45, skewX: 30, angle: 90, }); // the viewport is non influent. // @ts-expect-error -- partial canvas cObj.canvas = { viewportTransform: [2, 0, 0, 2, 35, 25], }; const coords = cObj.getCoords(); expect(coords).toMatchInlineSnapshot(` [ Point { "x": 54.5, "y": 15.628421096750428, }, Point { "x": 54.5, "y": 44.37157890324957, }, Point { "x": 25.5, "y": 44.37157890324957, }, Point { "x": 25.5, "y": 15.628421096750428, }, ] `); }); it('isPartiallyOnScreen', () => { const cObj = new FabricObject({ left: 100, top: 100, width: 100, height: 100, strokeWidth: 0, }); canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; canvas.calcViewportBoundaries(); // @ts-expect-error -- expects Canvas but we are setting StaticCanvas cObj.canvas = canvas; cObj.left = -10; cObj.top = -10; cObj.setCoords(); expect(cObj.isPartiallyOnScreen(), 'object is partially onScreen').toBe( true, ); cObj.left = -60; cObj.top = -60; cObj.setCoords(); expect( cObj.isPartiallyOnScreen(), 'object is completely offScreen and not partial', ).toBe(false); cObj.left = 95; cObj.top = 95; cObj.setCoords(); expect( cObj.isPartiallyOnScreen(), 'object is completely on screen and not partial', ).toBe(false); canvas.setZoom(2); expect( cObj.isPartiallyOnScreen(), 'after zooming object is partially onScreen and offScreen', ).toBe(true); }); it('isPartiallyOnScreen with object inside and outside of canvas', () => { const cObj = new FabricObject({ left: 55, top: 55, width: 100, height: 100, strokeWidth: 0, }); // @ts-expect-error -- expects Canvas but we are setting StaticCanvas cObj.canvas = new StaticCanvas(undefined, { width: 120, height: 120, enableRetinaScaling: false, }); cObj.canvas!.calcViewportBoundaries(); expect(cObj.isPartiallyOnScreen(), 'object is completely onScreen').toBe( false, ); cObj.left = 60; cObj.top = 60; cObj.scaleX = 2; cObj.scaleY = 2; cObj.setCoords(); expect( cObj.isPartiallyOnScreen(), 'object has all corners outside screen but contains canvas', ).toBe(true); }); });