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
text/typescript
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);
});
});