forked from amkovkov/GranuSightSoftware2
feat: views tpuchscreen zoom and pan
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
using Avalonia;
|
||||||
|
|
||||||
|
namespace GSS2.Core.Extentions;
|
||||||
|
|
||||||
|
public static class PointHelper
|
||||||
|
{
|
||||||
|
public static Point Center(Point p1, Point p2) => new Point((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
|
||||||
|
public static Point Center(params Point[] ps) => new Point(ps.Average(p => p.X), ps.Average(p => p.Y));
|
||||||
|
}
|
||||||
|
|
||||||
+81
-14
@@ -7,6 +7,8 @@ using Avalonia.Media.Imaging;
|
|||||||
using Mat = OpenCvSharp.Mat;
|
using Mat = OpenCvSharp.Mat;
|
||||||
using Cv2 = OpenCvSharp.Cv2;
|
using Cv2 = OpenCvSharp.Cv2;
|
||||||
|
|
||||||
|
using GSS2.Core.Extentions;
|
||||||
|
|
||||||
namespace GSS2.UI.Core;
|
namespace GSS2.UI.Core;
|
||||||
|
|
||||||
public class OpenCvImage : Control
|
public class OpenCvImage : Control
|
||||||
@@ -25,7 +27,8 @@ public class OpenCvImage : Control
|
|||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point? _lastPosition = null;
|
private Point? _lastmousePosition = null;
|
||||||
|
private Dictionary<int, Point>? _lastTouchPositions = null;
|
||||||
private float _zoom = 1;
|
private float _zoom = 1;
|
||||||
private float _panX = 0;
|
private float _panX = 0;
|
||||||
private float _panY = 0;
|
private float _panY = 0;
|
||||||
@@ -58,7 +61,14 @@ public class OpenCvImage : Control
|
|||||||
|
|
||||||
public OpenCvImage()
|
public OpenCvImage()
|
||||||
: base()
|
: base()
|
||||||
{ }
|
{
|
||||||
|
DoubleTapped += (_, _) =>
|
||||||
|
{
|
||||||
|
_zoom = 1f;
|
||||||
|
_panX = 0f;
|
||||||
|
_panY = 0f;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
{
|
{
|
||||||
@@ -124,10 +134,6 @@ public class OpenCvImage : Control
|
|||||||
_panY = 0f;
|
_panY = 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
|
||||||
{
|
|
||||||
Console.WriteLine(string.Format("PointerWheelChanged Type Touch {} {}", ea.GetPosition(this), ea.Delta));
|
|
||||||
}
|
|
||||||
base.OnPointerWheelChanged(ea);
|
base.OnPointerWheelChanged(ea);
|
||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
}
|
}
|
||||||
@@ -144,12 +150,18 @@ public class OpenCvImage : Control
|
|||||||
_panY = 0f;
|
_panY = 0f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_lastPosition = ea.GetPosition(this);
|
_lastmousePosition = ea.GetPosition(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
else if (ea.Pointer.Type == PointerType.Touch)
|
||||||
{
|
{
|
||||||
|
if (_lastTouchPositions is null)
|
||||||
|
_lastTouchPositions = new Dictionary<int, Point>();
|
||||||
|
|
||||||
|
if (!_lastTouchPositions.ContainsKey(ea.Pointer.Id) && _lastTouchPositions.Count() < 2)
|
||||||
|
_lastTouchPositions.Add(ea.Pointer.Id, ea.GetPosition(this));
|
||||||
|
else if (_lastTouchPositions.ContainsKey(ea.Pointer.Id))
|
||||||
|
_lastTouchPositions[ea.Pointer.Id] = ea.GetPosition(this);
|
||||||
}
|
}
|
||||||
base.OnPointerPressed(ea);
|
base.OnPointerPressed(ea);
|
||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
@@ -159,11 +171,12 @@ public class OpenCvImage : Control
|
|||||||
if (ea.Pointer.Type == PointerType.Mouse)
|
if (ea.Pointer.Type == PointerType.Mouse)
|
||||||
{
|
{
|
||||||
if (!ea.Properties.IsLeftButtonPressed)
|
if (!ea.Properties.IsLeftButtonPressed)
|
||||||
_lastPosition = null;
|
_lastmousePosition = null;
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
else if (ea.Pointer.Type == PointerType.Touch)
|
||||||
{
|
{
|
||||||
|
if (_lastTouchPositions is not null && _lastTouchPositions.ContainsKey(ea.Pointer.Id))
|
||||||
|
_lastTouchPositions.Remove(ea.Pointer.Id);
|
||||||
}
|
}
|
||||||
base.OnPointerReleased(ea);
|
base.OnPointerReleased(ea);
|
||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
@@ -172,21 +185,75 @@ public class OpenCvImage : Control
|
|||||||
{
|
{
|
||||||
if (ea.Pointer.Type == PointerType.Mouse)
|
if (ea.Pointer.Type == PointerType.Mouse)
|
||||||
{
|
{
|
||||||
if (_lastPosition is null || _zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
|
if (_lastmousePosition is null || _zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
|
||||||
{
|
{
|
||||||
base.OnPointerMoved(ea);
|
base.OnPointerMoved(ea);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var position = ea.GetPosition(this);
|
var position = ea.GetPosition(this);
|
||||||
_panX -= (float)(_lastPosition.Value.X - position.X);
|
_panX -= (float)(_lastmousePosition.Value.X - position.X);
|
||||||
_panY -= (float)(_lastPosition.Value.Y - position.Y);
|
_panY -= (float)(_lastmousePosition.Value.Y - position.Y);
|
||||||
_lastPosition = position;
|
_lastmousePosition = position;
|
||||||
base.OnPointerMoved(ea);
|
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
else if (ea.Pointer.Type == PointerType.Touch)
|
||||||
{
|
{
|
||||||
|
if (_lastTouchPositions is null)
|
||||||
|
{
|
||||||
|
base.OnPointerMoved(ea);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_lastTouchPositions.ContainsKey(ea.Pointer.Id) && _lastTouchPositions.Count() < 2)
|
||||||
|
{
|
||||||
|
_lastTouchPositions.Add(ea.Pointer.Id, ea.GetPosition(this));
|
||||||
|
base.OnPointerMoved(ea);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lastTouchPositions.Count() == 1 && _zoom != 1) // Если есть только одно прикосновение и зум != 1, то токлько перемещение
|
||||||
|
{
|
||||||
|
var position = ea.GetPosition(this);
|
||||||
|
_panX -= (float)(_lastTouchPositions[ea.Pointer.Id].X - position.X);
|
||||||
|
_panY -= (float)(_lastTouchPositions[ea.Pointer.Id].Y - position.Y); // Ось Y инвертированна
|
||||||
|
_lastTouchPositions[ea.Pointer.Id] = position;
|
||||||
|
}
|
||||||
|
else if (_lastTouchPositions.Count() == 2) // Если два прикосновения, то перемещение с приближением
|
||||||
|
{
|
||||||
|
var lastPosition1 = _lastTouchPositions.Skip(0).First().Value;
|
||||||
|
var lastPosition2 = _lastTouchPositions.Skip(1).First().Value;
|
||||||
|
var lastDistance = Point.Distance(lastPosition1, lastPosition2);
|
||||||
|
var lastCenter = PointHelper.Center(lastPosition1, lastPosition2);
|
||||||
|
|
||||||
|
_lastTouchPositions[ea.Pointer.Id] = ea.GetPosition(this);
|
||||||
|
|
||||||
|
var newPosition1 = _lastTouchPositions.Skip(0).First().Value;
|
||||||
|
var newPosition2 = _lastTouchPositions.Skip(1).First().Value;
|
||||||
|
var newDistance = Point.Distance(newPosition1, newPosition2);
|
||||||
|
var newCenter = PointHelper.Center(newPosition1, newPosition2);
|
||||||
|
|
||||||
|
var zoomFactor = newDistance / lastDistance;
|
||||||
|
var newZoom = (float)(_zoom * zoomFactor);
|
||||||
|
newZoom = (float)Math.Clamp(newZoom, 1f, 10f);
|
||||||
|
|
||||||
|
var imageX = (newCenter.X - Bounds.Width / 2 - _panX) / _zoom;
|
||||||
|
var imageY = (newCenter.Y - Bounds.Height / 2 - _panY) / _zoom;
|
||||||
|
|
||||||
|
_zoom = newZoom;
|
||||||
|
|
||||||
|
if (_zoom > 1f)
|
||||||
|
{
|
||||||
|
_panX = (float)(newCenter.X - Bounds.Width / 2 - imageX * _zoom);
|
||||||
|
_panY = (float)(newCenter.Y - Bounds.Height / 2 - imageY * _zoom);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_zoom = 1f;
|
||||||
|
_panX = 0f;
|
||||||
|
_panY = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
base.OnPointerMoved(ea);
|
||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Avalonia.Input;
|
|||||||
|
|
||||||
using GSS2.UI.Core.ViewModels;
|
using GSS2.UI.Core.ViewModels;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using GSS2.Core.Extentions;
|
||||||
|
|
||||||
namespace GSS2.UI.Core.Views;
|
namespace GSS2.UI.Core.Views;
|
||||||
|
|
||||||
@@ -191,7 +192,8 @@ public partial class CameraView : OpenGlControlBase
|
|||||||
private readonly Dictionary<int, IntPtr> _importedDmaBufers = new Dictionary<int, nint>();
|
private readonly Dictionary<int, IntPtr> _importedDmaBufers = new Dictionary<int, nint>();
|
||||||
private bool _isViewFinderStarted = false;
|
private bool _isViewFinderStarted = false;
|
||||||
private OpenGLContext? _openGLContext = null;
|
private OpenGLContext? _openGLContext = null;
|
||||||
private Point? _lastPosition = null;
|
private Point? _lastMousePosition = null;
|
||||||
|
private Dictionary<int, Point>? _lastTouchPositions = null;
|
||||||
private float _zoom = 1;
|
private float _zoom = 1;
|
||||||
private float _panX = 0;
|
private float _panX = 0;
|
||||||
private float _panY = 0;
|
private float _panY = 0;
|
||||||
@@ -220,6 +222,13 @@ public partial class CameraView : OpenGlControlBase
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
_logger.LogInformation("{} initialized", nameof(CameraView));
|
_logger.LogInformation("{} initialized", nameof(CameraView));
|
||||||
|
|
||||||
|
DoubleTapped += (_, _) =>
|
||||||
|
{
|
||||||
|
_zoom = 1f;
|
||||||
|
_panX = 0f;
|
||||||
|
_panY = 0f;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs ea)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs ea)
|
||||||
@@ -294,10 +303,6 @@ public partial class CameraView : OpenGlControlBase
|
|||||||
_panY = 0f;
|
_panY = 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("PointerWheelChanged Type Touch {} {}", ea.GetPosition(this), ea.Delta);
|
|
||||||
}
|
|
||||||
base.OnPointerWheelChanged(ea);
|
base.OnPointerWheelChanged(ea);
|
||||||
}
|
}
|
||||||
protected override void OnPointerPressed(PointerPressedEventArgs ea)
|
protected override void OnPointerPressed(PointerPressedEventArgs ea)
|
||||||
@@ -313,12 +318,18 @@ public partial class CameraView : OpenGlControlBase
|
|||||||
_panY = 0f;
|
_panY = 0f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_lastPosition = ea.GetPosition(this);
|
_lastMousePosition = ea.GetPosition(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
else if (ea.Pointer.Type == PointerType.Touch)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("PointerPressed Type Touch {}", ea.GetPosition(this));
|
if (_lastTouchPositions is null)
|
||||||
|
_lastTouchPositions = new Dictionary<int, Point>();
|
||||||
|
|
||||||
|
if (!_lastTouchPositions.ContainsKey(ea.Pointer.Id) && _lastTouchPositions.Count() < 2)
|
||||||
|
_lastTouchPositions.Add(ea.Pointer.Id, ea.GetPosition(this));
|
||||||
|
else if (_lastTouchPositions.ContainsKey(ea.Pointer.Id))
|
||||||
|
_lastTouchPositions[ea.Pointer.Id] = ea.GetPosition(this);
|
||||||
}
|
}
|
||||||
base.OnPointerPressed(ea);
|
base.OnPointerPressed(ea);
|
||||||
}
|
}
|
||||||
@@ -327,11 +338,12 @@ public partial class CameraView : OpenGlControlBase
|
|||||||
if (ea.Pointer.Type == PointerType.Mouse)
|
if (ea.Pointer.Type == PointerType.Mouse)
|
||||||
{
|
{
|
||||||
if (!ea.Properties.IsLeftButtonPressed)
|
if (!ea.Properties.IsLeftButtonPressed)
|
||||||
_lastPosition = null;
|
_lastMousePosition = null;
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
else if (ea.Pointer.Type == PointerType.Touch)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("PointerReleased Type Touch {}", ea.GetPosition(this));
|
if (_lastTouchPositions is not null && _lastTouchPositions.ContainsKey(ea.Pointer.Id))
|
||||||
|
_lastTouchPositions.Remove(ea.Pointer.Id);
|
||||||
}
|
}
|
||||||
base.OnPointerReleased(ea);
|
base.OnPointerReleased(ea);
|
||||||
}
|
}
|
||||||
@@ -339,21 +351,75 @@ public partial class CameraView : OpenGlControlBase
|
|||||||
{
|
{
|
||||||
if (ea.Pointer.Type == PointerType.Mouse)
|
if (ea.Pointer.Type == PointerType.Mouse)
|
||||||
{
|
{
|
||||||
if (_lastPosition is null || _zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
|
if (_lastMousePosition is null || _zoom == 1) // Если кнопка не нажата или зум == 1, то ничего не делаем
|
||||||
{
|
{
|
||||||
base.OnPointerMoved(ea);
|
base.OnPointerMoved(ea);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var position = ea.GetPosition(this);
|
var position = ea.GetPosition(this);
|
||||||
_panX -= (float)(_lastPosition.Value.X - position.X);
|
_panX -= (float)(_lastMousePosition.Value.X - position.X);
|
||||||
_panY += (float)(_lastPosition.Value.Y - position.Y); // Ось Y инвертированна
|
_panY += (float)(_lastMousePosition.Value.Y - position.Y); // Ось Y инвертированна
|
||||||
_lastPosition = position;
|
_lastMousePosition = position;
|
||||||
base.OnPointerMoved(ea);
|
|
||||||
}
|
}
|
||||||
else if (ea.Pointer.Type == PointerType.Touch)
|
else if (ea.Pointer.Type == PointerType.Touch)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("PointerMoved Type Touch {}", ea.GetPosition(this));
|
if (_lastTouchPositions is null)
|
||||||
|
{
|
||||||
|
base.OnPointerMoved(ea);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_lastTouchPositions.ContainsKey(ea.Pointer.Id) && _lastTouchPositions.Count() < 2)
|
||||||
|
{
|
||||||
|
_lastTouchPositions.Add(ea.Pointer.Id, ea.GetPosition(this));
|
||||||
|
base.OnPointerMoved(ea);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lastTouchPositions.Count() == 1 && _zoom != 1) // Если есть только одно прикосновение и зум != 1, то токлько перемещение
|
||||||
|
{
|
||||||
|
var position = ea.GetPosition(this);
|
||||||
|
_panX -= (float)(_lastTouchPositions[ea.Pointer.Id].X - position.X);
|
||||||
|
_panY += (float)(_lastTouchPositions[ea.Pointer.Id].Y - position.Y); // Ось Y инвертированна
|
||||||
|
_lastTouchPositions[ea.Pointer.Id] = position;
|
||||||
|
}
|
||||||
|
else if (_lastTouchPositions.Count() == 2) // Если два прикосновения, то перемещение с приближением
|
||||||
|
{
|
||||||
|
var lastPosition1 = _lastTouchPositions.Skip(0).First().Value;
|
||||||
|
var lastPosition2 = _lastTouchPositions.Skip(1).First().Value;
|
||||||
|
var lastDistance = Point.Distance(lastPosition1, lastPosition2);
|
||||||
|
var lastCenter = PointHelper.Center(lastPosition1, lastPosition2);
|
||||||
|
|
||||||
|
_lastTouchPositions[ea.Pointer.Id] = ea.GetPosition(this);
|
||||||
|
|
||||||
|
var newPosition1 = _lastTouchPositions.Skip(0).First().Value;
|
||||||
|
var newPosition2 = _lastTouchPositions.Skip(1).First().Value;
|
||||||
|
var newDistance = Point.Distance(newPosition1, newPosition2);
|
||||||
|
var newCenter = PointHelper.Center(newPosition1, newPosition2);
|
||||||
|
|
||||||
|
var zoomFactor = newDistance / lastDistance;
|
||||||
|
var newZoom = (float)(_zoom * zoomFactor);
|
||||||
|
newZoom = (float)Math.Clamp(newZoom, 1f, 10f);
|
||||||
|
|
||||||
|
var imageX = (newCenter.X - Bounds.Width / 2 - _panX) / _zoom;
|
||||||
|
var imageY = (-newCenter.Y + Bounds.Height / 2 - _panY) / _zoom; // Ось Y инвертированна
|
||||||
|
|
||||||
|
_zoom = newZoom;
|
||||||
|
|
||||||
|
if (_zoom > 1f)
|
||||||
|
{
|
||||||
|
_panX = (float)(newCenter.X - Bounds.Width / 2 - imageX * _zoom);
|
||||||
|
_panY = (float)(-newCenter.Y + Bounds.Height / 2 - imageY * _zoom); // Ось Y инвертированна
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_zoom = 1f;
|
||||||
|
_panX = 0f;
|
||||||
|
_panY = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
base.OnPointerMoved(ea);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(DrawingContext context)
|
public override void Render(DrawingContext context)
|
||||||
|
|||||||
Reference in New Issue
Block a user