Skip to content

Commit b034d23

Browse files
committed
Update jiggle vector generator to simplify caller usage
1 parent b27a22c commit b034d23

2 files changed

Lines changed: 79 additions & 43 deletions

File tree

src/main.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,30 +113,23 @@ async fn main(_spawner: Spawner) {
113113

114114
let in_fut = async {
115115
let mut rng = RoscRng;
116+
let movement = Movement::default();
116117
loop {
117118
// Feed controller
119+
// If true, it is time to issue a mouse report
118120
if CONTROLLER.feed().await {
119-
// To simulate more natural mouse movement, limit the maximum movement per report, and send multiple reports.
120-
let reverberations = 2;
121-
const JIGGLE_VECTOR_SIZE: usize = 64;
122-
let mut jiggle_vector_v: heapless::Vec<i8, JIGGLE_VECTOR_SIZE> =
123-
heapless::Vec::new();
124-
let mut jiggle_vector_h: heapless::Vec<i8, JIGGLE_VECTOR_SIZE> =
125-
heapless::Vec::new();
126-
let movement = Movement::new();
127-
for _ in 0..reverberations {
128-
movement.generate_vector(rng.next_u32(), &mut jiggle_vector_v);
129-
movement.generate_vector(rng.next_u32(), &mut jiggle_vector_h);
130-
}
131-
132121
// See https://wiki.osdev.org/USB_Human_Interface_Devices#USB_mouse for details on mouse reports.
133122
// tldr: x and y are signed 8-bit integers representing relative movement.
134-
for (x, y) in jiggle_vector_h.iter().zip(jiggle_vector_v.iter()) {
123+
for (dx, dy) in movement
124+
.generate_vector::<64>(rng.next_u32())
125+
.into_iter()
126+
.chain(movement.generate_vector::<64>(rng.next_u32()).into_iter())
127+
{
135128
// Create the mouse HID report.
136129
let report = MouseReport {
137130
buttons: 0,
138-
x: *x,
139-
y: *y,
131+
x: dx,
132+
y: dy,
140133
wheel: 0,
141134
pan: 0,
142135
};

src/movement.rs

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,94 @@ pub struct Movement {
77
}
88

99
impl Movement {
10-
pub const fn new() -> Self {
10+
pub const fn _new(upper_limit: u8, lower_limit: u8, step: i8) -> Self {
11+
Self {
12+
upper_limit: upper_limit,
13+
lower_limit: lower_limit,
14+
step: step as i8,
15+
}
16+
}
17+
18+
pub const fn default() -> Self {
1119
Self {
1220
upper_limit: 32,
1321
lower_limit: 6,
1422
step: 6,
1523
}
1624
}
1725

18-
/// Generate a relative movement vector suitable for use in a mouse HID report
19-
/// The generated vector is a for a single axis, and returns to the starting position
20-
pub fn generate_vector<const N: usize>(&self, seed: u32, vec: &mut heapless::Vec<i8, N>) {
21-
// Scale rng_value into the range [LOWER, UPPER] inclusive.
22-
// Use 64-bit intermediate to avoid overflow and get decent distribution.
26+
fn xorshift32(&self, mut x: u32) -> u32 {
27+
x ^= x << 13;
28+
x ^= x >> 17;
29+
x ^= x << 5;
30+
x
31+
}
32+
33+
fn scale_seed(&self, seed: u32) -> i8 {
2334
let range: u32 = (self.upper_limit - self.lower_limit) as u32;
2435
let scaled: u32 = if seed == u32::MAX {
2536
range
2637
} else {
2738
((seed as u64 * range as u64) / (u32::MAX as u64)) as u32
2839
};
29-
let x_u8 = (self.lower_limit as u32 + scaled) as u8;
30-
let mut remaining: i8 = x_u8 as i8;
31-
32-
// Populate forward movement in STEP-sized chunks (last chunk may be smaller).
33-
while remaining > 0 && !vec.is_full() {
34-
let to_push: i8 = if remaining >= self.step {
35-
self.step
36-
} else {
37-
remaining
38-
};
39-
if vec.push(to_push).is_err() {
40-
break;
41-
}
42-
remaining -= to_push;
40+
41+
let value = (self.lower_limit as u32 + scaled) as u8;
42+
value as i8
43+
}
44+
45+
fn step_toward_zero(&self, remaining: i8, rng: u32, max_step: i8) -> i8 {
46+
if remaining == 0 {
47+
return 0;
48+
}
49+
50+
let step = remaining.abs().min(max_step);
51+
let jitter = ((rng & 0x3) as i8) - 1; // [-1,0,1]
52+
let mut s = step + jitter;
53+
s = s.clamp(1, step); // ensure at least 1 and not overshoot
54+
55+
if remaining < 0 { -s } else { s }
56+
}
57+
58+
pub fn generate_vector<const N: usize>(&self, seed: u32) -> heapless::Vec<(i8, i8), N> {
59+
let mut vec: heapless::Vec<(i8, i8), N> = heapless::Vec::new();
60+
let mut rng = seed;
61+
62+
// Total magnitude per axis
63+
let x_total = self.scale_seed(seed);
64+
let y_total = self.scale_seed(seed.rotate_left(13));
65+
66+
// Determine direction: 50% chance to flip axes
67+
let flip = (rng & 1) == 1; // true => invert direction
68+
let (mut x_remaining, mut y_remaining) = if flip {
69+
(-x_total, -y_total)
70+
} else {
71+
(x_total, y_total)
72+
};
73+
74+
// ---- Forward path with jitter + easing ----
75+
while (x_remaining != 0 || y_remaining != 0) && !vec.is_full() {
76+
rng = self.xorshift32(rng);
77+
78+
let x_step = self.step_toward_zero(x_remaining, rng, self.step);
79+
let y_step = self.step_toward_zero(y_remaining, rng.rotate_left(5), self.step);
80+
81+
x_remaining -= x_step;
82+
y_remaining -= y_step;
83+
84+
vec.push((x_step, y_step)).ok();
4385
}
4486

45-
// Mirror back to origin. Iterate in reverse over current values and push negated values
46-
// until the vector is full.
47-
// Note: negating a value in the expected range (1..=16) is safe for i8.
48-
let clone = vec.clone();
49-
for &v in clone.iter().rev() {
87+
// ---- Mirror back exactly ----
88+
let len = vec.len();
89+
for i in (0..len).rev() {
5090
if vec.is_full() {
5191
break;
5292
}
53-
// push negated value; ignore push failure because we checked is_full above
54-
let _ = vec.push(-v);
93+
94+
let (x, y) = vec[i];
95+
vec.push((-x, -y)).ok();
5596
}
97+
98+
vec
5699
}
57100
}

0 commit comments

Comments
 (0)