1use thiserror::Error;
2
3use crate::Error;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[cfg_attr(
14 feature = "serde",
15 derive(serde::Serialize, serde::Deserialize),
16 serde(transparent)
17)]
18pub struct Enum {
19 value: u32,
20}
21
22impl Enum {
23 pub fn from_u32(value: u32) -> Self {
24 Self { value }
25 }
26
27 pub fn to_u32(self) -> u32 {
28 self.value
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize,))]
38pub struct EnumItem {
39 #[cfg_attr(feature = "serde", serde(rename = "type"))]
40 pub ty: String,
41 pub value: u32,
42}
43
44impl From<EnumItem> for Enum {
45 fn from(enum_item: EnumItem) -> Self {
46 Self {
47 value: enum_item.value,
48 }
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq)]
58pub struct Vector2 {
59 pub x: f32,
60 pub y: f32,
61}
62
63impl Vector2 {
64 pub fn new(x: f32, y: f32) -> Self {
65 Self { x, y }
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct Vector2int16 {
79 pub x: i16,
80 pub y: i16,
81}
82
83impl Vector2int16 {
84 pub fn new(x: i16, y: i16) -> Self {
85 Self { x, y }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
95pub struct Vector3 {
96 pub x: f32,
97 pub y: f32,
98 pub z: f32,
99}
100
101fn approx_unit_or_zero(value: f32) -> Option<i32> {
102 if value.abs() <= f32::EPSILON {
103 Some(0)
104 } else if value.abs() - 1.0 <= f32::EPSILON {
105 Some(1.0f32.copysign(value) as i32)
106 } else {
107 None
108 }
109}
110
111impl Vector3 {
112 pub fn new(x: f32, y: f32, z: f32) -> Self {
113 Self { x, y, z }
114 }
115
116 #[allow(clippy::wrong_self_convention)]
128 pub fn to_normal_id(&self) -> Option<u8> {
129 fn get_normal_id(position: u8, value: i32) -> Option<u8> {
130 match value {
131 1 => Some(position),
132 -1 => Some(position + 3),
133 _ => None,
134 }
135 }
136
137 let x = approx_unit_or_zero(self.x);
138 let y = approx_unit_or_zero(self.y);
139 let z = approx_unit_or_zero(self.z);
140
141 match (x, y, z) {
142 (Some(x), Some(0), Some(0)) => get_normal_id(0, x),
143 (Some(0), Some(y), Some(0)) => get_normal_id(1, y),
144 (Some(0), Some(0), Some(z)) => get_normal_id(2, z),
145 _ => None,
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct Vector3int16 {
160 pub x: i16,
161 pub y: i16,
162 pub z: i16,
163}
164
165impl Vector3int16 {
166 pub fn new(x: i16, y: i16, z: i16) -> Self {
167 Self { x, y, z }
168 }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq)]
176#[cfg_attr(
177 feature = "serde",
178 derive(serde::Serialize, serde::Deserialize),
179 serde(rename_all = "camelCase")
180)]
181pub struct CFrame {
182 pub position: Vector3,
183 pub orientation: Matrix3,
184}
185
186impl CFrame {
187 pub fn new(position: Vector3, orientation: Matrix3) -> Self {
188 Self {
189 position,
190 orientation,
191 }
192 }
193}
194
195#[derive(Debug, Clone, Copy, PartialEq)]
198pub struct Matrix3 {
199 pub x: Vector3,
200 pub y: Vector3,
201 pub z: Vector3,
202}
203
204#[derive(Debug, Error)]
205pub(crate) enum Matrix3Error {
206 #[error("invalid rotation ID: {id}")]
207 BadRotationId { id: u8 },
208}
209
210impl Matrix3 {
211 pub fn new(x: Vector3, y: Vector3, z: Vector3) -> Self {
212 Self { x, y, z }
213 }
214
215 pub fn identity() -> Self {
216 Self {
217 x: Vector3::new(1.0, 0.0, 0.0),
218 y: Vector3::new(0.0, 1.0, 0.0),
219 z: Vector3::new(0.0, 0.0, 1.0),
220 }
221 }
222
223 pub fn transpose(&self) -> Self {
224 Self {
225 x: Vector3::new(self.x.x, self.y.x, self.z.x),
226 y: Vector3::new(self.x.y, self.y.y, self.z.y),
227 z: Vector3::new(self.x.z, self.y.z, self.z.z),
228 }
229 }
230
231 pub fn to_basic_rotation_id(&self) -> Option<u8> {
232 let transpose = self.transpose();
233 let x_id = transpose.x.to_normal_id()?;
234 let y_id = transpose.y.to_normal_id()?;
235 let z_id = transpose.z.to_normal_id()?;
236 let basic_rotation_id = (6 * x_id) + y_id + 1;
237
238 if Matrix3::from_basic_rotation_id(basic_rotation_id)
242 .ok()?
243 .transpose()
244 .z
245 .to_normal_id()?
246 == z_id
247 {
248 Some(basic_rotation_id)
249 } else {
250 None
251 }
252 }
253
254 pub fn from_basic_rotation_id(id: u8) -> Result<Matrix3, Error> {
255 match id {
256 0x02 => Ok(Matrix3::identity()),
257 0x03 => Ok(Matrix3::new(
258 Vector3::new(1.0, 0.0, 0.0),
259 Vector3::new(0.0, 0.0, -1.0),
260 Vector3::new(0.0, 1.0, 0.0),
261 )),
262 0x05 => Ok(Matrix3::new(
263 Vector3::new(1.0, 0.0, 0.0),
264 Vector3::new(0.0, -1.0, 0.0),
265 Vector3::new(0.0, 0.0, -1.0),
266 )),
267 0x06 => Ok(Matrix3::new(
268 Vector3::new(1.0, 0.0, 0.0),
269 Vector3::new(0.0, 0.0, 1.0),
270 Vector3::new(0.0, -1.0, 0.0),
271 )),
272 0x07 => Ok(Matrix3::new(
273 Vector3::new(0.0, 1.0, 0.0),
274 Vector3::new(1.0, 0.0, 0.0),
275 Vector3::new(0.0, 0.0, -1.0),
276 )),
277 0x09 => Ok(Matrix3::new(
278 Vector3::new(0.0, 0.0, 1.0),
279 Vector3::new(1.0, 0.0, 0.0),
280 Vector3::new(0.0, 1.0, 0.0),
281 )),
282 0x0a => Ok(Matrix3::new(
283 Vector3::new(0.0, -1.0, 0.0),
284 Vector3::new(1.0, 0.0, 0.0),
285 Vector3::new(0.0, 0.0, 1.0),
286 )),
287 0x0c => Ok(Matrix3::new(
288 Vector3::new(0.0, 0.0, -1.0),
289 Vector3::new(1.0, 0.0, 0.0),
290 Vector3::new(0.0, -1.0, 0.0),
291 )),
292 0x0d => Ok(Matrix3::new(
293 Vector3::new(0.0, 1.0, 0.0),
294 Vector3::new(0.0, 0.0, 1.0),
295 Vector3::new(1.0, 0.0, 0.0),
296 )),
297 0x0e => Ok(Matrix3::new(
298 Vector3::new(0.0, 0.0, -1.0),
299 Vector3::new(0.0, 1.0, 0.0),
300 Vector3::new(1.0, 0.0, 0.0),
301 )),
302 0x10 => Ok(Matrix3::new(
303 Vector3::new(0.0, -1.0, 0.0),
304 Vector3::new(0.0, 0.0, -1.0),
305 Vector3::new(1.0, 0.0, 0.0),
306 )),
307 0x11 => Ok(Matrix3::new(
308 Vector3::new(0.0, 0.0, 1.0),
309 Vector3::new(0.0, -1.0, 0.0),
310 Vector3::new(1.0, 0.0, 0.0),
311 )),
312 0x14 => Ok(Matrix3::new(
313 Vector3::new(-1.0, 0.0, 0.0),
314 Vector3::new(0.0, 1.0, 0.0),
315 Vector3::new(0.0, 0.0, -1.0),
316 )),
317 0x15 => Ok(Matrix3::new(
318 Vector3::new(-1.0, 0.0, 0.0),
319 Vector3::new(0.0, 0.0, 1.0),
320 Vector3::new(0.0, 1.0, 0.0),
321 )),
322 0x17 => Ok(Matrix3::new(
323 Vector3::new(-1.0, 0.0, 0.0),
324 Vector3::new(0.0, -1.0, 0.0),
325 Vector3::new(0.0, 0.0, 1.0),
326 )),
327 0x18 => Ok(Matrix3::new(
328 Vector3::new(-1.0, 0.0, 0.0),
329 Vector3::new(0.0, 0.0, -1.0),
330 Vector3::new(0.0, -1.0, 0.0),
331 )),
332 0x19 => Ok(Matrix3::new(
333 Vector3::new(0.0, 1.0, 0.0),
334 Vector3::new(-1.0, 0.0, 0.0),
335 Vector3::new(0.0, 0.0, 1.0),
336 )),
337 0x1b => Ok(Matrix3::new(
338 Vector3::new(0.0, 0.0, -1.0),
339 Vector3::new(-1.0, 0.0, 0.0),
340 Vector3::new(0.0, 1.0, 0.0),
341 )),
342 0x1c => Ok(Matrix3::new(
343 Vector3::new(0.0, -1.0, 0.0),
344 Vector3::new(-1.0, 0.0, 0.0),
345 Vector3::new(0.0, 0.0, -1.0),
346 )),
347 0x1e => Ok(Matrix3::new(
348 Vector3::new(0.0, 0.0, 1.0),
349 Vector3::new(-1.0, 0.0, 0.0),
350 Vector3::new(0.0, -1.0, 0.0),
351 )),
352 0x1f => Ok(Matrix3::new(
353 Vector3::new(0.0, 1.0, 0.0),
354 Vector3::new(0.0, 0.0, -1.0),
355 Vector3::new(-1.0, 0.0, 0.0),
356 )),
357 0x20 => Ok(Matrix3::new(
358 Vector3::new(0.0, 0.0, 1.0),
359 Vector3::new(0.0, 1.0, 0.0),
360 Vector3::new(-1.0, 0.0, 0.0),
361 )),
362 0x22 => Ok(Matrix3::new(
363 Vector3::new(0.0, -1.0, 0.0),
364 Vector3::new(0.0, 0.0, 1.0),
365 Vector3::new(-1.0, 0.0, 0.0),
366 )),
367 0x23 => Ok(Matrix3::new(
368 Vector3::new(0.0, 0.0, -1.0),
369 Vector3::new(0.0, -1.0, 0.0),
370 Vector3::new(-1.0, 0.0, 0.0),
371 )),
372 _ => Err(Error::from(Matrix3Error::BadRotationId { id })),
373 }
374 }
375}
376
377#[derive(Debug, Clone, Copy, PartialEq)]
384pub struct Color3 {
385 pub r: f32,
386 pub g: f32,
387 pub b: f32,
388}
389
390impl Color3 {
391 pub fn new(r: f32, g: f32, b: f32) -> Self {
392 Self { r, g, b }
393 }
394}
395
396impl From<Color3uint8> for Color3 {
397 fn from(value: Color3uint8) -> Self {
398 Self {
399 r: value.r as f32 / 255.0,
400 g: value.g as f32 / 255.0,
401 b: value.b as f32 / 255.0,
402 }
403 }
404}
405
406#[derive(Debug, Clone, Copy, PartialEq, Eq)]
417pub struct Color3uint8 {
418 pub r: u8,
419 pub g: u8,
420 pub b: u8,
421}
422
423impl Color3uint8 {
424 pub fn new(r: u8, g: u8, b: u8) -> Self {
425 Self { r, g, b }
426 }
427}
428
429impl From<Color3> for Color3uint8 {
430 fn from(value: Color3) -> Self {
431 Self {
432 r: (value.r.clamp(0.0, 1.0) * 255.0).round() as u8,
433 g: (value.g.clamp(0.0, 1.0) * 255.0).round() as u8,
434 b: (value.b.clamp(0.0, 1.0) * 255.0).round() as u8,
435 }
436 }
437}
438
439#[derive(Debug, Clone, Copy, PartialEq)]
448#[cfg_attr(
449 feature = "serde",
450 derive(serde::Serialize, serde::Deserialize),
451 serde(rename_all = "camelCase")
452)]
453pub struct Ray {
454 pub origin: Vector3,
455 pub direction: Vector3,
456}
457
458impl Ray {
459 pub fn new(origin: Vector3, direction: Vector3) -> Self {
460 Self { origin, direction }
461 }
462}
463
464#[derive(Debug, Clone, Copy, PartialEq)]
470pub struct Region3 {
471 pub min: Vector3,
472 pub max: Vector3,
473}
474
475impl Region3 {
476 pub fn new(min: Vector3, max: Vector3) -> Self {
477 Self { min, max }
478 }
479}
480
481#[derive(Debug, Clone, Copy, PartialEq, Eq)]
490pub struct Region3int16 {
491 pub min: Vector3int16,
492 pub max: Vector3int16,
493}
494
495impl Region3int16 {
496 pub fn new(min: Vector3int16, max: Vector3int16) -> Self {
497 Self { min, max }
498 }
499}
500
501#[derive(Debug, Clone, Copy, PartialEq)]
506pub struct Rect {
507 pub min: Vector2,
508 pub max: Vector2,
509}
510
511impl Rect {
512 pub fn new(min: Vector2, max: Vector2) -> Self {
513 Self { min, max }
514 }
515}
516
517#[derive(Debug, Clone, Copy, PartialEq)]
523pub struct UDim {
524 pub scale: f32,
525 pub offset: i32,
526}
527
528impl UDim {
529 pub fn new(scale: f32, offset: i32) -> Self {
530 Self { scale, offset }
531 }
532}
533
534#[derive(Debug, Clone, Copy, PartialEq)]
540pub struct UDim2 {
541 pub x: UDim,
542 pub y: UDim,
543}
544
545impl UDim2 {
546 pub fn new(x: UDim, y: UDim) -> Self {
547 Self { x, y }
548 }
549}
550
551#[derive(Debug, Clone, Copy, PartialEq)]
556pub struct NumberRange {
557 pub min: f32,
558 pub max: f32,
559}
560
561impl NumberRange {
562 pub fn new(min: f32, max: f32) -> Self {
563 Self { min, max }
564 }
565}
566
567#[derive(Debug, Clone, PartialEq)]
572#[cfg_attr(
573 feature = "serde",
574 derive(serde::Serialize, serde::Deserialize),
575 serde(rename_all = "camelCase")
576)]
577pub struct ColorSequence {
578 pub keypoints: Vec<ColorSequenceKeypoint>,
579}
580
581#[derive(Debug, Clone, Copy, PartialEq)]
588#[cfg_attr(
589 feature = "serde",
590 derive(serde::Serialize, serde::Deserialize),
591 serde(rename_all = "camelCase")
592)]
593pub struct ColorSequenceKeypoint {
594 pub time: f32,
595 pub color: Color3,
596}
597
598impl ColorSequenceKeypoint {
599 pub fn new(time: f32, color: Color3) -> Self {
600 Self { time, color }
601 }
602}
603
604#[derive(Debug, Clone, PartialEq)]
610#[cfg_attr(
611 feature = "serde",
612 derive(serde::Serialize, serde::Deserialize),
613 serde(rename_all = "camelCase")
614)]
615pub struct NumberSequence {
616 pub keypoints: Vec<NumberSequenceKeypoint>,
617}
618
619#[derive(Debug, Clone, Copy, PartialEq)]
627#[cfg_attr(
628 feature = "serde",
629 derive(serde::Serialize, serde::Deserialize),
630 serde(rename_all = "camelCase")
631)]
632pub struct NumberSequenceKeypoint {
633 pub time: f32,
634 pub value: f32,
635 pub envelope: f32,
636}
637
638impl NumberSequenceKeypoint {
639 pub fn new(time: f32, value: f32, envelope: f32) -> Self {
640 Self {
641 time,
642 value,
643 envelope,
644 }
645 }
646}
647
648#[cfg(feature = "serde")]
649serde_tuple! {
650 Vector2(x: f32, y: f32),
651 Vector2int16(x: i16, y: i16),
652 Vector3(x: f32, y: f32, z: f32),
653 Vector3int16(x: i16, y: i16, z: i16),
654
655 Color3(r: f32, g: f32, b: f32),
656 Color3uint8(r: u8, g: u8, b: u8),
657
658 UDim(scale: f32, offset: i32),
659 UDim2(x: UDim, y: UDim),
660
661 NumberRange(min: f32, max: f32),
662
663 Rect(min: Vector2, max: Vector2),
664 Region3(min: Vector3, max: Vector3),
665 Region3int16(min: Vector3int16, max: Vector3int16),
666
667 Matrix3(x: Vector3, y: Vector3, z: Vector3),
668}
669
670#[cfg(all(test, feature = "serde"))]
671mod serde_test {
672 use super::*;
673
674 use std::fmt::Debug;
675
676 use serde::{de::DeserializeOwned, Serialize};
677
678 fn test_ser<T: Debug + PartialEq + Serialize + DeserializeOwned>(value: T, output: &str) {
679 let serialized = serde_json::to_string(&value).unwrap();
680 assert_eq!(serialized, output);
681
682 let deserialized: T = serde_json::from_str(output).unwrap();
683 assert_eq!(deserialized, value);
684 }
685
686 #[test]
687 fn vec2_json() {
688 test_ser(Vector2 { x: 2.0, y: 3.5 }, "[2.0,3.5]");
689 }
690
691 #[test]
692 fn udim_json() {
693 test_ser(
694 UDim {
695 scale: 1.0,
696 offset: 175,
697 },
698 "[1.0,175]",
699 );
700 }
701
702 #[test]
703 fn udim2_json() {
704 test_ser(
705 UDim2 {
706 x: UDim {
707 scale: 0.0,
708 offset: 30,
709 },
710 y: UDim {
711 scale: 1.0,
712 offset: 60,
713 },
714 },
715 "[[0.0,30],[1.0,60]]",
716 );
717 }
718
719 #[test]
720 fn region3_json() {
721 test_ser(
722 Region3 {
723 min: Vector3::new(-1.0, -2.0, -3.0),
724 max: Vector3::new(4.0, 5.0, 6.0),
725 },
726 "[[-1.0,-2.0,-3.0],[4.0,5.0,6.0]]",
727 );
728 }
729
730 #[test]
731 fn matrix3_json() {
732 test_ser(
733 Matrix3 {
734 x: Vector3::new(1.0, 2.0, 3.0),
735 y: Vector3::new(4.0, 5.0, 6.0),
736 z: Vector3::new(7.0, 8.0, 9.0),
737 },
738 "[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]",
739 );
740 }
741
742 #[test]
743 fn tagged_enum_json() {
744 test_ser(
745 EnumItem {
746 ty: "PlayTag".to_string(),
747 value: 3,
748 },
749 r#"{"type":"PlayTag","value":3}"#,
750 );
751 }
752}