Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mpulkki-mapbox committed Jan 14, 2020
1 parent 2ba76b8 commit 722a15c
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 45 deletions.
28 changes: 16 additions & 12 deletions src/geo/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,13 @@ class Transform {
if (options.minzoom !== undefined && z < options.minzoom) return [];
if (options.maxzoom !== undefined && z > options.maxzoom) z = options.maxzoom;

let minZoom = options.minzoom || 0;
const centerCoord = MercatorCoordinate.fromLngLat(this.center);
const numTiles = Math.pow(2, z);
const centerPoint = new Point(numTiles * centerCoord.x, numTiles * centerCoord.y);
const centerPoint = [numTiles * centerCoord.x, numTiles * centerCoord.y, 0];
const cameraFrustum = Frustum.fromInvProjectionMatrix(this.invProjMatrix, this.worldSize, z);

// No change of LOD behavior for pitch lower than 60: return only tile ids from the requested zoom level
let minZoom = options.minzoom || 0;
if (this.pitch <= 60.0)
minZoom = z;

Expand Down Expand Up @@ -316,10 +317,6 @@ class Transform {

stack.push(newRootTile(0));

// Stream position will determine the "center of the streaming",
// ie. where the most detailed tiles are loaded.
const streamPos = [centerPoint.x, centerPoint.y, 0];

while (stack.length > 0) {
const it = stack.pop();
const x = it.x;
Expand All @@ -330,28 +327,35 @@ class Transform {
if (!fullyVisible) {
const intersectResult = it.aabb.intersects(cameraFrustum);

if (intersectResult === 'none')
if (intersectResult === 0)
continue;

fullyVisible = intersectResult === 'contains';
fullyVisible = intersectResult === 2;
}

const distanceXY = it.aabb.distanceXY(streamPos);
const longestDim = Math.max(Math.abs(distanceXY[0]), Math.abs(distanceXY[1]));
const distanceX = it.aabb.distanceX(centerPoint);
const distanceY = it.aabb.distanceY(centerPoint);
const longestDim = Math.max(Math.abs(distanceX), Math.abs(distanceY));

// We're using distance based heuristics to determine if a tile should be split into quadrants or not.
// radiusOfMaxLvlLodInTiles defines that there's always a certain number of maxLevel tiles next to the map center.
// Using the fact that a parent node in quadtree is twice the size of its children (per dimension)
// we can define distance thresholds for each relative level:
// f(k) = offset + 2 + 4 + 8 + 16 + ... + 2^k. This is the same as "offset+2^(k+1)-2"
const distToSplit = radiusOfMaxLvlLodInTiles + (1 << (maxZoom - it.zoom)) - 2;

// Have we reached the target depth or is the tile too far away to be any split further?
if (it.zoom === maxZoom || (longestDim > distToSplit && it.zoom >= minZoom)) {
result.push({
tileID: new OverscaledTileID(it.zoom === maxZoom ? overscaledZ : it.zoom, it.wrap, it.zoom, x, y),
distanceSq: vec2.sqrLen([streamPos[0] - 0.5 - x, streamPos[1] - 0.5 - y])
distanceSq: vec2.sqrLen([centerPoint[0] - 0.5 - x, centerPoint[1] - 0.5 - y])
});
continue;
}

for (let i = 0; i < 4; i++) {
const childX = (x << 1) + (i % 2);
const childY = (y << 1) + Math.floor(i / 2);
const childY = (y << 1) + (i >> 1);

stack.push({aabb: it.aabb.quadrant(i), zoom: it.zoom + 1, x: childX, y: childY, wrap: it.wrap, fullyVisible});
}
Expand Down
1 change: 1 addition & 0 deletions src/source/source_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ class SourceCache extends Evented {
roundZoom: this._source.roundZoom,
reparseOverscaled: this._source.reparseOverscaled
});

if (this._source.hasTile) {
idealTileIDs = idealTileIDs.filter((coord) => (this._source.hasTile: any)(coord));
}
Expand Down
29 changes: 13 additions & 16 deletions src/util/primitives.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import {vec3, vec4} from 'gl-matrix';
import assert from 'assert';

type IntersectResult = 'none' | 'intersects' | 'contains';

class Frustum {
points: Array<Array<number>>;
planes: Array<Array<number>>;
Expand Down Expand Up @@ -78,20 +76,19 @@ class Aabb {
return new Aabb(qMin, qMax);
}

closestPoint(point: Array<number>): Array<number> {
const x = Math.max(Math.min(this.max[0], point[0]), this.min[0]);
const y = Math.max(Math.min(this.max[1], point[1]), this.min[1]);
return [x, y];
distanceX(point: Array<number>): number {
const pointOnAabb = Math.max(Math.min(this.max[0], point[0]), this.min[0]);
return pointOnAabb - point[0];
}

distanceXY(point: Array<number>): Array<number> {
const aabbPoint = this.closestPoint(point);
const dx = aabbPoint[0] - point[0];
const dy = aabbPoint[1] - point[1];
return [dx, dy];
distanceY(point: Array<number>): number {
const pointOnAabb = Math.max(Math.min(this.max[1], point[1]), this.min[1]);
return pointOnAabb - point[1];
}

intersects(frustum: Frustum): IntersectResult {
// Performs a frustum-aabb intersection test. Returns 0 if there's no intersection,
// 1 if shapes are intersecting and 2 if the aabb if fully inside the frustum.
intersects(frustum: Frustum): number {
// Execute separating axis test between two convex objects to find intersections
// Each frustum plane together with 3 major axes define the separating axes
// Note: test only 4 points as both min and max points have equal elevation
Expand All @@ -115,14 +112,14 @@ class Aabb {
}

if (pointsInside === 0)
return 'none';
return 0;

if (pointsInside !== aabbPoints.length)
fullyInside = false;
}

if (fullyInside)
return 'contains';
return 2;

for (let axis = 0; axis < 3; axis++) {
let projMin = Number.MAX_VALUE;
Expand All @@ -136,10 +133,10 @@ class Aabb {
}

if (projMax < 0 || projMin > this.max[axis] - this.min[axis])
return 'none';
return 0;
}

return 'intersects';
return 1;
}
}
export {
Expand Down
49 changes: 49 additions & 0 deletions test/unit/geo/transform.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,55 @@ test('transform', (t) => {
new OverscaledTileID(8, 0, 8, 146, 73)
]);

transform.zoom = 2;
transform.pitch = 0;
transform.bearing = 0;
transform.resize(300, 300);
t.test('calculates tile coverage at w > 0', (t) => {
transform.center = {lng: 630.01, lat: 0.01};
t.deepEqual(transform.coveringTiles(options), [
new OverscaledTileID(2, 2, 2, 1, 1),
new OverscaledTileID(2, 2, 2, 1, 2),
new OverscaledTileID(2, 2, 2, 0, 1),
new OverscaledTileID(2, 2, 2, 0, 2)
]);
t.end();
});

t.test('calculates tile coverage at w = -1', (t) => {
transform.center = {lng: -360.01, lat: 0.01};
t.deepEqual(transform.coveringTiles(options), [
new OverscaledTileID(2, -1, 2, 1, 1),
new OverscaledTileID(2, -1, 2, 1, 2),
new OverscaledTileID(2, -1, 2, 2, 1),
new OverscaledTileID(2, -1, 2, 2, 2)
]);
t.end();
});

t.test('calculates tile coverage across meridian', (t) => {
transform.zoom = 1;
transform.center = {lng: -180.01, lat: 0.01};
t.deepEqual(transform.coveringTiles(options), [
new OverscaledTileID(1, 0, 1, 0, 0),
new OverscaledTileID(1, 0, 1, 0, 1),
new OverscaledTileID(1, -1, 1, 1, 0),
new OverscaledTileID(1, -1, 1, 1, 1)
]);
t.end();
});

t.test('only includes tiles for a single world, if renderWorldCopies is set to false', (t) => {
transform.zoom = 1;
transform.center = {lng: -180.01, lat: 0.01};
transform.renderWorldCopies = false;
t.deepEqual(transform.coveringTiles(options), [
new OverscaledTileID(1, 0, 1, 0, 0),
new OverscaledTileID(1, 0, 1, 0, 1)
]);
t.end();
});

t.end();
});

Expand Down
30 changes: 13 additions & 17 deletions test/unit/util/primitives.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,22 @@ test('primitives', (t) => {
t.end();
});

t.test('Closest point inside of aabb', (t) => {
t.test('Distance to a point', (t) => {
const min = vec3.fromValues(-1, -1, -1);
const max = vec3.fromValues(1, 1, 1);
const aabb = new Aabb(min, max);

t.deepEqual(aabb.closestPoint([0.5, -0.5]), [0.5, -0.5]);
t.deepEqual(aabb.closestPoint([0, 10]), [0, 1]);
t.deepEqual(aabb.closestPoint([-2, -2]), [-1, -1]);
t.end();
});
t.equal(aabb.distanceX([0.5, -0.5]), 0);
t.equal(aabb.distanceY([0.5, -0.5]), 0);

t.test('Distance to a point', (t) => {
const min = vec3.fromValues(-1, -1, -1);
const max = vec3.fromValues(1, 1, 1);
const aabb = new Aabb(min, max);
t.equal(aabb.distanceX([1, 1]), 0);
t.equal(aabb.distanceY([1, 1]), 0);

t.equal(aabb.distanceX([0, 10]), 0);
t.equal(aabb.distanceY([0, 10]), -9);

t.deepEqual(aabb.distanceXY([0.5, -0.5]), [0, 0]);
t.deepEqual(aabb.distanceXY([1, 1]), [0, 0]);
t.deepEqual(aabb.distanceXY([0, 10]), [0, -9]);
t.deepEqual(aabb.distanceXY([-2, -2]), [1, 1]);
t.equal(aabb.distanceX([-2, -2]), 1);
t.equal(aabb.distanceY([-2, -2]), 1);
t.end();
});

Expand Down Expand Up @@ -76,7 +72,7 @@ test('primitives', (t) => {
];

for (const aabb of aabbList)
t.equal(aabb.intersects(frustum), 'contains');
t.equal(aabb.intersects(frustum), 2);

t.end();
});
Expand All @@ -90,7 +86,7 @@ test('primitives', (t) => {
];

for (const aabb of aabbList)
t.equal(aabb.intersects(frustum), 'intersects');
t.equal(aabb.intersects(frustum), 1);

t.end();
});
Expand All @@ -105,7 +101,7 @@ test('primitives', (t) => {
];

for (const aabb of aabbList)
t.equal(aabb.intersects(frustum), 'none');
t.equal(aabb.intersects(frustum), 0);

t.end();
});
Expand Down

0 comments on commit 722a15c

Please sign in to comment.