private void LinearAnimation(double t, out float zoomLevel, out LatLon location) { // First, update the zoom. zoomLevel = (float)(_startZoomLevel + (_endZoomLevel - _startZoomLevel) * t); // Update the location. if (_startMercatorCenter != _endMercatorCenter) { if (_startZoomLevel != _endZoomLevel) { // Adjust t so that it depends on the zoom level. This keeps the position animating correctly at a logarthmic scale to // match how zoom level is being calculated. var adjustedT = (_startHeightInMercator - ZoomLevelToMercatorAltitude(zoomLevel)) / (_startHeightInMercator - _endHeightInMercator); var mercatorCoordinate = Interpolate(_startMercatorCenter, _endMercatorCenter, adjustedT); location = mercatorCoordinate.ToLatLon(); } else { var mercatorCoordinate = Interpolate(_startMercatorCenter, _endMercatorCenter, t); location = mercatorCoordinate.ToLatLon(); } } else { location = _endMercatorCenter.ToLatLon(); } }
private void Update() { // First case handles when the map is selected and being dragged and/or zoomed. if (_isInteracting && _pointer != null && CoreServices.InputSystem.FocusProvider.TryGetFocusDetails(_pointer, out var focusDetails) && focusDetails.Object == gameObject) { // The current point the ray is targeting has been calculated in OnPointerDragged. Smooth it here. var panSmoothness = Mathf.Lerp(0.0f, 0.5f, _panSmoothness); _smoothedPointInLocalSpace = DynamicExpDecay(_smoothedPointInLocalSpace, _currentPointInLocalSpace, panSmoothness); // Reconstruct ray from pointer position to focus details. var rayTargetPoint = MapRenderer.transform.TransformPoint(_smoothedPointInLocalSpace); var ray = new Ray(_pointer.Position, (rayTargetPoint - _pointer.Position).normalized); MapInteractionController.PanAndZoom(ray, _targetPointInMercator, _targetAltitudeInMeters, ComputeZoomToApply()); // Update starting point so that the focus point tracks with this point. _targetPointInLocalSpace = MapRenderer.TransformMercatorWithAltitudeToLocalPoint(_targetPointInMercator, _targetAltitudeInMeters); // Also override the FocusDetails so that the pointer ray tracks the target coordinate. focusDetails.Point = MapRenderer.transform.TransformPoint(_targetPointInLocalSpace); focusDetails.PointLocalSpace = _targetPointInLocalSpace; CoreServices.InputSystem.FocusProvider.TryOverrideFocusDetails(_pointer, focusDetails); // Reset timings used for tap-and-hold and double tap. _lastPointerDownTime = float.MaxValue; _lastClickTime = float.MinValue; } else if (_zoomPointer != null && _isFocused) // This case handles when the map is just focused, not selected, and being zoomed. { var zoomToApply = ComputeZoomToApply(); if (zoomToApply != 0) { if (!_isInteracting) { _isInteracting = true; MapInteractionController.OnInteractionStarted?.Invoke(); } var pointerRayPosition = _zoomPointer.Position; var pointerRayDirection = (_zoomPointer.Rotation * Vector3.forward).normalized; MapInteractionController.Zoom(zoomToApply, new Ray(pointerRayPosition, pointerRayDirection)); // Reset timings used for tap-and-hold and double tap. _lastPointerDownTime = float.MaxValue; _lastClickTime = float.MinValue; } else { if (_zoomPointer != null && _isInteracting) { // We were zooming last frame. End the interaction. MapInteractionController.OnInteractionEnded?.Invoke(); _isInteracting = false; _zoomPointer = null; } } } else { if (_isInteracting) { // Last frame there was interaction happening. This is the first frame where the interaction has ended. MapInteractionController.OnInteractionEnded?.Invoke(); _isInteracting = false; } } // Check for a tap and hold. if (_pointer != null && (Time.time - _lastPointerDownTime) > TapAndHoldThresholdInSeconds) { MapInteractionController.OnTapAndHold?.Invoke(new LatLonAlt(_targetPointInMercator.ToLatLon(), _targetAltitudeInMeters)); // Reset timings used for tap-and-hold and double tap. _lastPointerDownTime = float.MaxValue; _lastClickTime = float.MinValue; } }
private void Update() { var isInteractingWithMap = _panningPointer != null; if (isInteractingWithMap && CoreServices.InputSystem.FocusProvider.TryGetFocusDetails(_panningPointer, out FocusDetails focusDetails)) { // Determine amount to zoom in or out. var directionalZoomAmount = 0.0f; var zoomMagnitude = _currentZoomValue.magnitude; if (zoomMagnitude > JoystickDeadZone) { var angle = Mathf.Rad2Deg * Mathf.Abs(Mathf.Atan2(_currentZoomValue.y, _currentZoomValue.x)); if (Mathf.Abs(90.0f - angle) < 75) { directionalZoomAmount = _currentZoomValue.y; } } // Compute more terms if there is any zoom to apply. var zoomRatio = 1.0; var zoomLevelToUseForInteraction = _mapRenderer.ZoomLevel; if (directionalZoomAmount != 0) { var zoomSpeed = Mathf.Lerp(0.01f, 0.045f, _zoomSpeed); var zoomToApply = zoomSpeed * directionalZoomAmount; var oldZoomLevel = _mapRenderer.ZoomLevel; var newZoomLevel = Math.Min( _mapRenderer.MaximumZoomLevel, Math.Max(_mapRenderer.MinimumZoomLevel, oldZoomLevel + zoomToApply)); zoomRatio = Math.Pow(2, oldZoomLevel - 1) / Math.Pow(2, newZoomLevel - 1); zoomLevelToUseForInteraction = newZoomLevel; } // The _startingPointInLocalSpace can be updated now as zoom changed so it's altitude may change as well. // A future improvement to make here is to actually requery the altitude of the _startingPointInMercatorSpace, // as this altitude can also change based on the level of detail being shown. var offsetAltitudeInMeters = _startingAltitudeInMeters - _mapRenderer.ElevationBaseline; var equatorialCircumferenceInLocalSpace = Math.Pow(2, zoomLevelToUseForInteraction - 1); var altitudeInLocalSpace = offsetAltitudeInMeters * _startingMercatorScale * (equatorialCircumferenceInLocalSpace / MapRendererTransformExtensions.EquatorialCircumferenceInWgs84Meters); _startingPointInLocalSpace.y = (float)(_mapRenderer.LocalMapBaseHeight + altitudeInLocalSpace); // Now we can raycast an imaginary plane orignating from the updated _startingPointInLocalSpace. var rayPositionInMapLocalSpace = _mapRenderer.transform.InverseTransformPoint(_panningPointer.Position); var rayDirectionInMapLocalSpace = _mapRenderer.transform.InverseTransformDirection(_panningPointer.Rotation * Vector3.forward).normalized; var rayInMapLocalSpace = new Ray(rayPositionInMapLocalSpace, rayDirectionInMapLocalSpace.normalized); var hitPlaneInMapLocalSpace = new Plane(Vector3.up, _startingPointInLocalSpace); if (hitPlaneInMapLocalSpace.Raycast(rayInMapLocalSpace, out float enter)) { // This point will be used to determine how much to translate the map. // Decaying the resulting position applies some smoothing to the input. var panSmoothness = Mathf.Lerp(0.0f, 0.5f, _panSmoothness); _currentPointInLocalSpace = DynamicExpDecay(_currentPointInLocalSpace, rayInMapLocalSpace.GetPoint(enter), panSmoothness); // Also override the FocusDetails so that the pointer ray tracks with the map. // Otherwise, it would remain fixed in world space. focusDetails.Point = _mapRenderer.transform.TransformPoint(_currentPointInLocalSpace); focusDetails.PointLocalSpace = _currentPointInLocalSpace; CoreServices.InputSystem.FocusProvider.TryOverrideFocusDetails(_panningPointer, focusDetails); } // Apply zoom now, if needed. if (directionalZoomAmount != 0) { _mapRenderer.ZoomLevel = zoomLevelToUseForInteraction; var deltaToCenterInMercatorSpace = _startingPointInMercator - _startingMapCenterInMercator; var adjustedDeltaToCenterInMercatorSpace = zoomRatio * deltaToCenterInMercatorSpace; _startingMapCenterInMercator = _startingPointInMercator - adjustedDeltaToCenterInMercatorSpace; } // Apply pan translation. var deltaInLocalSpace = _currentPointInLocalSpace - _startingPointInLocalSpace; var deltaInMercatorSpace = MapRendererTransformExtensions.TransformLocalDirectionToMercator(deltaInLocalSpace, zoomLevelToUseForInteraction); var newCenterInMercator = _startingMapCenterInMercator - deltaInMercatorSpace; var newClampedCenterInMercator = new MercatorCoordinate(newCenterInMercator.X, Math.Max(Math.Min(0.5, newCenterInMercator.Y), -0.5)); _mapRenderer.Center = newClampedCenterInMercator.ToLatLon(); } }