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