input_pipeline/gestures/
utils.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::gesture_arena::TouchpadEvent;
6use crate::utils::{euclidean_distance, Position};
7use std::collections::HashMap;
8
9/// Result of movement_from_events.
10#[derive(Debug, PartialEq)]
11pub(super) struct MovementDetail {
12    pub(super) euclidean_distance: f32,
13    pub(super) movement: Position,
14}
15
16/// `movement_from_events` is used to compute the movement in click-drag events.
17/// It take the finger that moves the furthest as events' movement.
18pub(super) fn movement_from_events(
19    previous_event: &TouchpadEvent,
20    new_event: &TouchpadEvent,
21) -> MovementDetail {
22    let mut previous_contacts: HashMap<u32, Position> = HashMap::new();
23    for c in &previous_event.contacts {
24        previous_contacts.insert(c.id, c.position.clone());
25    }
26
27    let mut movement = Position { x: 0.0, y: 0.0 };
28    let mut max_distance = 0.0;
29    for new_contact in &new_event.contacts {
30        let previous = previous_contacts.get(&new_contact.id);
31        match previous {
32            None => {}
33            Some(&previous) => {
34                let dis = euclidean_distance(new_contact.position, previous.clone());
35                if dis > max_distance {
36                    max_distance = dis;
37                    movement = new_contact.position - previous;
38                }
39            }
40        }
41    }
42
43    MovementDetail { euclidean_distance: max_distance, movement }
44}
45
46#[cfg(test)]
47mod tests {
48
49    use super::*;
50    use crate::gestures::gesture_arena::TouchpadEvent;
51    use crate::touch_binding;
52    use crate::utils::Position;
53
54    use test_case::test_case;
55
56    fn make_touch_contact(id: u32, position: Position) -> touch_binding::TouchContact {
57        touch_binding::TouchContact { id, position, pressure: None, contact_size: None }
58    }
59
60    #[test_case(
61        TouchpadEvent {
62            timestamp: zx::Instant::ZERO,
63            pressed_buttons: vec![],
64            contacts: vec![
65                make_touch_contact(1, Position{x: 1.0, y: 1.0}),
66                make_touch_contact(2, Position{x: 5.0, y: 5.0}),
67            ],
68            filtered_palm_contacts: vec![],
69        }; "previous contact stay, place new finger")]
70    #[test_case(
71        TouchpadEvent {
72            timestamp: zx::Instant::ZERO,
73            pressed_buttons: vec![],
74            contacts: vec![
75                make_touch_contact(2, Position{x: 5.0, y: 5.0}),
76            ],
77            filtered_palm_contacts: vec![],
78        }; "previous contact lift, place new finger")]
79    #[test_case(
80        TouchpadEvent {
81            timestamp: zx::Instant::ZERO,
82            pressed_buttons: vec![],
83            contacts: vec![],
84            filtered_palm_contacts: vec![],
85        }; "previous contact lift")]
86    #[fuchsia::test]
87    fn movement_from_events_no_movement(new_event: TouchpadEvent) {
88        let previous_event = TouchpadEvent {
89            timestamp: zx::Instant::ZERO,
90            pressed_buttons: vec![],
91            contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
92            filtered_palm_contacts: vec![],
93        };
94
95        let got = movement_from_events(&previous_event, &new_event);
96        let want = MovementDetail { euclidean_distance: 0.0, movement: Position::zero() };
97        pretty_assertions::assert_eq!(got, want);
98    }
99
100    #[test_case(
101        TouchpadEvent {
102            timestamp: zx::Instant::ZERO,
103            pressed_buttons: vec![],
104            contacts: vec![
105                make_touch_contact(1, Position{x: 1.0, y: 6.0}),
106                make_touch_contact(2, Position{x: 5.0, y: 5.0}),
107            ],
108            filtered_palm_contacts: vec![],
109        }; "contact 1 move, contact 2 stay")]
110    #[test_case(
111        TouchpadEvent {
112            timestamp: zx::Instant::ZERO,
113            pressed_buttons: vec![],
114            contacts: vec![
115                make_touch_contact(1, Position{x: 1.0, y: 1.0}),
116                make_touch_contact(2, Position{x: 5.0, y: 10.0}),
117            ],
118            filtered_palm_contacts: vec![],
119        }; "contact 2 move, contact 1 stay")]
120    #[test_case(
121        TouchpadEvent {
122            timestamp: zx::Instant::ZERO,
123            pressed_buttons: vec![],
124            contacts: vec![
125                make_touch_contact(1, Position{x: 1.0, y: 6.0}),
126                make_touch_contact(2, Position{x: 3.0, y: 5.0}),
127            ],
128            filtered_palm_contacts: vec![],
129        }; "contact 1 movement larger")]
130    #[test_case(
131        TouchpadEvent {
132            timestamp: zx::Instant::ZERO,
133            pressed_buttons: vec![],
134            contacts: vec![
135                make_touch_contact(1, Position{x: 1.0, y: 6.0}),
136            ],
137            filtered_palm_contacts: vec![],
138        }; "1 contact lift")]
139    #[fuchsia::test]
140    fn movement_from_events_furthest_movement(new_event: TouchpadEvent) {
141        let previous_event = TouchpadEvent {
142            timestamp: zx::Instant::ZERO,
143            pressed_buttons: vec![],
144            contacts: vec![
145                make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
146                make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
147            ],
148            filtered_palm_contacts: vec![],
149        };
150
151        let got = movement_from_events(&previous_event, &new_event);
152        let want =
153            MovementDetail { euclidean_distance: 5.0, movement: Position { x: 0.0, y: 5.0 } };
154        pretty_assertions::assert_eq!(got, want);
155    }
156}