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