Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

respect padding in cameraForBounds when using globe projection #13126

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
73 changes: 41 additions & 32 deletions src/ui/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,8 @@ class Camera extends Evented {

aabb = Aabb.applyTransform(aabb, mat4.multiply([], worldToCamera, aabbOrientation));

aabb = this._extendAABBWithPaddings(aabb, eOptions, tr, bearing);

vec3.transformMat4(center, center, worldToCamera);

const aabbHalfExtentZ = (aabb.max[2] - aabb.min[2]) * 0.5;
Expand Down Expand Up @@ -740,6 +742,42 @@ class Camera extends Evented {
return {center: tr.center, zoom, bearing, pitch};
}

_extendAABBWithPaddings(aabb: Aabb, eOptions: FullCameraOptions, tr: Transform, bearing: number): Aabb {
const size = vec3.sub([], aabb.max, aabb.min);

const screenPadL = tr.padding.left || 0;
const screenPadR = tr.padding.right || 0;
const screenPadB = tr.padding.bottom || 0;
const screenPadT = tr.padding.top || 0;

const {left: padL, right: padR, top: padT, bottom: padB} = eOptions.padding;

const halfScreenPadX = (screenPadL + screenPadR) * 0.5;
const halfScreenPadY = (screenPadT + screenPadB) * 0.5;

const scaleX = (tr.width - (screenPadL + screenPadR + padL + padR)) / size[0];
const scaleY = (tr.height - (screenPadB + screenPadT + padB + padT)) / size[1];

const zoomRef = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), eOptions.maxZoom);

const scaleRatio = tr.scale / tr.zoomScale(zoomRef);

aabb = new Aabb(
[aabb.min[0] - (padL + halfScreenPadX) * scaleRatio, aabb.min[1] - (padB + halfScreenPadY) * scaleRatio, aabb.min[2]],
[aabb.max[0] + (padR + halfScreenPadX) * scaleRatio, aabb.max[1] + (padT + halfScreenPadY) * scaleRatio, aabb.max[2]]);

const centerOffset = (typeof eOptions.offset.x === 'number' && typeof eOptions.offset.y === 'number') ?
new Point(eOptions.offset.x, eOptions.offset.y) :
Point.convert(eOptions.offset);

const rotatedOffset = centerOffset.rotate(-degToRad(bearing));

aabb.center[0] -= rotatedOffset.x * scaleRatio;
aabb.center[1] += rotatedOffset.y * scaleRatio;

return aabb;
}

/** @section {Querying features} */

/**
Expand Down Expand Up @@ -773,6 +811,7 @@ class Camera extends Evented {
* the highest zoom level up to and including `Map#getMaxZoom()` that fits
* the points in the viewport at the specified bearing.
* @memberof Map#
* @param transform The current transform
* @param {LngLatLike} p0 First point
* @param {LngLatLike} p1 Second point
* @param {number} bearing Desired map bearing at end of animation, in degrees
Expand All @@ -799,7 +838,6 @@ class Camera extends Evented {

const tr = transform.clone();
const eOptions = this._extendCameraOptions(options);
const edgePadding = tr.padding;

tr.bearing = bearing;
tr.pitch = pitch;
Expand Down Expand Up @@ -829,29 +867,9 @@ class Camera extends Evented {

aabb = Aabb.applyTransform(aabb, worldToCamera);

const size = vec3.sub([], aabb.max, aabb.min);

const screenPadL = edgePadding.left || 0;
const screenPadR = edgePadding.right || 0;
const screenPadB = edgePadding.bottom || 0;
const screenPadT = edgePadding.top || 0;

const {left: padL, right: padR, top: padT, bottom: padB} = eOptions.padding;

const halfScreenPadX = (screenPadL + screenPadR) * 0.5;
const halfScreenPadY = (screenPadT + screenPadB) * 0.5;

const scaleX = (tr.width - (screenPadL + screenPadR + padL + padR)) / size[0];
const scaleY = (tr.height - (screenPadB + screenPadT + padB + padT)) / size[1];

const zoomRef = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), eOptions.maxZoom);

const scaleRatio = tr.scale / tr.zoomScale(zoomRef);

aabb = new Aabb(
[aabb.min[0] - (padL + halfScreenPadX) * scaleRatio, aabb.min[1] - (padB + halfScreenPadY) * scaleRatio, aabb.min[2]],
[aabb.max[0] + (padR + halfScreenPadX) * scaleRatio, aabb.max[1] + (padT + halfScreenPadY) * scaleRatio, aabb.max[2]]);
aabb = this._extendAABBWithPaddings(aabb, eOptions, tr, bearing);

const size = vec3.sub([], aabb.max, aabb.min);
const aabbHalfExtentZ = size[2] * 0.5;
const frustumDistance = this._minimumAABBFrustumDistance(tr, aabb);

Expand All @@ -863,15 +881,6 @@ class Camera extends Evented {
const offset = vec3.scale([], normalZ, frustumDistance + aabbHalfExtentZ);
const cameraPosition = vec3.add([], aabb.center, offset);

const centerOffset = (typeof eOptions.offset.x === 'number' && typeof eOptions.offset.y === 'number') ?
new Point(eOptions.offset.x, eOptions.offset.y) :
Point.convert(eOptions.offset);

const rotatedOffset = centerOffset.rotate(-degToRad(bearing));

aabb.center[0] -= rotatedOffset.x * scaleRatio;
aabb.center[1] += rotatedOffset.y * scaleRatio;

vec3.transformMat4(aabb.center, aabb.center, cameraToWorld);
vec3.transformMat4(cameraPosition, cameraPosition, cameraToWorld);

Expand Down
9 changes: 9 additions & 0 deletions test/unit/ui/camera.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2299,6 +2299,15 @@ describe('camera', () => {
expect(fixedLngLat(transform.center, 4)).toEqual({lng: 180, lat: 80});
expect(fixedNum(transform.zoom, 3)).toEqual(1.072);
});

test('entire longitude range: -180 to 180 with asymmetrical padding', () => {
const camera = createCamera({projection: {name: 'globe'}});
const bb = [[-180, 10], [180, 50]];

const transform = camera.cameraForBounds(bb, {padding:{top: 10, right: 75, bottom: 50, left: 25}});
expect(fixedLngLat(transform.center, 4)).toEqual({lng: 180, lat: 80});
expect(fixedNum(transform.zoom, 3)).toEqual(0.892);
});
});

describe('#fitBounds', () => {
Expand Down