external library for math types

This commit is contained in:
2025-12-30 23:38:55 +09:00
parent bb8b91345c
commit 77db46198a
5 changed files with 50 additions and 206 deletions

10
Cargo.lock generated
View File

@@ -15,6 +15,7 @@ dependencies = [
name = "cursebreaker-parser" name = "cursebreaker-parser"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"glam",
"indexmap", "indexmap",
"pretty_assertions", "pretty_assertions",
"regex", "regex",
@@ -35,6 +36,15 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "glam"
version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.16.1"

View File

@@ -28,6 +28,9 @@ indexmap = { version = "2.1", features = ["serde"] }
# Regex for parsing # Regex for parsing
regex = "1.10" regex = "1.10"
# Math types (Vector2, Vector3, Quaternion, etc.)
glam = { version = "0.29", features = ["serde"] }
[dev-dependencies] [dev-dependencies]
# Testing utilities # Testing utilities
pretty_assertions = "1.4" pretty_assertions = "1.4"

View File

@@ -185,7 +185,7 @@ impl fmt::Display for PropertyValue {
PropertyValue::Null => write!(f, "null"), PropertyValue::Null => write!(f, "null"),
PropertyValue::Vector2(v) => write!(f, "({}, {})", v.x, v.y), PropertyValue::Vector2(v) => write!(f, "({}, {})", v.x, v.y),
PropertyValue::Vector3(v) => write!(f, "({}, {}, {})", v.x, v.y, v.z), PropertyValue::Vector3(v) => write!(f, "({}, {}, {})", v.x, v.y, v.z),
PropertyValue::Color(c) => write!(f, "rgba({}, {}, {}, {})", c.r, c.g, c.b, c.a), PropertyValue::Color(c) => write!(f, "rgba({}, {}, {}, {})", c.x, c.y, c.z, c.w),
PropertyValue::Quaternion(q) => write!(f, "({}, {}, {}, {})", q.x, q.y, q.z, q.w), PropertyValue::Quaternion(q) => write!(f, "({}, {}, {}, {})", q.x, q.y, q.z, q.w),
PropertyValue::FileRef(r) => write!(f, "{{fileID: {}}}", r.file_id), PropertyValue::FileRef(r) => write!(f, "{{fileID: {}}}", r.file_id),
PropertyValue::ExternalRef(r) => write!(f, "{{guid: {}, type: {}}}", r.guid, r.type_id), PropertyValue::ExternalRef(r) => write!(f, "{{guid: {}, type: {}}}", r.guid, r.type_id),
@@ -324,6 +324,7 @@ fn try_convert_unity_type(
if let (Some(r), Some(g), Some(b), Some(a)) = if let (Some(r), Some(g), Some(b), Some(a)) =
(get_f32("r"), get_f32("g"), get_f32("b"), get_f32("a")) (get_f32("r"), get_f32("g"), get_f32("b"), get_f32("a"))
{ {
// Color is Vec4 where (r,g,b,a) maps to (x,y,z,w)
return Ok(Some(PropertyValue::Color(Color::new(r, g, b, a)))); return Ok(Some(PropertyValue::Color(Color::new(r, g, b, a))));
} }
} }
@@ -338,7 +339,8 @@ fn try_convert_unity_type(
if let (Some(x), Some(y), Some(z), Some(w)) = if let (Some(x), Some(y), Some(z), Some(w)) =
(get_f32("x"), get_f32("y"), get_f32("z"), get_f32("w")) (get_f32("x"), get_f32("y"), get_f32("z"), get_f32("w"))
{ {
return Ok(Some(PropertyValue::Quaternion(Quaternion::new( // Quaternion uses from_xyzw constructor
return Ok(Some(PropertyValue::Quaternion(Quaternion::from_xyzw(
x, y, z, w, x, y, z, w,
)))); ))));
} }
@@ -427,10 +429,10 @@ mod tests {
let result = convert_yaml_value(&yaml).unwrap(); let result = convert_yaml_value(&yaml).unwrap();
if let PropertyValue::Color(c) = result { if let PropertyValue::Color(c) = result {
assert_eq!(c.r, 1.0); assert_eq!(c.x, 1.0); // r
assert_eq!(c.g, 0.5); assert_eq!(c.y, 0.5); // g
assert_eq!(c.b, 0.0); assert_eq!(c.z, 0.0); // b
assert_eq!(c.a, 1.0); assert_eq!(c.w, 1.0); // a
} else { } else {
panic!("Expected Color, got {:?}", result); panic!("Expected Color, got {:?}", result);
} }
@@ -518,8 +520,8 @@ mod tests {
fn test_type_checks() { fn test_type_checks() {
assert!(PropertyValue::Null.is_null()); assert!(PropertyValue::Null.is_null());
assert!(PropertyValue::Array(vec![]).is_array()); assert!(PropertyValue::Array(vec![]).is_array());
assert!(PropertyValue::Vector3(Vector3::zero()).is_vector3()); assert!(PropertyValue::Vector3(Vector3::ZERO).is_vector3());
assert!(PropertyValue::Color(Color::white()).is_color()); assert!(PropertyValue::Color(Color::ONE).is_color());
} }
#[test] #[test]

View File

@@ -242,11 +242,11 @@ mod tests {
); );
transform_props.insert( transform_props.insert(
"m_LocalRotation".to_string(), "m_LocalRotation".to_string(),
PropertyValue::Quaternion(Quaternion::identity()), PropertyValue::Quaternion(Quaternion::IDENTITY),
); );
transform_props.insert( transform_props.insert(
"m_LocalScale".to_string(), "m_LocalScale".to_string(),
PropertyValue::Vector3(Vector3::one()), PropertyValue::Vector3(Vector3::ONE),
); );
transform_props.insert( transform_props.insert(
"m_GameObject".to_string(), "m_GameObject".to_string(),
@@ -270,27 +270,27 @@ mod tests {
let mut rect_props = IndexMap::new(); let mut rect_props = IndexMap::new();
rect_props.insert( rect_props.insert(
"m_LocalPosition".to_string(), "m_LocalPosition".to_string(),
PropertyValue::Vector3(Vector3::zero()), PropertyValue::Vector3(Vector3::ZERO),
); );
rect_props.insert( rect_props.insert(
"m_LocalRotation".to_string(), "m_LocalRotation".to_string(),
PropertyValue::Quaternion(Quaternion::identity()), PropertyValue::Quaternion(Quaternion::IDENTITY),
); );
rect_props.insert( rect_props.insert(
"m_LocalScale".to_string(), "m_LocalScale".to_string(),
PropertyValue::Vector3(Vector3::one()), PropertyValue::Vector3(Vector3::ONE),
); );
rect_props.insert( rect_props.insert(
"m_AnchorMin".to_string(), "m_AnchorMin".to_string(),
PropertyValue::Vector2(Vector2::zero()), PropertyValue::Vector2(Vector2::ZERO),
); );
rect_props.insert( rect_props.insert(
"m_AnchorMax".to_string(), "m_AnchorMax".to_string(),
PropertyValue::Vector2(Vector2::one()), PropertyValue::Vector2(Vector2::ONE),
); );
rect_props.insert( rect_props.insert(
"m_AnchoredPosition".to_string(), "m_AnchoredPosition".to_string(),
PropertyValue::Vector2(Vector2::zero()), PropertyValue::Vector2(Vector2::ZERO),
); );
rect_props.insert( rect_props.insert(
"m_SizeDelta".to_string(), "m_SizeDelta".to_string(),
@@ -341,7 +341,7 @@ mod tests {
let doc = create_test_transform(); let doc = create_test_transform();
let transform = Transform::new(&doc).unwrap(); let transform = Transform::new(&doc).unwrap();
let scale = transform.local_scale().unwrap(); let scale = transform.local_scale().unwrap();
assert_eq!(scale, &Vector3::one()); assert_eq!(scale, &Vector3::ONE);
} }
#[test] #[test]
@@ -356,7 +356,7 @@ mod tests {
let doc = create_test_rect_transform(); let doc = create_test_rect_transform();
let rect_transform = RectTransform::new(&doc).unwrap(); let rect_transform = RectTransform::new(&doc).unwrap();
let anchor_min = rect_transform.anchor_min().unwrap(); let anchor_min = rect_transform.anchor_min().unwrap();
assert_eq!(anchor_min, &Vector2::zero()); assert_eq!(anchor_min, &Vector2::ZERO);
} }
#[test] #[test]
@@ -364,7 +364,7 @@ mod tests {
let doc = create_test_rect_transform(); let doc = create_test_rect_transform();
let rect_transform = RectTransform::new(&doc).unwrap(); let rect_transform = RectTransform::new(&doc).unwrap();
let anchor_max = rect_transform.anchor_max().unwrap(); let anchor_max = rect_transform.anchor_max().unwrap();
assert_eq!(anchor_max, &Vector2::one()); assert_eq!(anchor_max, &Vector2::ONE);
} }
#[test] #[test]

View File

@@ -2,176 +2,8 @@
use super::FileID; use super::FileID;
/// A 2D vector used in Unity // Re-export glam types for Unity compatibility
/// pub use glam::{Quat as Quaternion, Vec2 as Vector2, Vec3 as Vector3, Vec4 as Color};
/// # Examples
///
/// ```
/// use cursebreaker_parser::Vector2;
///
/// let v = Vector2::new(1.0, 2.0);
/// assert_eq!(v.x, 1.0);
/// assert_eq!(v.y, 2.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector2 {
pub x: f32,
pub y: f32,
}
impl Vector2 {
/// Create a new Vector2
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
/// Create a zero vector (0, 0)
pub fn zero() -> Self {
Self::new(0.0, 0.0)
}
/// Create a one vector (1, 1)
pub fn one() -> Self {
Self::new(1.0, 1.0)
}
}
/// A 3D vector used in Unity for positions, scales, etc.
///
/// # Examples
///
/// ```
/// use cursebreaker_parser::Vector3;
///
/// let v = Vector3::new(1.0, 2.0, 3.0);
/// assert_eq!(v.x, 1.0);
/// assert_eq!(v.y, 2.0);
/// assert_eq!(v.z, 3.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vector3 {
/// Create a new Vector3
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
/// Create a zero vector (0, 0, 0)
pub fn zero() -> Self {
Self::new(0.0, 0.0, 0.0)
}
/// Create a one vector (1, 1, 1)
pub fn one() -> Self {
Self::new(1.0, 1.0, 1.0)
}
/// Create an up vector (0, 1, 0)
pub fn up() -> Self {
Self::new(0.0, 1.0, 0.0)
}
/// Create a forward vector (0, 0, 1)
pub fn forward() -> Self {
Self::new(0.0, 0.0, 1.0)
}
/// Create a right vector (1, 0, 0)
pub fn right() -> Self {
Self::new(1.0, 0.0, 0.0)
}
}
/// A color with RGBA components
///
/// # Examples
///
/// ```
/// use cursebreaker_parser::Color;
///
/// let c = Color::new(1.0, 0.5, 0.0, 1.0);
/// assert_eq!(c.r, 1.0);
/// assert_eq!(c.a, 1.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
/// Create a new Color
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
/// Create a white color (1, 1, 1, 1)
pub fn white() -> Self {
Self::new(1.0, 1.0, 1.0, 1.0)
}
/// Create a black color (0, 0, 0, 1)
pub fn black() -> Self {
Self::new(0.0, 0.0, 0.0, 1.0)
}
/// Create a transparent color (0, 0, 0, 0)
pub fn clear() -> Self {
Self::new(0.0, 0.0, 0.0, 0.0)
}
/// Create a red color (1, 0, 0, 1)
pub fn red() -> Self {
Self::new(1.0, 0.0, 0.0, 1.0)
}
/// Create a green color (0, 1, 0, 1)
pub fn green() -> Self {
Self::new(0.0, 1.0, 0.0, 1.0)
}
/// Create a blue color (0, 0, 1, 1)
pub fn blue() -> Self {
Self::new(0.0, 0.0, 1.0, 1.0)
}
}
/// A quaternion used for rotations in Unity
///
/// # Examples
///
/// ```
/// use cursebreaker_parser::Quaternion;
///
/// let q = Quaternion::identity();
/// assert_eq!(q.w, 1.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Quaternion {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl Quaternion {
/// Create a new Quaternion
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self { x, y, z, w }
}
/// Create an identity quaternion (0, 0, 0, 1)
pub fn identity() -> Self {
Self::new(0.0, 0.0, 0.0, 1.0)
}
}
/// A reference to another Unity object by file ID /// A reference to another Unity object by file ID
/// ///
@@ -232,7 +64,7 @@ mod tests {
#[test] #[test]
fn test_vector2_zero() { fn test_vector2_zero() {
let v = Vector2::zero(); let v = Vector2::ZERO;
assert_eq!(v, Vector2::new(0.0, 0.0)); assert_eq!(v, Vector2::new(0.0, 0.0));
} }
@@ -246,46 +78,43 @@ mod tests {
#[test] #[test]
fn test_vector3_zero() { fn test_vector3_zero() {
let v = Vector3::zero(); let v = Vector3::ZERO;
assert_eq!(v, Vector3::new(0.0, 0.0, 0.0)); assert_eq!(v, Vector3::new(0.0, 0.0, 0.0));
} }
#[test] #[test]
fn test_vector3_directions() { fn test_vector3_directions() {
assert_eq!(Vector3::up(), Vector3::new(0.0, 1.0, 0.0)); assert_eq!(Vector3::Y, Vector3::new(0.0, 1.0, 0.0));
assert_eq!(Vector3::forward(), Vector3::new(0.0, 0.0, 1.0)); assert_eq!(Vector3::Z, Vector3::new(0.0, 0.0, 1.0));
assert_eq!(Vector3::right(), Vector3::new(1.0, 0.0, 0.0)); assert_eq!(Vector3::X, Vector3::new(1.0, 0.0, 0.0));
} }
#[test] #[test]
fn test_color_creation() { fn test_color_creation() {
let c = Color::new(1.0, 0.5, 0.0, 1.0); let c = Color::new(1.0, 0.5, 0.0, 1.0);
assert_eq!(c.r, 1.0); assert_eq!(c.x, 1.0); // r
assert_eq!(c.g, 0.5); assert_eq!(c.y, 0.5); // g
assert_eq!(c.b, 0.0); assert_eq!(c.z, 0.0); // b
assert_eq!(c.a, 1.0); assert_eq!(c.w, 1.0); // a
} }
#[test] #[test]
fn test_color_presets() { fn test_color_presets() {
assert_eq!(Color::white(), Color::new(1.0, 1.0, 1.0, 1.0)); assert_eq!(Color::ONE, Color::new(1.0, 1.0, 1.0, 1.0)); // white
assert_eq!(Color::black(), Color::new(0.0, 0.0, 0.0, 1.0)); assert_eq!(Color::new(0.0, 0.0, 0.0, 1.0), Color::new(0.0, 0.0, 0.0, 1.0)); // black
assert_eq!(Color::red(), Color::new(1.0, 0.0, 0.0, 1.0));
assert_eq!(Color::green(), Color::new(0.0, 1.0, 0.0, 1.0));
assert_eq!(Color::blue(), Color::new(0.0, 0.0, 1.0, 1.0));
} }
#[test] #[test]
fn test_quaternion_creation() { fn test_quaternion_creation() {
let q = Quaternion::new(0.0, 0.0, 0.0, 1.0); let q = Quaternion::from_xyzw(0.0, 0.0, 0.0, 1.0);
assert_eq!(q.x, 0.0); assert_eq!(q.x, 0.0);
assert_eq!(q.w, 1.0); assert_eq!(q.w, 1.0);
} }
#[test] #[test]
fn test_quaternion_identity() { fn test_quaternion_identity() {
let q = Quaternion::identity(); let q = Quaternion::IDENTITY;
assert_eq!(q, Quaternion::new(0.0, 0.0, 0.0, 1.0)); assert_eq!(q, Quaternion::from_xyzw(0.0, 0.0, 0.0, 1.0));
} }
#[test] #[test]