Skip to main content

token_value_map/
data.rs

1use crate::{
2    DataTypeOps,
3    macros::{impl_data_arithmetic, impl_data_type_ops, impl_try_from_vec},
4    *,
5};
6#[cfg(feature = "matrix3")]
7use bytemuck::cast;
8use bytemuck::cast_slice;
9use std::{
10    fmt::Display,
11    ops::{Add, Div, Mul, Sub},
12    str::FromStr,
13};
14
15/// A variant `enum` containing all supported data types.
16///
17/// [`Data`] can hold scalar values ([`Boolean`], [`Integer`], [`Real`],
18/// [`String`]), vector types ([`Vector2`], [`Vector3`], [`Color`],
19/// [`Matrix3`]), and collections of these types ([`BooleanVec`],
20/// [`IntegerVec`], etc.).
21#[derive(Debug, Clone, PartialEq, strum::AsRefStr, strum::EnumDiscriminants)]
22#[strum_discriminants(name(DataType), derive(Hash))]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "facet", derive(Facet))]
25#[cfg_attr(feature = "facet", facet(opaque))]
26#[cfg_attr(feature = "facet", repr(u8))]
27#[cfg_attr(feature = "rkyv", derive(Archive, RkyvSerialize, RkyvDeserialize))]
28pub enum Data {
29    /// A boolean value.
30    Boolean(Boolean),
31    /// A 64-bit signed integer.
32    Integer(Integer),
33    /// A 64-bit floating-point number.
34    Real(Real),
35    /// A UTF-8 string.
36    String(String),
37    /// A 4-component RGBA color.
38    Color(Color),
39    /// A 2D vector.
40    #[cfg(feature = "vector2")]
41    Vector2(Vector2),
42    /// A 3D vector.
43    #[cfg(feature = "vector3")]
44    Vector3(Vector3),
45    /// A 3×3 transformation matrix.
46    #[cfg(feature = "matrix3")]
47    Matrix3(Matrix3),
48    /// A 3D normal vector.
49    #[cfg(feature = "normal3")]
50    Normal3(Normal3),
51    /// A 3D point.
52    #[cfg(feature = "point3")]
53    Point3(Point3),
54    /// A 4×4 transformation matrix.
55    #[cfg(feature = "matrix4")]
56    Matrix4(Matrix4),
57    /// A vector of boolean values.
58    BooleanVec(BooleanVec),
59    /// A vector of integer values.
60    IntegerVec(IntegerVec),
61    /// A vector of real values.
62    RealVec(RealVec),
63    /// A vector of color values.
64    ColorVec(ColorVec),
65    /// A vector of string values.
66    StringVec(StringVec),
67    /// A vector of 2D vectors.
68    #[cfg(all(feature = "vector2", feature = "vec_variants"))]
69    Vector2Vec(Vector2Vec),
70    /// A vector of 3D vectors.
71    #[cfg(all(feature = "vector3", feature = "vec_variants"))]
72    Vector3Vec(Vector3Vec),
73    /// A vector of matrices.
74    #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
75    Matrix3Vec(Matrix3Vec),
76    /// A vector of 3D normals.
77    #[cfg(all(feature = "normal3", feature = "vec_variants"))]
78    Normal3Vec(Normal3Vec),
79    /// A vector of 3D points.
80    #[cfg(all(feature = "point3", feature = "vec_variants"))]
81    Point3Vec(Point3Vec),
82    /// A vector of 4×4 matrices.
83    #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
84    Matrix4Vec(Matrix4Vec),
85    /// A real-valued curve (Position → Real).
86    #[cfg(feature = "curves")]
87    RealCurve(RealCurve),
88    /// A color-valued curve (Position → Color).
89    #[cfg(feature = "curves")]
90    ColorCurve(ColorCurve),
91}
92
93impl_data_type_ops!(Data);
94
95impl Data {
96    /// Get the length of a vector value, or `1` for scalar values
97    #[allow(clippy::len_without_is_empty)]
98    pub fn len(&self) -> usize {
99        self.try_len().unwrap_or(1)
100    }
101
102    /// Get the length of a vector value, or None for scalar values
103    pub fn try_len(&self) -> Option<usize> {
104        match self {
105            Data::BooleanVec(v) => Some(v.0.len()),
106            Data::IntegerVec(v) => Some(v.0.len()),
107            Data::RealVec(v) => Some(v.0.len()),
108            Data::StringVec(v) => Some(v.0.len()),
109            Data::ColorVec(v) => Some(v.0.len()),
110            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
111            Data::Vector2Vec(v) => Some(v.0.len()),
112            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
113            Data::Vector3Vec(v) => Some(v.0.len()),
114            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
115            Data::Matrix3Vec(v) => Some(v.0.len()),
116            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
117            Data::Normal3Vec(v) => Some(v.0.len()),
118            #[cfg(all(feature = "point3", feature = "vec_variants"))]
119            Data::Point3Vec(v) => Some(v.0.len()),
120            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
121            Data::Matrix4Vec(v) => Some(v.0.len()),
122            _ => None,
123        }
124    }
125
126    /// If this is a vector value.
127    pub fn is_vec(&self) -> bool {
128        self.try_len().is_some()
129    }
130
131    #[named]
132    pub fn to_bool(&self) -> Result<bool> {
133        match self {
134            Data::Boolean(value) => Ok(value.0),
135            Data::Real(value) => Ok(value.0 != 0.0),
136            Data::Integer(value) => Ok(value.0 != 0),
137            Data::String(value) => Ok(value.0.parse::<bool>().unwrap_or(false)),
138            _ => Err(Error::IncompatibleType {
139                method: function_name!(),
140                got: self.data_type(),
141            }),
142        }
143    }
144
145    pub fn to_f32(&self) -> Result<f32> {
146        match self {
147            Data::Boolean(value) => {
148                if value.0 {
149                    Ok(1.0)
150                } else {
151                    Ok(0.0)
152                }
153            }
154            Data::Real(value) => Ok(value.0 as _),
155            Data::Integer(value) => Ok(value.0 as _),
156            _ => Err(Error::IncompatibleType {
157                method: "to_f32",
158                got: self.data_type(),
159            }),
160        }
161    }
162
163    pub fn to_f64(&self) -> Result<f64> {
164        match self {
165            Data::Boolean(value) => {
166                if value.0 {
167                    Ok(1.0)
168                } else {
169                    Ok(0.0)
170                }
171            }
172            Data::Real(value) => Ok(value.0),
173            Data::Integer(value) => Ok(value.0 as _),
174            _ => Err(Error::IncompatibleType {
175                method: "to_f64",
176                got: self.data_type(),
177            }),
178        }
179    }
180
181    #[named]
182    pub fn to_i32(&self) -> Result<i32> {
183        match self {
184            Data::Boolean(value) => Ok(if value.0 { 1 } else { 0 }),
185            Data::Real(value) => Ok((value.0 + 0.5) as i32),
186            Data::Integer(value) => value.0.try_into().map_err(Error::IntegerOverflow),
187            _ => Err(Error::IncompatibleType {
188                method: function_name!(),
189                got: self.data_type(),
190            }),
191        }
192    }
193
194    #[named]
195    pub fn to_i64(&self) -> Result<i64> {
196        match self {
197            Data::Boolean(value) => {
198                if value.0 {
199                    Ok(1)
200                } else {
201                    Ok(0)
202                }
203            }
204            Data::Real(value) => Ok((value.0 + 0.5) as _),
205            Data::Integer(value) => Ok(value.0),
206            _ => Err(Error::IncompatibleType {
207                method: function_name!(),
208                got: self.data_type(),
209            }),
210        }
211    }
212
213    #[named]
214    pub fn as_slice_f64(&self) -> Result<&[f64]> {
215        match self {
216            Data::RealVec(value) => Ok(value.0.as_slice()),
217            #[cfg(feature = "matrix4")]
218            Data::Matrix4(value) => Ok(crate::math::mat4_as_slice(&value.0)),
219            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
220            Data::Matrix4Vec(value) => {
221                // Convert Vec<nalgebra::Matrix4<f64>> to &[f64]
222                Ok(bytemuck::cast_slice(&value.0))
223            }
224            _ => Err(Error::IncompatibleType {
225                method: function_name!(),
226                got: self.data_type(),
227            }),
228        }
229    }
230
231    #[named]
232    pub fn as_slice_f32(&self) -> Result<&[f32]> {
233        match self {
234            Data::Color(value) => Ok(value.0.as_slice()),
235            #[cfg(feature = "vector2")]
236            Data::Vector2(value) => Ok(crate::math::vec2_as_slice(&value.0)),
237            #[cfg(feature = "vector3")]
238            Data::Vector3(value) => Ok(crate::math::vec3_as_slice(&value.0)),
239            #[cfg(feature = "matrix3")]
240            Data::Matrix3(value) => Ok(crate::math::mat3_as_slice(&value.0)),
241            #[cfg(feature = "normal3")]
242            Data::Normal3(value) => Ok(crate::math::vec3_as_slice(&value.0)),
243            #[cfg(feature = "point3")]
244            Data::Point3(value) => Ok(crate::math::point3_as_slice(&value.0)),
245            Data::ColorVec(value) => Ok(cast_slice(value.0.as_slice())),
246            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
247            Data::Vector2Vec(value) => {
248                // Convert Vec<nalgebra::Vector2<f32>> to &[f32]
249                Ok(bytemuck::cast_slice(&value.0))
250            }
251            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
252            Data::Vector3Vec(value) => {
253                // Convert Vec<nalgebra::Vector3<f32>> to &[f32]
254                Ok(bytemuck::cast_slice(&value.0))
255            }
256            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
257            Data::Matrix3Vec(value) => {
258                // Convert Vec<nalgebra::Matrix3<f32>> to &[f32]
259                Ok(bytemuck::cast_slice(&value.0))
260            }
261            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
262            Data::Normal3Vec(value) => {
263                // Convert Vec<nalgebra::Point3<f32>> to &[f32]
264                Ok(bytemuck::cast_slice(&value.0))
265            }
266            #[cfg(all(feature = "point3", feature = "vec_variants"))]
267            Data::Point3Vec(value) => {
268                // Convert Vec<nalgebra::Point3<f32>> to &[f32]
269                Ok(bytemuck::cast_slice(&value.0))
270            }
271            _ => Err(Error::IncompatibleType {
272                method: function_name!(),
273                got: self.data_type(),
274            }),
275        }
276    }
277
278    #[named]
279    pub fn as_slice_i64(&self) -> Result<&[i64]> {
280        match self {
281            Data::IntegerVec(value) => Ok(value.0.as_slice()),
282            _ => Err(Error::IncompatibleType {
283                method: function_name!(),
284                got: self.data_type(),
285            }),
286        }
287    }
288
289    #[named]
290    pub fn as_vector2_ref(&self) -> Result<&[f32; 2]> {
291        match self {
292            #[cfg(feature = "vector2")]
293            Data::Vector2(value) => Ok(math::vec2_as_ref(&value.0)),
294            _ => Err(Error::IncompatibleType {
295                method: function_name!(),
296                got: self.data_type(),
297            }),
298        }
299    }
300
301    #[named]
302    pub fn as_vector3_ref(&self) -> Result<&[f32; 3]> {
303        match self {
304            #[cfg(feature = "vector3")]
305            Data::Vector3(value) => Ok(math::vec3_as_ref(&value.0)),
306            _ => Err(Error::IncompatibleType {
307                method: function_name!(),
308                got: self.data_type(),
309            }),
310        }
311    }
312
313    #[named]
314    pub fn as_matrix3_ref(&self) -> Result<&[f32; 9]> {
315        match self {
316            #[cfg(feature = "matrix3")]
317            Data::Matrix3(value) => {
318                // nalgebra Matrix3 stores data in column-major order, cast
319                // directly from Matrix3
320                Ok(bytemuck::cast_ref(&value.0))
321            }
322            _ => Err(Error::IncompatibleType {
323                method: function_name!(),
324                got: self.data_type(),
325            }),
326        }
327    }
328
329    #[named]
330    pub fn as_color_ref(&self) -> Result<&[f32; 4]> {
331        match self {
332            Data::Color(value) => Ok(&value.0),
333            _ => Err(Error::IncompatibleType {
334                method: function_name!(),
335                got: self.data_type(),
336            }),
337        }
338    }
339
340    #[named]
341    #[cfg(feature = "normal3")]
342    pub fn as_normal3_ref(&self) -> Result<&[f32; 3]> {
343        match self {
344            Data::Normal3(value) => Ok(math::vec3_as_ref(&value.0)),
345            _ => Err(Error::IncompatibleType {
346                method: function_name!(),
347                got: self.data_type(),
348            }),
349        }
350    }
351
352    #[named]
353    #[cfg(feature = "point3")]
354    pub fn as_point3_ref(&self) -> Result<&[f32; 3]> {
355        match self {
356            Data::Point3(value) => Ok(crate::math::point3_as_ref(&value.0)),
357            _ => Err(Error::IncompatibleType {
358                method: function_name!(),
359                got: self.data_type(),
360            }),
361        }
362    }
363
364    #[named]
365    #[cfg(feature = "matrix4")]
366    pub fn as_matrix4_ref(&self) -> Result<&[f64; 16]> {
367        match self {
368            Data::Matrix4(value) => {
369                // nalgebra Matrix4 stores data in column-major order, cast
370                // directly from Matrix4
371                Ok(bytemuck::cast_ref(&value.0))
372            }
373            _ => Err(Error::IncompatibleType {
374                method: function_name!(),
375                got: self.data_type(),
376            }),
377        }
378    }
379
380    #[named]
381    pub fn as_str(&self) -> Result<&str> {
382        match self {
383            Data::String(value) => Ok(value.0.as_str()),
384            _ => Err(Error::IncompatibleType {
385                method: function_name!(),
386                got: self.data_type(),
387            }),
388        }
389    }
390
391    #[named]
392    pub fn as_slice_string(&self) -> Result<&[std::string::String]> {
393        match self {
394            Data::StringVec(value) => Ok(value.0.as_slice()),
395            _ => Err(Error::IncompatibleType {
396                method: function_name!(),
397                got: self.data_type(),
398            }),
399        }
400    }
401}
402
403// Macro to implement From trait for primitive types
404macro_rules! impl_from_primitive {
405    ($from:ty, $variant:ident, $wrapper:ident) => {
406        impl From<$from> for Data {
407            fn from(v: $from) -> Self {
408                Data::$variant($wrapper(v as _))
409            }
410        }
411    };
412}
413
414// Implement From for all primitive types
415impl_from_primitive!(i64, Integer, Integer);
416impl_from_primitive!(i32, Integer, Integer);
417impl_from_primitive!(i16, Integer, Integer);
418impl_from_primitive!(i8, Integer, Integer);
419impl_from_primitive!(u32, Integer, Integer);
420impl_from_primitive!(u16, Integer, Integer);
421impl_from_primitive!(u8, Integer, Integer);
422
423impl_from_primitive!(f64, Real, Real);
424impl_from_primitive!(f32, Real, Real);
425
426impl_from_primitive!(bool, Boolean, Boolean);
427
428impl From<std::string::String> for Data {
429    fn from(v: std::string::String) -> Self {
430        Data::String(String(v))
431    }
432}
433
434impl From<&str> for Data {
435    fn from(v: &str) -> Self {
436        Data::String(String(v.into()))
437    }
438}
439
440// From implementations for arrays
441#[cfg(feature = "vector2")]
442impl From<[f32; 2]> for Data {
443    fn from(v: [f32; 2]) -> Self {
444        Data::Vector2(Vector2(v.into()))
445    }
446}
447
448#[cfg(feature = "vector3")]
449impl From<[f32; 3]> for Data {
450    fn from(v: [f32; 3]) -> Self {
451        Data::Vector3(Vector3(v.into()))
452    }
453}
454
455#[cfg(feature = "matrix3")]
456impl From<[[f32; 3]; 3]> for Data {
457    fn from(v: [[f32; 3]; 3]) -> Self {
458        let arr: [f32; 9] = cast(v);
459        Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&arr)))
460    }
461}
462
463#[cfg(feature = "matrix3")]
464impl From<[f32; 9]> for Data {
465    fn from(v: [f32; 9]) -> Self {
466        Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&v)))
467    }
468}
469
470impl From<[f32; 4]> for Data {
471    fn from(v: [f32; 4]) -> Self {
472        Data::Color(Color(v))
473    }
474}
475
476// From implementations for math backend types.
477#[cfg(feature = "vector2")]
478impl From<crate::math::Vec2Impl> for Data {
479    fn from(v: crate::math::Vec2Impl) -> Self {
480        Data::Vector2(Vector2(v))
481    }
482}
483
484#[cfg(feature = "vector3")]
485impl From<crate::math::Vec3Impl> for Data {
486    fn from(v: crate::math::Vec3Impl) -> Self {
487        Data::Vector3(Vector3(v))
488    }
489}
490
491#[cfg(feature = "matrix3")]
492impl From<crate::math::Mat3Impl> for Data {
493    fn from(v: crate::math::Mat3Impl) -> Self {
494        Data::Matrix3(Matrix3(v))
495    }
496}
497
498// From implementations for Vec types
499impl TryFrom<Vec<i64>> for Data {
500    type Error = Error;
501
502    fn try_from(v: Vec<i64>) -> Result<Self> {
503        Ok(Data::IntegerVec(IntegerVec::new(v)?))
504    }
505}
506
507impl TryFrom<Vec<f64>> for Data {
508    type Error = Error;
509
510    fn try_from(v: Vec<f64>) -> Result<Self> {
511        Ok(Data::RealVec(RealVec::new(v)?))
512    }
513}
514
515impl TryFrom<Vec<bool>> for Data {
516    type Error = Error;
517
518    fn try_from(v: Vec<bool>) -> Result<Self> {
519        Ok(Data::BooleanVec(BooleanVec::new(v)?))
520    }
521}
522
523impl TryFrom<Vec<&str>> for Data {
524    type Error = Error;
525
526    fn try_from(v: Vec<&str>) -> Result<Self> {
527        let string_vec: Vec<std::string::String> = v.into_iter().map(|s| s.to_string()).collect();
528        Ok(Data::StringVec(StringVec::new(string_vec)?))
529    }
530}
531
532impl TryFrom<Vec<[f32; 4]>> for Data {
533    type Error = Error;
534
535    fn try_from(v: Vec<[f32; 4]>) -> Result<Self> {
536        Ok(Data::ColorVec(ColorVec::new(v)?))
537    }
538}
539
540#[cfg(all(feature = "vector2", feature = "vec_variants"))]
541impl TryFrom<Vec<crate::math::Vec2Impl>> for Data {
542    type Error = Error;
543
544    fn try_from(v: Vec<crate::math::Vec2Impl>) -> Result<Self> {
545        Ok(Data::Vector2Vec(Vector2Vec::new(v)?))
546    }
547}
548
549#[cfg(all(feature = "vector3", feature = "vec_variants"))]
550impl TryFrom<Vec<crate::math::Vec3Impl>> for Data {
551    type Error = Error;
552
553    fn try_from(v: Vec<crate::math::Vec3Impl>) -> Result<Self> {
554        Ok(Data::Vector3Vec(Vector3Vec::new(v)?))
555    }
556}
557
558#[cfg(all(feature = "matrix3", feature = "vec_variants"))]
559impl TryFrom<Vec<crate::math::Mat3Impl>> for Data {
560    type Error = Error;
561
562    fn try_from(v: Vec<crate::math::Mat3Impl>) -> Result<Self> {
563        Ok(Data::Matrix3Vec(Matrix3Vec::new(v)?))
564    }
565}
566
567impl From<Vec<u32>> for Data {
568    fn from(v: Vec<u32>) -> Self {
569        let int_vec: Vec<i64> = v.into_iter().map(|x| x as i64).collect();
570        Data::IntegerVec(IntegerVec(int_vec))
571    }
572}
573
574impl From<Vec<f32>> for Data {
575    fn from(v: Vec<f32>) -> Self {
576        let real_vec: Vec<f64> = v.into_iter().map(|x| x as f64).collect();
577        Data::RealVec(RealVec(real_vec))
578    }
579}
580
581impl TryFrom<Vec<std::string::String>> for Data {
582    type Error = Error;
583
584    fn try_from(v: Vec<std::string::String>) -> Result<Self> {
585        Ok(Data::StringVec(StringVec::new(v)?))
586    }
587}
588
589macro_rules! impl_try_from_value {
590    ($target:ty, $variant:ident) => {
591        impl TryFrom<Data> for $target {
592            type Error = Error;
593
594            fn try_from(value: Data) -> std::result::Result<Self, Self::Error> {
595                match value {
596                    Data::$variant(v) => Ok(v.0),
597                    _ => Err(Error::IncompatibleType {
598                        method: concat!("TryFrom<Data> for ", stringify!($target)),
599                        got: value.data_type(),
600                    }),
601                }
602            }
603        }
604
605        impl TryFrom<&Data> for $target {
606            type Error = Error;
607
608            fn try_from(value: &Data) -> std::result::Result<Self, Self::Error> {
609                match value {
610                    Data::$variant(v) => Ok(v.0.clone()),
611                    _ => Err(Error::IncompatibleType {
612                        method: concat!("TryFrom<&Data> for ", stringify!($target)),
613                        got: value.data_type(),
614                    }),
615                }
616            }
617        }
618    };
619}
620
621// Implement `TryFrom` for all types
622impl_try_from_value!(bool, Boolean);
623impl_try_from_value!(i64, Integer);
624impl_try_from_value!(f64, Real);
625impl_try_from_value!(std::string::String, String);
626impl_try_from_value!([f32; 4], Color);
627#[cfg(feature = "vector2")]
628impl_try_from_value!(crate::math::Vec2Impl, Vector2);
629#[cfg(feature = "vector3")]
630impl_try_from_value!(crate::math::Vec3Impl, Vector3);
631#[cfg(feature = "matrix3")]
632impl_try_from_value!(crate::math::Mat3Impl, Matrix3);
633
634// TryFrom implementations for Vec types using macro
635impl_try_from_vec!(
636    bool, BooleanVec, "bool";
637    i64, IntegerVec, "i64";
638    f64, RealVec, "f64";
639    std::string::String, StringVec, "String";
640    [f32; 4], ColorVec, "[f32; 4]";
641);
642
643#[cfg(all(feature = "vector2", feature = "vec_variants"))]
644impl_try_from_vec!(
645    crate::math::Vec2Impl, Vector2Vec, "Vector2<f32>";
646);
647
648#[cfg(all(feature = "vector3", feature = "vec_variants"))]
649impl_try_from_vec!(
650    crate::math::Vec3Impl, Vector3Vec, "Vector3<f32>";
651);
652
653#[cfg(all(feature = "matrix3", feature = "vec_variants"))]
654impl_try_from_vec!(
655    crate::math::Mat3Impl, Matrix3Vec, "Matrix3<f32>";
656);
657
658// Custom Hash implementation
659impl Hash for Data {
660    fn hash<H: Hasher>(&self, state: &mut H) {
661        std::mem::discriminant(self).hash(state);
662        match self {
663            Data::Boolean(Boolean(b)) => b.hash(state),
664            Data::Integer(Integer(i)) => i.hash(state),
665            Data::Real(Real(f)) => f.to_bits().hash(state),
666            Data::String(String(s)) => s.hash(state),
667            Data::Color(Color(c)) => {
668                c.iter().for_each(|v| v.to_bits().hash(state));
669            }
670            #[cfg(feature = "vector2")]
671            Data::Vector2(Vector2(v)) => {
672                crate::math::vec2_as_slice(v)
673                    .iter()
674                    .for_each(|v| v.to_bits().hash(state));
675            }
676            #[cfg(feature = "vector3")]
677            Data::Vector3(Vector3(v)) => {
678                crate::math::vec3_as_slice(v)
679                    .iter()
680                    .for_each(|v| v.to_bits().hash(state));
681            }
682            #[cfg(feature = "matrix3")]
683            Data::Matrix3(Matrix3(m)) => {
684                crate::math::mat3_iter(m).for_each(|v| v.to_bits().hash(state));
685            }
686            #[cfg(feature = "normal3")]
687            Data::Normal3(Normal3(v)) => {
688                crate::math::vec3_as_slice(v)
689                    .iter()
690                    .for_each(|v| v.to_bits().hash(state));
691            }
692            #[cfg(feature = "point3")]
693            Data::Point3(Point3(p)) => {
694                crate::math::point3_as_slice(p)
695                    .iter()
696                    .for_each(|v| v.to_bits().hash(state));
697            }
698            #[cfg(feature = "matrix4")]
699            Data::Matrix4(Matrix4(m)) => {
700                crate::math::mat4_iter(m).for_each(|v| v.to_bits().hash(state));
701            }
702            Data::BooleanVec(BooleanVec(v)) => v.hash(state),
703            Data::IntegerVec(IntegerVec(v)) => v.hash(state),
704            Data::RealVec(RealVec(v)) => {
705                v.len().hash(state);
706                v.iter().for_each(|v| v.to_bits().hash(state));
707            }
708            Data::StringVec(StringVec(v)) => v.hash(state),
709            Data::ColorVec(ColorVec(v)) => {
710                v.len().hash(state);
711                v.iter()
712                    .for_each(|c| c.iter().for_each(|v| v.to_bits().hash(state)));
713            }
714            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
715            Data::Vector2Vec(Vector2Vec(v)) => {
716                v.len().hash(state);
717                v.iter().for_each(|v| {
718                    crate::math::vec2_as_slice(v)
719                        .iter()
720                        .for_each(|v| v.to_bits().hash(state))
721                });
722            }
723            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
724            Data::Vector3Vec(Vector3Vec(v)) => {
725                v.len().hash(state);
726                v.iter().for_each(|v| {
727                    crate::math::vec3_as_slice(v)
728                        .iter()
729                        .for_each(|v| v.to_bits().hash(state))
730                });
731            }
732            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
733            Data::Matrix3Vec(Matrix3Vec(v)) => {
734                v.len().hash(state);
735                v.iter()
736                    .for_each(|m| crate::math::mat3_iter(m).for_each(|v| v.to_bits().hash(state)));
737            }
738            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
739            Data::Normal3Vec(Normal3Vec(v)) => {
740                v.len().hash(state);
741                v.iter().for_each(|v| {
742                    crate::math::vec3_as_slice(v)
743                        .iter()
744                        .for_each(|v| v.to_bits().hash(state))
745                });
746            }
747            #[cfg(all(feature = "point3", feature = "vec_variants"))]
748            Data::Point3Vec(Point3Vec(v)) => {
749                v.len().hash(state);
750                v.iter().for_each(|p| {
751                    crate::math::point3_as_slice(p)
752                        .iter()
753                        .for_each(|v| v.to_bits().hash(state))
754                });
755            }
756            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
757            Data::Matrix4Vec(Matrix4Vec(v)) => {
758                v.len().hash(state);
759                v.iter()
760                    .for_each(|m| crate::math::mat4_iter(m).for_each(|v| v.to_bits().hash(state)));
761            }
762            #[cfg(feature = "curves")]
763            Data::RealCurve(c) => c.hash(state),
764            #[cfg(feature = "curves")]
765            Data::ColorCurve(c) => c.hash(state),
766        }
767    }
768}
769
770impl Data {
771    /// Ensure a vector value has at least the specified length by padding with
772    /// defaults
773    pub fn pad_to_length(&mut self, target_len: usize) {
774        match self {
775            Data::BooleanVec(BooleanVec(v)) => v.resize(target_len, false),
776            Data::IntegerVec(IntegerVec(v)) => v.resize(target_len, 0),
777            Data::RealVec(RealVec(v)) => v.resize(target_len, 0.0),
778            Data::StringVec(StringVec(v)) => v.resize(target_len, std::string::String::new()),
779            Data::ColorVec(ColorVec(v)) => v.resize(target_len, [0.0, 0.0, 0.0, 1.0]),
780            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
781            Data::Vector2Vec(Vector2Vec(v)) => v.resize(target_len, crate::math::vec2_zeros()),
782            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
783            Data::Vector3Vec(Vector3Vec(v)) => v.resize(target_len, crate::math::vec3_zeros()),
784            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
785            Data::Matrix3Vec(Matrix3Vec(v)) => v.resize(target_len, crate::math::mat3_zeros()),
786            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
787            Data::Normal3Vec(Normal3Vec(v)) => v.resize(target_len, crate::math::vec3_zeros()),
788            #[cfg(all(feature = "point3", feature = "vec_variants"))]
789            Data::Point3Vec(Point3Vec(v)) => v.resize(target_len, crate::math::point3_origin()),
790            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
791            Data::Matrix4Vec(Matrix4Vec(v)) => v.resize(target_len, crate::math::mat4_zeros()),
792            _ => {} // Non-vector types are ignored
793        }
794    }
795
796    /// Try to convert this value to another type
797    pub fn try_convert(&self, to: DataType) -> Result<Data> {
798        match (self, to) {
799            // Same type - just clone
800            (v, target) if v.data_type() == target => Ok(v.clone()),
801
802            // To Integer conversions
803            (Data::Real(Real(f)), DataType::Integer) => Ok(Data::Integer(Integer(*f as i64))),
804            (Data::Boolean(Boolean(b)), DataType::Integer) => {
805                Ok(Data::Integer(Integer(if *b { 1 } else { 0 })))
806            }
807            (Data::String(String(s)), DataType::Integer) => s
808                .parse::<i64>()
809                .map(|i| Data::Integer(Integer(i)))
810                .map_err(|_| Error::ParseFailed {
811                    input: s.clone(),
812                    target_type: "Integer",
813                }),
814
815            // To Real conversions
816            (Data::Integer(Integer(i)), DataType::Real) => Ok(Data::Real(Real(*i as f64))),
817            (Data::Boolean(Boolean(b)), DataType::Real) => {
818                Ok(Data::Real(Real(if *b { 1.0 } else { 0.0 })))
819            }
820            (Data::String(String(s)), DataType::Real) => s
821                .parse::<f64>()
822                .map(|f| Data::Real(Real(f)))
823                .map_err(|_| Error::ParseFailed {
824                    input: s.clone(),
825                    target_type: "Real",
826                }),
827
828            // To Boolean conversions
829            (Data::Integer(Integer(i)), DataType::Boolean) => Ok(Data::Boolean(Boolean(*i != 0))),
830            (Data::Real(Real(f)), DataType::Boolean) => Ok(Data::Boolean(Boolean(*f != 0.0))),
831            (Data::String(String(s)), DataType::Boolean) => match s.to_lowercase().as_str() {
832                "true" | "yes" | "1" | "on" => Ok(Data::Boolean(Boolean(true))),
833                "false" | "no" | "0" | "off" | "" => Ok(Data::Boolean(Boolean(false))),
834                _ => Err(Error::ParseFailed {
835                    input: s.clone(),
836                    target_type: "Boolean",
837                }),
838            },
839
840            // To String conversions
841            (Data::Integer(Integer(i)), DataType::String) => {
842                Ok(Data::String(String(i.to_string())))
843            }
844            (Data::Real(Real(f)), DataType::String) => Ok(Data::String(String(f.to_string()))),
845            (Data::Boolean(Boolean(b)), DataType::String) => {
846                Ok(Data::String(String(b.to_string())))
847            }
848            #[cfg(feature = "vector2")]
849            (Data::Vector2(Vector2(v)), DataType::String) => {
850                Ok(Data::String(String(format!("[{}, {}]", v[0], v[1]))))
851            }
852            #[cfg(feature = "vector3")]
853            (Data::Vector3(Vector3(v)), DataType::String) => Ok(Data::String(String(format!(
854                "[{}, {}, {}]",
855                v[0], v[1], v[2]
856            )))),
857            (Data::Color(Color(c)), DataType::String) => Ok(Data::String(String(format!(
858                "[{}, {}, {}, {}]",
859                c[0], c[1], c[2], c[3]
860            )))),
861            #[cfg(feature = "matrix3")]
862            (Data::Matrix3(Matrix3(m)), DataType::String) => {
863                Ok(Data::String(String(format!("{m:?}"))))
864            }
865            #[cfg(feature = "normal3")]
866            (Data::Normal3(Normal3(v)), DataType::String) => Ok(Data::String(String(format!(
867                "[{}, {}, {}]",
868                v[0], v[1], v[2]
869            )))),
870            #[cfg(feature = "point3")]
871            (Data::Point3(Point3(p)), DataType::String) => {
872                Ok(Data::String(String(format!("[{}, {}, {}]", p.x, p.y, p.z))))
873            }
874            #[cfg(feature = "matrix4")]
875            (Data::Matrix4(Matrix4(m)), DataType::String) => {
876                Ok(Data::String(String(format!("{m:?}"))))
877            }
878
879            // To Vec2 conversions
880            #[cfg(feature = "vector2")]
881            (Data::Integer(Integer(i)), DataType::Vector2) => {
882                let v = *i as f32;
883                Ok(Data::Vector2(Vector2(crate::math::Vec2Impl::new(v, v))))
884            }
885            #[cfg(feature = "vector2")]
886            (Data::Real(Real(f)), DataType::Vector2) => {
887                let v = *f as f32;
888                Ok(Data::Vector2(Vector2(crate::math::Vec2Impl::new(v, v))))
889            }
890            #[cfg(feature = "vector2")]
891            (Data::RealVec(RealVec(vec)), DataType::Vector2) if vec.len() >= 2 => {
892                let v: Vec<f32> = vec.iter().take(2).map(|&x| x as f32).collect();
893                Ok(Data::Vector2(Vector2(crate::math::Vec2Impl::new(
894                    v[0], v[1],
895                ))))
896            }
897            #[cfg(feature = "vector2")]
898            (Data::IntegerVec(IntegerVec(vec)), DataType::Vector2) if vec.len() >= 2 => {
899                let v: Vec<f32> = vec.iter().take(2).map(|&x| x as f32).collect();
900                Ok(Data::Vector2(Vector2(crate::math::Vec2Impl::new(
901                    v[0], v[1],
902                ))))
903            }
904            #[cfg(feature = "vector2")]
905            (Data::String(String(s)), DataType::Vector2) => {
906                parse_to_array::<f32, 2>(s).map(|v| Data::Vector2(Vector2(v.into())))
907            }
908
909            // To Vec3 conversions
910            #[cfg(feature = "vector3")]
911            (Data::Integer(Integer(i)), DataType::Vector3) => {
912                let v = *i as f32;
913                Ok(Data::Vector3(Vector3(crate::math::Vec3Impl::new(v, v, v))))
914            }
915            #[cfg(feature = "vector3")]
916            (Data::Real(Real(f)), DataType::Vector3) => {
917                let v = *f as f32;
918                Ok(Data::Vector3(Vector3(crate::math::Vec3Impl::new(v, v, v))))
919            }
920            #[cfg(all(feature = "vector2", feature = "vector3"))]
921            (Data::Vector2(Vector2(v)), DataType::Vector3) => Ok(Data::Vector3(Vector3(
922                crate::math::Vec3Impl::new(v.x, v.y, 0.0),
923            ))),
924            #[cfg(feature = "vector3")]
925            (Data::RealVec(RealVec(vec)), DataType::Vector3) if vec.len() >= 3 => {
926                let v: Vec<f32> = vec.iter().take(3).map(|&x| x as f32).collect();
927                Ok(Data::Vector3(Vector3(crate::math::Vec3Impl::new(
928                    v[0], v[1], v[2],
929                ))))
930            }
931            #[cfg(feature = "vector3")]
932            (Data::IntegerVec(IntegerVec(vec)), DataType::Vector3) if vec.len() >= 3 => {
933                let v: Vec<f32> = vec.iter().take(3).map(|&x| x as f32).collect();
934                Ok(Data::Vector3(Vector3(crate::math::Vec3Impl::new(
935                    v[0], v[1], v[2],
936                ))))
937            }
938            #[cfg(feature = "vector3")]
939            (Data::ColorVec(ColorVec(vec)), DataType::Vector3) if !vec.is_empty() => {
940                let c = &vec[0];
941                Ok(Data::Vector3(Vector3(crate::math::Vec3Impl::new(
942                    c[0], c[1], c[2],
943                ))))
944            }
945            #[cfg(feature = "vector3")]
946            (Data::String(String(s)), DataType::Vector3) => {
947                parse_to_array::<f32, 3>(s).map(|v| Data::Vector3(Vector3(v.into())))
948            }
949
950            // To Color conversions
951            (Data::Real(Real(f)), DataType::Color) => {
952                let f = *f as f32;
953                Ok(Data::Color(Color([f, f, f, 1.0])))
954            }
955            (Data::RealVec(RealVec(vec)), DataType::Color) if vec.len() >= 3 => {
956                let mut color = [0.0f32; 4];
957                vec.iter()
958                    .take(4)
959                    .enumerate()
960                    .for_each(|(i, &v)| color[i] = v as f32);
961                if vec.len() < 4 {
962                    color[3] = 1.0;
963                }
964                Ok(Data::Color(Color(color)))
965            }
966            (Data::IntegerVec(IntegerVec(vec)), DataType::Color) if vec.len() >= 3 => {
967                let mut color = [0.0f32; 4];
968                vec.iter()
969                    .take(4)
970                    .enumerate()
971                    .for_each(|(i, &v)| color[i] = v as f32);
972                if vec.len() < 4 {
973                    color[3] = 1.0;
974                }
975                Ok(Data::Color(Color(color)))
976            }
977            #[cfg(feature = "vector3")]
978            (Data::Vector3(Vector3(v)), DataType::Color) => {
979                Ok(Data::Color(Color([v.x, v.y, v.z, 1.0])))
980            }
981            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
982            (Data::Vector3Vec(Vector3Vec(vec)), DataType::Color) if !vec.is_empty() => {
983                Ok(Data::Color(Color([vec[0].x, vec[0].y, vec[0].z, 1.0])))
984            }
985            #[cfg(feature = "vector2")]
986            (Data::Vector2(Vector2(v)), DataType::Color) => {
987                Ok(Data::Color(Color([v.x, v.y, 0.0, 1.0])))
988            }
989            #[cfg(feature = "point3")]
990            (Data::Point3(Point3(p)), DataType::Color) => {
991                Ok(Data::Color(Color([p.x, p.y, p.z, 1.0])))
992            }
993            #[cfg(feature = "normal3")]
994            (Data::Normal3(Normal3(v)), DataType::Color) => {
995                Ok(Data::Color(Color([v.x, v.y, v.z, 1.0])))
996            }
997            (Data::String(String(s)), DataType::Color) => parse_color_from_string(s)
998                .map(|c| Data::Color(Color(c)))
999                .ok_or_else(|| Error::ParseFailed {
1000                    input: s.clone(),
1001                    target_type: "Color",
1002                }),
1003
1004            // To Mat3 conversions
1005            #[cfg(feature = "matrix3")]
1006            (Data::Integer(Integer(i)), DataType::Matrix3) => {
1007                let v = *i as f32;
1008                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&[
1009                    v, 0.0, 0.0, 0.0, v, 0.0, 0.0, 0.0, 1.0,
1010                ]))))
1011            }
1012            #[cfg(feature = "matrix3")]
1013            (Data::Real(Real(f)), DataType::Matrix3) => {
1014                let v = *f as f32;
1015                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&[
1016                    v, 0.0, 0.0, 0.0, v, 0.0, 0.0, 0.0, 1.0,
1017                ]))))
1018            }
1019            #[cfg(feature = "matrix3")]
1020            (Data::RealVec(RealVec(vec)), DataType::Matrix3) if vec.len() >= 9 => {
1021                // AIDEV-NOTE: Using iterator for efficient conversion.
1022                let m: Vec<f32> = vec.iter().take(9).map(|&x| x as f32).collect();
1023                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&m))))
1024            }
1025            #[cfg(feature = "matrix3")]
1026            (Data::IntegerVec(IntegerVec(vec)), DataType::Matrix3) if vec.len() >= 9 => {
1027                let m: Vec<f32> = vec.iter().take(9).map(|&x| x as f32).collect();
1028                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&m))))
1029            }
1030            #[cfg(all(feature = "vector3", feature = "matrix3"))]
1031            (Data::Vector3(Vector3(v)), DataType::Matrix3) => {
1032                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&[
1033                    v.x, 0.0, 0.0, 0.0, v.y, 0.0, 0.0, 0.0, v.z,
1034                ]))))
1035            }
1036            #[cfg(all(feature = "vector3", feature = "matrix3", feature = "vec_variants"))]
1037            (Data::Vector3Vec(Vector3Vec(vec)), DataType::Matrix3) if vec.len() >= 3 => {
1038                // Use 3 Vector3s as columns of the matrix.
1039                let cols: Vec<f32> = vec.iter().take(3).flat_map(|v| [v.x, v.y, v.z]).collect();
1040                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_column_slice(
1041                    &cols,
1042                ))))
1043            }
1044            #[cfg(feature = "matrix3")]
1045            (Data::ColorVec(ColorVec(vec)), DataType::Matrix3) if vec.len() >= 3 => {
1046                // Use RGB components of 3 colors as rows.
1047                let rows: Vec<f32> = vec
1048                    .iter()
1049                    .take(3)
1050                    .flat_map(|c| c[0..3].iter().copied())
1051                    .collect();
1052                Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(
1053                    &rows,
1054                ))))
1055            }
1056            #[cfg(feature = "matrix3")]
1057            (Data::String(String(s)), DataType::Matrix3) => {
1058                // Try to parse as a single value first for diagonal matrix
1059                if let Ok(single_val) = s.trim().parse::<f32>() {
1060                    Ok(Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&[
1061                        single_val, 0.0, 0.0, 0.0, single_val, 0.0, 0.0, 0.0, single_val,
1062                    ]))))
1063                } else {
1064                    // Parse as 9 separate values
1065                    parse_to_array::<f32, 9>(s)
1066                        .map(|m| Data::Matrix3(Matrix3(crate::math::mat3_from_row_slice(&m))))
1067                }
1068            }
1069
1070            // To Normal3 conversions
1071            #[cfg(feature = "normal3")]
1072            (Data::Integer(Integer(i)), DataType::Normal3) => {
1073                let v = *i as f32;
1074                Ok(Data::Normal3(Normal3(crate::math::Vec3Impl::new(v, v, v))))
1075            }
1076            #[cfg(feature = "normal3")]
1077            (Data::Real(Real(f)), DataType::Normal3) => {
1078                let v = *f as f32;
1079                Ok(Data::Normal3(Normal3(crate::math::Vec3Impl::new(v, v, v))))
1080            }
1081            #[cfg(all(feature = "vector3", feature = "normal3"))]
1082            (Data::Vector3(Vector3(v)), DataType::Normal3) => {
1083                Ok(Data::Normal3(Normal3(math::vec3_normalized(v))))
1084            }
1085            #[cfg(feature = "normal3")]
1086            (Data::String(String(s)), DataType::Normal3) => parse_to_array::<f32, 3>(s).map(|v| {
1087                let vec = crate::math::Vec3Impl::new(v[0], v[1], v[2]);
1088                Data::Normal3(Normal3(math::vec3_normalized(&vec)))
1089            }),
1090
1091            // To Point3 conversions
1092            #[cfg(feature = "point3")]
1093            (Data::Integer(Integer(i)), DataType::Point3) => {
1094                let v = *i as f32;
1095                Ok(Data::Point3(Point3(crate::math::Point3Impl::new(v, v, v))))
1096            }
1097            #[cfg(feature = "point3")]
1098            (Data::Real(Real(f)), DataType::Point3) => {
1099                let v = *f as f32;
1100                Ok(Data::Point3(Point3(crate::math::Point3Impl::new(v, v, v))))
1101            }
1102            #[cfg(all(feature = "vector3", feature = "point3"))]
1103            (Data::Vector3(Vector3(v)), DataType::Point3) => Ok(Data::Point3(Point3(
1104                crate::math::Point3Impl::new(v.x, v.y, v.z),
1105            ))),
1106            #[cfg(feature = "point3")]
1107            (Data::String(String(s)), DataType::Point3) => parse_to_array::<f32, 3>(s)
1108                .map(|v| Data::Point3(Point3(crate::math::Point3Impl::new(v[0], v[1], v[2])))),
1109
1110            // To Matrix4 conversions
1111            #[cfg(feature = "matrix4")]
1112            (Data::Integer(Integer(i)), DataType::Matrix4) => {
1113                let v = *i as f64;
1114                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&[
1115                    v, 0.0, 0.0, 0.0, 0.0, v, 0.0, 0.0, 0.0, 0.0, v, 0.0, 0.0, 0.0, 0.0, 1.0,
1116                ]))))
1117            }
1118            #[cfg(feature = "matrix4")]
1119            (Data::Real(Real(f)), DataType::Matrix4) => {
1120                let v = *f;
1121                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&[
1122                    v, 0.0, 0.0, 0.0, 0.0, v, 0.0, 0.0, 0.0, 0.0, v, 0.0, 0.0, 0.0, 0.0, 1.0,
1123                ]))))
1124            }
1125            #[cfg(feature = "matrix4")]
1126            (Data::RealVec(RealVec(vec)), DataType::Matrix4) if vec.len() >= 16 => {
1127                // AIDEV-NOTE: Direct copy when types match, using bytemuck for exact size.
1128                let m: Vec<f64> = vec.iter().take(16).copied().collect();
1129                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&m))))
1130            }
1131            #[cfg(feature = "matrix4")]
1132            (Data::IntegerVec(IntegerVec(vec)), DataType::Matrix4) if vec.len() >= 16 => {
1133                let m: Vec<f64> = vec.iter().take(16).map(|&x| x as f64).collect();
1134                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&m))))
1135            }
1136            #[cfg(all(feature = "matrix3", feature = "matrix4"))]
1137            (Data::Matrix3(Matrix3(m)), DataType::Matrix4) => {
1138                // AIDEV-NOTE: Use mat3_row() for row-major data since
1139                // mat4_from_row_slice() expects row-major input.
1140                let r0 = crate::math::mat3_row(m, 0);
1141                let r1 = crate::math::mat3_row(m, 1);
1142                let r2 = crate::math::mat3_row(m, 2);
1143                // Expand to 4x4 with identity in the last row/column.
1144                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&[
1145                    r0[0] as f64,
1146                    r0[1] as f64,
1147                    r0[2] as f64,
1148                    0.0,
1149                    r1[0] as f64,
1150                    r1[1] as f64,
1151                    r1[2] as f64,
1152                    0.0,
1153                    r2[0] as f64,
1154                    r2[1] as f64,
1155                    r2[2] as f64,
1156                    0.0,
1157                    0.0,
1158                    0.0,
1159                    0.0,
1160                    1.0,
1161                ]))))
1162            }
1163            #[cfg(all(feature = "matrix3", feature = "matrix4", feature = "vec_variants"))]
1164            (Data::Matrix3Vec(Matrix3Vec(vec)), DataType::Matrix4) if !vec.is_empty() => {
1165                let m = &vec[0];
1166                // AIDEV-NOTE: Use mat3_row() for row-major data since
1167                // mat4_from_row_slice() expects row-major input.
1168                let r0 = crate::math::mat3_row(m, 0);
1169                let r1 = crate::math::mat3_row(m, 1);
1170                let r2 = crate::math::mat3_row(m, 2);
1171                // Expand to 4x4 with identity in the last row/column.
1172                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&[
1173                    r0[0] as f64,
1174                    r0[1] as f64,
1175                    r0[2] as f64,
1176                    0.0,
1177                    r1[0] as f64,
1178                    r1[1] as f64,
1179                    r1[2] as f64,
1180                    0.0,
1181                    r2[0] as f64,
1182                    r2[1] as f64,
1183                    r2[2] as f64,
1184                    0.0,
1185                    0.0,
1186                    0.0,
1187                    0.0,
1188                    1.0,
1189                ]))))
1190            }
1191            #[cfg(feature = "matrix4")]
1192            (Data::ColorVec(ColorVec(vec)), DataType::Matrix4) if vec.len() >= 4 => {
1193                // Use RGBA components of 4 colors as rows.
1194                let rows: Vec<f64> = vec
1195                    .iter()
1196                    .take(4)
1197                    .flat_map(|c| c.iter().map(|&x| x as f64))
1198                    .collect();
1199                Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(
1200                    &rows,
1201                ))))
1202            }
1203            #[cfg(feature = "matrix4")]
1204            (Data::String(String(s)), DataType::Matrix4) => {
1205                // Try to parse as a single value first for diagonal matrix
1206                if let Ok(single_val) = s.trim().parse::<f64>() {
1207                    Ok(Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&[
1208                        single_val, 0.0, 0.0, 0.0, 0.0, single_val, 0.0, 0.0, 0.0, 0.0, single_val,
1209                        0.0, 0.0, 0.0, 0.0, single_val,
1210                    ]))))
1211                } else {
1212                    // Parse as 16 separate values
1213                    parse_to_array::<f64, 16>(s)
1214                        .map(|m| Data::Matrix4(Matrix4(crate::math::mat4_from_row_slice(&m))))
1215                }
1216            }
1217
1218            // Vec conversions from scalars and other types
1219            // To RealVec conversions
1220            (Data::Integer(Integer(i)), DataType::RealVec) => {
1221                Ok(Data::RealVec(RealVec(vec![*i as f64])))
1222            }
1223            (Data::Real(Real(f)), DataType::RealVec) => Ok(Data::RealVec(RealVec(vec![*f]))),
1224            #[cfg(feature = "vector2")]
1225            (Data::Vector2(Vector2(v)), DataType::RealVec) => Ok(Data::RealVec(RealVec(
1226                crate::math::vec2_as_slice(v)
1227                    .iter()
1228                    .map(|&x| x as f64)
1229                    .collect(),
1230            ))),
1231            #[cfg(feature = "vector3")]
1232            (Data::Vector3(Vector3(v)), DataType::RealVec) => Ok(Data::RealVec(RealVec(
1233                crate::math::vec3_as_slice(v)
1234                    .iter()
1235                    .map(|&x| x as f64)
1236                    .collect(),
1237            ))),
1238            (Data::Color(Color(c)), DataType::RealVec) => Ok(Data::RealVec(RealVec(
1239                c.iter().map(|&x| x as f64).collect(),
1240            ))),
1241            #[cfg(feature = "matrix3")]
1242            (Data::Matrix3(Matrix3(m)), DataType::RealVec) => Ok(Data::RealVec(RealVec(
1243                crate::math::mat3_iter(m).map(|&x| x as f64).collect(),
1244            ))),
1245            #[cfg(feature = "matrix4")]
1246            (Data::Matrix4(Matrix4(m)), DataType::RealVec) => Ok(Data::RealVec(RealVec(
1247                crate::math::mat4_iter(m).copied().collect(),
1248            ))),
1249            #[cfg(feature = "normal3")]
1250            (Data::Normal3(Normal3(v)), DataType::RealVec) => Ok(Data::RealVec(RealVec(
1251                crate::math::vec3_as_slice(v)
1252                    .iter()
1253                    .map(|&x| x as f64)
1254                    .collect(),
1255            ))),
1256            #[cfg(feature = "point3")]
1257            (Data::Point3(Point3(p)), DataType::RealVec) => Ok(Data::RealVec(RealVec(vec![
1258                p.x as f64, p.y as f64, p.z as f64,
1259            ]))),
1260
1261            // To IntegerVec conversions
1262            (Data::Boolean(Boolean(b)), DataType::IntegerVec) => {
1263                Ok(Data::IntegerVec(IntegerVec(vec![if *b { 1 } else { 0 }])))
1264            }
1265            (Data::Integer(Integer(i)), DataType::IntegerVec) => {
1266                Ok(Data::IntegerVec(IntegerVec(vec![*i])))
1267            }
1268            (Data::Real(Real(f)), DataType::IntegerVec) => {
1269                Ok(Data::IntegerVec(IntegerVec(vec![*f as i64])))
1270            }
1271            #[cfg(feature = "vector2")]
1272            (Data::Vector2(Vector2(v)), DataType::IntegerVec) => Ok(Data::IntegerVec(IntegerVec(
1273                crate::math::vec2_as_slice(v)
1274                    .iter()
1275                    .map(|&x| x as i64)
1276                    .collect(),
1277            ))),
1278            #[cfg(feature = "vector3")]
1279            (Data::Vector3(Vector3(v)), DataType::IntegerVec) => Ok(Data::IntegerVec(IntegerVec(
1280                crate::math::vec3_as_slice(v)
1281                    .iter()
1282                    .map(|&x| x as i64)
1283                    .collect(),
1284            ))),
1285            (Data::Color(Color(c)), DataType::IntegerVec) => Ok(Data::IntegerVec(IntegerVec(
1286                c.iter().map(|&x| (x * 255.0) as i64).collect(),
1287            ))),
1288            #[cfg(feature = "matrix3")]
1289            (Data::Matrix3(Matrix3(m)), DataType::IntegerVec) => Ok(Data::IntegerVec(IntegerVec(
1290                crate::math::mat3_iter(m).map(|&x| x as i64).collect(),
1291            ))),
1292            #[cfg(feature = "matrix4")]
1293            (Data::Matrix4(Matrix4(m)), DataType::IntegerVec) => Ok(Data::IntegerVec(IntegerVec(
1294                crate::math::mat4_iter(m).map(|&x| x as i64).collect(),
1295            ))),
1296
1297            // To ColorVec conversions
1298            (Data::Color(Color(c)), DataType::ColorVec) => Ok(Data::ColorVec(ColorVec(vec![*c]))),
1299            #[cfg(feature = "vector3")]
1300            (Data::Vector3(Vector3(v)), DataType::ColorVec) => {
1301                Ok(Data::ColorVec(ColorVec(vec![[v.x, v.y, v.z, 1.0]])))
1302            }
1303            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
1304            (Data::Vector3Vec(Vector3Vec(vec)), DataType::ColorVec) => {
1305                let colors = vec.iter().map(|v| [v.x, v.y, v.z, 1.0]).collect();
1306                Ok(Data::ColorVec(ColorVec(colors)))
1307            }
1308            #[cfg(feature = "matrix3")]
1309            (Data::Matrix3(Matrix3(m)), DataType::ColorVec) => {
1310                // Convert each row to a color.
1311                let colors = (0..3)
1312                    .map(|i| {
1313                        let row = crate::math::mat3_row(m, i);
1314                        [row[0], row[1], row[2], 1.0]
1315                    })
1316                    .collect();
1317                Ok(Data::ColorVec(ColorVec(colors)))
1318            }
1319
1320            // To Vector2Vec conversions
1321            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
1322            (Data::Vector2(Vector2(v)), DataType::Vector2Vec) => {
1323                Ok(Data::Vector2Vec(Vector2Vec(vec![*v])))
1324            }
1325            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
1326            (Data::RealVec(RealVec(vec)), DataType::Vector2Vec)
1327                if vec.len() >= 2 && vec.len() % 2 == 0 =>
1328            {
1329                let vectors = vec
1330                    .chunks_exact(2)
1331                    .map(|chunk| crate::math::Vec2Impl::new(chunk[0] as f32, chunk[1] as f32))
1332                    .collect();
1333                Ok(Data::Vector2Vec(Vector2Vec(vectors)))
1334            }
1335
1336            // To Vector3Vec conversions
1337            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
1338            (Data::Vector3(Vector3(v)), DataType::Vector3Vec) => {
1339                Ok(Data::Vector3Vec(Vector3Vec(vec![*v])))
1340            }
1341            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
1342            (Data::RealVec(RealVec(vec)), DataType::Vector3Vec)
1343                if vec.len() >= 3 && vec.len() % 3 == 0 =>
1344            {
1345                let vectors = vec
1346                    .chunks_exact(3)
1347                    .map(|chunk| {
1348                        crate::math::Vec3Impl::new(
1349                            chunk[0] as f32,
1350                            chunk[1] as f32,
1351                            chunk[2] as f32,
1352                        )
1353                    })
1354                    .collect();
1355                Ok(Data::Vector3Vec(Vector3Vec(vectors)))
1356            }
1357            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
1358            (Data::IntegerVec(IntegerVec(vec)), DataType::Vector3Vec)
1359                if vec.len() >= 3 && vec.len() % 3 == 0 =>
1360            {
1361                let vectors = vec
1362                    .chunks_exact(3)
1363                    .map(|chunk| {
1364                        crate::math::Vec3Impl::new(
1365                            chunk[0] as f32,
1366                            chunk[1] as f32,
1367                            chunk[2] as f32,
1368                        )
1369                    })
1370                    .collect();
1371                Ok(Data::Vector3Vec(Vector3Vec(vectors)))
1372            }
1373            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
1374            (Data::ColorVec(ColorVec(vec)), DataType::Vector3Vec) => {
1375                let vectors = vec
1376                    .iter()
1377                    .map(|c| crate::math::Vec3Impl::new(c[0], c[1], c[2]))
1378                    .collect();
1379                Ok(Data::Vector3Vec(Vector3Vec(vectors)))
1380            }
1381
1382            // To Matrix3Vec conversions
1383            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
1384            (Data::Matrix3(Matrix3(m)), DataType::Matrix3Vec) => {
1385                Ok(Data::Matrix3Vec(Matrix3Vec(vec![*m])))
1386            }
1387            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
1388            (Data::RealVec(RealVec(vec)), DataType::Matrix3Vec)
1389                if vec.len() >= 9 && vec.len() % 9 == 0 =>
1390            {
1391                let matrices = vec
1392                    .chunks_exact(9)
1393                    .map(|chunk| {
1394                        let m: Vec<f32> = chunk.iter().map(|&x| x as f32).collect();
1395                        crate::math::mat3_from_row_slice(&m)
1396                    })
1397                    .collect();
1398                Ok(Data::Matrix3Vec(Matrix3Vec(matrices)))
1399            }
1400
1401            // To Matrix4Vec conversions
1402            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
1403            (Data::Matrix4(Matrix4(m)), DataType::Matrix4Vec) => {
1404                Ok(Data::Matrix4Vec(Matrix4Vec(vec![*m])))
1405            }
1406            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
1407            (Data::RealVec(RealVec(vec)), DataType::Matrix4Vec)
1408                if vec.len() >= 16 && vec.len() % 16 == 0 =>
1409            {
1410                let matrices = vec
1411                    .chunks_exact(16)
1412                    .map(crate::math::mat4_from_row_slice)
1413                    .collect();
1414                Ok(Data::Matrix4Vec(Matrix4Vec(matrices)))
1415            }
1416
1417            // Vec to Vec conversions
1418            #[cfg(feature = "vec_variants")]
1419            (Data::RealVec(RealVec(vec)), DataType::IntegerVec) => {
1420                // AIDEV-NOTE: Converting RealVec to IntegerVec by rounding each element.
1421                Ok(Data::IntegerVec(IntegerVec(
1422                    vec.iter().map(|&f| f.round() as i64).collect(),
1423                )))
1424            }
1425            #[cfg(feature = "vec_variants")]
1426            (Data::IntegerVec(IntegerVec(vec)), DataType::RealVec) => {
1427                // AIDEV-NOTE: Converting IntegerVec to RealVec by casting each element.
1428                Ok(Data::RealVec(RealVec(
1429                    vec.iter().map(|&i| i as f64).collect(),
1430                )))
1431            }
1432            #[cfg(feature = "vec_variants")]
1433            (Data::BooleanVec(BooleanVec(vec)), DataType::IntegerVec) => {
1434                // AIDEV-NOTE: Converting BooleanVec to IntegerVec (true -> 1, false -> 0).
1435                Ok(Data::IntegerVec(IntegerVec(
1436                    vec.iter().map(|&b| if b { 1 } else { 0 }).collect(),
1437                )))
1438            }
1439            #[cfg(feature = "vec_variants")]
1440            (Data::IntegerVec(IntegerVec(vec)), DataType::BooleanVec) => {
1441                // AIDEV-NOTE: Converting IntegerVec to BooleanVec (0 -> false, non-0 -> true).
1442                Ok(Data::BooleanVec(BooleanVec(
1443                    vec.iter().map(|&i| i != 0).collect(),
1444                )))
1445            }
1446            #[cfg(feature = "vec_variants")]
1447            (Data::BooleanVec(BooleanVec(vec)), DataType::RealVec) => {
1448                // AIDEV-NOTE: Converting BooleanVec to RealVec (true -> 1.0, false -> 0.0).
1449                Ok(Data::RealVec(RealVec(
1450                    vec.iter().map(|&b| if b { 1.0 } else { 0.0 }).collect(),
1451                )))
1452            }
1453            #[cfg(feature = "vec_variants")]
1454            (Data::RealVec(RealVec(vec)), DataType::BooleanVec) => {
1455                // AIDEV-NOTE: Converting RealVec to BooleanVec (0.0 -> false, non-0.0 -> true).
1456                Ok(Data::BooleanVec(BooleanVec(
1457                    vec.iter().map(|&f| f != 0.0).collect(),
1458                )))
1459            }
1460
1461            // Unsupported conversions
1462            _ => Err(Error::ConversionUnsupported {
1463                from: self.data_type(),
1464                to,
1465            }),
1466        }
1467    }
1468}
1469
1470fn parse_color_from_string(s: &str) -> Option<[f32; 4]> {
1471    let s = s.trim();
1472
1473    // Try hex format first (#RRGGBB or #RRGGBBAA)
1474    if s.starts_with('#') {
1475        let hex = s.trim_start_matches('#');
1476        if (hex.len() == 6 || hex.len() == 8)
1477            && let Ok(val) = u32::from_str_radix(hex, 16)
1478        {
1479            let r = ((val >> 16) & 0xFF) as f32 / 255.0;
1480            let g = ((val >> 8) & 0xFF) as f32 / 255.0;
1481            let b = (val & 0xFF) as f32 / 255.0;
1482            let a = if hex.len() == 8 {
1483                ((val >> 24) & 0xFF) as f32 / 255.0
1484            } else {
1485                1.0
1486            };
1487            Some([r, g, b, a])
1488        } else {
1489            None
1490        }
1491    } else {
1492        // Try array format
1493        let s = s.trim_start_matches('[').trim_end_matches(']');
1494        let parts: Vec<&str> = s
1495            .split([',', ' '])
1496            .map(|p| p.trim())
1497            .filter(|p| !p.is_empty())
1498            .collect();
1499
1500        match parts.len() {
1501            4 => {
1502                if let (Ok(r), Ok(g), Ok(b), Ok(a)) = (
1503                    parts[0].parse::<f32>(),
1504                    parts[1].parse::<f32>(),
1505                    parts[2].parse::<f32>(),
1506                    parts[3].parse::<f32>(),
1507                ) {
1508                    Some([r, g, b, a])
1509                } else {
1510                    None
1511                }
1512            }
1513            3 => {
1514                if let (Ok(r), Ok(g), Ok(b)) = (
1515                    parts[0].parse::<f32>(),
1516                    parts[1].parse::<f32>(),
1517                    parts[2].parse::<f32>(),
1518                ) {
1519                    Some([r, g, b, 1.0])
1520                } else {
1521                    None
1522                }
1523            }
1524            1 => {
1525                // Single value - grayscale
1526                if let Ok(v) = parts[0].parse::<f32>() {
1527                    Some([v, v, v, 1.0])
1528                } else {
1529                    None
1530                }
1531            }
1532            _ => None,
1533        }
1534    }
1535}
1536
1537/// Parse a string into an array of `N` elements.
1538///
1539/// Missing elements are filled with `T::default()`.
1540///
1541/// Returns an error if the string can't be parsed to an array of N elements.
1542fn parse_to_array<T, const N: usize>(input: &str) -> Result<[T; N]>
1543where
1544    T: FromStr + Default + Debug,
1545    <T as FromStr>::Err: Display,
1546{
1547    // Strip brackets first
1548    let cleaned_input = input.trim().trim_start_matches('[').trim_end_matches(']');
1549
1550    let mut result = cleaned_input
1551        .split(&[',', ' '][..])
1552        .map(|s| s.trim())
1553        .filter(|&s| !s.is_empty())
1554        .take(N)
1555        .map(|s| {
1556            s.parse::<T>().map_err(|_| Error::ParseFailed {
1557                input: input.to_string(),
1558                target_type: std::any::type_name::<T>(),
1559            })
1560        })
1561        .collect::<std::result::Result<SmallVec<[T; N]>, _>>()?;
1562
1563    if result.len() < N {
1564        result.extend((0..N - result.len()).map(|_| T::default()));
1565    }
1566
1567    // SAFETY: SmallVec was extended to exactly N elements above; into_inner() cannot fail.
1568    Ok(result.into_inner().unwrap())
1569}
1570
1571// Arithmetic operations for Data enum - generated by macros
1572impl_data_arithmetic!(binary Add, add, "add");
1573impl_data_arithmetic!(binary Sub, sub, "subtract");
1574impl_data_arithmetic!(scalar f32);
1575impl_data_arithmetic!(scalar f64);
1576impl_data_arithmetic!(div f32);
1577impl_data_arithmetic!(div f64);
1578
1579// Manual Eq implementation for Data
1580// This is safe because we handle floating point comparison deterministically
1581impl Eq for Data {}
1582
1583// Implement DataSystem trait for the built-in Data type.
1584impl crate::traits::DataSystem for Data {
1585    type Animated = AnimatedData;
1586    type DataType = DataType;
1587
1588    fn discriminant(&self) -> DataType {
1589        DataTypeOps::data_type(self)
1590    }
1591
1592    fn variant_name(&self) -> &'static str {
1593        DataTypeOps::type_name(self)
1594    }
1595
1596    fn try_len(&self) -> Option<usize> {
1597        Data::try_len(self)
1598    }
1599
1600    fn pad_to_length(&mut self, target_len: usize) {
1601        Data::pad_to_length(self, target_len)
1602    }
1603}