openigtlink_rust/protocol/types/
polydata.rs

1//! POLYDATA message type implementation
2//!
3//! The POLYDATA message is used to transfer 3D polygon/mesh data for surgical navigation,
4//! visualization of anatomical structures, or surgical planning.
5
6use crate::error::{IgtlError, Result};
7use crate::protocol::message::Message;
8use bytes::{Buf, BufMut};
9
10/// Attribute type for polygon data
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum AttributeType {
13    Point = 0,
14    Cell = 1,
15}
16
17impl AttributeType {
18    /// Create from type value
19    pub fn from_u8(value: u8) -> Result<Self> {
20        match value {
21            0 => Ok(AttributeType::Point),
22            1 => Ok(AttributeType::Cell),
23            _ => Err(IgtlError::InvalidSize {
24                expected: 0,
25                actual: value as usize,
26            }),
27        }
28    }
29}
30
31/// Attribute data for points or cells
32#[derive(Debug, Clone, PartialEq)]
33pub struct Attribute {
34    /// Attribute type (point or cell)
35    pub attr_type: AttributeType,
36    /// Number of components per attribute
37    pub num_components: u8,
38    /// Attribute name (max 64 chars)
39    pub name: String,
40    /// Attribute data (length = n_points/cells * num_components)
41    pub data: Vec<f32>,
42}
43
44impl Attribute {
45    /// Create a new attribute
46    pub fn new(
47        attr_type: AttributeType,
48        num_components: u8,
49        name: impl Into<String>,
50        data: Vec<f32>,
51    ) -> Self {
52        Attribute {
53            attr_type,
54            num_components,
55            name: name.into(),
56            data,
57        }
58    }
59}
60
61/// POLYDATA message for 3D polygon/mesh data
62///
63/// # OpenIGTLink Specification
64/// - Message type: "POLYDATA"
65/// - Format: Points + Vertices + Lines + Polygons + Triangle Strips + Attributes
66/// - Complex variable-length structure
67#[derive(Debug, Clone, PartialEq)]
68pub struct PolyDataMessage {
69    /// 3D points (x, y, z)
70    pub points: Vec<[f32; 3]>,
71    /// Vertex indices
72    pub vertices: Vec<u32>,
73    /// Line connectivity (first element = count, followed by indices)
74    pub lines: Vec<u32>,
75    /// Polygon connectivity (first element = count, followed by indices)
76    pub polygons: Vec<u32>,
77    /// Triangle strip connectivity
78    pub triangle_strips: Vec<u32>,
79    /// Attribute data
80    pub attributes: Vec<Attribute>,
81}
82
83impl PolyDataMessage {
84    /// Create a new POLYDATA message
85    pub fn new(points: Vec<[f32; 3]>) -> Self {
86        PolyDataMessage {
87            points,
88            vertices: Vec::new(),
89            lines: Vec::new(),
90            polygons: Vec::new(),
91            triangle_strips: Vec::new(),
92            attributes: Vec::new(),
93        }
94    }
95
96    /// Add vertices
97    pub fn with_vertices(mut self, vertices: Vec<u32>) -> Self {
98        self.vertices = vertices;
99        self
100    }
101
102    /// Add lines
103    pub fn with_lines(mut self, lines: Vec<u32>) -> Self {
104        self.lines = lines;
105        self
106    }
107
108    /// Add polygons
109    pub fn with_polygons(mut self, polygons: Vec<u32>) -> Self {
110        self.polygons = polygons;
111        self
112    }
113
114    /// Add triangle strips
115    pub fn with_triangle_strips(mut self, strips: Vec<u32>) -> Self {
116        self.triangle_strips = strips;
117        self
118    }
119
120    /// Add attribute
121    pub fn add_attribute(&mut self, attr: Attribute) {
122        self.attributes.push(attr);
123    }
124
125    /// Get number of points
126    pub fn num_points(&self) -> usize {
127        self.points.len()
128    }
129}
130
131impl Message for PolyDataMessage {
132    fn message_type() -> &'static str {
133        "POLYDATA"
134    }
135
136    fn encode_content(&self) -> Result<Vec<u8>> {
137        let mut buf = Vec::new();
138
139        // Encode number of points (uint32)
140        buf.put_u32(self.points.len() as u32);
141
142        // Encode points
143        for point in &self.points {
144            for &coord in point {
145                buf.put_f32(coord);
146            }
147        }
148
149        // Encode vertices (uint32 count + data)
150        buf.put_u32(self.vertices.len() as u32);
151        for &v in &self.vertices {
152            buf.put_u32(v);
153        }
154
155        // Encode lines
156        buf.put_u32(self.lines.len() as u32);
157        for &l in &self.lines {
158            buf.put_u32(l);
159        }
160
161        // Encode polygons
162        buf.put_u32(self.polygons.len() as u32);
163        for &p in &self.polygons {
164            buf.put_u32(p);
165        }
166
167        // Encode triangle strips
168        buf.put_u32(self.triangle_strips.len() as u32);
169        for &t in &self.triangle_strips {
170            buf.put_u32(t);
171        }
172
173        // Encode number of attributes
174        buf.put_u32(self.attributes.len() as u32);
175
176        // Encode each attribute
177        for attr in &self.attributes {
178            // Attribute type (uint8)
179            buf.put_u8(attr.attr_type as u8);
180
181            // Number of components (uint8)
182            buf.put_u8(attr.num_components);
183
184            // Name (char[64])
185            let mut name_bytes = [0u8; 64];
186            let name_str = attr.name.as_bytes();
187            let copy_len = name_str.len().min(63);
188            name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
189            buf.extend_from_slice(&name_bytes);
190
191            // Data size (uint32)
192            buf.put_u32(attr.data.len() as u32);
193
194            // Data (float32[])
195            for &val in &attr.data {
196                buf.put_f32(val);
197            }
198        }
199
200        Ok(buf)
201    }
202
203    fn decode_content(mut data: &[u8]) -> Result<Self> {
204        if data.len() < 4 {
205            return Err(IgtlError::InvalidSize {
206                expected: 4,
207                actual: data.len(),
208            });
209        }
210
211        // Decode number of points
212        let num_points = data.get_u32() as usize;
213
214        // Decode points
215        let mut points = Vec::with_capacity(num_points);
216        for _ in 0..num_points {
217            if data.remaining() < 12 {
218                return Err(IgtlError::InvalidSize {
219                    expected: 12,
220                    actual: data.remaining(),
221                });
222            }
223            points.push([data.get_f32(), data.get_f32(), data.get_f32()]);
224        }
225
226        // Decode vertices
227        let num_vertices = data.get_u32() as usize;
228        let mut vertices = Vec::with_capacity(num_vertices);
229        for _ in 0..num_vertices {
230            vertices.push(data.get_u32());
231        }
232
233        // Decode lines
234        let num_lines = data.get_u32() as usize;
235        let mut lines = Vec::with_capacity(num_lines);
236        for _ in 0..num_lines {
237            lines.push(data.get_u32());
238        }
239
240        // Decode polygons
241        let num_polygons = data.get_u32() as usize;
242        let mut polygons = Vec::with_capacity(num_polygons);
243        for _ in 0..num_polygons {
244            polygons.push(data.get_u32());
245        }
246
247        // Decode triangle strips
248        let num_strips = data.get_u32() as usize;
249        let mut triangle_strips = Vec::with_capacity(num_strips);
250        for _ in 0..num_strips {
251            triangle_strips.push(data.get_u32());
252        }
253
254        // Decode attributes
255        let num_attributes = data.get_u32() as usize;
256        let mut attributes = Vec::with_capacity(num_attributes);
257
258        for _ in 0..num_attributes {
259            // Attribute type
260            let attr_type = AttributeType::from_u8(data.get_u8())?;
261
262            // Number of components
263            let num_components = data.get_u8();
264
265            // Name (char[64])
266            let name_bytes = &data[..64];
267            data.advance(64);
268            let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(64);
269            let name = String::from_utf8(name_bytes[..name_len].to_vec())?;
270
271            // Data size
272            let data_size = data.get_u32() as usize;
273
274            // Data
275            let mut attr_data = Vec::with_capacity(data_size);
276            for _ in 0..data_size {
277                attr_data.push(data.get_f32());
278            }
279
280            attributes.push(Attribute {
281                attr_type,
282                num_components,
283                name,
284                data: attr_data,
285            });
286        }
287
288        if !data.is_empty() {
289            return Err(IgtlError::InvalidSize {
290                expected: 0,
291                actual: data.remaining(),
292            });
293        }
294
295        Ok(PolyDataMessage {
296            points,
297            vertices,
298            lines,
299            polygons,
300            triangle_strips,
301            attributes,
302        })
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[test]
311    fn test_message_type() {
312        assert_eq!(PolyDataMessage::message_type(), "POLYDATA");
313    }
314
315    #[test]
316    fn test_new() {
317        let points = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
318        let poly = PolyDataMessage::new(points.clone());
319        assert_eq!(poly.num_points(), 3);
320        assert_eq!(poly.points, points);
321    }
322
323    #[test]
324    fn test_with_polygons() {
325        let points = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
326        let poly = PolyDataMessage::new(points).with_polygons(vec![3, 0, 1, 2]); // Triangle with 3 vertices
327
328        assert_eq!(poly.polygons, vec![3, 0, 1, 2]);
329    }
330
331    #[test]
332    fn test_add_attribute() {
333        let points = vec![[0.0, 0.0, 0.0]];
334        let mut poly = PolyDataMessage::new(points);
335
336        let attr = Attribute::new(AttributeType::Point, 3, "Normals", vec![0.0, 0.0, 1.0]);
337        poly.add_attribute(attr);
338
339        assert_eq!(poly.attributes.len(), 1);
340    }
341
342    #[test]
343    fn test_encode_simple() {
344        let points = vec![[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]];
345        let poly = PolyDataMessage::new(points);
346        let encoded = poly.encode_content().unwrap();
347
348        // Should contain: point count (4) + points (24) + 5 counts (20) = 48 bytes minimum
349        assert!(encoded.len() >= 48);
350    }
351
352    #[test]
353    fn test_roundtrip_points_only() {
354        let original = PolyDataMessage::new(vec![
355            [0.0, 0.0, 0.0],
356            [1.0, 0.0, 0.0],
357            [0.0, 1.0, 0.0],
358            [0.0, 0.0, 1.0],
359        ]);
360
361        let encoded = original.encode_content().unwrap();
362        let decoded = PolyDataMessage::decode_content(&encoded).unwrap();
363
364        assert_eq!(decoded.num_points(), 4);
365        assert_eq!(decoded.points, original.points);
366    }
367
368    #[test]
369    fn test_roundtrip_with_polygons() {
370        let original =
371            PolyDataMessage::new(vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
372                .with_polygons(vec![3, 0, 1, 2]);
373
374        let encoded = original.encode_content().unwrap();
375        let decoded = PolyDataMessage::decode_content(&encoded).unwrap();
376
377        assert_eq!(decoded.polygons, vec![3, 0, 1, 2]);
378    }
379
380    #[test]
381    fn test_roundtrip_with_attribute() {
382        let mut original = PolyDataMessage::new(vec![[0.0, 0.0, 0.0]]);
383
384        original.add_attribute(Attribute::new(
385            AttributeType::Point,
386            3,
387            "Normals",
388            vec![0.0, 0.0, 1.0],
389        ));
390
391        let encoded = original.encode_content().unwrap();
392        let decoded = PolyDataMessage::decode_content(&encoded).unwrap();
393
394        assert_eq!(decoded.attributes.len(), 1);
395        assert_eq!(decoded.attributes[0].name, "Normals");
396        assert_eq!(decoded.attributes[0].num_components, 3);
397        assert_eq!(decoded.attributes[0].data, vec![0.0, 0.0, 1.0]);
398    }
399
400    #[test]
401    fn test_roundtrip_complex() {
402        let mut original = PolyDataMessage::new(vec![
403            [0.0, 0.0, 0.0],
404            [1.0, 0.0, 0.0],
405            [1.0, 1.0, 0.0],
406            [0.0, 1.0, 0.0],
407        ])
408        .with_polygons(vec![4, 0, 1, 2, 3])
409        .with_lines(vec![2, 0, 1]);
410
411        original.add_attribute(Attribute::new(
412            AttributeType::Cell,
413            1,
414            "Quality",
415            vec![0.95],
416        ));
417
418        let encoded = original.encode_content().unwrap();
419        let decoded = PolyDataMessage::decode_content(&encoded).unwrap();
420
421        assert_eq!(decoded.num_points(), 4);
422        assert_eq!(decoded.polygons, vec![4, 0, 1, 2, 3]);
423        assert_eq!(decoded.lines, vec![2, 0, 1]);
424        assert_eq!(decoded.attributes.len(), 1);
425    }
426
427    #[test]
428    fn test_decode_invalid() {
429        let data = vec![0u8; 2]; // Too short
430        let result = PolyDataMessage::decode_content(&data);
431        assert!(result.is_err());
432    }
433}