[core, ios] replace altitude with fov
ported from -js: eb6c6596c6a7a61363d30356674e0002153b1d19 `altitude` was a terribly-named variable that was used to indirectly control the fov. This should eliminate some confusion. `altitude` was equivalent to `cameraToCenterDistance / height`
This commit is contained in:
committed by
John Firebaugh
parent
ebd241363d
commit
38b5cb0c77
@@ -25,7 +25,7 @@ struct CustomLayerRenderParameters {
|
||||
double zoom;
|
||||
double bearing;
|
||||
double pitch;
|
||||
double altitude;
|
||||
double fieldOfView;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"ejs": "^2.4.1",
|
||||
"express": "^4.11.1",
|
||||
"lodash": "^4.16.4",
|
||||
"mapbox-gl": "mapbox/mapbox-gl-js#c52a09639ceeeb809cd837360993df9c45b45420",
|
||||
"mapbox-gl": "mapbox/mapbox-gl-js#eb6c6596c6a7a61363d30356674e0002153b1d19",
|
||||
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#49e8b407bdbbe6f7c92dbcb56d3d51f425fc2653",
|
||||
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#da53a81453068f4c2b440f9077d6bd5e7e14ff3d",
|
||||
"mkdirp": "^0.5.1",
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
/** Vertical field of view, measured in degrees, for determining the altitude
|
||||
of the viewpoint.
|
||||
|
||||
TransformState::getProjMatrix() assumes a vertical field of view of
|
||||
2 arctan ⅓ rad ≈ 36.9°, but MapKit uses a vertical field of view of 30°.
|
||||
TransformState::getProjMatrix() has a variable vertical field of view that
|
||||
defaults to 2 arctan ⅓ rad ≈ 36.9° but MapKit uses a vertical field of view of 30°.
|
||||
flyTo() assumes a field of view of 2 arctan ½ rad. */
|
||||
const CLLocationDegrees MGLAngularFieldOfView = 30;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ typedef struct MGLStyleLayerDrawingContext {
|
||||
double zoomLevel;
|
||||
CLLocationDirection direction;
|
||||
CGFloat pitch;
|
||||
CGFloat perspectiveSkew;
|
||||
CGFloat fieldOfView;
|
||||
} MGLStyleLayerDrawingContext;
|
||||
|
||||
@interface MGLOpenGLStyleLayer : MGLStyleLayer
|
||||
|
||||
@@ -34,7 +34,7 @@ void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRender
|
||||
.zoomLevel = params.zoom,
|
||||
.direction = mbgl::util::wrap(params.bearing, 0., 360.),
|
||||
.pitch = static_cast<CGFloat>(params.pitch),
|
||||
.perspectiveSkew = static_cast<CGFloat>(params.altitude),
|
||||
.fieldOfView = static_cast<CGFloat>(params.fieldOfView),
|
||||
};
|
||||
[layer drawInMapView:layer.mapView withContext:drawingContext];
|
||||
}
|
||||
|
||||
@@ -27,22 +27,24 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons
|
||||
}
|
||||
|
||||
void TransformState::getProjMatrix(mat4& projMatrix) const {
|
||||
double halfFov = std::atan(0.5 / getAltitude());
|
||||
double topHalfSurfaceDistance = std::sin(halfFov) * getAltitude() /
|
||||
std::sin(M_PI / 2.0f - getPitch() - halfFov);
|
||||
|
||||
// Find the distance from the center point [width/2, height/2] to the
|
||||
// center top point [width/2, 0] in Z units, using the law of sines.
|
||||
// 1 Z unit is equivalent to 1 horizontal px at the center of the map
|
||||
// (the distance between[width/2, height/2] and [width/2 + 1, height/2])
|
||||
const double halfFov = getFieldOfView() / 2.0;
|
||||
const double groundAngle = M_PI / 2.0 + getPitch();
|
||||
const double topHalfSurfaceDistance = std::sin(halfFov) * getCameraToCenterDistance() / std::sin(M_PI - groundAngle - halfFov);
|
||||
|
||||
// Calculate z value of the farthest fragment that should be rendered.
|
||||
double farZ = std::cos(M_PI / 2.0f - getPitch()) * topHalfSurfaceDistance + getAltitude();
|
||||
const double farZ = std::cos(M_PI / 2.0 - getPitch()) * topHalfSurfaceDistance + getCameraToCenterDistance();
|
||||
|
||||
matrix::perspective(projMatrix, 2.0f * std::atan((size.height / 2.0f) / getAltitude()),
|
||||
double(size.width) / size.height, 0.1, farZ);
|
||||
matrix::perspective(projMatrix, getFieldOfView(), double(size.width) / size.height, 0.1, farZ);
|
||||
|
||||
matrix::translate(projMatrix, projMatrix, 0, 0, -getAltitude());
|
||||
|
||||
// After the rotateX, z values are in pixel units. Convert them to
|
||||
// altitude unites. 1 altitude unit = the screen height.
|
||||
const bool flippedY = viewportMode == ViewportMode::FlippedY;
|
||||
matrix::scale(projMatrix, projMatrix, 1, flippedY ? 1 : -1,
|
||||
1.0f / (rotatedNorth() ? size.width : size.height));
|
||||
matrix::scale(projMatrix, projMatrix, 1, flippedY ? 1 : -1, 1);
|
||||
|
||||
matrix::translate(projMatrix, projMatrix, 0, 0, -getCameraToCenterDistance());
|
||||
|
||||
using NO = NorthOrientation;
|
||||
switch (getNorthOrientation()) {
|
||||
@@ -164,8 +166,12 @@ float TransformState::getAngle() const {
|
||||
return angle;
|
||||
}
|
||||
|
||||
float TransformState::getAltitude() const {
|
||||
return altitude;
|
||||
float TransformState::getFieldOfView() const {
|
||||
return fov;
|
||||
}
|
||||
|
||||
float TransformState::getCameraToCenterDistance() const {
|
||||
return 0.5 * size.height / std::tan(fov / 2.0);
|
||||
}
|
||||
|
||||
float TransformState::getPitch() const {
|
||||
|
||||
@@ -56,7 +56,8 @@ public:
|
||||
|
||||
// Rotation
|
||||
float getAngle() const;
|
||||
float getAltitude() const;
|
||||
float getFieldOfView() const;
|
||||
float getCameraToCenterDistance() const;
|
||||
float getPitch() const;
|
||||
|
||||
// State
|
||||
@@ -109,7 +110,11 @@ private:
|
||||
double x = 0, y = 0;
|
||||
double angle = 0;
|
||||
double scale = 1;
|
||||
double altitude = 1.5;
|
||||
// This fov value is somewhat arbitrary. The altitude of the camera used
|
||||
// to be defined as 1.5 screen heights above the ground, which was an
|
||||
// arbitrary choice. This is the fov equivalent to that value calculated with:
|
||||
// `fov = 2 * arctan((height / 2) / (height * 1.5))`
|
||||
double fov = 0.6435011087932844;
|
||||
double pitch = 0.0;
|
||||
|
||||
// cache values for spherical mercator math
|
||||
|
||||
@@ -25,7 +25,7 @@ Values makeValues(const LinePaintProperties::Evaluated& properties,
|
||||
|
||||
// calculate how much longer the real world distance is at the top of the screen
|
||||
// than at the middle of the screen.
|
||||
float topedgelength = std::sqrt(std::pow(state.getSize().height, 2.0f) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2.0f)));
|
||||
float topedgelength = std::sqrt(std::pow(state.getSize().height, 2.0f) / 4.0f * (1.0f + std::pow(state.getCameraToCenterDistance(), 2.0f)));
|
||||
float x = state.getSize().height / 2.0f * std::tan(state.getPitch());
|
||||
|
||||
return Values {
|
||||
|
||||
@@ -23,8 +23,8 @@ Values makeValues(const style::SymbolPropertyValues& values,
|
||||
extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * scale);
|
||||
} else {
|
||||
extrudeScale = {{
|
||||
pixelsToGLUnits[0] * scale * state.getAltitude(),
|
||||
pixelsToGLUnits[1] * scale * state.getAltitude()
|
||||
pixelsToGLUnits[0] * scale * state.getCameraToCenterDistance(),
|
||||
pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance()
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ static SymbolSDFProgram::UniformValues makeSDFValues(const style::SymbolProperty
|
||||
const float gammaBase = 0.105 * values.sdfScale / values.paintSize / pixelRatio;
|
||||
const float gammaScale = (values.pitchAlignment == AlignmentType::Map
|
||||
? 1.0 / std::cos(state.getPitch())
|
||||
: 1.0) / state.getAltitude();
|
||||
: 1.0) / state.getCameraToCenterDistance();
|
||||
|
||||
return makeValues<SymbolSDFProgram::UniformValues>(
|
||||
values,
|
||||
|
||||
@@ -47,8 +47,8 @@ void Painter::renderCircle(PaintParameters& parameters,
|
||||
uniforms::u_scale_with_map::Value{ scaleWithMap },
|
||||
uniforms::u_extrude_scale::Value{ scaleWithMap
|
||||
? std::array<float, 2> {{
|
||||
pixelsToGLUnits[0] * state.getAltitude(),
|
||||
pixelsToGLUnits[1] * state.getAltitude()
|
||||
pixelsToGLUnits[0] * state.getCameraToCenterDistance(),
|
||||
pixelsToGLUnits[1] * state.getCameraToCenterDistance()
|
||||
}}
|
||||
: pixelsToGLUnits }
|
||||
},
|
||||
|
||||
@@ -57,7 +57,7 @@ void CustomLayer::Impl::render(const TransformState& state) const {
|
||||
parameters.zoom = state.getZoom();
|
||||
parameters.bearing = -state.getAngle() * util::RAD2DEG;
|
||||
parameters.pitch = state.getPitch();
|
||||
parameters.altitude = state.getAltitude();
|
||||
parameters.fieldOfView = state.getFieldOfView();
|
||||
|
||||
renderFn(context, parameters);
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ TEST(Transform, Padding) {
|
||||
|
||||
const LatLng shiftedCenter = transform.getLatLng(padding);
|
||||
ASSERT_NE(trueCenter.latitude, shiftedCenter.latitude);
|
||||
ASSERT_DOUBLE_EQ(trueCenter.longitude, shiftedCenter.longitude);
|
||||
ASSERT_NEAR(trueCenter.longitude, shiftedCenter.longitude, 1e-9);
|
||||
ASSERT_DOUBLE_EQ(manualShiftedCenter.latitude, shiftedCenter.latitude);
|
||||
ASSERT_DOUBLE_EQ(manualShiftedCenter.longitude, shiftedCenter.longitude);
|
||||
}
|
||||
@@ -401,12 +401,12 @@ TEST(Transform, Antimeridian) {
|
||||
|
||||
const LatLng coordinateSanFrancisco { 37.7833, -122.4167 };
|
||||
ScreenCoordinate pixelSF = transform.latLngToScreenCoordinate(coordinateSanFrancisco);
|
||||
ASSERT_DOUBLE_EQ(151.79409149185352, pixelSF.x);
|
||||
ASSERT_DOUBLE_EQ(383.76774094913071, pixelSF.y);
|
||||
ASSERT_NEAR(151.79409149185352, pixelSF.x, 1e-2);
|
||||
ASSERT_NEAR(383.76774094913071, pixelSF.y, 1e-2);
|
||||
|
||||
transform.setLatLng({ 0, -181 });
|
||||
ScreenCoordinate pixelSFBackwards = transform.latLngToScreenCoordinate(coordinateSanFrancisco);
|
||||
ASSERT_DOUBLE_EQ(666.63617954008976, pixelSFBackwards.x);
|
||||
ASSERT_NEAR(666.63617954008976, pixelSFBackwards.x, 1e-2);
|
||||
ASSERT_DOUBLE_EQ(pixelSF.y, pixelSFBackwards.y);
|
||||
|
||||
transform.setLatLng({ 0, 179 });
|
||||
@@ -417,12 +417,12 @@ TEST(Transform, Antimeridian) {
|
||||
const LatLng coordinateWaikiri{ -16.9310, 179.9787 };
|
||||
transform.setLatLngZoom(coordinateWaikiri, 10);
|
||||
ScreenCoordinate pixelWaikiri = transform.latLngToScreenCoordinate(coordinateWaikiri);
|
||||
ASSERT_DOUBLE_EQ(500.00000000007759, pixelWaikiri.x);
|
||||
ASSERT_DOUBLE_EQ(500, pixelWaikiri.y);
|
||||
ASSERT_NEAR(500, pixelWaikiri.x, 1e-2);
|
||||
ASSERT_NEAR(500, pixelWaikiri.y, 1e-2);
|
||||
|
||||
transform.setLatLng({ coordinateWaikiri.latitude, 180.0213 });
|
||||
ScreenCoordinate pixelWaikiriForwards = transform.latLngToScreenCoordinate(coordinateWaikiri);
|
||||
ASSERT_DOUBLE_EQ(437.95953728819512, pixelWaikiriForwards.x);
|
||||
ASSERT_NEAR(437.95953728819512, pixelWaikiriForwards.x, 1e-2);
|
||||
ASSERT_DOUBLE_EQ(pixelWaikiri.y, pixelWaikiriForwards.y);
|
||||
LatLng coordinateFromPixel = transform.screenCoordinateToLatLng(pixelWaikiriForwards);
|
||||
ASSERT_NEAR(coordinateWaikiri.latitude, coordinateFromPixel.latitude, 0.000001);
|
||||
|
||||
@@ -30,6 +30,8 @@ TEST(TileCover, WorldZ0) {
|
||||
TEST(TileCover, Pitch) {
|
||||
Transform transform;
|
||||
transform.resize({ 512, 512 });
|
||||
// slightly offset center so that tile order is better defined
|
||||
transform.setLatLng({ 0.01, -0.01 });
|
||||
transform.setZoom(2);
|
||||
transform.setPitch(40.0 * M_PI / 180.0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user