maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
322 lines (281 loc) • 13.1 kB
text/typescript
import {describe, test, expect} from 'vitest';
import {type SymbolProjectionContext, type ProjectionSyntheticVertexArgs, findOffsetIntersectionPoint, projectWithMatrix, transformToOffsetNormal, projectLineVertexToLabelPlane, getPitchedLabelPlaneMatrix, getGlCoordMatrix, getTileSkewVectors} from './projection';
import Point from '@mapbox/point-geometry';
import {mat4} from 'gl-matrix';
import {SymbolLineVertexArray} from '../data/array_types.g';
import {MercatorTransform} from '../geo/projection/mercator_transform';
import {expectToBeCloseToArray} from '../util/test/util';
describe('Projection', () => {
test('matrix float precision', () => {
const point = new Point(10.000000005, 0);
const matrix = mat4.create();
expect(projectWithMatrix(point.x, point.y, matrix).point.x).toBeCloseTo(point.x, 10);
});
});
describe('Vertex to viewport projection', () => {
// A three point line along the x axis
const lineVertexArray = new SymbolLineVertexArray();
lineVertexArray.emplaceBack(-10, 0, -10);
lineVertexArray.emplaceBack(0, 0, 0);
lineVertexArray.emplaceBack(10, 0, 10);
const transform = new MercatorTransform();
test('projecting with null matrix', () => {
const projectionContext: SymbolProjectionContext = {
projectionCache: {projections: {}, offsets: {}, cachedAnchorPoint: undefined, anyProjectionOccluded: false},
lineVertexArray,
pitchedLabelPlaneMatrix: mat4.create(),
getElevation: (_x, _y) => 0,
// Only relevant in "behind the camera" case, can't happen with null projection matrix
tileAnchorPoint: new Point(0, 0),
pitchWithMap: true,
unwrappedTileID: null,
transform,
width: 1,
height: 1,
translation: [0, 0]
};
const syntheticVertexArgs: ProjectionSyntheticVertexArgs = {
distanceFromAnchor: 0,
previousVertex: new Point(0, 0),
direction: 1,
absOffsetX: 0
};
const first = projectLineVertexToLabelPlane(0, projectionContext, syntheticVertexArgs);
const second = projectLineVertexToLabelPlane(1, projectionContext, syntheticVertexArgs);
const third = projectLineVertexToLabelPlane(2, projectionContext, syntheticVertexArgs);
expect(first.x).toBeCloseTo(-10);
expect(second.x).toBeCloseTo(0);
expect(third.x).toBeCloseTo(10);
});
});
describe('Find offset line intersections', () => {
const lineVertexArray = new SymbolLineVertexArray();
// A three point line along x axis, to origin, and then up y axis
lineVertexArray.emplaceBack(-10, 0, -10);
lineVertexArray.emplaceBack(0, 0, 0);
lineVertexArray.emplaceBack(0, 10, 10);
// A three point line along the x axis
lineVertexArray.emplaceBack(-10, 0, -10);
lineVertexArray.emplaceBack(0, 0, 0);
lineVertexArray.emplaceBack(10, 0, 10);
const transform = new MercatorTransform();
const projectionContext: SymbolProjectionContext = {
projectionCache: {projections: {}, offsets: {}, cachedAnchorPoint: undefined, anyProjectionOccluded: false},
lineVertexArray,
pitchedLabelPlaneMatrix: mat4.create(),
getElevation: (_x, _y) => 0,
tileAnchorPoint: new Point(0, 0),
transform,
pitchWithMap: true,
unwrappedTileID: null,
width: 1,
height: 1,
translation: [0, 0]
};
// Only relevant in "behind the camera" case, can't happen with null projection matrix
const syntheticVertexArgs: ProjectionSyntheticVertexArgs = {
direction: 1,
distanceFromAnchor: 0,
previousVertex: new Point(0, 0),
absOffsetX: 0
};
test('concave', () => {
/*
| |
| |
________| |
__________| <- origin
*/
projectionContext.projectionCache = {projections: {}, offsets: {}, cachedAnchorPoint: undefined, anyProjectionOccluded: false};
const lineOffsetY = 1;
const prevToCurrent = new Point(10, 0);
const normal = transformToOffsetNormal(prevToCurrent, lineOffsetY, syntheticVertexArgs.direction);
expect(normal.y).toBeCloseTo(1);
expect(normal.x).toBeCloseTo(0);
const intersectionPoint = findOffsetIntersectionPoint(
1,
normal,
new Point(0, 0),
0,
3,
new Point(-10, 1),
lineOffsetY,
projectionContext,
syntheticVertexArgs
);
expect(intersectionPoint.y).toBeCloseTo(1);
expect(intersectionPoint.x).toBeCloseTo(-1);
});
test('convex', () => {
/*
| |
| |
origin \ | |
__________| |
____________|
*/
projectionContext.projectionCache = {projections: {}, offsets: {}, cachedAnchorPoint: undefined, anyProjectionOccluded: false};
const lineOffsetY = -1;
const prevToCurrent = new Point(10, 0);
const normal = transformToOffsetNormal(prevToCurrent, lineOffsetY, syntheticVertexArgs.direction);
expect(normal.y).toBeCloseTo(-1);
expect(normal.x).toBeCloseTo(0);
const intersectionPoint = findOffsetIntersectionPoint(
1,
normal,
new Point(0, 0),
0,
3,
new Point(-10, -1),
lineOffsetY,
projectionContext,
syntheticVertexArgs
);
expect(intersectionPoint.y).toBeCloseTo(-1);
expect(intersectionPoint.x).toBeCloseTo(1);
});
test('parallel', () => {
/*
______._____
______|_____
*/
projectionContext.projectionCache = {projections: {}, offsets: {}, cachedAnchorPoint: undefined, anyProjectionOccluded: false};
const lineOffsetY = 1;
const prevToCurrent = new Point(10, 0);
const intersectionPoint = findOffsetIntersectionPoint(
1,
transformToOffsetNormal(prevToCurrent, lineOffsetY, syntheticVertexArgs.direction),
new Point(0, 0),
3,
5,
new Point(-10, 1),
lineOffsetY,
projectionContext,
syntheticVertexArgs
);
expect(intersectionPoint.x).toBeCloseTo(0);
expect(intersectionPoint.y).toBeCloseTo(1);
});
test('getPitchedLabelPlaneMatrix: bearing and roll', () => {
const transform = new MercatorTransform();
transform.setBearing(0);
transform.setPitch(45);
transform.setRoll(45);
expectToBeCloseToArray([...getPitchedLabelPlaneMatrix(false, transform, 2).values()],
[0.4330127239227295, -0.4330127239227295, 0, 0, 0.3061862289905548, 0.3061862289905548, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
expectToBeCloseToArray([...getPitchedLabelPlaneMatrix(true, transform, 2).values()],
[0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
});
test('getPitchedLabelPlaneMatrix: bearing and pitch', () => {
const transform = new MercatorTransform();
transform.setBearing(45);
transform.setPitch(45);
transform.setRoll(0);
expectToBeCloseToArray([...getPitchedLabelPlaneMatrix(false, transform, 2).values()],
[0.3535533845424652, -0.3535533845424652, 0, 0, 0.3535533845424652, 0.3535533845424652, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
expectToBeCloseToArray([...getPitchedLabelPlaneMatrix(true, transform, 2).values()],
[0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
});
test('getPitchedLabelPlaneMatrix: bearing, pitch, and roll', () => {
const transform = new MercatorTransform();
transform.setBearing(45);
transform.setPitch(45);
transform.setRoll(45);
expectToBeCloseToArray([...getPitchedLabelPlaneMatrix(false, transform, 2).values()],
[0.08967986702919006, -0.5226925611495972, 0, 0, 0.5226925611495972, -0.08967986702919006, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
expectToBeCloseToArray([...getPitchedLabelPlaneMatrix(true, transform, 2).values()],
[0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
});
test('getGlCoordMatrix: bearing, pitch, and roll', () => {
const transform = new MercatorTransform();
transform.resize(128, 128);
transform.setBearing(45);
transform.setPitch(45);
transform.setRoll(45);
expectToBeCloseToArray([...getGlCoordMatrix(false, false, transform, 2).values()],
[...transform.pixelsToClipSpaceMatrix.values()], 9);
expectToBeCloseToArray([...getGlCoordMatrix(false, true, transform, 2).values()],
[...transform.pixelsToClipSpaceMatrix.values()], 9);
expectToBeCloseToArray([...getGlCoordMatrix(true, false, transform, 2).values()],
[-0.33820396661758423, 1.9711971282958984, 0, 0, -1.9711971282958984, 0.33820396661758423, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
expectToBeCloseToArray([...getGlCoordMatrix(true, true, transform, 2).values()],
[2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 9);
});
test('getTileSkewVectors: bearing', () => {
const transform = new MercatorTransform();
transform.setBearing(45);
transform.setPitch(0);
transform.setRoll(0);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[0.7071067690849304, 0.7071067690849304]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[-0.7071067690849304, 0.7071067690849304], 9);
});
test('getTileSkewVectors: roll', () => {
const transform = new MercatorTransform();
transform.setBearing(0);
transform.setPitch(0);
transform.setRoll(45);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[0.7071067690849304, 0.7071067690849304]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[-0.7071067690849304, 0.7071067690849304], 9);
});
test('getTileSkewVectors: pitch', () => {
const transform = new MercatorTransform();
transform.setBearing(0);
transform.setPitch(45);
transform.setRoll(0);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[1.0, 0.0]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[0.0, 1.0], 9);
});
test('getTileSkewVectors: roll pitch bearing', () => {
const transform = new MercatorTransform();
transform.setBearing(45);
transform.setPitch(45);
transform.setRoll(45);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[-0.16910198330879211, 0.9855985641479492]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[-0.9855985641479492, 0.16910198330879211], 9);
});
test('getTileSkewVectors: pitch 90 degrees', () => {
const transform = new MercatorTransform();
transform.setMaxPitch(180);
transform.setBearing(0);
transform.setPitch(89);
transform.setRoll(0);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[1, 0]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[0, 1], 9);
transform.setPitch(90);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[0, 0]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[0, 1], 9);
transform.setBearing(90);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[0, 0]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[-1, 0], 9);
});
test('getTileSkewVectors: pitch 90 degrees with roll and bearing', () => {
const transform = new MercatorTransform();
transform.setMaxPitch(180);
transform.setBearing(45);
transform.setPitch(89);
transform.setRoll(45);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[-0.6946603059768677, 0.7193379402160645]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[-0.7193379402160645, 0.6946603059768677], 9);
transform.setPitch(90);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecEast.values()],
[-0.7071067690849304, 0.7071067690849304]);
expectToBeCloseToArray([...getTileSkewVectors(transform).vecSouth.values()],
[-0.7071067690849304, 0.7071067690849304], 9);
});
});