Skip to content
This repository has been archived by the owner on Mar 8, 2023. It is now read-only.

Commit

Permalink
MAPSJS-2660: Support off-center projection in getFitBoundsDistance.
Browse files Browse the repository at this point in the history
Signed-off-by: Andres Mandado <andres.mandado-almajano@here.com>
  • Loading branch information
atomicsulfate committed Aug 23, 2021
1 parent 387d3c5 commit c522597
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 16 deletions.
40 changes: 26 additions & 14 deletions @here/harp-mapview/lib/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ export namespace MapViewUtils {
// |<-------->| Ps
// constD pEyeZ| /| ^
// |<-->|<--->| / | |
// | | | / | | ndcY*h/2
// | | | / | | |ndcY-O.y|*h/2
// | | | / | |
// <---T----P'----C0----O v
// camZ |_| /| |
Expand All @@ -961,7 +961,7 @@ export namespace MapViewUtils {
// camY
// constD newPEyeZ ^ Ps
// |<-->|<--------------->| _-`| ^
// | | | _-` | | h/2
// | | | _-` | | |sign(ndcY)-O.y|h/2
// | | | _-` | |
// <---T----P'----C0----------C1---------O v
// camZ |_| _-`| | C0 - Initial camera position
Expand All @@ -976,27 +976,39 @@ export namespace MapViewUtils {
// initial camera position, but calculations are equivalent for points beyond the target
// (pEyeZ negative) or behind the camera (constD negative).
// Right triangles PP'C0 and PsOC0 are equivalent, as well as PP'C1 and Ps0C1, that means:
// |ndcY|*h/(2*f) = PcamY / |pEyeZ| (1) (ndcY,pEyeZ may be negative so take abs vals).
// h/(2*f) = PcamY / newPEyeZ (2)
// Dividing (1) by (2) and solving for newPEyeZ we get: newPEyeZ = |ndcY| * |pEyeZ|
// |ndcY-O.y|*h/(2*f) = PcamY / |pEyeZ| (1) (ndcY-O.y,pEyeZ may be negative, take abs vals).
// |sign(ndcY)-O.y|h/(2*f) = PcamY / newPEyeZ (2)
// Dividing (1) by (2) and solving for newPEyeZ we get:
// newPEyeZ = | pEyeZ || ndcY - O.y | / |sign(ndcY)-O.y|
// The target distance to project P at the top/bottom border of the viewport is then:
// constD + newPEyeZ = targetDist - pEyeZ + |ndcY|*|pEyeZ|
// constD + newPEyeZ = targetDist - pEyeZ + |pEyeZ||ndcY-O.y| / |sign(ndcY)-O.y|
// The target distance to project P at the left/right border of the viewport is similarly:
// targetDist - pEyeZ + |ndcX|*|pEyeZ|
// Take the largest of both distances to ensure the point is inside the viewport, i.e., take
// the largest ndc coordinate value:
// newDistance = targetDist - pEyeZ + max(|ndcX|, |ndcY|)*|pEyeZ|
// targetDist - pEyeZ + |pEyeZ||ndcX-O.x| / |sign(ndcX)-O.x|
// Take the largest of both distances to ensure the point is inside the viewport:
// newDistance = targetDist - pEyeZ +
// max(| ndcX - O.x | /|sign(ndcX)-O.x|, |ndcY-O.y|/sign(ndcY) - O.y |) *| pEyeZ |

const targetDist = cache.vector3[0].copy(worldTarget).sub(camera.position).length();
const ppalPoint = CameraUtils.getPrincipalPoint(camera);
let newDistance = targetDist;

for (const point of points) {
const pEyeZ = -cache.vector3[0].copy(point).applyMatrix4(camera.matrixWorldInverse).z;
const pointNDC = cache.vector3[0].applyMatrix4(camera.projectionMatrix);
const constDist = targetDist - pEyeZ;
const newPEyeZ =
Math.abs(pEyeZ) * Math.max(Math.abs(pointNDC.x), Math.abs(pointNDC.y)) + constDist;
newDistance = Math.max(newDistance, newPEyeZ);
const xFactor =
Math.abs(pointNDC.x) > 1
? Math.abs((pointNDC.x - ppalPoint.x) / (Math.sign(pointNDC.x) - ppalPoint.x))
: 1;
const yFactor =
Math.abs(pointNDC.y) > 1
? Math.abs((pointNDC.y - ppalPoint.y) / (Math.sign(pointNDC.y) - ppalPoint.y))
: 1;
const maxFactor = Math.max(xFactor, yFactor);
if (maxFactor > 1) {
const constDist = targetDist - pEyeZ;
const newPEyeZ = Math.abs(pEyeZ) * maxFactor + constDist;
newDistance = Math.max(newDistance, newPEyeZ);
}
}
return newDistance;
}
Expand Down
49 changes: 47 additions & 2 deletions @here/harp-mapview/test/MapViewTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as sinon from "sinon";
import * as THREE from "three";

import { BackgroundDataSource } from "../lib/BackgroundDataSource";
import { CameraUtils } from "../lib/CameraUtils";
import { DataSource } from "../lib/DataSource";
import { ElevationProvider } from "../lib/ElevationProvider";
import { CalculationStatus, ElevationRangeSource } from "../lib/ElevationRangeSource";
Expand Down Expand Up @@ -140,6 +141,7 @@ describe("MapView", function () {
const tests: Array<{
testName: string;
lookAtParams: Partial<LookAtParams>;
ppalPoint?: { x: number; y: number };
expectAllInView?: boolean;
}> = [
{
Expand Down Expand Up @@ -199,6 +201,16 @@ describe("MapView", function () {
)
}
},
{
testName: "berlin bounds only, off-center projection",
lookAtParams: {
bounds: new GeoBox(
new GeoCoordinates(52.438917, 13.275001),
new GeoCoordinates(52.590844, 13.522331)
)
},
ppalPoint: { x: 0.5, y: -0.3 }
},
{
testName: "berlin bounds + zoomLevel",
lookAtParams: {
Expand Down Expand Up @@ -231,6 +243,18 @@ describe("MapView", function () {
heading: 45
}
},
{
testName: "berlin bounds + angles, off-center projection",
lookAtParams: {
bounds: new GeoBox(
new GeoCoordinates(52.438917, 13.275001),
new GeoCoordinates(52.590844, 13.522331)
),
tilt: 45,
heading: 45
},
ppalPoint: { x: 0.5, y: -0.3 }
},
{
testName: "berlin polygon bounds only",
lookAtParams: {
Expand Down Expand Up @@ -266,6 +290,20 @@ describe("MapView", function () {
heading: 30
}
},
{
testName: "large polygon bounds + angles, off-center projection",
lookAtParams: {
bounds: new GeoPolygon([
new GeoCoordinates(40.0, 13.0),
new GeoCoordinates(40.0, 20.0),
new GeoCoordinates(52.0, 20.0),
new GeoCoordinates(52.0, 13.0)
]),
tilt: 80,
heading: 30
},
ppalPoint: { x: -0.8, y: 0.1 }
},
{
testName: "large polygon with spike bounds only",
lookAtParams: {
Expand Down Expand Up @@ -357,11 +395,15 @@ describe("MapView", function () {
[mercatorProjection, 1e-13]
] as Array<[Projection, number]>) {
describe(`${getProjectionName(projection)} projection`, function () {
for (const { testName, lookAtParams, expectAllInView } of tests) {
for (const { testName, lookAtParams, ppalPoint, expectAllInView } of tests) {
const target = lookAtParams.target as GeoCoordinates | undefined;

it(`obeys constructor params - ${testName}`, function () {
mapView = new MapView({ ...mapViewOptions, projection, ...lookAtParams });
if (ppalPoint) {
CameraUtils.setPrincipalPoint(mapView.camera, ppalPoint);
mapView.camera.updateProjectionMatrix();
}
if (lookAtParams.zoomLevel !== undefined) {
expect(mapView.zoomLevel).to.be.closeTo(lookAtParams.zoomLevel, eps);
}
Expand All @@ -378,7 +420,10 @@ describe("MapView", function () {
});
it(`obeys #lookAt params - ${testName}`, function () {
mapView = new MapView({ ...mapViewOptions, projection });

if (ppalPoint) {
CameraUtils.setPrincipalPoint(mapView.camera, ppalPoint);
mapView.camera.updateProjectionMatrix();
}
mapView.lookAt(lookAtParams);

if (lookAtParams.zoomLevel !== undefined) {
Expand Down

0 comments on commit c522597

Please sign in to comment.