Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
PinchGestureRecognizer.cs
1using System.Drawing;
2
4
23public sealed class PinchGestureRecognizer : IDisposable
24{
25 private readonly ITouchInput _touchInput;
26 private readonly Dictionary<int, Point> _activePoints = new();
27 private double _startingDistance;
28 private double _lastDistance = 0;
29 private bool _isDisposed;
30
32 public event EventHandler<PinchedEventArgs>? PinchStarted;
33
38 public event EventHandler<PinchedEventArgs>? PinchUpdated;
39
41 public event EventHandler<PinchedEventArgs>? PinchEnded;
42
54 {
55 _touchInput = touchInput ?? throw new ArgumentNullException(nameof(touchInput));
56 _touchInput.TouchBegan += OnTouchBegan;
57 _touchInput.TouchMoved += OnTouchMoved;
58 _touchInput.TouchEnded += OnTouchEnded;
59 }
60
61 private void OnTouchBegan(object? sender, TouchEventArgs e)
62 {
63 var prevCount = _activePoints.Count;
64
65 if (prevCount == 2)
66 Emit(PinchPhase.Ended, PinchEnded);
67
68 _activePoints[e.Touch.Id] = e.Touch.Position;
69 var newCount = _activePoints.Count;
70
71 if (prevCount != 2 && newCount == 2)
72 BeginPinch();
73 else if (newCount != 2)
74 ClearBaseline();
75 }
76
77 private void OnTouchMoved(object? sender, TouchEventArgs e)
78 {
79 if (!_activePoints.ContainsKey(e.Touch.Id))
80 return;
81
82 _activePoints[e.Touch.Id] = e.Touch.Position;
83
84 // Only emit updates when exactly two contacts are tracked and a baseline exists.
85 if (_activePoints.Count == 2 && _lastDistance > 0)
86 {
87 var currentDistance = GetCurrentDistance();
88
89 if (currentDistance > 0)
90 {
91 Emit(PinchPhase.Updated, PinchUpdated, currentDistance);
92 }
93
94 _lastDistance = currentDistance;
95 }
96 }
97
98 private void OnTouchEnded(object? sender, TouchEventArgs e)
99 {
100 var prevCount = _activePoints.Count;
101
102 if (_activePoints.ContainsKey(e.Touch.Id))
103 _activePoints[e.Touch.Id] = e.Touch.Position;
104
105 if (prevCount == 2)
106 Emit(PinchPhase.Ended, PinchEnded);
107
108 _activePoints.Remove(e.Touch.Id);
109 var newCount = _activePoints.Count;
110
111 if (prevCount != 2 && newCount == 2)
112 BeginPinch();
113 else if (newCount < 2)
114 ClearBaseline();
115 }
116
117 private void BeginPinch()
118 {
119 _startingDistance = GetCurrentDistance();
120 _lastDistance = _startingDistance;
121 Emit(PinchPhase.Began, PinchStarted, _startingDistance);
122 }
123
124 private void ClearBaseline()
125 {
126 _startingDistance = 0;
127 _lastDistance = 0;
128 }
129
130 private void Emit(PinchPhase phase,
131 EventHandler<PinchedEventArgs>? handler,
132 double? currentDistance = null)
133 {
134 if (_activePoints.Count != 2)
135 return;
136
137 var pair = _activePoints.OrderBy(kvp => kvp.Key).Take(2).ToArray();
138 var a = pair[0].Value;
139 var b = pair[1].Value;
140 var distance = currentDistance ?? GetDistance(a, b);
141 var center = new PointF((a.X + b.X) / 2f, (a.Y + b.Y) / 2f);
142
143 handler?.Invoke(this, new PinchedEventArgs(phase,
144 new[] { pair[0].Key, pair[1].Key },
145 center,
146 _startingDistance,
147 _lastDistance,
148 distance));
149 }
150
151 private double GetCurrentDistance()
152 {
153 using var enumerator = _activePoints.Values.GetEnumerator();
154 if (!enumerator.MoveNext())
155 return 0;
156
157 var a = enumerator.Current;
158
159 if (!enumerator.MoveNext())
160 return 0;
161
162 var b = enumerator.Current;
163
164 return GetDistance(a, b);
165 }
166
167 private static double GetDistance(Point a, Point b)
168 {
169 var dx = a.X - b.X;
170 var dy = a.Y - b.Y;
171 return Math.Sqrt(dx * dx + dy * dy);
172 }
173
178 public void Dispose()
179 {
180 if (_isDisposed) return;
181 _isDisposed = true;
182
183 _touchInput.TouchBegan -= OnTouchBegan;
184 _touchInput.TouchMoved -= OnTouchMoved;
185 _touchInput.TouchEnded -= OnTouchEnded;
186 Reset();
187 }
188
189 internal void Reset()
190 {
191 _activePoints.Clear();
192 ClearBaseline();
193 }
194}
void Dispose()
Releases all resources held by this recognizer and unsubscribes from the touch input source....
EventHandler< PinchedEventArgs >? PinchEnded
Occurs when the current two-contact pinch ends or is interrupted.
EventHandler< PinchedEventArgs >? PinchUpdated
Occurs whenever the distance between two active touch contact points changes during a pinch or spread...
EventHandler< PinchedEventArgs >? PinchStarted
Occurs when exactly two contacts establish a pinch gesture.
PinchGestureRecognizer(ITouchInput touchInput)
Initializes a new instance of the PinchGestureRecognizer class, subscribing to touch events from the ...
Provides data for touch contact events raised by ITouchInput, including the touch point and engine ti...
TouchPoint Touch
Gets the touch contact point associated with this event.
Represents a touch input source that tracks active contact points and raises lifecycle events when fi...
PinchPhase
Identifies the lifecycle phase of a two-contact pinch gesture.
Definition PinchPhase.cs:7