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
7pub trait EncodeOsc<T> {
9 fn encode_object_bundle<'a, I>(object_collection: I, source_name: String, frame_id: i32) -> T where I: IntoIterator<Item = &'a Object>;
15
16 fn encode_cursor_bundle<'a, I>(cursor_collection: I, source_name: String, frame_id: i32) -> T where I: IntoIterator<Item = &'a Cursor>;
22
23 fn encode_blob_bundle<'a, I>(blob_collection: I, source_name: String, frame_id: i32) -> T where I: IntoIterator<Item = &'a Blob>;
29}
30
31pub 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
202pub 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
216pub 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
226pub 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
242pub 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#[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
268pub trait DecodeOsc<T> {
270 fn decode_bundle(bundle: T) -> Result<TuioBundle, TuioError>;
271}
272
273pub 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}