oxihuman_export/
pcd_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9pub enum PcdDataType {
10 Ascii,
11 Binary,
12}
13
14#[allow(dead_code)]
16pub struct PcdExport {
17 pub points: Vec<[f32; 3]>,
18 pub data_type: PcdDataType,
19 pub fields: Vec<String>,
20}
21
22#[allow(dead_code)]
24pub fn new_pcd_export() -> PcdExport {
25 PcdExport {
26 points: Vec::new(),
27 data_type: PcdDataType::Ascii,
28 fields: vec!["x".to_string(), "y".to_string(), "z".to_string()],
29 }
30}
31
32#[allow(dead_code)]
34pub fn add_pcd_point(export: &mut PcdExport, x: f32, y: f32, z: f32) {
35 export.points.push([x, y, z]);
36}
37
38#[allow(dead_code)]
40pub fn pcd_point_count(export: &PcdExport) -> usize {
41 export.points.len()
42}
43
44#[allow(dead_code)]
46pub fn build_pcd_header(export: &PcdExport) -> String {
47 let fields = export.fields.join(" ");
48 format!(
49 "VERSION 0.7\nFIELDS {}\nSIZE 4 4 4\nTYPE F F F\nCOUNT 1 1 1\nWIDTH {}\nHEIGHT 1\nPOINTS {}\nDATA ascii\n",
50 fields,
51 export.points.len(),
52 export.points.len()
53 )
54}
55
56#[allow(dead_code)]
58pub fn export_pcd_ascii(export: &PcdExport) -> String {
59 let mut s = build_pcd_header(export);
60 for &p in &export.points {
61 s.push_str(&format!("{} {} {}\n", p[0], p[1], p[2]));
62 }
63 s
64}
65
66#[allow(dead_code)]
68pub fn export_pcd_binary(export: &PcdExport) -> Vec<u8> {
69 let mut buf = Vec::new();
70 for &p in &export.points {
71 buf.extend_from_slice(&p[0].to_le_bytes());
72 buf.extend_from_slice(&p[1].to_le_bytes());
73 buf.extend_from_slice(&p[2].to_le_bytes());
74 }
75 buf
76}
77
78#[allow(dead_code)]
80pub fn validate_pcd(export: &PcdExport) -> bool {
81 !export.points.is_empty() && !export.fields.is_empty()
82}
83
84#[allow(dead_code)]
86pub fn pcd_from_positions(positions: &[[f32; 3]]) -> PcdExport {
87 let mut e = new_pcd_export();
88 for &p in positions {
89 add_pcd_point(&mut e, p[0], p[1], p[2]);
90 }
91 e
92}
93
94#[allow(dead_code)]
96pub fn pcd_centroid(export: &PcdExport) -> [f32; 3] {
97 if export.points.is_empty() {
98 return [0.0; 3];
99 }
100 let mut sum = [0.0f32; 3];
101 for &p in &export.points {
102 sum[0] += p[0];
103 sum[1] += p[1];
104 sum[2] += p[2];
105 }
106 let n = export.points.len() as f32;
107 [sum[0] / n, sum[1] / n, sum[2] / n]
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn new_export_empty() {
116 let e = new_pcd_export();
117 assert_eq!(pcd_point_count(&e), 0);
118 }
119
120 #[test]
121 fn add_point() {
122 let mut e = new_pcd_export();
123 add_pcd_point(&mut e, 1.0, 2.0, 3.0);
124 assert_eq!(pcd_point_count(&e), 1);
125 }
126
127 #[test]
128 fn header_contains_version() {
129 let e = new_pcd_export();
130 let h = build_pcd_header(&e);
131 assert!(h.contains("VERSION 0.7"));
132 }
133
134 #[test]
135 fn ascii_export_contains_coords() {
136 let mut e = new_pcd_export();
137 add_pcd_point(&mut e, 1.5, 2.5, 3.5);
138 let s = export_pcd_ascii(&e);
139 assert!(s.contains("1.5"));
140 }
141
142 #[test]
143 fn binary_export_size() {
144 let mut e = new_pcd_export();
145 add_pcd_point(&mut e, 0.0, 0.0, 0.0);
146 let b = export_pcd_binary(&e);
147 assert_eq!(b.len(), 12);
148 }
149
150 #[test]
151 fn validate_fails_empty() {
152 let e = new_pcd_export();
153 assert!(!validate_pcd(&e));
154 }
155
156 #[test]
157 fn validate_passes() {
158 let mut e = new_pcd_export();
159 add_pcd_point(&mut e, 0.0, 0.0, 0.0);
160 assert!(validate_pcd(&e));
161 }
162
163 #[test]
164 fn from_positions() {
165 let pos = vec![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]];
166 let e = pcd_from_positions(&pos);
167 assert_eq!(pcd_point_count(&e), 2);
168 }
169
170 #[test]
171 fn centroid_correct() {
172 let pos = vec![[0.0f32, 0.0, 0.0], [2.0, 0.0, 0.0]];
173 let e = pcd_from_positions(&pos);
174 let c = pcd_centroid(&e);
175 assert!((c[0] - 1.0).abs() < 1e-5);
176 }
177}