openigtlink_rust/protocol/types/
point.rs1use crate::protocol::message::Message;
100use crate::error::{IgtlError, Result};
101use bytes::{Buf, BufMut};
102
103#[derive(Debug, Clone, PartialEq)]
105pub struct PointElement {
106 pub name: String,
108 pub group: String,
110 pub rgba: [u8; 4],
112 pub position: [f32; 3],
114 pub diameter: f32,
116 pub owner: String,
118}
119
120impl PointElement {
121 pub fn new(
123 name: impl Into<String>,
124 group: impl Into<String>,
125 position: [f32; 3],
126 ) -> Self {
127 PointElement {
128 name: name.into(),
129 group: group.into(),
130 rgba: [255, 255, 255, 255], position,
132 diameter: 0.0,
133 owner: String::new(),
134 }
135 }
136
137 pub fn with_color(
139 name: impl Into<String>,
140 group: impl Into<String>,
141 rgba: [u8; 4],
142 position: [f32; 3],
143 ) -> Self {
144 PointElement {
145 name: name.into(),
146 group: group.into(),
147 rgba,
148 position,
149 diameter: 0.0,
150 owner: String::new(),
151 }
152 }
153
154 pub fn with_details(
156 name: impl Into<String>,
157 group: impl Into<String>,
158 rgba: [u8; 4],
159 position: [f32; 3],
160 diameter: f32,
161 owner: impl Into<String>,
162 ) -> Self {
163 PointElement {
164 name: name.into(),
165 group: group.into(),
166 rgba,
167 position,
168 diameter,
169 owner: owner.into(),
170 }
171 }
172}
173
174#[derive(Debug, Clone, PartialEq)]
181pub struct PointMessage {
182 pub points: Vec<PointElement>,
184}
185
186impl PointMessage {
187 pub fn new(points: Vec<PointElement>) -> Self {
189 PointMessage { points }
190 }
191
192 pub fn empty() -> Self {
194 PointMessage { points: Vec::new() }
195 }
196
197 pub fn add_point(&mut self, point: PointElement) {
199 self.points.push(point);
200 }
201
202 pub fn len(&self) -> usize {
204 self.points.len()
205 }
206
207 pub fn is_empty(&self) -> bool {
209 self.points.is_empty()
210 }
211}
212
213impl Message for PointMessage {
214 fn message_type() -> &'static str {
215 "POINT"
216 }
217
218 fn encode_content(&self) -> Result<Vec<u8>> {
219 let mut buf = Vec::with_capacity(self.points.len() * 136);
220
221 for point in &self.points {
222 let mut name_bytes = [0u8; 64];
224 let name_str = point.name.as_bytes();
225 let copy_len = name_str.len().min(63);
226 name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
227 buf.extend_from_slice(&name_bytes);
228
229 let mut group_bytes = [0u8; 32];
231 let group_str = point.group.as_bytes();
232 let copy_len = group_str.len().min(31);
233 group_bytes[..copy_len].copy_from_slice(&group_str[..copy_len]);
234 buf.extend_from_slice(&group_bytes);
235
236 buf.extend_from_slice(&point.rgba);
238
239 for &coord in &point.position {
241 buf.put_f32(coord);
242 }
243
244 buf.put_f32(point.diameter);
246
247 let mut owner_bytes = [0u8; 20];
249 let owner_str = point.owner.as_bytes();
250 let copy_len = owner_str.len().min(19);
251 owner_bytes[..copy_len].copy_from_slice(&owner_str[..copy_len]);
252 buf.extend_from_slice(&owner_bytes);
253 }
254
255 Ok(buf)
256 }
257
258 fn decode_content(mut data: &[u8]) -> Result<Self> {
259 let mut points = Vec::new();
260
261 while data.len() >= 136 {
262 let name_bytes = &data[..64];
264 data.advance(64);
265 let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(64);
266 let name = String::from_utf8(name_bytes[..name_len].to_vec())?;
267
268 let group_bytes = &data[..32];
270 data.advance(32);
271 let group_len = group_bytes.iter().position(|&b| b == 0).unwrap_or(32);
272 let group = String::from_utf8(group_bytes[..group_len].to_vec())?;
273
274 let rgba = [data.get_u8(), data.get_u8(), data.get_u8(), data.get_u8()];
276
277 let position = [data.get_f32(), data.get_f32(), data.get_f32()];
279
280 let diameter = data.get_f32();
282
283 let owner_bytes = &data[..20];
285 data.advance(20);
286 let owner_len = owner_bytes.iter().position(|&b| b == 0).unwrap_or(20);
287 let owner = String::from_utf8(owner_bytes[..owner_len].to_vec())?;
288
289 points.push(PointElement {
290 name,
291 group,
292 rgba,
293 position,
294 diameter,
295 owner,
296 });
297 }
298
299 if !data.is_empty() {
300 return Err(IgtlError::InvalidSize {
301 expected: 0,
302 actual: data.len(),
303 });
304 }
305
306 Ok(PointMessage { points })
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn test_message_type() {
316 assert_eq!(PointMessage::message_type(), "POINT");
317 }
318
319 #[test]
320 fn test_empty() {
321 let msg = PointMessage::empty();
322 assert!(msg.is_empty());
323 assert_eq!(msg.len(), 0);
324 }
325
326 #[test]
327 fn test_new_point() {
328 let point = PointElement::new("Fiducial1", "Landmark", [10.0, 20.0, 30.0]);
329 assert_eq!(point.name, "Fiducial1");
330 assert_eq!(point.group, "Landmark");
331 assert_eq!(point.position, [10.0, 20.0, 30.0]);
332 assert_eq!(point.rgba, [255, 255, 255, 255]);
333 }
334
335 #[test]
336 fn test_point_with_color() {
337 let point = PointElement::with_color(
338 "Point1",
339 "Fiducial",
340 [255, 0, 0, 255],
341 [1.0, 2.0, 3.0],
342 );
343 assert_eq!(point.rgba, [255, 0, 0, 255]);
344 }
345
346 #[test]
347 fn test_add_point() {
348 let mut msg = PointMessage::empty();
349 msg.add_point(PointElement::new("P1", "Landmark", [0.0, 0.0, 0.0]));
350 assert_eq!(msg.len(), 1);
351 }
352
353 #[test]
354 fn test_encode_single_point() {
355 let point = PointElement::new("Test", "Fiducial", [1.0, 2.0, 3.0]);
356 let msg = PointMessage::new(vec![point]);
357 let encoded = msg.encode_content().unwrap();
358
359 assert_eq!(encoded.len(), 136);
360 }
361
362 #[test]
363 fn test_roundtrip_single() {
364 let original = PointMessage::new(vec![PointElement::with_details(
365 "Fiducial1",
366 "Landmark",
367 [255, 128, 64, 255],
368 [100.5, 200.5, 300.5],
369 5.0,
370 "Image1",
371 )]);
372
373 let encoded = original.encode_content().unwrap();
374 let decoded = PointMessage::decode_content(&encoded).unwrap();
375
376 assert_eq!(decoded.points.len(), 1);
377 assert_eq!(decoded.points[0].name, "Fiducial1");
378 assert_eq!(decoded.points[0].group, "Landmark");
379 assert_eq!(decoded.points[0].rgba, [255, 128, 64, 255]);
380 assert_eq!(decoded.points[0].position, [100.5, 200.5, 300.5]);
381 assert_eq!(decoded.points[0].diameter, 5.0);
382 assert_eq!(decoded.points[0].owner, "Image1");
383 }
384
385 #[test]
386 fn test_roundtrip_multiple() {
387 let original = PointMessage::new(vec![
388 PointElement::new("P1", "Landmark", [1.0, 2.0, 3.0]),
389 PointElement::new("P2", "Fiducial", [4.0, 5.0, 6.0]),
390 PointElement::new("P3", "Target", [7.0, 8.0, 9.0]),
391 ]);
392
393 let encoded = original.encode_content().unwrap();
394 let decoded = PointMessage::decode_content(&encoded).unwrap();
395
396 assert_eq!(decoded.points.len(), 3);
397 assert_eq!(decoded.points[0].name, "P1");
398 assert_eq!(decoded.points[1].name, "P2");
399 assert_eq!(decoded.points[2].name, "P3");
400 }
401
402 #[test]
403 fn test_name_truncation() {
404 let long_name = "A".repeat(100);
405 let point = PointElement::new(&long_name, "Group", [0.0, 0.0, 0.0]);
406 let msg = PointMessage::new(vec![point]);
407
408 let encoded = msg.encode_content().unwrap();
409 let decoded = PointMessage::decode_content(&encoded).unwrap();
410
411 assert!(decoded.points[0].name.len() <= 63);
412 }
413
414 #[test]
415 fn test_empty_message() {
416 let msg = PointMessage::empty();
417 let encoded = msg.encode_content().unwrap();
418 let decoded = PointMessage::decode_content(&encoded).unwrap();
419
420 assert_eq!(decoded.points.len(), 0);
421 assert_eq!(encoded.len(), 0);
422 }
423
424 #[test]
425 fn test_decode_invalid_size() {
426 let data = vec![0u8; 135]; let result = PointMessage::decode_content(&data);
428 assert!(result.is_err());
429 }
430
431 #[test]
432 fn test_color_values() {
433 let point = PointElement::with_color(
434 "ColorTest",
435 "Test",
436 [128, 64, 32, 200],
437 [0.0, 0.0, 0.0],
438 );
439 let msg = PointMessage::new(vec![point]);
440
441 let encoded = msg.encode_content().unwrap();
442 let decoded = PointMessage::decode_content(&encoded).unwrap();
443
444 assert_eq!(decoded.points[0].rgba, [128, 64, 32, 200]);
445 }
446}