summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikko Pulkki <mikko.pulkki@mapbox.com>2020-04-27 13:28:46 +0300
committerMikko Pulkki <55925868+mpulkki-mapbox@users.noreply.github.com>2020-05-02 17:07:02 +0300
commit83c06c1f99bf82fb1bc695b8b38fe1ea1dd91dde (patch)
treedbe6a4d917458b67787ad0b2d35e1bf23db062fb
parent57b4b2829e8033d6cf3f7bd48c1fe511e00b830c (diff)
downloadqtlocation-mapboxgl-83c06c1f99bf82fb1bc695b8b38fe1ea1dd91dde.tar.gz
Add FreeCameraOptions to the Map class
-rw-r--r--include/mbgl/map/camera.hpp46
-rw-r--r--include/mbgl/map/map.hpp8
-rw-r--r--include/mbgl/util/geo.hpp5
-rw-r--r--src/mbgl/map/map.cpp10
-rw-r--r--src/mbgl/map/transform.cpp9
-rw-r--r--src/mbgl/map/transform.hpp3
-rw-r--r--src/mbgl/map/transform_state.cpp81
-rw-r--r--src/mbgl/map/transform_state.hpp5
-rw-r--r--src/mbgl/util/camera.cpp47
9 files changed, 210 insertions, 4 deletions
diff --git a/include/mbgl/map/camera.hpp b/include/mbgl/map/camera.hpp
index 726c009231..5f723db1cc 100644
--- a/include/mbgl/map/camera.hpp
+++ b/include/mbgl/map/camera.hpp
@@ -1,9 +1,10 @@
#pragma once
-#include <mbgl/util/geo.hpp>
#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/unitbezier.hpp>
+#include <mbgl/util/geo.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/unitbezier.hpp>
+#include <mbgl/util/vectors.hpp>
#include <functional>
@@ -96,4 +97,45 @@ struct AnimationOptions {
: duration(d) {}
};
+/** Various options for accessing physical properties of the underlying camera entity.
+ A direct access to these properties allows more flexible and precise controlling of the camera
+ while also being fully compatible and interchangeable with CameraOptions. All fields are optional. */
+struct FreeCameraOptions {
+ /** Position of the camera in slightly modified web mercator coordinates
+ - The size of 1 unit is the width of the projected world instead of the "mercator meter".
+ Coordinate [0, 0, 0] is the north-west corner and [1, 1, 0] is the south-east corner.
+ - Z coordinate is conformal and must respect minimum and maximum zoom values.
+ - Zoom is automatically computed from the altitude (z)
+ */
+ optional<vec3> position = nullopt;
+
+ /** Orientation of the camera represented as a unit quaternion [x, y, z, w].
+ The default pose of the camera is such that the forward vector is looking up the -Z axis and
+ the up vector is aligned with north orientation of the map:
+ forward: [0, 0, -1]
+ up: [0, -1, 0]
+ right [1, 0, 0]
+
+ Orientation can be set freely but certain constraints still apply
+ - Orientation must be representable with only pitch and bearing.
+ - Pitch has an upper limit */
+ optional<vec4> orientation = nullopt;
+
+ /** Helper function for setting the mercator position as Lat&Lng and altitude in meters */
+ void setLocation(const LatLngAltitude& location);
+
+ /** Helper function for converting mercator position into Lat&Lng and altitude in meters.
+ This function fails to return a value if `position` is invalid or is not set */
+ optional<LatLngAltitude> getLocation() const;
+
+ /** Helper function for setting orientation of the camera by defining a focus point
+ on the map. Up vector is required in certain scenarios where bearing can't be deduced
+ from the viewing direction */
+ void lookAtPoint(const LatLng& location, const optional<vec3>& upVector = nullopt);
+
+ /** Helper function for setting the orientation of the camera as a pitch and a bearing.
+ Both values are in degrees */
+ void setPitchBearing(double pitch, double bearing);
+};
+
} // namespace mbgl
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 9aa0cb165e..3c61140eb8 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -135,6 +135,14 @@ public:
bool isFullyLoaded() const;
void dumpDebugLogs() const;
+ // FreeCameraOptions provides more direct access to the underlying camera entity.
+ // For backwards compatibility the state set using this API must be representable with
+ // `CameraOptions` as well. Parameters are clamped to a valid range or discarded as invalid
+ // if the conversion to the pitch and bearing presentation is ambiguous. For example orientation
+ // can be invalid if it leads to the camera being upside down or the quaternion has zero length.
+ void setFreeCameraOptions(const FreeCameraOptions& camera);
+ FreeCameraOptions getFreeCameraOptions() const;
+
protected:
class Impl;
const std::unique_ptr<Impl> impl;
diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp
index a653d053f5..ae6c1550fa 100644
--- a/include/mbgl/util/geo.hpp
+++ b/include/mbgl/util/geo.hpp
@@ -241,4 +241,9 @@ public:
}
};
+struct LatLngAltitude {
+ LatLng location = {0.0, 0.0};
+ double altitude = 0.0;
+};
+
} // namespace mbgl
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 91a657294c..afaa1223ce 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -500,4 +500,14 @@ void Map::dumpDebugLogs() const {
Log::Info(Event::General, "--------------------------------------------------------------------------------");
}
+void Map::setFreeCameraOptions(const FreeCameraOptions& camera) {
+ impl->transform.setFreeCameraOptions(camera);
+ impl->cameraMutated = true;
+ impl->onUpdate();
+}
+
+FreeCameraOptions Map::getFreeCameraOptions() const {
+ return impl->transform.getFreeCameraOptions();
+}
+
} // namespace mbgl
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 3e3584e5ba..b20f9b59fd 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -650,4 +650,13 @@ double Transform::getMaxPitchForEdgeInsets(const EdgeInsets& insets) const {
// e.g. Maximum pitch of 60 degrees is when perspective center's offset from the top is 84% of screen height.
}
+FreeCameraOptions Transform::getFreeCameraOptions() const {
+ return state.getFreeCameraOptions();
+}
+
+void Transform::setFreeCameraOptions(const FreeCameraOptions& options) {
+ cancelTransitions();
+ state.setFreeCameraOptions(options);
+}
+
} // namespace mbgl
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 38fb302b7b..90be7a9ef7 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -113,6 +113,9 @@ public:
ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const;
LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Wrapped) const;
+ FreeCameraOptions getFreeCameraOptions() const;
+ void setFreeCameraOptions(const FreeCameraOptions& options);
+
private:
MapObserver& observer;
TransformState state;
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index a61d02963a..d9acecf888 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -177,8 +177,8 @@ void TransformState::updateCameraState() const {
// x & y tracks the center of the map in pixels. However as rendering is done in pixel coordinates the rendering
// origo is actually in the middle of the map (0.5 * worldSize). x&y positions have to be negated because it defines
- // position of the map, not the camera. Moving map 10 units left has the same effect as moving camera 10 units to the
- // right.
+ // position of the map, not the camera. Moving map 10 units left has the same effect as moving camera 10 units to
+ // the right.
const double dx = 0.5 * worldSize - x;
const double dy = 0.5 * worldSize - y;
@@ -226,6 +226,83 @@ void TransformState::updateStateFromCamera() {
setBearing(newBearing);
setPitch(newPitch);
}
+
+FreeCameraOptions TransformState::getFreeCameraOptions() const {
+ updateCameraState();
+
+ FreeCameraOptions options;
+ options.position = camera.getPosition();
+ options.orientation = camera.getOrientation().m;
+
+ return options;
+}
+
+bool TransformState::setCameraPosition(const vec3& position) {
+ if (std::isnan(position[0]) || std::isnan(position[1]) || std::isnan(position[2])) return false;
+
+ const double maxWorldSize = Projection::worldSize(std::pow(2.0, getMaxZoom()));
+ const double minWorldSize = Projection::worldSize(std::pow(2.0, getMinZoom()));
+ const double distToCenter = getCameraToCenterDistance();
+
+ const vec3 updatedPos = vec3{
+ {position[0], position[1], util::clamp(position[2], distToCenter / maxWorldSize, distToCenter / minWorldSize)}};
+
+ camera.setPosition(updatedPos);
+ return true;
+}
+
+bool TransformState::setCameraOrientation(const Quaternion& orientation_) {
+ const vec4& c = orientation_.m;
+ if (std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(c[2]) || std::isnan(c[3])) {
+ return false;
+ }
+
+ // Zero-length quaternions are not valid
+ if (orientation_.length() == 0.0) {
+ return false;
+ }
+
+ Quaternion unitQuat = orientation_.normalized();
+ const vec3 forward = unitQuat.transform({{0.0, 0.0, -1.0}});
+ const vec3 up = unitQuat.transform({{0.0, -1.0, 0.0}});
+
+ if (up[2] < 0.0) {
+ // Camera is upside down and not recoverable
+ return false;
+ }
+
+ const optional<Quaternion> updatedOrientation = util::Camera::orientationFromFrame(forward, up);
+ if (!updatedOrientation) return false;
+
+ camera.setOrientation(updatedOrientation.value());
+ return true;
+}
+
+void TransformState::setFreeCameraOptions(const FreeCameraOptions& options) {
+ if (!valid()) {
+ return;
+ }
+
+ if (!options.position && !options.orientation) return;
+
+ // Check if the state is dirty and camera needs to be synchronized
+ updateMatricesIfNeeded();
+
+ bool changed = false;
+ if (options.orientation && options.orientation.value() != camera.getOrientation().m) {
+ changed |= setCameraOrientation(options.orientation.value());
+ }
+
+ if (options.position && options.position.value() != camera.getPosition()) {
+ changed |= setCameraPosition(options.position.value());
+ }
+
+ if (changed) {
+ updateStateFromCamera();
+ requestMatricesUpdate = true;
+ }
+}
+
void TransformState::updateMatricesIfNeeded() const {
if (!needsMatricesUpdate() || size.isEmpty()) return;
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index aade9be098..aa27067172 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -219,6 +219,9 @@ public:
const mat4& getProjectionMatrix() const;
const mat4& getInvProjectionMatrix() const;
+ FreeCameraOptions getFreeCameraOptions() const;
+ void setFreeCameraOptions(const FreeCameraOptions& options);
+
private:
bool rotatedNorth() const;
@@ -249,6 +252,8 @@ private:
void updateMatricesIfNeeded() const;
bool needsMatricesUpdate() const { return requestMatricesUpdate; }
+ bool setCameraPosition(const vec3& position);
+ bool setCameraOrientation(const Quaternion& orientation);
void updateCameraState() const;
void updateStateFromCamera();
diff --git a/src/mbgl/util/camera.cpp b/src/mbgl/util/camera.cpp
index f64e24ab2d..34d27620ac 100644
--- a/src/mbgl/util/camera.cpp
+++ b/src/mbgl/util/camera.cpp
@@ -212,4 +212,51 @@ optional<Quaternion> Camera::orientationFromFrame(const vec3& forward, const vec
return util::orientationFromPitchBearing(pitch, bearing);
}
} // namespace util
+
+void FreeCameraOptions::setLocation(const LatLngAltitude& location) {
+ position = util::toMercator(location.location, location.altitude);
+}
+
+optional<LatLngAltitude> FreeCameraOptions::getLocation() const {
+ if (!position) {
+ return nullopt;
+ }
+
+ const vec3 positionValue = position.value();
+ if (positionValue[1] < 0.0 || positionValue[1] > 1.0) {
+ return nullopt;
+ }
+
+ const LatLng location = {util::latFromMercatorY(positionValue[1]), util::lngFromMercatorX(positionValue[0])};
+
+ const double metersPerPixel = Projection::getMetersPerPixelAtLatitude(location.latitude(), 0.0);
+ const double worldSize = Projection::worldSize(std::pow(2.0, 0.0));
+ const double altitude = positionValue[2] * worldSize * metersPerPixel;
+
+ return LatLngAltitude{location, altitude};
+}
+
+void FreeCameraOptions::lookAtPoint(const LatLng& location, const optional<vec3>& upVector) {
+ orientation = nullopt;
+ if (!position) {
+ return;
+ }
+
+ const vec3 target = util::toMercator(location, 0.0);
+ const vec3 forward = vec3Sub(target, position.value());
+ vec3 up = upVector ? upVector.value() : vec3{{0.0, 0.0, 1.0}};
+
+ // Flip z-component of the up vector if it's pointing downwards
+ up[2] = std::abs(up[2]);
+
+ const auto newOrientation = util::Camera::orientationFromFrame(forward, up);
+ if (newOrientation) {
+ orientation = newOrientation.value().m;
+ }
+}
+
+void FreeCameraOptions::setPitchBearing(double pitch, double bearing) {
+ orientation = util::orientationFromPitchBearing(pitch * util::DEG2RAD, bearing * util::DEG2RAD).m;
+}
+
} // namespace mbgl \ No newline at end of file