rustpix_core/
clustering.rs1pub use crate::error::ClusteringError;
6
7#[derive(Clone, Debug)]
12pub struct ClusteringConfig {
13 pub radius: f64,
15 pub temporal_window_ns: f64,
17 pub min_cluster_size: u16,
19 pub max_cluster_size: Option<u16>,
21}
22
23impl Default for ClusteringConfig {
24 fn default() -> Self {
25 Self {
26 radius: 5.0,
27 temporal_window_ns: 75.0,
28 min_cluster_size: 1,
29 max_cluster_size: None,
30 }
31 }
32}
33
34impl ClusteringConfig {
35 #[must_use]
37 pub fn venus_defaults() -> Self {
38 Self::default()
39 }
40
41 #[inline]
43 #[must_use]
44 pub fn window_tof(&self) -> u32 {
45 let window = (self.temporal_window_ns / 25.0).ceil();
46 if window <= 0.0 {
47 return 0;
48 }
49 if window >= f64::from(u32::MAX) {
50 return u32::MAX;
51 }
52 format!("{window:.0}").parse::<u32>().unwrap_or(u32::MAX)
53 }
54
55 #[must_use]
57 pub fn with_radius(mut self, radius: f64) -> Self {
58 self.radius = radius;
59 self
60 }
61
62 #[must_use]
64 pub fn with_temporal_window_ns(mut self, window_ns: f64) -> Self {
65 self.temporal_window_ns = window_ns;
66 self
67 }
68
69 #[must_use]
71 pub fn with_min_cluster_size(mut self, size: u16) -> Self {
72 self.min_cluster_size = size;
73 self
74 }
75
76 #[must_use]
78 pub fn with_max_cluster_size(mut self, size: u16) -> Self {
79 self.max_cluster_size = Some(size);
80 self
81 }
82}
83
84#[derive(Clone, Debug, Default)]
86pub struct ClusteringStatistics {
87 pub hits_processed: usize,
89 pub clusters_found: usize,
91 pub noise_hits: usize,
93 pub largest_cluster_size: usize,
95 pub mean_cluster_size: f64,
97 pub processing_time_us: u64,
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_config_defaults() {
107 let config = ClusteringConfig::default();
108 assert!((config.radius - 5.0).abs() < f64::EPSILON);
109 assert!((config.temporal_window_ns - 75.0).abs() < f64::EPSILON);
110 assert_eq!(config.min_cluster_size, 1);
111 assert_eq!(config.max_cluster_size, None);
112 }
113
114 #[test]
115 fn test_window_tof_conversion() {
116 let config = ClusteringConfig::default();
117 assert_eq!(config.window_tof(), 3);
119 }
120
121 #[test]
122 fn test_config_builder() {
123 let config = ClusteringConfig::default()
124 .with_radius(10.0)
125 .with_temporal_window_ns(100.0)
126 .with_min_cluster_size(2)
127 .with_max_cluster_size(100);
128
129 assert!((config.radius - 10.0).abs() < f64::EPSILON);
130 assert!((config.temporal_window_ns - 100.0).abs() < f64::EPSILON);
131 assert_eq!(config.min_cluster_size, 2);
132 assert_eq!(config.max_cluster_size, Some(100));
133 }
134}