diff --git a/shared/src/map/camera/MapCamera2d.cpp b/shared/src/map/camera/MapCamera2d.cpp index 2a82db097..3e903cb34 100644 --- a/shared/src/map/camera/MapCamera2d.cpp +++ b/shared/src/map/camera/MapCamera2d.cpp @@ -44,6 +44,11 @@ MapCamera2d::MapCamera2d(const std::shared_ptr &mapInterface, floa zoom = zoomMax; } +MapCamera2d::~MapCamera2d() { + // Stop any ongoing animations to avoid callbacks after destruction. + freeze(true); +} + void MapCamera2d::viewportSizeChanged() { Vec2I viewportSize = mapInterface->getRenderingContext()->getViewportSize(); if (viewportSize.x > 0 && viewportSize.y > 0 && zoomMin < 0) { diff --git a/shared/src/map/camera/MapCamera2d.h b/shared/src/map/camera/MapCamera2d.h index 563c39977..623495fe7 100644 --- a/shared/src/map/camera/MapCamera2d.h +++ b/shared/src/map/camera/MapCamera2d.h @@ -32,7 +32,7 @@ class MapCamera2d : public MapCameraInterface, public: MapCamera2d(const std::shared_ptr &mapInterface, float screenDensityPpi); - ~MapCamera2d(){}; + ~MapCamera2d(); void freeze(bool freeze) override; diff --git a/shared/src/map/camera/MapCamera3d.cpp b/shared/src/map/camera/MapCamera3d.cpp index b6b34df87..be452d9cd 100644 --- a/shared/src/map/camera/MapCamera3d.cpp +++ b/shared/src/map/camera/MapCamera3d.cpp @@ -56,6 +56,11 @@ MapCamera3d::MapCamera3d(const std::shared_ptr &mapInterface, floa updateZoom(GLOBE_MIN_ZOOM); } +MapCamera3d::~MapCamera3d() { + // Ensure no animation callback touches a partially destroyed camera. + freeze(true); +} + void MapCamera3d::viewportSizeChanged() { auto mapInterface = this->mapInterface; auto renderingContext = mapInterface ? mapInterface->getRenderingContext() : nullptr; @@ -114,8 +119,14 @@ void MapCamera3d::moveToCenterPositionZoom(const ::Coord ¢erPosition, double if (animated) { std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); std::lock_guard lock(animationMutex); + long long animationDurationMs; + { + std::lock_guard lock(paramMutex); + animationDurationMs = cameraZoomConfig.animationDurationMs; + } + coordAnimation = std::make_shared( - cameraZoomConfig.animationDurationMs, focusPointPosition, focusPosition, centerPosition, + animationDurationMs, focusPointPosition, focusPosition, centerPosition, InterpolatorFunction::EaseInOut, [weakSelf](Coord positionMapSystem) { if (auto selfPtr = weakSelf.lock()) { @@ -187,8 +198,14 @@ void MapCamera3d::moveToCenterPosition(const ::Coord ¢erPosition, bool anima if (animated) { std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); std::lock_guard lock(animationMutex); + long long animationDurationMs; + { + std::lock_guard lock(paramMutex); + animationDurationMs = cameraZoomConfig.animationDurationMs; + } + coordAnimation = std::make_shared( - cameraZoomConfig.animationDurationMs, focusPointPosition, focusPosition, centerPosition, + animationDurationMs, focusPointPosition, focusPosition, centerPosition, InterpolatorFunction::EaseInOut, [weakSelf](Coord positionMapSystem) { if (auto selfPtr = weakSelf.lock()) { @@ -251,7 +268,11 @@ void MapCamera3d::moveToBoundingBox(const RectCoord &boundingBox, float paddingP // the user of moveToBoundingBox wants that the bounding box is centered, // if you have camera verticaldisplacement, this is not the case, this should // be fixed for this function as it only works with 0 - assert(valueForZoom(cameraZoomConfig.verticalDisplacementInterpolationValues, zoom) == 0); + { + // Protect against concurrent config updates while reading interpolation values + std::lock_guard lock(paramMutex); + assert(valueForZoom(cameraZoomConfig.verticalDisplacementInterpolationValues, zoom) == 0); + } auto x = 0.5 * boundingBox.topLeft.x + 0.5 * boundingBox.bottomRight.x; auto y = 0.5 * boundingBox.topLeft.y + 0.5 * boundingBox.bottomRight.y; @@ -275,9 +296,15 @@ void MapCamera3d::setZoom(double zoom, bool animated) { if (animated) { std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); + long long animationDurationMs; + { + std::lock_guard lock(paramMutex); + animationDurationMs = cameraZoomConfig.animationDurationMs; + } + std::lock_guard lock(animationMutex); zoomAnimation = std::make_shared( - cameraZoomConfig.animationDurationMs, this->zoom, targetZoom, InterpolatorFunction::EaseIn, + animationDurationMs, this->zoom, targetZoom, InterpolatorFunction::EaseIn, [weakSelf](double zoom) { if (auto selfPtr = weakSelf.lock()) { selfPtr->setZoom(zoom, false); @@ -313,9 +340,15 @@ void MapCamera3d::setRotation(float angle, bool animated) { newAngle -= 360.0; } std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); + long long animationDurationMs; + { + std::lock_guard lock(paramMutex); + animationDurationMs = cameraZoomConfig.animationDurationMs; + } + std::lock_guard lock(animationMutex); rotationAnimation = std::make_shared( - cameraZoomConfig.animationDurationMs, currentAngle, newAngle, InterpolatorFunction::Linear, + animationDurationMs, currentAngle, newAngle, InterpolatorFunction::Linear, [weakSelf](double angle) { if (auto selfPtr = weakSelf.lock()) { selfPtr->setRotation(angle, false); @@ -1169,9 +1202,16 @@ bool MapCamera3d::onTwoFingerMoveComplete() { if (config.snapToNorthEnabled && !cameraFrozen && (angle < ROTATION_LOCKING_ANGLE || angle > (360 - ROTATION_LOCKING_ANGLE))) { std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); + + long long animationDurationMs; + { + std::lock_guard lock(paramMutex); + animationDurationMs = cameraZoomConfig.animationDurationMs; + } + std::lock_guard lock(animationMutex); rotationAnimation = std::make_shared( - cameraZoomConfig.animationDurationMs, this->angle, angle < ROTATION_LOCKING_ANGLE ? 0 : 360, + animationDurationMs, this->angle, angle < ROTATION_LOCKING_ANGLE ? 0 : 360, InterpolatorFunction::EaseInOut, [weakSelf](double angle) { if (auto selfPtr = weakSelf.lock()) { @@ -1769,7 +1809,10 @@ void MapCamera3d::setCameraConfig(const Camera3dConfig &config, std::optional lock(paramMutex); + return cameraZoomConfig; +} void MapCamera3d::notifyListenerBoundsChange() { notifyListeners(ListenerType::BOUNDS); } @@ -1779,15 +1822,25 @@ void MapCamera3d::updateZoom(double zoom_) { std::lock_guard lock(paramMutex); zoom = std::clamp(zoom_, zoomMax, zoomMin); - cameraVerticalDisplacement = getCameraVerticalDisplacement(); - cameraPitch = getCameraPitch(); + cameraVerticalDisplacement = getCameraVerticalDisplacementUnlocked(); + cameraPitch = getCameraPitchUnlocked(); } double MapCamera3d::getCameraVerticalDisplacement() { + std::lock_guard lock(paramMutex); + return getCameraVerticalDisplacementUnlocked(); +} + +double MapCamera3d::getCameraVerticalDisplacementUnlocked() { return valueForZoom(cameraZoomConfig.verticalDisplacementInterpolationValues, zoom); } double MapCamera3d::getCameraPitch() { + std::lock_guard lock(paramMutex); + return getCameraPitchUnlocked(); +} + +double MapCamera3d::getCameraPitchUnlocked() { return valueForZoom(cameraZoomConfig.pitchInterpolationValues, zoom); } diff --git a/shared/src/map/camera/MapCamera3d.h b/shared/src/map/camera/MapCamera3d.h index 3a9b39717..1a060deee 100644 --- a/shared/src/map/camera/MapCamera3d.h +++ b/shared/src/map/camera/MapCamera3d.h @@ -37,8 +37,7 @@ class MapCamera3d : public MapCameraInterface, public std::enable_shared_from_this { public: MapCamera3d(const std::shared_ptr &mapInterface, float screenDensityPpi); - - ~MapCamera3d(){}; + ~MapCamera3d(); void freeze(bool freeze) override; @@ -195,6 +194,11 @@ class MapCamera3d : public MapCameraInterface, double getCameraVerticalDisplacement(); double getCameraPitch(); + private: + // Unlocked versions - caller must hold paramMutex + double getCameraVerticalDisplacementUnlocked(); + double getCameraPitchUnlocked(); + Vec2F calculateDistance(double latTopLeft, double lonTopLeft, double latBottomRight, double lonBottomRight); double haversineDistance(double lat1, double lon1, double lat2, double lon2); double zoomForMeterWidth(Vec2I sizeViewport, Vec2F sizeMeters); @@ -213,7 +217,6 @@ class MapCamera3d : public MapCameraInterface, Coord focusPointPosition; double cameraVerticalDisplacement = 0.0; double cameraPitch = 0; // looking up or down - double fieldOfView = 10.0f; double zoom; double angle = 0;