Spaces:
Sleeping
Sleeping
import { compressPath, distance, manhattanDistance, normalize, orientationDegrees, pathOverlaps, pathPosition, pointsEqual, vector, vectorLength } from './geometry'; | |
import { Path, Vector } from './types'; | |
describe('distance', () => { | |
test('should return the correct distance for two points', () => { | |
const p0 = { x: 0, y: 0 }; | |
const p1 = { x: 3, y: 4 }; | |
const expectedDistance = 5; | |
const actualDistance = distance(p0, p1); | |
expect(actualDistance).toBe(expectedDistance); | |
}); | |
test('should return 0 for the same point', () => { | |
const p0 = { x: 1, y: 2 }; | |
const expectedDistance = 0; | |
const actualDistance = distance(p0, p0); | |
expect(actualDistance).toBe(expectedDistance); | |
}); | |
test('should return the correct distance for negative points', () => { | |
const p0 = { x: -2, y: -3 }; | |
const p1 = { x: 1, y: 2 }; | |
const expectedDistance = 5.83; | |
const actualDistance = distance(p0, p1); | |
expect(actualDistance).toBeCloseTo(expectedDistance); | |
}); | |
}); | |
describe('pointsEqual', () => { | |
test('should return true for identical points', () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 1, y: 2 }; | |
expect(pointsEqual(p0, p1)).toBe(true); | |
}); | |
test('should return false for non-idential points', () => { | |
const p0 = { x: 3, y: 2 }; | |
const p1 = { x: 5, y: 3 }; | |
expect(pointsEqual(p0, p1)).toBe(false); | |
}); | |
test('should return false for different x coordinates', () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 2, y: 2 }; | |
expect(pointsEqual(p0, p1)).toBe(false); | |
}); | |
test('should return false for different y coordinates', () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 1, y: 3 }; | |
expect(pointsEqual(p0, p1)).toBe(false); | |
}); | |
}); | |
describe("manhattanDistance", () => { | |
test("should return correct distance for points on the same axis", () => { | |
const p0 = { x: 1, y: 0 }; | |
const p1 = { x: 1, y: 2 }; | |
expect(manhattanDistance(p0, p1)).toBe(2); | |
}); | |
test("should return correct distance for points on different axes", () => { | |
const p0 = { x: 1, y: 0 }; | |
const p1 = { x: 3, y: 2 }; | |
expect(manhattanDistance(p0, p1)).toBe(4); | |
}); | |
test("should return correct distance for negative points", () => { | |
const p0 = { x: -2, y: 0 }; | |
const p1 = { x: 1, y: -2 }; | |
expect(manhattanDistance(p0, p1)).toBe(5); | |
}); | |
test("should return correct distance for identical points", () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 1, y: 2 }; | |
expect(manhattanDistance(p0, p1)).toBe(0); | |
}); | |
}); | |
describe('pathOverlaps', () => { | |
test('should throw an error if the path does not have 2 entries', () => { | |
const path: Path = [ | |
[0, 0, 0, 1, 0] | |
]; | |
const time = 0; | |
expect(() => pathOverlaps(path, time)).toThrowError('Invalid path: [[0,0,0,1,0]]'); | |
}); | |
test('should return true if the time is within the path', () => { | |
const path: Path = [ | |
[0, 0, 0, 1, 1], | |
[0, 2, 0, 1, 2] | |
]; | |
const time = 1.5; | |
expect(pathOverlaps(path, time)).toBe(true); | |
}); | |
test('should return false if the time is before the start of the path', () => { | |
const path: Path = [ | |
[0, 0, 0, 1, 1], | |
[0, 2, 0, 1, 2] | |
]; | |
const time = 0.5; | |
expect(pathOverlaps(path, time)).toBe(false); | |
}); | |
test('should return false if the time is after the end of the path', () => { | |
const path: Path = [ | |
[0, 0, 0, 1, 1], | |
[0, 2, 0, 1, 2] | |
]; | |
const time = 2.5; | |
expect(pathOverlaps(path, time)).toBe(false); | |
}); | |
}); | |
describe('pathPosition', () => { | |
test('should throw an error if the path does not have 2 entries', () => { | |
const path: Path = [ | |
[0, 0, 0, 1, 0] | |
]; | |
const time = 0; | |
expect(() => pathPosition(path, time)).toThrowError('Invalid path: [[0,0,0,1,0]]'); | |
}); | |
test('returns the first point when time is less than the start time', () => { | |
const path: Path = [ | |
[1, 2, 3, 4, 2], | |
[5, 6, 3, 4, 3] | |
]; | |
const result = pathPosition(path, 1); | |
expect(result.position).toEqual({ x: 1, y: 2 }); | |
expect(result.facing).toEqual({ dx: 3, dy: 4 }); | |
expect(result.velocity).toBe(0); | |
}); | |
test('returns the last point when time is greater than the end time', () => { | |
const path: Path = [ | |
[1, 2, 3, 4, 2], | |
[5, 6, 3, 4, 3] | |
]; | |
const result = pathPosition(path, 4); | |
expect(result.position).toEqual({ x: 5, y: 6 }); | |
expect(result.facing).toEqual({ dx: 3, dy: 4 }); | |
expect(result.velocity).toBe(0); | |
}); | |
test('returns the interpolated point for time between two segments', () => { | |
const path: Path = [ | |
[1, 2, 7, 8, 2], | |
[5, 6, 7, 8, 3], | |
[10, 11, 7, 8, 4], | |
[14, 15, 7, 8, 5] | |
]; | |
const result = pathPosition(path, 4.5); | |
expect(result.position).toEqual({ x: 12, y: 13 }); | |
expect(result.facing).toEqual({ dx: 7, dy: 8 }); | |
expect(result.velocity).toBeCloseTo(5.657); | |
}); | |
}); | |
describe('vector', () => { | |
test('should return a vector with dx = 1 and dy = 2', () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 2, y: 4 }; | |
const expected = { dx: 1, dy: 2 }; | |
const actual = vector(p0, p1); | |
expect(actual).toEqual(expected); | |
}); | |
test('should return a vector with dx = 0 and dy = 0', () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 1, y: 2 }; | |
const expected = { dx: 0, dy: 0 }; | |
const actual = vector(p0, p1); | |
expect(actual).toEqual(expected); | |
}); | |
test('should return a vector with dx = 0 and dy = -1', () => { | |
const p0 = { x: 1, y: 2 }; | |
const p1 = { x: 1, y: 1 }; | |
const expected = { dx: 0, dy: -1 }; | |
const actual = vector(p0, p1); | |
expect(actual).toEqual(expected); | |
}); | |
}); | |
describe('vectorLength', () => { | |
test('returns the correct length for a vector', () => { | |
const vector: Vector = { dx: 3.14, dy: 4 }; | |
expect(vectorLength(vector)).toBeCloseTo(5.09); | |
}); | |
test('returns the correct length for a vector with negative components', () => { | |
const vector: Vector = { dx: -3, dy: -4 }; | |
expect(vectorLength(vector)).toBeCloseTo(5); | |
}); | |
test('returns the correct length for a vector with zero components', () => { | |
const vector: Vector = { dx: 0, dy: 0 }; | |
expect(vectorLength(vector)).toBeCloseTo(0); | |
}); | |
}); | |
describe('normalize', () => { | |
test('should return null for vector length less than EPSILON', () => { | |
const vector: Vector = { dx: 0, dy: 0 }; | |
const result = normalize(vector); | |
expect(result).toBeNull(); | |
}); | |
test('should return a normalized vector', () => { | |
const vector: Vector = { dx: 3, dy: 4 }; | |
const result = normalize(vector); | |
expect(result).toEqual({ dx: 0.6, dy: 0.8 }); | |
}); | |
}); | |
describe('orientationDegrees', () => { | |
test('should throw an error for a vector length smaller than EPSILON', () => { | |
expect(() => orientationDegrees({ dx: 0, dy: 0 })).toThrowError("Can't compute the orientation of too small vector {\"dx\":0,\"dy\":0}"); | |
}); | |
test('should return 0 for a vector pointing to the right', () => { | |
expect(orientationDegrees({ dx: 1, dy: 0 })).toBe(0); | |
}); | |
test('should return 90 for a vector pointing up', () => { | |
expect(orientationDegrees({ dx: 0, dy: 1 })).toBe(90); | |
}); | |
test('should return 180 for a vector pointing to the left', () => { | |
expect(orientationDegrees({ dx: -1, dy: 0 })).toBe(180); | |
}); | |
test('should return 270 for a vector pointing down', () => { | |
expect(orientationDegrees({ dx: 0, dy: -1 })).toBe(270); | |
}); | |
}); | |
describe('compressPath', () => { | |
test('should not compress a path with only 2 entries', () => { | |
const facing = { dx: 0, dy: 1 }; | |
const compressed = compressPath([ | |
{ position: { x: 0, y: 0 }, facing, t: 0 }, | |
{ position: { x: 0, y: 1 }, facing, t: 1 }, | |
]); | |
expect(compressed).toEqual([ | |
[0, 0, 0, 1, 0], | |
[0, 1, 0, 1, 1], | |
]); | |
}); | |
test('should compress a line', () => { | |
const facing = { dx: 0, dy: 1 }; | |
const compressed = compressPath([ | |
{ position: { x: 0, y: 0 }, facing, t: 0 }, | |
{ position: { x: 0, y: 1 }, facing, t: 1 }, | |
{ position: { x: 0, y: 2 }, facing, t: 2 }, | |
{ position: { x: 0, y: 3 }, facing, t: 3 }, | |
{ position: { x: 0, y: 4 }, facing, t: 4 }, | |
]); | |
expect(compressed).toEqual([ | |
[0, 0, 0, 1, 0], | |
[0, 4, 0, 1, 4], | |
]); | |
}); | |
test('should compress a line with a turn', () => { | |
const facingUp = { dx: 0, dy: 1 }; | |
const facingRight = { dx: 1, dy: 0 }; | |
const compressed = compressPath([ | |
{ position: { x: 0, y: 0 }, facing: facingUp, t: 0 }, | |
{ position: { x: 0, y: 1 }, facing: facingUp, t: 1 }, | |
{ position: { x: 0, y: 2 }, facing: facingRight, t: 2 }, | |
{ position: { x: 1, y: 2 }, facing: facingRight, t: 3 }, | |
{ position: { x: 2, y: 2 }, facing: facingRight, t: 4 }, | |
]); | |
expect(compressed).toEqual([ | |
[0, 0, 0, 1, 0], | |
[0, 2, 1, 0, 2], | |
[2, 2, 1, 0, 4], | |
]); | |
}); | |
}); | |