add tests (!)
This commit is contained in:
parent
8508607b6d
commit
632bd43dc1
|
|
@ -19,6 +19,7 @@
|
|||
"@types/leaflet": "^1.9.14",
|
||||
"@types/node": "^22.10.1",
|
||||
"@webgpu/types": "^0.1.51",
|
||||
"bun-types": "^1.1.38",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,224 @@
|
|||
import { expect, test, describe } from 'bun:test';
|
||||
import { RotatedDOMRect } from '../common/rotated-dom-rect-2';
|
||||
import { Vector } from '../common/Vector';
|
||||
|
||||
// Helper for comparing points with floating point values
|
||||
const expectPointClose = (actual: { x: number; y: number }, expected: { x: number; y: number }) => {
|
||||
expect(actual.x).toBeCloseTo(expected.x);
|
||||
expect(actual.y).toBeCloseTo(expected.y);
|
||||
};
|
||||
|
||||
describe('RotatedDOMRect', () => {
|
||||
describe('constructor', () => {
|
||||
test('initializes with default values', () => {
|
||||
const rect = new RotatedDOMRect();
|
||||
expect(rect.x).toBe(0);
|
||||
expect(rect.y).toBe(0);
|
||||
expect(rect.width).toBe(0);
|
||||
expect(rect.height).toBe(0);
|
||||
expect(rect.rotation).toBe(0);
|
||||
});
|
||||
|
||||
test('initializes with custom values', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 10,
|
||||
y: 20,
|
||||
width: 100,
|
||||
height: 50,
|
||||
rotation: Math.PI / 4,
|
||||
});
|
||||
expect(rect.x).toBe(10);
|
||||
expect(rect.y).toBe(20);
|
||||
expect(rect.width).toBe(100);
|
||||
expect(rect.height).toBe(50);
|
||||
expect(rect.rotation).toBe(Math.PI / 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('corner calculations', () => {
|
||||
test('calculates corners for unrotated rectangle', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 50,
|
||||
rotation: 0,
|
||||
});
|
||||
|
||||
expectPointClose(rect.topLeft, { x: -50, y: -25 });
|
||||
expectPointClose(rect.topRight, { x: 50, y: -25 });
|
||||
expectPointClose(rect.bottomLeft, { x: -50, y: 25 });
|
||||
expectPointClose(rect.bottomRight, { x: 50, y: 25 });
|
||||
});
|
||||
|
||||
test('calculates corners for 90-degree rotated rectangle', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
rotation: Math.PI / 2,
|
||||
});
|
||||
|
||||
expectPointClose(rect.topLeft, { x: 50, y: -50 });
|
||||
expectPointClose(rect.topRight, { x: 50, y: 50 });
|
||||
expectPointClose(rect.bottomLeft, { x: -50, y: -50 });
|
||||
expectPointClose(rect.bottomRight, { x: -50, y: 50 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('bounds', () => {
|
||||
test('calculates bounds for unrotated rectangle', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 50,
|
||||
rotation: 0,
|
||||
});
|
||||
|
||||
expect(rect.getBounds()).toEqual({
|
||||
x: -50,
|
||||
y: -25,
|
||||
width: 100,
|
||||
height: 50,
|
||||
});
|
||||
});
|
||||
|
||||
test('calculates bounds for 45-degree rotated rectangle', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 50,
|
||||
rotation: Math.PI / 4,
|
||||
});
|
||||
|
||||
const bounds = rect.getBounds();
|
||||
const cos45 = Math.cos(Math.PI / 4);
|
||||
const sin45 = Math.sin(Math.PI / 4);
|
||||
const expectedWidth = Math.abs(100 * cos45) + Math.abs(50 * sin45);
|
||||
const expectedHeight = Math.abs(100 * sin45) + Math.abs(50 * cos45);
|
||||
|
||||
expect(bounds.width).toBeCloseTo(expectedWidth);
|
||||
expect(bounds.height).toBeCloseTo(expectedHeight);
|
||||
expect(bounds.x).toBeCloseTo(-expectedWidth / 2);
|
||||
expect(bounds.y).toBeCloseTo(-expectedHeight / 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setters', () => {
|
||||
test('updates corners when center is modified', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
width: 100,
|
||||
height: 50,
|
||||
});
|
||||
|
||||
rect.center = { x: 100, y: 100 };
|
||||
expectPointClose(rect.topLeft, { x: 50, y: 75 });
|
||||
expectPointClose(rect.bottomRight, { x: 150, y: 125 });
|
||||
});
|
||||
|
||||
test('updates dimensions and rotation when setting topRight', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 50,
|
||||
});
|
||||
|
||||
expect(rect.width).toBe(100);
|
||||
expect(rect.height).toBe(50);
|
||||
expect(rect.rotation).toBe(0);
|
||||
expectPointClose(rect.bottomLeft, { x: -50, y: 25 });
|
||||
expectPointClose(rect.bottomRight, { x: 50, y: 25 });
|
||||
expectPointClose(rect.center, { x: 0, y: 0 });
|
||||
expectPointClose(rect.topLeft, { x: -50, y: -25 });
|
||||
|
||||
expectPointClose(rect.topRight, { x: 50, y: -25 });
|
||||
rect.topRight = { x: 100, y: -50 };
|
||||
expect(rect.width).toBe(150);
|
||||
});
|
||||
});
|
||||
|
||||
describe('corner setters', () => {
|
||||
test('updates dimensions when setting bottomLeft', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 50,
|
||||
});
|
||||
|
||||
// Store original topRight position as it should remain fixed
|
||||
const originalTopRight = { ...rect.topRight }; // (50, -25)
|
||||
|
||||
// Set new bottomLeft position
|
||||
rect.bottomLeft = { x: -100, y: 100 };
|
||||
|
||||
// Verify topRight hasn't moved
|
||||
expectPointClose(rect.topRight, originalTopRight);
|
||||
|
||||
// Verify bottomLeft is at new position
|
||||
expectPointClose(rect.bottomLeft, { x: -100, y: 100 });
|
||||
|
||||
// Verify center is halfway between bottomLeft and topRight
|
||||
expectPointClose(rect.center, {
|
||||
x: (-100 + 50) / 2, // -25
|
||||
y: (100 + -25) / 2, // 37.5
|
||||
});
|
||||
|
||||
// Verify new dimensions
|
||||
expect(rect.width).toBeCloseTo(150); // abs(-100 - 50) = 150
|
||||
expect(rect.height).toBeCloseTo(125); // abs(100 - -25) = 125
|
||||
});
|
||||
|
||||
test('maintains rectangle properties when setting corners', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 50,
|
||||
rotation: Math.PI / 6,
|
||||
});
|
||||
|
||||
rect.bottomRight = { x: 75, y: 75 };
|
||||
|
||||
// After setting bottomRight, topLeft and bottomRight should be equidistant from center
|
||||
const distanceToTopLeft = Vector.distance(rect.center, rect.topLeft);
|
||||
const distanceToBottomRight = Vector.distance(rect.center, rect.bottomRight);
|
||||
expect(distanceToTopLeft).toBeCloseTo(distanceToBottomRight);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
test('handles zero dimensions', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 0,
|
||||
height: 0,
|
||||
rotation: Math.PI / 4,
|
||||
});
|
||||
|
||||
expect(rect.getBounds()).toEqual({
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('handles 360-degree rotation', () => {
|
||||
const rect = new RotatedDOMRect({
|
||||
width: 100,
|
||||
height: 50,
|
||||
rotation: Math.PI * 2,
|
||||
});
|
||||
|
||||
// Should be equivalent to rotation: 0
|
||||
expectPointClose(rect.topLeft, { x: -50, y: -25 });
|
||||
expectPointClose(rect.topRight, { x: 50, y: -25 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
import { expect, test, describe } from 'bun:test';
|
||||
import { Vector } from '../common/Vector';
|
||||
|
||||
describe('Vector', () => {
|
||||
describe('basic operations', () => {
|
||||
test('zero() returns zero vector', () => {
|
||||
expect(Vector.zero()).toEqual({ x: 0, y: 0 });
|
||||
});
|
||||
|
||||
test('add() combines two vectors', () => {
|
||||
const a = { x: 1, y: 2 };
|
||||
const b = { x: 3, y: 4 };
|
||||
expect(Vector.add(a, b)).toEqual({ x: 4, y: 6 });
|
||||
});
|
||||
|
||||
test('sub() subtracts vectors', () => {
|
||||
const a = { x: 3, y: 4 };
|
||||
const b = { x: 1, y: 2 };
|
||||
expect(Vector.sub(a, b)).toEqual({ x: 2, y: 2 });
|
||||
});
|
||||
|
||||
test('mult() multiplies vectors component-wise', () => {
|
||||
const a = { x: 2, y: 3 };
|
||||
const b = { x: 4, y: 5 };
|
||||
expect(Vector.mult(a, b)).toEqual({ x: 8, y: 15 });
|
||||
});
|
||||
|
||||
test('scale() multiplies vector by scalar', () => {
|
||||
const v = { x: 2, y: 3 };
|
||||
expect(Vector.scale(v, 2)).toEqual({ x: 4, y: 6 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('vector properties', () => {
|
||||
test('mag() calculates magnitude', () => {
|
||||
const v = { x: 3, y: 4 };
|
||||
expect(Vector.mag(v)).toBe(5);
|
||||
});
|
||||
|
||||
test('normalized() returns unit vector', () => {
|
||||
const v = { x: 3, y: 4 };
|
||||
const normalized = Vector.normalized(v);
|
||||
expect(normalized.x).toBeCloseTo(0.6);
|
||||
expect(normalized.y).toBeCloseTo(0.8);
|
||||
});
|
||||
|
||||
test('normalized() handles zero vector', () => {
|
||||
const v = { x: 0, y: 0 };
|
||||
expect(Vector.normalized(v)).toEqual({ x: 0, y: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('distance calculations', () => {
|
||||
test('distance() calculates Euclidean distance', () => {
|
||||
const a = { x: 0, y: 0 };
|
||||
const b = { x: 3, y: 4 };
|
||||
expect(Vector.distance(a, b)).toBe(5);
|
||||
});
|
||||
|
||||
test('distanceSquared() calculates squared distance', () => {
|
||||
const a = { x: 0, y: 0 };
|
||||
const b = { x: 3, y: 4 };
|
||||
expect(Vector.distanceSquared(a, b)).toBe(25);
|
||||
});
|
||||
});
|
||||
|
||||
describe('interpolation and rotation', () => {
|
||||
test('lerp() interpolates between points', () => {
|
||||
const a = { x: 0, y: 0 };
|
||||
const b = { x: 10, y: 10 };
|
||||
expect(Vector.lerp(a, b, 0.5)).toEqual({ x: 5, y: 5 });
|
||||
});
|
||||
|
||||
test('rotate() rotates vector by angle', () => {
|
||||
const v = { x: 1, y: 0 };
|
||||
const rotated = Vector.rotate(v, Math.PI / 2);
|
||||
expect(rotated.x).toBeCloseTo(0);
|
||||
expect(rotated.y).toBeCloseTo(1);
|
||||
});
|
||||
|
||||
test('rotateAround() rotates point around pivot', () => {
|
||||
const point = { x: 2, y: 0 };
|
||||
const pivot = { x: 0, y: 0 };
|
||||
const rotated = Vector.rotateAround(point, pivot, Math.PI / 2);
|
||||
expect(rotated.x).toBeCloseTo(0);
|
||||
expect(rotated.y).toBeCloseTo(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('angle calculations', () => {
|
||||
test('angle() calculates angle from x-axis', () => {
|
||||
const v = { x: 1, y: 1 };
|
||||
expect(Vector.angle(v)).toBeCloseTo(Math.PI / 4);
|
||||
});
|
||||
|
||||
test('angleTo() calculates angle between vectors', () => {
|
||||
const a = { x: 1, y: 0 };
|
||||
const b = { x: 0, y: 1 };
|
||||
expect(Vector.angleTo(b, a)).toBeCloseTo(Math.PI / 2);
|
||||
});
|
||||
|
||||
test('angleFromOrigin() calculates angle relative to origin', () => {
|
||||
const point = { x: 1, y: 1 };
|
||||
const origin = { x: 0, y: 0 };
|
||||
expect(Vector.angleFromOrigin(point, origin)).toBeCloseTo(Math.PI / 4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
"useDefineForClassFields": true,
|
||||
"skipLibCheck": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||
"types": ["@webgpu/types", "@types/node"]
|
||||
"types": ["@webgpu/types", "@types/node", "bun-types"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "demo/**/*.ts", "vite.config.ts"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue