Skip to main content

tuio_rs/
osc_encode_decode.rs

1use std::{time::{SystemTime}, iter};
2
3use rosc::{OscBundle, OscPacket, OscMessage, OscType, OscTime};
4
5use crate::{object::Object, cursor::{Cursor, Position, Velocity}, blob::Blob, errors::TuioError};
6
7/// Base trait to implement an OSC encoder
8pub trait EncodeOsc<T> {
9    /// Encodes an [Object] collection into an OSC bundle
10    /// # Arguments
11    /// * `object_collection` - an iterable [Object] collection
12    /// * `source_name` - the source's name
13    /// * `frame_id` - the current's frame id
14    fn encode_object_bundle<'a, I>(object_collection: I, source_name: String, frame_id: i32) -> T where I: IntoIterator<Item = &'a Object>;
15
16    /// Encodes an [Cursor] collection into an OSC bundle
17    /// # Arguments
18    /// * `cursor_collection` - an iterable [Cursor] collection
19    /// * `source_name` - the source's name
20    /// * `frame_id` - the current's frame id
21    fn encode_cursor_bundle<'a, I>(cursor_collection: I, source_name: String, frame_id: i32) -> T where I: IntoIterator<Item = &'a Cursor>;
22
23    /// Encodes an [Blob] collection into an OSC bundle
24    /// # Arguments
25    /// * `blob_collection` - an iterable [Blob] collection
26    /// * `source_name` - the source's name
27    /// * `frame_id` - the current's frame id
28    fn encode_blob_bundle<'a, I>(blob_collection: I, source_name: String, frame_id: i32) -> T where I: IntoIterator<Item = &'a Blob>;
29}
30
31/// An implementation of trait [EncodeOsc] based on [rosc]
32pub struct OscEncoder;
33
34impl EncodeOsc<OscBundle> for OscEncoder {
35    fn encode_object_bundle<'a, I>(object_collection: I, source_name: String, frame_id: i32) -> OscBundle where I: IntoIterator<Item = &'a Object> {
36        let source_message = OscPacket::Message(OscMessage {
37            addr: "/tuio/2Dobj".into(),
38            args: vec![
39                OscType::String("source".into()),
40                OscType::String(source_name)
41            ]
42        });
43    
44        let mut set_messages = vec![];
45        let mut object_ids: Vec<OscType>= vec![];
46    
47        for object in object_collection.into_iter()  {
48            let id = object.get_session_id();
49            object_ids.push(OscType::Int(id));
50    
51            set_messages.push(OscPacket::Message(OscMessage {
52                addr: "/tuio/2Dobj".into(),
53                args: vec![
54                    OscType::String("set".into()),
55                    OscType::Int(id),
56                    OscType::Int(object.get_class_id()),
57                    OscType::Float(object.get_x_position()),
58                    OscType::Float(object.get_y_position()),
59                    OscType::Float(object.get_angle()),
60                    OscType::Float(object.get_x_velocity()),
61                    OscType::Float(object.get_y_velocity()),
62                    OscType::Float(object.get_rotation_speed()),
63                    OscType::Float(object.get_acceleration()),
64                    OscType::Float(object.get_rotation_acceleration())
65                ]
66            }));
67        }
68        
69        let alive_message = OscPacket::Message(OscMessage {
70            addr: "/tuio/2Dobj".into(),
71            args: vec![OscType::String("alive".into())].into_iter().chain(object_ids.into_iter()).collect()
72        });
73    
74        let frame_message = OscPacket::Message(OscMessage {
75            addr: "/tuio/2Dobj".into(),
76            args: vec![OscType::String("fseq".into()), OscType::Int(frame_id)]
77        });
78    
79        OscBundle { 
80            timetag: OscTime::try_from(SystemTime::now()).expect("failed with system time conversion"), 
81            content: vec![
82                source_message,
83                alive_message
84            ].into_iter()
85            .chain(set_messages.into_iter())
86            .chain(iter::once(frame_message))
87            .collect()
88        }
89    }
90
91    fn encode_cursor_bundle<'a, I>(cursor_collection: I, source_name: String, frame_id: i32) -> OscBundle where I: IntoIterator<Item = &'a Cursor> {
92        let source_message = OscPacket::Message(OscMessage {
93            addr: "/tuio/2Dcur".into(),
94            args: vec![
95                OscType::String("source".into()),
96                OscType::String(source_name)
97            ]
98        });
99    
100        let mut set_messages = vec![];
101        let mut cursor_ids: Vec<OscType>= vec![];
102    
103        for cursor in cursor_collection.into_iter()  {
104            let id = cursor.get_session_id();
105            cursor_ids.push(OscType::Int(id));
106
107            set_messages.push(OscPacket::Message(OscMessage {
108                addr: "/tuio/2Dcur".into(),
109                args: vec![
110                    OscType::String("set".into()),
111                    OscType::Int(id),
112                    OscType::Float(cursor.get_x_position()),
113                    OscType::Float(cursor.get_y_position()),
114                    OscType::Float(cursor.get_x_velocity()),
115                    OscType::Float(cursor.get_y_velocity()),
116                    OscType::Float(cursor.get_acceleration())
117                ]
118            }));
119        }
120        
121        let alive_message = OscPacket::Message(OscMessage {
122            addr: "/tuio/2Dcur".into(),
123            args: vec![OscType::String("alive".into())].into_iter().chain(cursor_ids.into_iter()).collect()
124        });
125    
126        let frame_message = OscPacket::Message(OscMessage {
127            addr: "/tuio/2Dcur".into(),
128            args: vec![OscType::String("fseq".into()), OscType::Int(frame_id)]
129        });
130    
131        OscBundle { 
132            timetag: OscTime::try_from(SystemTime::now()).expect("failed with system time conversion"), 
133            content: vec![
134                source_message,
135                alive_message
136            ].into_iter()
137            .chain(set_messages.into_iter())
138            .chain(iter::once(frame_message))
139            .collect()
140        }
141    }
142
143    fn encode_blob_bundle<'a, I>(blob_collection: I, source_name: String, frame_id: i32) -> OscBundle where I: IntoIterator<Item = &'a Blob> {
144        let source_message = OscPacket::Message(OscMessage {
145            addr: "/tuio/2Dblb".into(),
146            args: vec![
147                OscType::String("source".into()),
148                OscType::String(source_name)
149            ]
150        });
151    
152        let mut set_messages = vec![];
153        let mut blob_ids: Vec<OscType>= vec![];
154    
155        for blob in blob_collection.into_iter() {     
156            let id = blob.get_session_id();       
157            blob_ids.push(OscType::Int(id));
158            
159            set_messages.push(OscPacket::Message(OscMessage {
160                addr: "/tuio/2Dblb".into(),
161                args: vec![
162                    OscType::String("set".into()),
163                    OscType::Int(id),
164                    OscType::Float(blob.get_x_position()),
165                    OscType::Float(blob.get_y_position()),
166                    OscType::Float(blob.get_angle()),
167                    OscType::Float(blob.get_width()),
168                    OscType::Float(blob.get_height()),
169                    OscType::Float(blob.get_area()),
170                    OscType::Float(blob.get_x_velocity()),
171                    OscType::Float(blob.get_y_velocity()),
172                    OscType::Float(blob.get_rotation_speed()),
173                    OscType::Float(blob.get_acceleration()),
174                    OscType::Float(blob.get_rotation_acceleration())
175                ]
176            }));
177        }
178        
179        let alive_message = OscPacket::Message(OscMessage {
180            addr: "/tuio/2Dblb".into(),
181            args: vec![OscType::String("alive".into())].into_iter().chain(blob_ids.into_iter()).collect()
182        });
183    
184        let frame_message = OscPacket::Message(OscMessage {
185            addr: "/tuio/2Dblb".into(),
186            args: vec![OscType::String("fseq".into()), OscType::Int(frame_id)]
187        });
188    
189        OscBundle { 
190            timetag: OscTime::try_from(SystemTime::now()).expect("failed with system time conversion"), 
191            content: vec![
192                source_message,
193                alive_message
194            ].into_iter()
195            .chain(set_messages.into_iter())
196            .chain(iter::once(frame_message))
197            .collect()
198        }
199    }
200}
201
202/// A struct containg all parameters of a TUIO Object
203pub struct ObjectParams {
204    pub session_id: i32,
205    pub class_id: i32,
206    pub x_pos: f32,
207    pub y_pos: f32,
208    pub angle: f32,
209    pub x_vel: f32,
210    pub y_vel: f32,
211    pub rotation_speed: f32,
212    pub acceleration: f32,
213    pub rotation_acceleration: f32
214}
215
216/// A struct containg all parameters of a TUIO Cursor
217pub struct CursorParams {
218    pub session_id: i32,
219    pub x_pos: f32,
220    pub y_pos: f32,
221    pub x_vel: f32,
222    pub y_vel: f32,
223    pub acceleration: f32
224}
225
226/// A struct containg all parameters of a TUIO Blob
227pub struct BlobParams {
228    pub session_id: i32,
229    pub x_pos: f32,
230    pub y_pos: f32,
231    pub angle: f32,
232    pub width: f32,
233    pub height: f32,
234    pub area: f32,
235    pub x_vel: f32,
236    pub y_vel: f32,
237    pub rotation_speed: f32,
238    pub acceleration: f32,
239    pub rotation_acceleration: f32
240}
241
242/// An enum of a "set" TUIO message
243pub enum Set {
244    Cursor(Vec<Cursor>),
245    Object(Vec<Object>),
246    Blob(Vec<Blob>),
247}
248
249#[derive(Default)]
250pub enum TuioBundleType {
251    Cursor,
252    Object,
253    Blob,
254    #[default]
255    Unknown
256}
257
258/// A struct containing informations of a TUIO bundle
259#[derive(Default)]
260pub struct TuioBundle {
261    pub tuio_type: TuioBundleType,
262    pub source: String,
263    pub alive: Vec<i32>,
264    pub set: Option<Set>,
265    pub fseq: i32
266}
267
268/// Base trait to implement an OSC decoder
269pub trait DecodeOsc<T> {
270    fn decode_bundle(bundle: T) -> Result<TuioBundle, TuioError>;
271}
272
273/// An implementation of trait [DecodeOsc] based on [rosc]
274pub struct OscDecoder;
275
276fn try_unwrap_source_name(message: &OscMessage) -> Result<String, TuioError> {
277    match message.args.get(1) {
278        Some(arg) => {
279            match arg.clone().string() {
280                Some(source_name) => Ok(source_name),
281                None => Err(TuioError::WrongArgumentType(message.clone(), 1)),
282            }
283        },
284        None => Err(TuioError::MissingSource(message.clone())),
285    }
286}
287
288fn try_unwrap_object_args(args: &[OscType]) -> Result<Object, u8> {
289    Ok(Object {
290        session_id: args[1].clone().int().ok_or(1)?,
291        class_id: args[2].clone().int().ok_or(2)?,
292        position: Position {x: args[3].clone().float().ok_or(3)?, y: args[4].clone().float().ok_or(4)?},
293        angle: args[5].clone().float().ok_or(5)?,
294        velocity: Velocity {x: args[6].clone().float().ok_or(6)?, y: args[7].clone().float().ok_or(7)?},
295        rotation_speed: args[8].clone().float().ok_or(8)?,
296        acceleration: args[9].clone().float().ok_or(9)?,
297        rotation_acceleration: args[10].clone().float().ok_or(10)?,
298    })
299}
300
301fn try_unwrap_cursor_args(args: &[OscType]) -> Result<Cursor, u8> {
302    Ok(Cursor {
303        session_id: args[1].clone().int().ok_or(1)?,
304        position: Position {x: args[2].clone().float().ok_or(2)?, y: args[3].clone().float().ok_or(3)?},
305        velocity: Velocity {x: args[4].clone().float().ok_or(4)?, y: args[5].clone().float().ok_or(5)?},
306        acceleration: args[6].clone().float().ok_or(6)?,
307    })
308}
309
310fn try_unwrap_blob_args(args: &[OscType]) -> Result<Blob, u8> {
311    Ok(Blob {
312        session_id: args[1].clone().int().ok_or(1)?,
313        position: Position {x: args[2].clone().float().ok_or(2)?, y: args[3].clone().float().ok_or(3)?},
314        angle: args[4].clone().float().ok_or(4)?,
315        width: args[5].clone().float().ok_or(5)?,
316        height: args[6].clone().float().ok_or(6)?,
317        area: args[7].clone().float().ok_or(7)?,
318        velocity: Velocity {x: args[8].clone().float().ok_or(8)?, y: args[9].clone().float().ok_or(9)?},
319        rotation_speed: args[10].clone().float().ok_or(10)?,
320        acceleration: args[11].clone().float().ok_or(11)?,
321        rotation_acceleration: args[12].clone().float().ok_or(12)?,
322    })
323}
324
325impl DecodeOsc<OscBundle> for OscDecoder {
326    fn decode_bundle(bundle: OscBundle) -> Result<TuioBundle, TuioError> {
327        let mut decoded_bundle = TuioBundle::default();
328        
329        for packet in &bundle.content {
330            if let OscPacket::Message(message) = packet {
331                match message.args.first() {
332                    Some(OscType::String(arg)) => {
333                        match arg.as_str() {
334                            "source" => {
335                                decoded_bundle.tuio_type = match message.addr.as_str() {
336                                    "/tuio/2Dobj" => TuioBundleType::Object,
337                                    "/tuio/2Dcur" => TuioBundleType::Cursor,
338                                    "/tuio/2Dblb" => TuioBundleType::Blob,
339                                    _ => return Err(TuioError::UnknownAddress(message.clone()))
340                                };
341
342                                decoded_bundle.source = try_unwrap_source_name(message)?;
343                            },
344                            "alive" => {
345                                decoded_bundle.alive = message.args.iter().skip(1).filter_map(|e| e.clone().int()).collect();
346                            },
347                            "set" => {
348                                match decoded_bundle.tuio_type {
349                                    TuioBundleType::Cursor => {
350                                        if let Set::Cursor(set) = decoded_bundle.set.get_or_insert(Set::Cursor(Vec::new())) {
351                                            if message.args.len() != 7 {
352                                                return Err(TuioError::MissingArguments(message.clone()));
353                                            }
354        
355                                            match try_unwrap_cursor_args(&message.args) {
356                                                Ok(params) => {
357                                                    set.push(params);
358                                                },
359                                                Err(index) => return Err(TuioError::WrongArgumentType(message.clone(), index)),
360                                            }
361                                        }
362                                    },
363                                    TuioBundleType::Object => {
364                                        if let Set::Object(set) = decoded_bundle.set.get_or_insert(Set::Object(Vec::new())) {
365                                            if message.args.len() != 11 {
366                                                return Err(TuioError::MissingArguments(message.clone()));
367                                            }
368                                            
369                                            match try_unwrap_object_args(&message.args) {
370                                                Ok(params) => {
371                                                    set.push(params);
372                                                },
373                                                Err(index) => return Err(TuioError::WrongArgumentType(message.clone(), index)),
374                                            }
375                                        }
376                                    },
377                                    TuioBundleType::Blob => {
378                                        if let Set::Blob(set) = decoded_bundle.set.get_or_insert(Set::Blob(Vec::new())) {
379                                            if message.args.len() != 13 {
380                                                return Err(TuioError::MissingArguments(message.clone()));
381                                            }
382        
383                                            match try_unwrap_blob_args(&message.args) {
384                                                Ok(params) => {
385                                                    set.push(params);
386                                                },
387                                                Err(index) => return Err(TuioError::WrongArgumentType(message.clone(), index)),
388                                            }
389                                        }
390
391                                    },
392                                    TuioBundleType::Unknown => return Err(TuioError::IncompleteBundle(bundle)),
393                                }
394                            },
395                            "fseq" => {
396                                if let Some(OscType::Int(fseq)) = message.args.get(1) {
397                                    decoded_bundle.fseq = *fseq;
398                                }
399                                else {
400                                    return Err(TuioError::MissingArguments(message.clone()))
401                                }
402                            },
403                            _ => return Err(TuioError::UnknownMessageType(message.clone()))
404                        }
405                    },
406                    None => return Err(TuioError::EmptyMessage(message.clone())),
407                    _ => return Err(TuioError::UnknownMessageType(message.clone()))
408                }
409            };
410        }
411
412        Ok(decoded_bundle)
413    }
414}
415
416#[cfg(test)]
417mod tests {
418    use crate::{cursor::{Cursor, Position}, object::Object, blob::Blob, osc_encode_decode::{OscEncoder, EncodeOsc}};
419
420    use super::*;
421
422    #[test]
423    fn encoding_decoding() {
424        let source = "test".to_string();
425
426        let cursors = vec![Cursor::new(0, Position {x: 0., y: 0.}), Cursor::new(1, Position {x: 0.5, y: 0.5})];
427        let objects = vec![Object::new(0, 0, Position {x: 0., y: 0.}, 0.), Object::new(1, 1, Position {x: 0.5, y: 0.5}, 0.)];
428        let blobs = vec![Blob::new(0, Position {x: 0., y: 0.}, 0., 0.3, 0.3, 0.09), Blob::new(1, Position {x: 0.5, y: 0.5}, 0., 0.5, 0.5, 0.25)];
429
430        let cursor_bundle = OscEncoder::encode_cursor_bundle(&cursors, source.clone(), 0);
431        let object_bundle = OscEncoder::encode_object_bundle(&objects, source.clone(), 0);
432        let blob_bundle = OscEncoder::encode_blob_bundle(&blobs, source, 0);
433
434        match OscDecoder::decode_bundle(cursor_bundle) {
435            Ok(decoded_bundle) => {
436                if let Some(Set::Cursor(decoded_cursors)) = decoded_bundle.set {
437                    assert_eq!(decoded_cursors.len(), 2);
438                    assert_eq!(cursors[0], decoded_cursors[0]);
439                    assert_eq!(cursors[1], decoded_cursors[1]);
440                }
441            },
442            Err(err) => {println!("{err}"); panic!()},
443        }
444
445        match OscDecoder::decode_bundle(object_bundle) {
446            Ok(decoded_bundle) => {
447                if let Some(Set::Object(decoded_objects)) = decoded_bundle.set {
448                    assert_eq!(decoded_objects.len(), 2);
449                    assert_eq!(objects[0], decoded_objects[0]);
450                    assert_eq!(objects[1], decoded_objects[1]);
451                }
452            },
453            Err(err) => {println!("{err}"); panic!()},
454        }
455
456        match OscDecoder::decode_bundle(blob_bundle) {
457            Ok(decoded_bundle) => {
458                if let Some(Set::Blob(decoded_blobs)) = decoded_bundle.set {
459                    assert_eq!(decoded_blobs.len(), 2);
460                    assert_eq!(blobs[0], decoded_blobs[0]);
461                    assert_eq!(blobs[1], decoded_blobs[1]);
462                }
463            },
464            Err(err) => {println!("{err}"); panic!()},
465        }
466    }
467}