Files
prosperon/gestures.cm
2026-01-19 01:06:51 -06:00

120 lines
3.5 KiB
Plaintext

var time = use('time')
var audio = use('sound')
var math = use('math/radians')
var gesture = {}
gesture.MIN_SWIPE = 30
gesture.MAX_TIME = 500
gesture.PINCH_TH = 10
gesture.reset = function() {
this.touches = {} // Track active touches by finger id
this.gestureState = null // Current gesture being tracked
this.startTime = 0
this.startDist = 0
}
gesture.on_input = function(action_id, action) {
if (search(action_id, 'gamepad_touchpad_') == null) return
var finger = action.finger || 0
var touchpad = action.touchpad || 0
var fingerId = `${touchpad}_${finger}`
if (action_id == 'gamepad_touchpad_down') {
// Add new touch
this.touches[fingerId] = {
x: action.x,
y: action.y,
startX: action.x,
startY: action.y,
startTime: time.number()
}
var touchCount = length(array(this.touches))
if (touchCount == 1) {
// Single touch started
this.gestureState = 'single'
this.startTime = time.number()
} else if (touchCount == 2) {
// Two touches - potential pinch
this.gestureState = 'multi'
var fingers = object.values(this.touches)
this.startDist = this.dist(fingers[0], fingers[1])
}
}
else if (action_id == 'gamepad_touchpad_motion') {
if (this.touches[fingerId]) {
// Update touch position
this.touches[fingerId].x = action.x
this.touches[fingerId].y = action.y
var touchCount = length(array(this.touches))
if (touchCount == 2 && this.gestureState == 'multi') {
// Check for pinch gesture
var fingers = object.values(this.touches)
var currentDist = this.dist(fingers[0], fingers[1])
var d = currentDist - this.startDist
if (abs(d) >= this.PINCH_TH) {
var gesture_type = d > 0 ? 'pinch_out' : 'pinch_in'
// scene.recurse(game.root, 'on_input', [gesture_type, { delta: d }])
this.startDist = currentDist
}
}
}
}
else if (action_id == 'gamepad_touchpad_up') {
if (this.touches[fingerId]) {
var touch = this.touches[fingerId]
var touchCount = length(array(this.touches))
// Check for swipe if this was the only/last touch
if (touchCount == 1 && this.gestureState == 'single') {
var dt = time.number() - touch.startTime
var dx = action.x - touch.startX
var dy = action.y - touch.startY
if (dt < this.MAX_TIME / 1000) { // Convert to seconds
var absX = abs(dx), absY = abs(dy)
if (absX > this.MIN_SWIPE / 100 || absY > this.MIN_SWIPE / 100) { // Normalize for 0-1 range
var dir = absX > absY
? (dx > 0 ? 'swipe_right' : 'swipe_left')
: (dy > 0 ? 'swipe_down' : 'swipe_up')
audio.play('swipe')
// scene.recurse(game.root, 'on_input', [dir, {pressed:true}])
}
}
}
// Remove touch
delete this.touches[fingerId]
// Reset if no touches left
if (length(array(this.touches)) == 0) {
this.gestureState = null
}
}
}
}
gesture.dist = function(p1, p2) {
var dx = p2.x - p1.x
var dy = p2.y - p1.y
return math.sqrt(math.power(dx, 2) + math.power(dy, 2))
}
return function(options) {
var obj = meme(gesture)
if (options) {
obj.MIN_SWIPE = options.minSwipe || gesture.MIN_SWIPE
obj.MAX_TIME = options.maxTime || gesture.MAX_TIME
obj.PINCH_TH = options.pinchThreshold || gesture.PINCH_TH
}
obj.reset()
return obj
}