-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathgesture.go
More file actions
161 lines (137 loc) · 5.1 KB
/
Copy pathgesture.go
File metadata and controls
161 lines (137 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2026 The gogpu Authors
// SPDX-License-Identifier: MIT
package gpucontext
import "time"
// GestureEvent contains computed gesture deltas per frame.
//
// This event follows the Vello multi-touch pattern where gesture deltas
// are computed once per frame from the set of active pointers. This approach
// avoids jitter from individual pointer moves and provides smooth, predictable
// gesture values.
//
// The event is designed for multi-touch gestures (pinch-to-zoom, rotation, pan)
// but degrades gracefully with fewer pointers:
// - 0-1 pointers: Empty event (no gesture possible)
// - 2+ pointers: Full gesture with zoom, rotation, and translation
//
// Example usage:
//
// source.OnGesture(func(ev gpucontext.GestureEvent) {
// if ev.NumPointers >= 2 {
// camera.Zoom(ev.ZoomDelta)
// camera.Rotate(ev.RotationDelta)
// camera.Pan(ev.TranslationDelta)
// }
// })
type GestureEvent struct {
// NumPointers is the number of active touch points.
// Gestures require at least 2 pointers.
NumPointers int
// ZoomDelta is the proportional zoom factor for this frame.
// 1.0 = no change, >1.0 = zoom in, <1.0 = zoom out.
// Computed from change in average distance from centroid.
ZoomDelta float64
// ZoomDelta2D provides non-proportional zoom (stretch) deltas.
// This allows independent X and Y scaling for non-uniform zoom.
// For most use cases, use ZoomDelta instead.
ZoomDelta2D Point
// RotationDelta is the rotation change in radians for this frame.
// Positive = counter-clockwise, negative = clockwise.
// Computed from angle change of first pointer relative to centroid.
RotationDelta float64
// TranslationDelta is the pan movement in logical pixels for this frame.
// Computed from change in centroid position.
TranslationDelta Point
// PinchType classifies the pinch gesture based on finger geometry.
// Useful for constraining zoom to one axis (e.g., timeline scrubbing).
PinchType PinchType
// Center is the centroid of all active touch points.
// Use this as the zoom/rotation pivot point.
Center Point
// Timestamp is the event time as duration since an arbitrary reference.
// Useful for velocity calculations or animation timing.
// Zero if timestamps are not available.
Timestamp time.Duration
}
// PinchType classifies a two-finger pinch gesture based on finger geometry.
type PinchType uint8
const (
// PinchNone indicates no pinch gesture (fewer than 2 pointers).
PinchNone PinchType = iota
// PinchHorizontal indicates horizontal separation exceeds vertical by 3x.
// The fingers are spread horizontally, suggesting horizontal zoom/scrub.
PinchHorizontal
// PinchVertical indicates vertical separation exceeds horizontal by 3x.
// The fingers are spread vertically, suggesting vertical zoom.
PinchVertical
// PinchProportional indicates uniform pinch (default).
// Neither axis dominates, suggesting proportional zoom.
PinchProportional
)
// String returns the pinch type name for debugging.
func (p PinchType) String() string {
switch p {
case PinchNone:
return stringNone
case PinchHorizontal:
return "Horizontal"
case PinchVertical:
return "Vertical"
case PinchProportional:
return "Proportional"
default:
return "Unknown"
}
}
// Point represents a 2D coordinate in logical pixels.
type Point struct {
X, Y float64
}
// Add returns the sum of two points.
func (p Point) Add(other Point) Point {
return Point{X: p.X + other.X, Y: p.Y + other.Y}
}
// Sub returns the difference of two points.
func (p Point) Sub(other Point) Point {
return Point{X: p.X - other.X, Y: p.Y - other.Y}
}
// Scale returns the point scaled by a factor.
func (p Point) Scale(factor float64) Point {
return Point{X: p.X * factor, Y: p.Y * factor}
}
// GestureEventSource provides gesture event callbacks.
//
// This interface extends EventSource with high-level gesture recognition.
// The gesture recognizer computes deltas once per frame from pointer events,
// following the Vello pattern for smooth, predictable gestures.
//
// Type assertion pattern:
//
// if ges, ok := eventSource.(gpucontext.GestureEventSource); ok {
// ges.OnGesture(handleGestureEvent)
// }
//
// For applications that need gesture support:
//
// ges.OnGesture(func(ev gpucontext.GestureEvent) {
// if ev.NumPointers >= 2 {
// handlePinchZoom(ev.ZoomDelta, ev.Center)
// }
// })
type GestureEventSource interface {
// OnGesture registers a callback for gesture events.
// The callback receives a GestureEvent containing computed deltas.
//
// Callback threading: Called on the main/UI thread at end of frame.
// Callbacks should be fast and non-blocking.
//
// Gesture events are delivered once per frame when 2+ pointers are active.
OnGesture(fn func(GestureEvent))
}
// NullGestureEventSource implements GestureEventSource by ignoring all registrations.
// Useful for platforms or configurations where gesture input is not available.
type NullGestureEventSource struct{}
// OnGesture does nothing.
func (NullGestureEventSource) OnGesture(func(GestureEvent)) {}
// Ensure NullGestureEventSource implements GestureEventSource.
var _ GestureEventSource = NullGestureEventSource{}