1use crate::{Color, Point, Vector, Xform};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5use uuid::Uuid;
6
7#[derive(Debug, Clone)]
8pub struct PointCloud {
9 pub guid: String,
10 pub name: String,
11 pub points: Vec<Point>,
12 pub normals: Vec<Vector>,
13 pub colors: Vec<Color>,
14 pub xform: Xform,
15}
16
17impl Default for PointCloud {
18 fn default() -> Self {
19 Self {
20 guid: Uuid::new_v4().to_string(),
21 name: "my_pointcloud".to_string(),
22 points: Vec::new(),
23 normals: Vec::new(),
24 colors: Vec::new(),
25 xform: Xform::identity(),
26 }
27 }
28}
29
30impl PointCloud {
31 pub fn new(points: Vec<Point>, normals: Vec<Vector>, colors: Vec<Color>) -> Self {
32 Self {
33 points,
34 normals,
35 colors,
36 ..Default::default()
37 }
38 }
39
40 pub fn len(&self) -> usize {
41 self.points.len()
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.points.is_empty()
46 }
47
48 pub fn jsondump(&self) -> Result<String, Box<dyn std::error::Error>> {
53 let mut buf = Vec::new();
54 let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
55 let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
56 serde::Serialize::serialize(self, &mut ser)?;
57 Ok(String::from_utf8(buf)?)
58 }
59
60 pub fn jsonload(json_str: &str) -> Result<Self, Box<dyn std::error::Error>> {
61 Ok(serde_json::from_str(json_str)?)
62 }
63
64 pub fn to_json(&self, filepath: &str) -> Result<(), Box<dyn std::error::Error>> {
65 let json_str = self.jsondump()?;
66 std::fs::write(filepath, json_str)?;
67 Ok(())
68 }
69
70 pub fn from_json(filepath: &str) -> Result<Self, Box<dyn std::error::Error>> {
71 let json_str = std::fs::read_to_string(filepath)?;
72 Self::jsonload(&json_str)
73 }
74}
75
76impl AddAssign<Vector> for PointCloud {
81 fn add_assign(&mut self, other: Vector) {
82 for p in &mut self.points {
83 *p += other.clone();
84 }
85 }
86}
87
88impl SubAssign<Vector> for PointCloud {
89 fn sub_assign(&mut self, other: Vector) {
90 for p in &mut self.points {
91 *p -= other.clone();
92 }
93 }
94}
95
96impl Add<Vector> for PointCloud {
101 type Output = PointCloud;
102
103 fn add(self, other: Vector) -> PointCloud {
104 let mut result = self.clone();
105 result += other;
106 result
107 }
108}
109
110impl Sub<Vector> for PointCloud {
111 type Output = PointCloud;
112
113 fn sub(self, other: Vector) -> PointCloud {
114 let mut result = self.clone();
115 result -= other;
116 result
117 }
118}
119
120impl fmt::Display for PointCloud {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(
123 f,
124 "PointCloud(points={}, normals={}, colors={}, guid={}, name={})",
125 self.points.len(),
126 self.normals.len(),
127 self.colors.len(),
128 self.guid,
129 self.name
130 )
131 }
132}
133
134impl Serialize for PointCloud {
139 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140 where
141 S: Serializer,
142 {
143 use serde::ser::SerializeStruct;
144 let mut state = serializer.serialize_struct("PointCloud", 6)?;
145
146 state.serialize_field("type", "PointCloud")?;
147 state.serialize_field("guid", &self.guid)?;
148 state.serialize_field("name", &self.name)?;
149
150 let points_flat: Vec<f32> = self
152 .points
153 .iter()
154 .flat_map(|p| vec![p.x(), p.y(), p.z()])
155 .collect();
156 state.serialize_field("points", &points_flat)?;
157
158 let normals_flat: Vec<f32> = self
160 .normals
161 .iter()
162 .flat_map(|n| vec![n.x(), n.y(), n.z()])
163 .collect();
164 state.serialize_field("normals", &normals_flat)?;
165
166 let colors_flat: Vec<u8> = self
168 .colors
169 .iter()
170 .flat_map(|c| vec![c.r, c.g, c.b])
171 .collect();
172 state.serialize_field("colors", &colors_flat)?;
173
174 state.serialize_field("xform", &self.xform)?;
175
176 state.end()
177 }
178}
179
180impl<'de> Deserialize<'de> for PointCloud {
181 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 use serde::de::{self, MapAccess, Visitor};
186
187 #[derive(Deserialize)]
188 #[serde(field_identifier, rename_all = "lowercase")]
189 enum Field {
190 Type,
191 Guid,
192 Name,
193 Points,
194 Normals,
195 Colors,
196 Xform,
197 }
198
199 struct PointCloudVisitor;
200
201 impl<'de> Visitor<'de> for PointCloudVisitor {
202 type Value = PointCloud;
203
204 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
205 formatter.write_str("struct PointCloud")
206 }
207
208 fn visit_map<V>(self, mut map: V) -> Result<PointCloud, V::Error>
209 where
210 V: MapAccess<'de>,
211 {
212 let mut guid = None;
213 let mut name = None;
214 let mut points_flat: Option<Vec<f32>> = None;
215 let mut normals_flat: Option<Vec<f32>> = None;
216 let mut colors_flat: Option<Vec<u8>> = None;
217 let mut xform = None;
218
219 while let Some(key) = map.next_key()? {
220 match key {
221 Field::Type => {
222 let _: String = map.next_value()?;
223 }
224 Field::Guid => {
225 guid = Some(map.next_value()?);
226 }
227 Field::Name => {
228 name = Some(map.next_value()?);
229 }
230 Field::Points => {
231 points_flat = Some(map.next_value()?);
232 }
233 Field::Normals => {
234 normals_flat = Some(map.next_value()?);
235 }
236 Field::Colors => {
237 colors_flat = Some(map.next_value()?);
238 }
239 Field::Xform => {
240 xform = Some(map.next_value()?);
241 }
242 }
243 }
244
245 let guid = guid.ok_or_else(|| de::Error::missing_field("guid"))?;
246 let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
247 let points_flat = points_flat.ok_or_else(|| de::Error::missing_field("points"))?;
248 let normals_flat =
249 normals_flat.ok_or_else(|| de::Error::missing_field("normals"))?;
250 let colors_flat = colors_flat.ok_or_else(|| de::Error::missing_field("colors"))?;
251 let xform = xform.ok_or_else(|| de::Error::missing_field("xform"))?;
252
253 let points: Vec<Point> = points_flat
255 .chunks(3)
256 .map(|chunk| Point::new(chunk[0], chunk[1], chunk[2]))
257 .collect();
258
259 let normals: Vec<Vector> = normals_flat
261 .chunks(3)
262 .map(|chunk| Vector::new(chunk[0], chunk[1], chunk[2]))
263 .collect();
264
265 let colors: Vec<Color> = colors_flat
267 .chunks(3)
268 .map(|chunk| Color::new(chunk[0], chunk[1], chunk[2], 255))
269 .collect();
270
271 Ok(PointCloud {
272 guid,
273 name,
274 points,
275 normals,
276 colors,
277 xform,
278 })
279 }
280 }
281
282 const FIELDS: &[&str] = &[
283 "type", "guid", "name", "points", "normals", "colors", "xform",
284 ];
285 deserializer.deserialize_struct("PointCloud", FIELDS, PointCloudVisitor)
286 }
287}
288
289#[cfg(test)]
290#[path = "pointcloud_test.rs"]
291mod pointcloud_test;