13public sealed class View
16 private PointF _zoomAnchorScreenPoint;
17 private PointF _zoomAnchorWorldPoint;
25 public Guid
Id {
get; } = Guid.NewGuid();
55 public float MinZoom {
get;
set; } = 0.1f;
64 internal float EffectOpacity {
get;
set; } = 1f;
65 internal float EffectReveal {
get;
set; } = 1f;
67 internal PointF EffectOffsetFactor {
get;
set; } = PointF.Empty;
68 internal PointF EffectOffsetPx {
get;
set; } = PointF.Empty;
70 internal bool HasPresentationEffect =>
71 EffectOpacity < 0.9999f
72 || EffectReveal < 0.9999f
73 || Math.Abs(EffectOffsetFactor.X) > 0.0001f
74 || Math.Abs(EffectOffsetFactor.Y) > 0.0001f
75 || Math.Abs(EffectOffsetPx.X) > 0.0001f
76 || Math.Abs(EffectOffsetPx.Y) > 0.0001f;
78 internal bool BlocksViewsBelow => !HasPresentationEffect;
85 Camera.GetVisibleWorldSizePx = () =>
Viewport.VisibleWorldSizePx;
112 ArgumentNullException.ThrowIfNull(layer);
123 if (durationSeconds <= 0f)
127 SnapCameraToZoomAnchor(layer, screenPoint, worldUnderCursor);
131 _zoomAnchorLayer = layer;
132 _zoomAnchorScreenPoint = screenPoint;
133 _zoomAnchorWorldPoint = worldUnderCursor;
135 Viewport.ZoomToOverDuration(targetZoom, durationSeconds);
142 internal void Update(
float dtSeconds)
144 if (_zoomAnchorLayer is { } anchorLayer)
149 SnapCameraToZoomAnchor(
151 _zoomAnchorScreenPoint,
152 _zoomAnchorWorldPoint);
164 private void SnapCameraToZoomAnchor(
169 float zoom = Viewport.Zoom > 0f
173 PointF effectOffset = GetEffectOffsetPx(layer);
176 Viewport.TargetRectPx.Left
177 + Viewport.ScreenOffsetPx.X
181 Viewport.TargetRectPx.Top
182 + Viewport.ScreenOffsetPx.Y
186 if (Math.Abs(parallax) < 1e-6f)
189 float localX = screenPoint.X - offsetX;
190 float localY = screenPoint.Y - offsetY;
195 (worldPoint.X - localX / zoom)
199 (worldPoint.Y - localY / zoom)
205 private void ClearZoomAnchor()
207 _zoomAnchorLayer =
null;
208 _zoomAnchorScreenPoint = PointF.Empty;
209 _zoomAnchorWorldPoint = PointF.Empty;
212 #region Coordinate conversion methods
235 RenderContext? context = GetCurrentRenderContext();
236 float zoom = context?.ViewportZoom
238 Rectangle targetRect = context?.ViewportTargetRectPx
240 PointF screenOffset = context?.ViewportScreenOffsetPx
242 PointF cameraPosition = context?.CameraPositionPx
244 PointF effectOffset = GetEffectOffsetPx(layer);
245 float offsetX = targetRect.Left + screenOffset.X + effectOffset.X;
246 float offsetY = targetRect.Top + screenOffset.Y + effectOffset.Y;
249 float worldX = cameraPosition.X * parallax
250 + (screenPx.X - offsetX) / zoom;
252 float worldY = cameraPosition.Y * parallax
253 + (screenPx.Y - offsetY) / zoom;
255 return new PointF(worldX, worldY);
280 RenderContext? context = GetCurrentRenderContext();
281 float zoom = context?.ViewportZoom
283 Rectangle targetRect = context?.ViewportTargetRectPx
285 PointF screenOffset = context?.ViewportScreenOffsetPx
287 PointF cameraPosition = context?.CameraPositionPx
289 PointF effectOffset = GetEffectOffsetPx(layer);
290 float offsetX = targetRect.Left + screenOffset.X + effectOffset.X;
291 float offsetY = targetRect.Top + screenOffset.Y + effectOffset.Y;
294 float screenX = offsetX + (worldPx.X - cameraPosition.X * parallax) * zoom;
295 float screenY = offsetY + (worldPx.Y - cameraPosition.Y * parallax) * zoom;
297 return new PointF(screenX, screenY);
311 return layer.CoordinateSystem.GetSceneLayerCoordinatesAtPixel(layer, worldPx);
327 throw new ArgumentNullException(nameof(layer));
329 RenderContext? context = GetCurrentRenderContext();
330 float zoom = context?.ViewportZoom
332 Rectangle targetRect = context?.ViewportTargetRectPx
334 PointF screenOffset = context?.ViewportScreenOffsetPx
336 PointF cameraPosition = context?.CameraPositionPx
339 PointF effectOffset = GetEffectOffsetPx(layer);
340 float offsetX = targetRect.Left + screenOffset.X + effectOffset.X;
341 float offsetY = targetRect.Top + screenOffset.Y + effectOffset.Y;
346 float localLeft = worldRect.Left - cameraPosition.X * parallax;
347 float localTop = worldRect.Top - cameraPosition.Y * parallax;
349 float scaledLeft = localLeft * zoom;
350 float scaledTop = localTop * zoom;
351 float scaledWidth = worldRect.Width * zoom;
352 float scaledHeight = worldRect.Height * zoom;
354 float screenLeft = offsetX + scaledLeft;
355 float screenTop = offsetY + scaledTop;
357 return new RectangleF(screenLeft, screenTop, scaledWidth, scaledHeight);
374 throw new ArgumentNullException(nameof(layer));
376 RenderContext? context = GetCurrentRenderContext();
377 float zoom = context?.ViewportZoom
379 Rectangle targetRect = context?.ViewportTargetRectPx
381 PointF screenOffset = context?.ViewportScreenOffsetPx
383 PointF cameraPosition = context?.CameraPositionPx
386 PointF effectOffset = GetEffectOffsetPx(layer);
387 float offsetX = targetRect.Left + screenOffset.X + effectOffset.X;
388 float offsetY = targetRect.Top + screenOffset.Y + effectOffset.Y;
393 float localLeft = screenRect.Left - offsetX;
394 float localTop = screenRect.Top - offsetY;
397 float worldLeft = cameraPosition.X * parallax + localLeft / zoom;
398 float worldTop = cameraPosition.Y * parallax + localTop / zoom;
400 float worldWidth = screenRect.Width / zoom;
401 float worldHeight = screenRect.Height / zoom;
403 return new RectangleF(worldLeft, worldTop, worldWidth, worldHeight);
406 internal PointF GetEffectOffsetPx(
SceneLayer? layer)
408 RenderContext? context = GetCurrentRenderContext();
409 PointF factor = context?.ViewEffectOffsetFactor
410 ?? EffectOffsetFactor;
411 PointF pixels = context?.ViewEffectOffsetPx
413 Rectangle targetRect = context?.ViewportTargetRectPx
416 if (layer is not
null)
419 factor.X + layer.EffectOffsetFactor.X,
420 factor.Y + layer.EffectOffsetFactor.Y);
422 pixels.X + layer.EffectOffsetPx.X,
423 pixels.Y + layer.EffectOffsetPx.Y);
426 var wrappedOffset = layer is
null ? PointF.Empty :
Gondwana.
Drawing.WrappedDrawable.ScreenOffset(
this, layer);
428 pixels.X + factor.X * targetRect.Width + wrappedOffset.X,
429 pixels.Y + factor.Y * targetRect.Height + wrappedOffset.Y);
432 internal RectangleF GetPresentationBoundsPx()
434 PointF offset = GetEffectOffsetPx(layer:
null);
435 RectangleF viewport = GetRenderViewportTargetRectPx();
437 return new RectangleF(
438 viewport.Left + offset.X,
439 viewport.Top + offset.Y,
444 internal Rectangle GetRenderViewportTargetRectPx() =>
445 GetCurrentRenderContext()?.ViewportTargetRectPx
448 private RenderContext? GetCurrentRenderContext()
450 RenderContext? context = RenderContext.Current;
451 return context is not
null && ReferenceEquals(context.View,
this)
456 #endregion Coordinate conversion methods