1use std::{collections::HashMap, fmt::Debug};
7
8use anyhow::Result;
9use bytemuck::{Pod, Zeroable};
10use strum::FromRepr;
11
12use crate::tf::Token;
13
14mod asset_path;
15mod change;
16mod copy;
17mod data;
18pub mod expr;
19mod file_format;
20mod layer;
21pub(crate) mod layer_registry;
22mod ordering;
23mod path;
24mod path_table;
25pub mod schema;
26pub mod sink;
27mod spec;
28mod value;
29
30pub use asset_path::AssetPath;
31pub use change::{ChangeEntry, ChangeFlags, ChangeList};
32pub(crate) use copy::{author_spec, is_children_field};
33pub use copy::{
34 copy_spec, copy_spec_with, copy_spec_within, should_copy_children, should_copy_value, CopyChildren,
35 CopyChildrenArgs, CopyValue, CopyValueArgs,
36};
37pub use data::{AbstractData, CowData, Data, DataError, Patch};
38pub use expr::{Evaluation, EvaluationValue, Expr, StringEvaluation, StringSegment};
39pub use file_format::{FileFormat, FileFormatCaps, WriteSeek};
40pub(crate) use layer::{dry_run_layers, edit_layers};
41pub use layer::{AuthoringError, EditError, Layer, LayerEdit, LayerSink, LayerSinkId, PendingLayerChange};
42pub use layer_registry::LayerRegistry;
43pub use ordering::{apply_ordering, element_cmp};
44pub use path::{path, Path, PathComponent, PathComponents, PathElement};
45pub use path_table::PathTable;
46pub use schema::{ChildrenKey, FieldKey};
47pub use spec::{
48 AttributeSpec, AttributeSpecMut, AttributeSpecRef, PrimSpec, PrimSpecMut, PrimSpecRef, PropertySpec,
49 PropertySpecMut, PropertySpecRef, PseudoRootSpec, PseudoRootSpecMut, PseudoRootSpecRef, RelationshipSpec,
50 RelationshipSpecMut, RelationshipSpecRef, Spec, SpecData, SpecError, SpecMut, SpecRef, SpecType,
51};
52pub use value::{CastError, FromValueCast, Value, ValueConversionError};
53
54#[repr(i32)]
55#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromRepr)]
56#[cfg_attr(feature = "serde", derive(serde::Serialize))]
57#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
58pub enum Specifier {
59 Def,
60 Over,
61 Class,
62}
63
64#[repr(i32)]
71#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromRepr)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
74pub enum Permission {
75 Public,
76 Private,
77}
78
79#[repr(i32)]
84#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromRepr)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize))]
86#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
87pub enum Variability {
88 #[default]
89 Varying,
90 Uniform,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, derive_more::From)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize))]
104pub struct TimeCode(pub f64);
105
106impl TimeCode {
107 #[inline]
109 pub fn value(self) -> f64 {
110 self.0
111 }
112}
113
114impl TryFrom<Value> for TimeCode {
115 type Error = ValueConversionError;
116
117 fn try_from(value: Value) -> Result<Self, Self::Error> {
118 match value {
119 Value::TimeCode(v) => Ok(v),
120 other => ValueConversionError::err("TimeCode", &other),
121 }
122 }
123}
124
125#[repr(C)]
127#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize))]
129pub struct LayerOffset {
130 pub offset: f64,
132 pub scale: f64,
134}
135
136impl Default for LayerOffset {
137 fn default() -> Self {
138 Self {
139 offset: 0.0,
140 scale: 1.0,
141 }
142 }
143}
144
145impl LayerOffset {
146 pub const IDENTITY: LayerOffset = LayerOffset {
148 offset: 0.0,
149 scale: 1.0,
150 };
151
152 #[inline]
153 pub fn new(offset: f64, scale: f64) -> Self {
154 Self { offset, scale }
155 }
156
157 #[inline]
161 pub fn scale_only(scale: f64) -> Self {
162 Self { offset: 0.0, scale }
163 }
164
165 #[inline]
166 pub fn is_valid(&self) -> bool {
167 self.offset.is_finite() && self.scale.is_finite()
168 }
169
170 #[inline]
172 pub fn is_identity(&self) -> bool {
173 self.offset == 0.0 && self.scale == 1.0
174 }
175
176 #[inline]
180 pub fn apply(&self, time: f64) -> f64 {
181 self.offset + self.scale * time
182 }
183
184 #[inline]
189 pub fn inverse(&self) -> LayerOffset {
190 if self.scale == 0.0 {
191 return LayerOffset::IDENTITY;
192 }
193 LayerOffset {
194 offset: -self.offset / self.scale,
195 scale: 1.0 / self.scale,
196 }
197 }
198
199 #[inline]
205 pub fn is_valid_composition(&self) -> bool {
206 self.offset.is_finite() && self.scale.is_finite() && self.scale > 0.0
207 }
208
209 #[inline]
214 pub fn sanitized(self) -> Self {
215 if self.is_valid_composition() {
216 self
217 } else {
218 Self::IDENTITY
219 }
220 }
221
222 #[inline]
234 pub fn concatenate(&self, inner: &LayerOffset) -> LayerOffset {
235 LayerOffset {
236 offset: self.offset + self.scale * inner.offset,
237 scale: self.scale * inner.scale,
238 }
239 }
240}
241
242#[derive(Debug, Default, Clone, PartialEq)]
252#[cfg_attr(feature = "serde", derive(serde::Serialize))]
253pub struct Payload {
254 #[cfg_attr(feature = "serde", serde(rename = "asset", skip_serializing_if = "String::is_empty"))]
256 pub asset_path: String,
257 #[cfg_attr(feature = "serde", serde(rename = "path", skip_serializing_if = "Path::is_empty"))]
259 pub prim_path: Path,
260 #[cfg_attr(
262 feature = "serde",
263 serde(rename = "layerOffset", skip_serializing_if = "Option::is_none")
264 )]
265 pub layer_offset: Option<LayerOffset>,
266}
267
268#[derive(Debug, Default, Clone, PartialEq)]
275#[cfg_attr(feature = "serde", derive(serde::Serialize))]
276pub struct Reference {
277 #[cfg_attr(feature = "serde", serde(rename = "asset", skip_serializing_if = "String::is_empty"))]
279 pub asset_path: String,
280 #[cfg_attr(feature = "serde", serde(rename = "path", skip_serializing_if = "Path::is_empty"))]
282 pub prim_path: Path,
283 #[cfg_attr(feature = "serde", serde(rename = "layerOffset"))]
285 pub layer_offset: LayerOffset,
286 #[cfg_attr(
288 feature = "serde",
289 serde(rename = "customData", skip_serializing_if = "HashMap::is_empty")
290 )]
291 pub custom_data: HashMap<String, Value>,
292}
293
294mod list_op;
295
296pub use list_op::ListOp;
297
298pub type Dictionary = std::collections::HashMap<String, Value>;
301
302pub type IntListOp = ListOp<i32>;
303pub type UintListOp = ListOp<u32>;
304
305pub type Int64ListOp = ListOp<i64>;
306pub type Uint64ListOp = ListOp<u64>;
307
308pub type StringListOp = ListOp<String>;
309pub type TokenListOp = ListOp<Token>;
310pub type PathListOp = ListOp<Path>;
311pub type ReferenceListOp = ListOp<Reference>;
312pub type PayloadListOp = ListOp<Payload>;
313
314pub type TimeSampleMap = Vec<(f64, Value)>;
315
316pub type Relocate = (Path, Path);
321
322pub type RelocateList = Vec<Relocate>;
325
326pub type LayerData = Box<dyn AbstractData>;
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn layer_offset_identity_is_identity() {
335 assert!(LayerOffset::IDENTITY.is_identity());
336 assert!(LayerOffset::default().is_identity());
337 assert!(!LayerOffset::new(0.0, 2.0).is_identity());
338 assert!(!LayerOffset::new(1.0, 1.0).is_identity());
339 }
340
341 #[test]
342 fn layer_offset_valid_composition_rejects_non_positive_scale() {
343 assert!(LayerOffset::new(10.0, 1.0).is_valid_composition());
344 assert!(!LayerOffset::new(10.0, 0.0).is_valid_composition());
345 assert!(!LayerOffset::new(10.0, -1.0).is_valid_composition());
346 assert!(!LayerOffset::new(f64::INFINITY, 1.0).is_valid_composition());
347 assert!(!LayerOffset::new(0.0, f64::NAN).is_valid_composition());
348 }
349
350 #[test]
351 fn layer_offset_sanitized_drops_invalid_to_identity() {
352 assert_eq!(LayerOffset::new(10.0, 2.0).sanitized(), LayerOffset::new(10.0, 2.0));
353 assert_eq!(LayerOffset::new(5.0, -1.0).sanitized(), LayerOffset::IDENTITY);
354 assert_eq!(LayerOffset::new(5.0, 0.0).sanitized(), LayerOffset::IDENTITY);
355 }
356
357 #[test]
358 fn layer_offset_concatenate_matches_spec_formula() {
359 let outer = LayerOffset::new(10.0, 2.0);
360 let inner = LayerOffset::new(20.0, 1.0);
361 assert_eq!(outer.concatenate(&inner), LayerOffset::new(50.0, 2.0));
363 }
364
365 #[test]
366 fn layer_offset_concatenate_is_associative() {
367 let a = LayerOffset::new(10.0, 2.0);
368 let b = LayerOffset::new(20.0, 0.5);
369 let c = LayerOffset::new(5.0, 3.0);
370 let ab_c = a.concatenate(&b).concatenate(&c);
371 let a_bc = a.concatenate(&b.concatenate(&c));
372 assert!((ab_c.offset - a_bc.offset).abs() < 1e-12);
373 assert!((ab_c.scale - a_bc.scale).abs() < 1e-12);
374 }
375
376 #[test]
377 fn layer_offset_identity_is_neutral() {
378 let a = LayerOffset::new(10.0, 2.0);
379 assert_eq!(a.concatenate(&LayerOffset::IDENTITY), a);
380 assert_eq!(LayerOffset::IDENTITY.concatenate(&a), a);
381 }
382
383 #[test]
384 fn layer_offset_inverse_undoes_apply() {
385 let a = LayerOffset::new(10.0, 2.0);
387 assert_eq!(a.inverse(), LayerOffset::new(-5.0, 0.5));
388 assert_eq!(a.inverse().apply(a.apply(7.0)), 7.0);
389 let b = LayerOffset::new(1.0, 3.0);
392 assert!((b.inverse().apply(b.apply(7.0)) - 7.0).abs() < 1e-9);
393 assert_eq!(LayerOffset::IDENTITY.inverse(), LayerOffset::IDENTITY);
395 assert_eq!(LayerOffset::new(3.0, 0.0).inverse(), LayerOffset::IDENTITY);
396 }
397}