maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
141 lines (109 loc) • 5.37 kB
text/typescript
import {Aabb, Frustum} from './primitives';
import {mat4, vec3, vec4} from 'gl-matrix';
describe('primitives', () => {
test('Create an aabb', () => {
const min = vec3.fromValues(0, 0, 0);
const max = vec3.fromValues(2, 4, 6);
const aabb = new Aabb(min, max);
expect(aabb.min).toBe(min);
expect(aabb.max).toBe(max);
expect(aabb.center).toEqual([1, 2, 3]);
});
test('Create 4 quadrants', () => {
const min = vec3.fromValues(0, 0, 0);
const max = vec3.fromValues(2, 4, 1);
const aabb = new Aabb(min, max);
expect(aabb.quadrant(0)).toEqual(new Aabb(vec3.fromValues(0, 0, 0), vec3.fromValues(1, 2, 1)));
expect(aabb.quadrant(1)).toEqual(new Aabb(vec3.fromValues(1, 0, 0), vec3.fromValues(2, 2, 1)));
expect(aabb.quadrant(2)).toEqual(new Aabb(vec3.fromValues(0, 2, 0), vec3.fromValues(1, 4, 1)));
expect(aabb.quadrant(3)).toEqual(new Aabb(vec3.fromValues(1, 2, 0), vec3.fromValues(2, 4, 1)));
});
test('Distance to a point', () => {
const min = vec3.fromValues(-1, -1, -1);
const max = vec3.fromValues(1, 1, 1);
const aabb = new Aabb(min, max);
expect(aabb.distanceX([0.5, -0.5])).toBe(0);
expect(aabb.distanceY([0.5, -0.5])).toBe(0);
expect(aabb.distanceX([1, 1])).toBe(0);
expect(aabb.distanceY([1, 1])).toBe(0);
expect(aabb.distanceX([0, 10])).toBe(0);
expect(aabb.distanceY([0, 10])).toBe(-9);
expect(aabb.distanceX([-2, -2])).toBe(1);
expect(aabb.distanceY([-2, -2])).toBe(1);
});
const createTestCameraFrustum = (fovy, aspectRatio, zNear, zFar, elevation, rotation) => {
const proj = new Float64Array(16) as any as mat4;
const invProj = new Float64Array(16) as any as mat4;
// Note that left handed coordinate space is used where z goes towards the sky.
// Y has to be flipped as well because it's part of the projection/camera matrix used in transform.js
mat4.perspective(proj, fovy, aspectRatio, zNear, zFar);
mat4.scale(proj, proj, [1, -1, 1]);
mat4.translate(proj, proj, [0, 0, elevation]);
mat4.rotateZ(proj, proj, rotation);
mat4.invert(invProj, proj);
return Frustum.fromInvProjectionMatrix(invProj, 1.0, 0.0);
};
test('Aabb fully inside a frustum', () => {
const frustum = createTestCameraFrustum(Math.PI / 2, 1.0, 0.1, 100.0, -5, 0);
// Intersection test is done in xy-plane
const aabbList = [
new Aabb(vec3.fromValues(-1, -1, 0), vec3.fromValues(1, 1, 0)),
new Aabb(vec3.fromValues(-5, -5, 0), vec3.fromValues(5, 5, 0)),
new Aabb(vec3.fromValues(-5, -5, 0), vec3.fromValues(-4, -2, 0))
];
for (const aabb of aabbList)
expect(aabb.intersects(frustum)).toBe(2);
});
test('Aabb intersecting with a frustum', () => {
const frustum = createTestCameraFrustum(Math.PI / 2, 1.0, 0.1, 100.0, -5, 0);
const aabbList = [
new Aabb(vec3.fromValues(-6, -6, 0), vec3.fromValues(6, 6, 0)),
new Aabb(vec3.fromValues(-6, -6, 0), vec3.fromValues(-5, -5, 0))
];
for (const aabb of aabbList)
expect(aabb.intersects(frustum)).toBe(1);
});
test('No intersection between aabb and frustum', () => {
const frustum = createTestCameraFrustum(Math.PI / 2, 1.0, 0.1, 100.0, -5, 0);
const aabbList = [
new Aabb(vec3.fromValues(-6, 0, 0), vec3.fromValues(-5.5, 0, 0)),
new Aabb(vec3.fromValues(-6, -6, 0), vec3.fromValues(-5.5, -5.5, 0)),
new Aabb(vec3.fromValues(7, -10, 0), vec3.fromValues(7.1, 20, 0))
];
for (const aabb of aabbList)
expect(aabb.intersects(frustum)).toBe(0);
});
});
describe('frustum', () => {
test('Create a frustum from inverse projection matrix', () => {
const proj = new Float64Array(16) as any as mat4;
const invProj = new Float64Array(16) as any as mat4;
mat4.perspective(proj, Math.PI / 2, 1.0, 0.1, 100.0);
mat4.invert(invProj, proj);
const frustum = Frustum.fromInvProjectionMatrix(invProj, 1.0, 0.0);
// mat4.perspective generates a projection matrix for right handed coordinate space.
// This means that forward direction will be -z
const expectedFrustumPoints = [
[-0.1, 0.1, -0.1, 1.0],
[0.1, 0.1, -0.1, 1.0],
[0.1, -0.1, -0.1, 1.0],
[-0.1, -0.1, -0.1, 1.0],
[-100.0, 100.0, -100.0, 1.0],
[100.0, 100.0, -100.0, 1.0],
[100.0, -100.0, -100.0, 1.0],
[-100.0, -100.0, -100.0, 1.0],
];
frustum.points = frustum.points.map(array => array.map(n => Math.round(n * 10) / 10)) as vec4[];
frustum.planes = frustum.planes.map(array => array.map(n => Math.round(n * 1000) / 1000)) as vec4[];
const expectedFrustumPlanes = [
[0, 0, 1.0, 0.1],
[-0, -0, -1.0, -100.0],
[-0.707, 0, 0.707, -0],
[0.707, 0, 0.707, -0],
[0, -0.707, 0.707, -0],
[-0, 0.707, 0.707, -0]
];
expect(frustum.points).toEqual(expectedFrustumPoints);
expect(frustum.planes).toEqual(expectedFrustumPlanes);
});
});