@technobuddha/library
Version:
A large library of useful functions
239 lines (202 loc) • 8.16 kB
text/typescript
import { type Polygon, type Rect } from './@types/geometry.ts';
import { isInPolygon } from './is-in-polygon.ts';
// Helper polygons
const square: Polygon = [
{ x: 0, y: 0 },
{ x: 10, y: 0 },
{ x: 10, y: 10 },
{ x: 0, y: 10 },
];
const triangle: Polygon = [
{ x: 0, y: 0 },
{ x: 5, y: 10 },
{ x: 10, y: 0 },
];
const concave: Polygon = [
{ x: 0, y: 0 },
{ x: 5, y: 5 },
{ x: 10, y: 0 },
{ x: 10, y: 10 },
{ x: 0, y: 10 },
];
describe('isInPolygon', () => {
test('returns true for a point clearly inside a square', () => {
expect(isInPolygon({ x: 5, y: 5 }, square)).toBeTrue();
});
test('returns false for a point clearly outside a square', () => {
expect(isInPolygon({ x: 15, y: 5 }, square)).toBeFalse();
});
test('returns true for a point on the edge of a square', () => {
expect(isInPolygon({ x: 0, y: 5 }, square)).toBeTrue();
expect(isInPolygon({ x: 10, y: 5 }, square)).toBeTrue();
expect(isInPolygon({ x: 5, y: 0 }, square)).toBeTrue();
expect(isInPolygon({ x: 5, y: 10 }, square)).toBeTrue();
});
test('returns true for a point inside a triangle', () => {
expect(isInPolygon({ x: 5, y: 5 }, triangle)).toBeTrue();
});
test('returns false for a point outside a triangle', () => {
expect(isInPolygon({ x: 5, y: -1 }, triangle)).toBeFalse();
});
test('returns true for a point on the edge of a triangle', () => {
expect(isInPolygon({ x: 0, y: 0 }, triangle)).toBeTrue();
expect(isInPolygon({ x: 5, y: 10 }, triangle)).toBeTrue();
expect(isInPolygon({ x: 10, y: 0 }, triangle)).toBeTrue();
// Test points on the edges (not just vertices)
expect(isInPolygon({ x: 2.5, y: 5 }, triangle)).toBeTrue(); // left edge
expect(isInPolygon({ x: 7.5, y: 5 }, triangle)).toBeTrue(); // right edge
expect(isInPolygon({ x: 5, y: 0 }, triangle)).toBeTrue(); // bottom edge
});
test('returns true for a point inside a concave polygon', () => {
expect(isInPolygon({ x: 5, y: 8 }, concave)).toBeTrue();
});
test('returns false for a point in the "dent" of a concave polygon', () => {
expect(isInPolygon({ x: 5, y: 3 }, concave)).toBeFalse();
});
test('returns false for a point outside a concave polygon', () => {
expect(isInPolygon({ x: 11, y: 5 }, concave)).toBeFalse();
});
test('returns true for a point on a vertex', () => {
expect(isInPolygon({ x: 0, y: 0 }, square)).toBeTrue();
expect(isInPolygon({ x: 10, y: 0 }, square)).toBeTrue();
expect(isInPolygon({ x: 10, y: 10 }, square)).toBeTrue();
expect(isInPolygon({ x: 0, y: 10 }, square)).toBeTrue();
});
test('returns true for points on concave polygon edges', () => {
// Test edge from (0,0) to (5,5)
expect(isInPolygon({ x: 2.5, y: 2.5 }, concave)).toBeTrue();
// Test edge from (5,5) to (10,0)
expect(isInPolygon({ x: 7.5, y: 2.5 }, concave)).toBeTrue();
// Test vertical edge from (10,0) to (10,10)
expect(isInPolygon({ x: 10, y: 5 }, concave)).toBeTrue();
// Test horizontal edge from (10,10) to (0,10)
expect(isInPolygon({ x: 5, y: 10 }, concave)).toBeTrue();
// Test vertical edge from (0,10) to (0,0)
expect(isInPolygon({ x: 0, y: 5 }, concave)).toBeTrue();
});
test('handles floating point precision on edges', () => {
// Points very close to edges should still be considered on the edge
expect(isInPolygon({ x: 5.000000000001, y: 0 }, square)).toBeTrue();
expect(isInPolygon({ x: 0, y: 4.999999999999 }, square)).toBeTrue();
expect(isInPolygon({ x: 9.999999999999, y: 5 }, square)).toBeTrue();
});
test('handles horizontal and vertical edges correctly', () => {
const rect: Polygon = [
{ x: 0, y: 0 },
{ x: 10, y: 0 },
{ x: 10, y: 5 },
{ x: 0, y: 5 },
];
// Horizontal edges
expect(isInPolygon({ x: 5, y: 0 }, rect)).toBeTrue(); // bottom edge
expect(isInPolygon({ x: 5, y: 5 }, rect)).toBeTrue(); // top edge
expect(isInPolygon({ x: 2.5, y: 0 }, rect)).toBeTrue(); // bottom edge midpoint
expect(isInPolygon({ x: 7.5, y: 5 }, rect)).toBeTrue(); // top edge midpoint
// Vertical edges
expect(isInPolygon({ x: 0, y: 2.5 }, rect)).toBeTrue(); // left edge
expect(isInPolygon({ x: 10, y: 2.5 }, rect)).toBeTrue(); // right edge
});
test('returns false for a degenerate polygon (line)', () => {
const line: Polygon = [
{ x: 0, y: 0 },
{ x: 10, y: 0 },
];
expect(isInPolygon({ x: 5, y: 0 }, line)).toBeFalse();
});
test('returns false for a degenerate polygon (single point)', () => {
const pointPoly: Polygon = [{ x: 1, y: 1 }];
expect(isInPolygon({ x: 1, y: 1 }, pointPoly)).toBeFalse();
});
test('returns false for an empty polygon', () => {
expect(isInPolygon({ x: 0, y: 0 }, [])).toBeFalse();
});
// Test with a rectangle
test('rectangle fully inside square', () => {
const rect: Rect = { x: 2, y: 2, width: 5, height: 5 };
expect(isInPolygon(rect, square)).toBeTrue();
});
test('rectangle partially outside square', () => {
const rect: Rect = { x: 8, y: 8, width: 5, height: 5 };
expect(isInPolygon(rect, square)).toBeFalse();
});
test('rectangle exactly matches square', () => {
const rect: Rect = { x: 0, y: 0, width: 10, height: 10 };
expect(isInPolygon(rect, square)).toBeTrue(); // Changed: boundary points are now considered inside
});
test('rectangle fully outside square', () => {
const rect: Rect = { x: 20, y: 20, width: 2, height: 2 };
expect(isInPolygon(rect, square)).toBeFalse();
});
test('rectangle inside triangle', () => {
const rect: Rect = { x: 3, y: 2, width: 2, height: 2 };
expect(isInPolygon(rect, triangle)).toBeTrue();
});
test('rectangle partially outside triangle', () => {
const rect: Rect = { x: 4, y: 8, width: 3, height: 3 };
expect(isInPolygon(rect, triangle)).toBeFalse();
});
test('rectangle inside concave polygon', () => {
const rect: Rect = { x: 4, y: 6, width: 2, height: 2 };
expect(isInPolygon(rect, concave)).toBeTrue(); // Changed: this rectangle is actually inside
});
test('rectangle in concavity (partially outside)', () => {
const rect: Rect = { x: 4, y: 2, width: 2, height: 2 };
expect(isInPolygon(rect, concave)).toBeFalse();
});
test('rectangle with zero width or height', () => {
const rect1: Rect = { x: 2, y: 2, width: 0, height: 5 };
const rect2: Rect = { x: 2, y: 2, width: 5, height: 0 };
expect(isInPolygon(rect1, square)).toBeTrue(); // line inside polygon
expect(isInPolygon(rect2, square)).toBeTrue(); // Changed: removed conflicting comment
});
test('empty polygon', () => {
const rect: Rect = { x: 1, y: 1, width: 2, height: 2 };
expect(isInPolygon(rect, [])).toBeFalse();
});
test('degenerate polygon (line)', () => {
const line: Polygon = [
{ x: 0, y: 0 },
{ x: 10, y: 0 },
];
const rect: Rect = { x: 1, y: 0, width: 2, height: 2 };
expect(isInPolygon(rect, line)).toBeFalse();
});
test('polygon completely inside another polygon', () => {
const innerTriangle: Polygon = [
{ x: 0.2, y: 0.2 },
{ x: 0.8, y: 0.2 },
{ x: 0.5, y: 0.8 },
];
const outerSquare: Polygon = [
{ x: 0, y: 0 },
{ x: 1, y: 0 },
{ x: 1, y: 1 },
{ x: 0, y: 1 },
];
expect(isInPolygon(innerTriangle, outerSquare)).toBeTrue();
});
test('polygon partially outside another polygon', () => {
const partialTriangle: Polygon = [
{ x: 5, y: 5 },
{ x: 15, y: 5 },
{ x: 10, y: 15 },
];
expect(isInPolygon(partialTriangle, square)).toBeFalse();
});
test('polygon completely outside another polygon', () => {
const outsideTriangle: Polygon = [
{ x: 20, y: 20 },
{ x: 30, y: 20 },
{ x: 25, y: 30 },
];
expect(isInPolygon(outsideTriangle, square)).toBeFalse();
});
test('polygon edge intersects with another polygon', () => {
const intersectingTriangle: Polygon = [
{ x: 5, y: -5 },
{ x: 15, y: 5 },
{ x: 5, y: 15 },
];
expect(isInPolygon(intersectingTriangle, square)).toBeFalse();
});
});