Skip to main content

eventcv_core/transform/
algebra.rs

1//! Polarity and stream-algebra transforms — filtering, reordering and combining streams.
2
3use crate::{EventStream, EventStreamBuilder};
4
5impl EventStream {
6    /// Keeps only events of the given polarity.
7    pub fn filter_polarity(&self, polarity: bool) -> EventStream {
8        let (width, height) = self.sensor_size();
9        self.remap(width, height, |x, y, t, p| {
10            (p == polarity).then_some((x, y, t, p))
11        })
12    }
13
14    /// Flips every event's polarity. Sensor and timestamps unchanged.
15    pub fn invert_polarity(&self) -> EventStream {
16        let (width, height) = self.sensor_size();
17        self.remap(width, height, |x, y, t, p| Some((x, y, t, !p)))
18    }
19
20    /// Returns a copy reordered by ascending timestamp (stable for equal timestamps).
21    pub fn sort_by_time(&self) -> EventStream {
22        let (width, height) = self.sensor_size();
23        let ts = self.ts();
24        let mut order: Vec<usize> = (0..self.len()).collect();
25        order.sort_by_key(|&index| ts[index]); // stable
26        let (xs, ys, ps) = (self.xs(), self.ys(), self.ps());
27        let mut builder =
28            EventStreamBuilder::with_capacity(width, height, self.timestamp_scale_ms(), self.len());
29        for index in order {
30            builder.push(xs[index], ys[index], ts[index], ps[index]);
31        }
32        builder.build()
33    }
34
35    /// Concatenates several streams into one (in argument order, not time-sorted). The sensor
36    /// size is the element-wise maximum of the inputs; the timestamp scale comes from `self`.
37    pub fn concat(&self, others: &[&EventStream]) -> EventStream {
38        let (mut width, mut height) = self.sensor_size();
39        for other in others {
40            let (w, h) = other.sensor_size();
41            width = width.max(w);
42            height = height.max(h);
43        }
44        let total = self.len() + others.iter().map(|s| s.len()).sum::<usize>();
45        let mut builder =
46            EventStreamBuilder::with_capacity(width, height, self.timestamp_scale_ms(), total);
47        for stream in std::iter::once(self).chain(others.iter().copied()) {
48            let (xs, ys, ts, ps) = (stream.xs(), stream.ys(), stream.ts(), stream.ps());
49            for index in 0..stream.len() {
50                builder.push(xs[index], ys[index], ts[index], ps[index]);
51            }
52        }
53        builder.build()
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use crate::{EventStream, EventStreamBuilder};
60
61    fn sample() -> EventStream {
62        let mut builder = EventStreamBuilder::new(4, 3, 0.001);
63        builder.push(0, 0, 30, true);
64        builder.push(1, 1, 10, false);
65        builder.push(2, 2, 20, true);
66        builder.build()
67    }
68
69    #[test]
70    fn filter_polarity_splits_and_conserves_count() {
71        let s = sample();
72        let on = s.filter_polarity(true);
73        let off = s.filter_polarity(false);
74        assert_eq!(on.len() + off.len(), s.len());
75        assert!(on.ps().iter().all(|&p| p));
76        assert!(off.ps().iter().all(|&p| !p));
77    }
78
79    #[test]
80    fn invert_polarity_is_its_own_inverse() {
81        let s = sample();
82        assert_eq!(s.invert_polarity().invert_polarity().ps(), s.ps());
83        assert_eq!(s.invert_polarity().ps(), &[false, true, false]);
84    }
85
86    #[test]
87    fn sort_by_time_orders_ascending() {
88        let sorted = sample().sort_by_time();
89        assert_eq!(sorted.ts(), &[10, 20, 30]);
90        assert_eq!(sorted.xs(), &[1, 2, 0]); // rows follow their timestamps
91    }
92
93    #[test]
94    fn concat_appends_and_takes_max_sensor() {
95        let a = sample();
96        let mut b_builder = EventStreamBuilder::new(8, 6, 0.001);
97        b_builder.push(7, 5, 50, true);
98        let b = b_builder.build();
99
100        let combined = a.concat(&[&b]);
101        assert_eq!(combined.len(), 4);
102        assert_eq!(combined.sensor_size(), (8, 6)); // element-wise max
103        assert_eq!(combined.ts(), &[30, 10, 20, 50]); // argument order, not sorted
104    }
105
106    #[test]
107    fn algebra_ops_handle_empty() {
108        let empty = EventStreamBuilder::new(4, 3, 0.001).build();
109        assert!(empty.filter_polarity(true).is_empty());
110        assert!(empty.sort_by_time().is_empty());
111        assert_eq!(empty.concat(&[&sample()]).len(), 3);
112    }
113}