Files
Aislo/B07_wf4_DesignDetail/openwebcad/src/helpers/contain-rect.test.ts
T
2026-07-19 17:01:48 +09:00

169 lines
6.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { containRectangle } from './contain-rect.ts';
describe('containRectangle', () => {
it('scales down a larger rectangle to fit into a smaller wrapper', () => {
const result = containRectangle(
0,
0,
200,
200, // contained: a 200x200 square
0,
0,
100,
100, // wrapper: a 100x100 square
);
// Expected: scale down by factor of 0.5 to fit, centered at (25,25) to (125,125) if it was not restricted,
// but since wrapper is only 100x100, final should be (0,0) + 100x100, scaled rect is 100x100.
expect(result).toEqual({ minX: 0, minY: 0, maxX: 100, maxY: 100 });
});
it('scales up a smaller rectangle to fit inside a larger wrapper without exceeding boundaries', () => {
const result = containRectangle(
0,
0,
50,
50, // contained: 50x50
0,
0,
200,
200, // wrapper: 200x200
);
// Expected: scale up by factor of 4 to fill as much space as possible while containing
// But scaling up a 50x50 by factor 4 gives 200x200 exactly, centered at (0,0).
expect(result).toEqual({ minX: 0, minY: 0, maxX: 200, maxY: 200 });
});
it('maintains aspect ratio when wrapper is rectangular and contained is square', () => {
const result = containRectangle(
0,
0,
50,
50, // contained: 50x50 square
0,
0,
200,
100, // wrapper: 200x100
);
// Scale to fit inside 200x100. The width scale = 200/50=4, height scale=100/50=2.
// Min scale = 2, so final size = 100x100.
// Center horizontally: (200 - 100)/2 = 50 offset, vertically: (100 - 100)/2=0 offset.
// Result = (50,0) to (150,100)
expect(result.minX).toBeCloseTo(50);
expect(result.minY).toBeCloseTo(0);
expect(result.maxX).toBeCloseTo(150);
expect(result.maxY).toBeCloseTo(100);
});
it('maintains aspect ratio when wrapper is rectangular and contained is also rectangular', () => {
const result = containRectangle(
0,
0,
200,
50, // contained: 200x50
0,
0,
300,
100, // wrapper: 300x100
);
// Contained AR = 200/50 = 4:1
// Wrapper AR = 300/100 = 3:1
// To fit inside 300x100:
// Scale factors: width scale = 300/200=1.5, height scale=100/50=2.
// min scale = 1.5
// Final size: 200*1.5=300 width, 50*1.5=75 height
// Center vertically: (100 - 75)/2=12.5 offset, horizontally just fits width fully
expect(result).toEqual({ minX: 0, minY: 12.5, maxX: 300, maxY: 87.5 });
});
it('handles zero-width/height contained rectangle gracefully', () => {
// Contained rectangle is essentially a line or point
const result = containRectangle(
10,
10,
10,
10, // contained has 0 width/height
0,
0,
200,
200, // wrapper
);
// Center as a single point at (100,100)
expect(result).toEqual({ minX: 100, minY: 100, maxX: 100, maxY: 100 });
});
it('does not scale if contained rectangle already fits', () => {
const result = containRectangle(
0,
0,
100,
100, // contained fits easily
0,
0,
300,
300, // wrapper
);
// Scale factor: width scale = 300/100=3, height scale=300/100=3, min=3, so max scale is 3.
// But we want to "contain" fully, ideally it should scale up to take as much space as possible without exceeding,
// So final size is 300x300, centered at (0,0).
expect(result).toEqual({ minX: 0, minY: 0, maxX: 300, maxY: 300 });
});
it('correctly centers when wrapper and contained have different origins', () => {
const result = containRectangle(
5,
5,
15,
35, // contained: 10 wide x 30 tall
10,
20,
110,
220, // wrapper: 100x200
);
// Wrapper size: 100x200
// Contained size: 10x30
// Scale factors: width scale = 100/10=10, height scale=200/30 ≈ 6.666...
// min scale = 6.666...
// Final size: width = 10 * 6.666... ≈ 66.666..., height = 30 * 6.666... ≈ 200
// After scaling, top-left corner should be placed so it centers:
// Horizontal center: (100 - 66.666...)/2 = 16.666... offset from wrapperMinX=10 => minX≈26.666...
// Vertical center: fits height exactly, so minY=20, maxY=20+200=220
expect(result.minX).toBeCloseTo(26.6667);
expect(result.minY).toBeCloseTo(20);
expect(result.maxX).toBeCloseTo(93.3333);
expect(result.maxY).toBeCloseTo(220);
});
it('handles negative coordinates in wrapper and contained rectangles', () => {
const result = containRectangle(
-50,
-25,
50,
25, // contained: 100 wide x 50 tall
-100,
-50,
100,
50, // wrapper: 200 wide x 100 tall
);
// Scale factors: width scale = 200/100=2, height scale=100/50=2
// min scale = 2, final size: 200x100 exactly.
// Centering: wrapper ranges from -100 to 100 (x) and -50 to 50 (y)
// After scaling contained to 200x100, it fits exactly. minX = -100, maxX=100, minY=-50, maxY=50
expect(result).toEqual({ minX: -100, minY: -50, maxX: 100, maxY: 50 });
});
it('handles negative coordinates in contained rectangles', () => {
const result = containRectangle(
-50,
-25,
50,
25, // contained: 100 wide x 50 tall
0,
0,
100,
100, // wrapper: 100 wide x 100 tall
);
expect(result).toEqual({ minX: 0, minY: 25, maxX: 100, maxY: 75 });
});
});