1use crate::{EventStream, EventStreamBuilder};
5
6impl EventStream {
7 pub fn time_window(&self, t0: i64, t1: i64) -> EventStream {
15 let (width, height) = self.sensor_size();
16 let ts = self.ts();
17 if !ts.is_sorted() {
18 return self.remap(width, height, |x, y, t, p| {
19 (t >= t0 && t < t1).then_some((x, y, t, p))
20 });
21 }
22 let (lo, hi) = (
23 ts.partition_point(|&t| t < t0),
24 ts.partition_point(|&t| t < t1),
25 );
26 if (lo, hi) == (0, self.len()) {
27 return self.clone();
28 }
29 let mut builder =
30 EventStreamBuilder::with_capacity(width, height, self.timestamp_scale_ms(), hi - lo);
31 builder.extend_from_columns(
32 &self.xs()[lo..hi],
33 &self.ys()[lo..hi],
34 &ts[lo..hi],
35 &self.ps()[lo..hi],
36 );
37 builder.build()
38 }
39
40 pub fn time_shift(&self, dt: i64) -> EventStream {
42 let (width, height) = self.sensor_size();
43 self.map_columns(width, height, |out| {
44 for t in &mut out.ts {
45 *t += dt;
46 }
47 })
48 }
49
50 pub fn time_scale(&self, factor: f64) -> EventStream {
52 let (width, height) = self.sensor_size();
53 self.map_columns(width, height, |out| {
54 for t in &mut out.ts {
55 *t = (*t as f64 * factor).round() as i64;
56 }
57 })
58 }
59
60 pub fn normalize_time(&self) -> EventStream {
62 match self.ts().iter().min() {
63 Some(&t_min) => self.time_shift(-t_min),
64 None => self.clone(),
65 }
66 }
67
68 pub fn decimate(&self, k: usize) -> EventStream {
70 let k = k.max(1);
71 let (width, height) = self.sensor_size();
72 let mut builder = EventStreamBuilder::with_capacity(
73 width,
74 height,
75 self.timestamp_scale_ms(),
76 self.len() / k + 1,
77 );
78 let (xs, ys, ts, ps) = (self.xs(), self.ys(), self.ts(), self.ps());
79 for index in (0..self.len()).step_by(k) {
80 builder.push(xs[index], ys[index], ts[index], ps[index]);
81 }
82 builder.build()
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::{EventStream, EventStreamBuilder};
89
90 fn sample() -> EventStream {
91 let mut builder = EventStreamBuilder::new(4, 3, 0.001);
92 for i in 0..6u16 {
93 builder.push(i % 4, i % 3, 100 + i64::from(i) * 10, i % 2 == 0);
94 }
95 builder.build()
96 }
97
98 #[test]
99 fn time_window_is_half_open() {
100 let windowed = sample().time_window(110, 140); assert_eq!(windowed.ts(), &[110, 120, 130]);
102 }
103
104 #[test]
108 fn time_window_agrees_on_unordered_streams() {
109 let mut builder = EventStreamBuilder::new(4, 3, 0.001);
110 for (index, t) in [130_i64, 100, 150, 110, 140, 120].into_iter().enumerate() {
111 let index = index as u16;
112 builder.push(index % 4, index % 3, t, index.is_multiple_of(2));
113 }
114 let unordered = builder.build();
115
116 let windowed = unordered.time_window(110, 140);
117 assert_eq!(windowed.ts(), &[130, 110, 120]); let sorted = unordered.sort_by_time().time_window(110, 140);
121 assert_eq!(sorted.ts(), &[110, 120, 130]);
122
123 assert_eq!(unordered.time_window(0, 1_000).ts(), unordered.ts());
125 assert_eq!(sample().time_window(0, 1_000).ts(), sample().ts());
126 }
127
128 #[test]
129 fn time_shift_inverts_and_preserves_count() {
130 let s = sample();
131 let back = s.time_shift(1000).time_shift(-1000);
132 assert_eq!(back.ts(), s.ts());
133 assert_eq!(back.len(), s.len());
134 }
135
136 #[test]
137 fn normalize_time_starts_at_zero() {
138 let n = sample().normalize_time();
139 assert_eq!(n.ts()[0], 0);
140 assert_eq!(n.ts(), &[0, 10, 20, 30, 40, 50]);
141 }
142
143 #[test]
144 fn time_scale_rounds() {
145 let scaled = sample().time_scale(0.5);
146 assert_eq!(scaled.ts(), &[50, 55, 60, 65, 70, 75]);
147 }
148
149 #[test]
150 fn decimate_keeps_every_kth_event() {
151 let d = sample().decimate(2);
152 assert_eq!(d.len(), 3);
153 assert_eq!(d.ts(), &[100, 120, 140]);
154 assert_eq!(sample().decimate(0).len(), 6); }
156
157 #[test]
158 fn temporal_ops_handle_empty() {
159 let empty = EventStreamBuilder::new(4, 3, 0.001).build();
160 assert!(empty.normalize_time().is_empty());
161 assert!(empty.time_shift(5).is_empty());
162 assert!(empty.decimate(3).is_empty());
163 }
164}