inkanim_types/ink/anim/
mod.rs

1//! All animations in Cyberpunk 2077 UI
2//! are similar to web and traditional 2D animations frameworks.
3
4mod display;
5
6use serde::{Deserialize, Serialize};
7use serde_aux::prelude::*;
8
9use crate::{HDRColor, Name, Vector2};
10
11use super::InkWrapper;
12
13#[cfg(feature = "clap")]
14mod implementation;
15
16use super::conversion::deserialize_vector2_from_anything;
17
18pub const OPACITY: InkAnimInterpolatorType = InkAnimInterpolatorType::Transparency(None);
19pub const FADEIN: InkAnimInterpolatorType = InkAnimInterpolatorType::Transparency(Some(Fade::In));
20pub const FADEOUT: InkAnimInterpolatorType = InkAnimInterpolatorType::Transparency(Some(Fade::Out));
21
22/// orphan interpolator
23pub struct OrphanInkAnimInterpolator {
24    pub index: usize,
25    pub interpolator: InkWrapper<InkAnimInterpolator>,
26}
27
28/// transparency interpolation direction
29#[derive(Debug, Clone, Copy, PartialEq)]
30pub enum Fade {
31    /// transparency interpolates toward `1.`
32    In,
33    /// transparency interpolates toward `0.`
34    Out,
35}
36
37/// every kind of possible interpolation
38#[derive(Debug, Clone, Copy, PartialEq)]
39pub enum InkAnimInterpolatorType {
40    Color,
41    Size,
42    Scale,
43    Translation,
44    Transparency(Option<Fade>),
45    TextValueProgress,
46    Effect,
47    Anchor,
48    Pivot,
49    Shear,
50    Rotation,
51    Margin,
52    Padding,
53    TextReplace,
54    TextOffset,
55}
56
57/// see [NativeDB](https://nativedb.red4ext.com/inkanimInterpolationDirection)
58#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
59pub enum Direction {
60    To = 0,
61    From = 1,
62    FromTo = 2,
63}
64
65/// see [NativeDB](https://nativedb.red4ext.com/inkanimInterpolationMode)
66#[allow(clippy::enum_variant_names)]
67#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
68pub enum Mode {
69    EasyIn = 0,
70    EasyOut = 1,
71    EasyInOut = 2,
72}
73
74/// see [NativeDB](https://nativedb.red4ext.com/inkanimInterpolationType)
75#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
76pub enum Type {
77    Linear = 0,
78    Quadratic = 1,
79    Qubic = 2,
80    Quartic = 3,
81    Quintic = 4,
82    Sinusoidal = 5,
83    Exponential = 6,
84    Elastic = 7,
85    Circular = 8,
86    Back = 9,
87}
88
89/// specific interpolator values interpretation
90///
91/// possible interpretations: percent-based (scale), positions-based (translation), color-based
92#[allow(non_camel_case_types)]
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
94#[serde(untagged)]
95pub enum Range {
96    Percent(f32),
97    Position(Vector2),
98    Color(HDRColor),
99}
100
101#[derive(Debug, Clone)]
102pub struct Transformation {
103    pub from: Range,
104    pub to: Range,
105}
106
107/// generic interpolator
108#[allow(non_camel_case_types)]
109#[derive(Debug, Clone, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct Interpolator {
112    pub duration: f32,
113    #[serde(deserialize_with = "deserialize_vector2_from_anything")]
114    pub end_value: Range,
115    pub interpolation_direction: Direction,
116    pub interpolation_mode: Mode,
117    pub interpolation_type: Type,
118    #[serde(deserialize_with = "deserialize_bool_from_anything")]
119    pub is_additive: bool,
120    pub start_delay: f32,
121    #[serde(deserialize_with = "deserialize_vector2_from_anything")]
122    pub start_value: Range,
123    #[serde(deserialize_with = "deserialize_bool_from_anything")]
124    pub use_relative_duration: bool,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128#[serde(rename_all = "camelCase")]
129pub struct EffectInterpolator {
130    pub effect_type: inkEffectType,
131    pub effect_name: Name,
132    pub param_name: Name,
133    #[serde(flatten)]
134    pub base: Interpolator,
135}
136
137#[allow(non_camel_case_types)]
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub enum inkEffectType {
140    ScanlineWipe = 0,
141    LinearWipe = 1,
142    RadialWipe = 2,
143    LightSweep = 3,
144    BoxBlur = 4,
145    Mask = 5,
146    Glitch = 6,
147    PointCloud = 7,
148    ColorFill = 8,
149    InnerGlow = 9,
150    ColorCorrection = 10,
151    Multisampling = 11,
152    Blackwall = 12,
153}
154
155/// any interpolator
156///
157/// possible kinds include: scale, translation, transparency, etc
158#[allow(clippy::enum_variant_names)]
159#[allow(non_camel_case_types)]
160#[derive(Debug, Clone, Serialize, Deserialize)]
161#[serde(tag = "$type")]
162pub enum InkAnimInterpolator {
163    inkanimScaleInterpolator(Interpolator),
164    inkanimTranslationInterpolator(Interpolator),
165    inkanimTransparencyInterpolator(Interpolator),
166    inkanimSizeInterpolator(Interpolator),
167    inkanimColorInterpolator(Interpolator),
168    inkanimTextValueProgressInterpolator(Interpolator),
169    inkanimEffectInterpolator(EffectInterpolator),
170    inkanimAnchorInterpolator(Interpolator),
171    inkanimPivotInterpolator(Interpolator),
172    inkanimShearInterpolator(Interpolator),
173    inkanimRotationInterpolator(Interpolator),
174    inkanimMarginInterpolator(Interpolator),
175    inkanimPaddingInterpolator(Interpolator),
176    inkanimTextReplaceInterpolator(Interpolator),
177    inkanimTextOffsetInterpolator(Interpolator),
178}
179
180impl AsRef<Interpolator> for InkAnimInterpolator {
181    fn as_ref(&self) -> &Interpolator {
182        match self {
183            Self::inkanimEffectInterpolator(interpolator) => &interpolator.base,
184            Self::inkanimScaleInterpolator(interpolator)
185            | Self::inkanimTranslationInterpolator(interpolator)
186            | Self::inkanimTransparencyInterpolator(interpolator)
187            | Self::inkanimSizeInterpolator(interpolator)
188            | Self::inkanimColorInterpolator(interpolator)
189            | Self::inkanimTextValueProgressInterpolator(interpolator)
190            | Self::inkanimAnchorInterpolator(interpolator)
191            | Self::inkanimPivotInterpolator(interpolator)
192            | Self::inkanimShearInterpolator(interpolator)
193            | Self::inkanimRotationInterpolator(interpolator)
194            | Self::inkanimMarginInterpolator(interpolator)
195            | Self::inkanimPaddingInterpolator(interpolator)
196            | Self::inkanimTextReplaceInterpolator(interpolator)
197            | Self::inkanimTextOffsetInterpolator(interpolator) => interpolator,
198        }
199    }
200}
201
202impl InkAnimInterpolator {
203    pub fn as_short_display(&self) -> &str {
204        match self {
205            Self::inkanimScaleInterpolator(_) => "scale",
206            Self::inkanimTranslationInterpolator(_) => "translation",
207            Self::inkanimTransparencyInterpolator(_) => "transparency",
208            Self::inkanimSizeInterpolator(_) => "size",
209            Self::inkanimColorInterpolator(_) => "color",
210            Self::inkanimTextValueProgressInterpolator(_) => "text value progress",
211            Self::inkanimEffectInterpolator(effect) => match effect.effect_type {
212                inkEffectType::ScanlineWipe => "effect (scan line wipe)",
213                inkEffectType::LinearWipe => "effect (linear wipe)",
214                inkEffectType::RadialWipe => "effect (radial wipe)",
215                inkEffectType::LightSweep => "effect (light sweep)",
216                inkEffectType::BoxBlur => "effect (box blur)",
217                inkEffectType::Mask => "effect (mask)",
218                inkEffectType::Glitch => "effect (glitch)",
219                inkEffectType::PointCloud => "effect (point cloud)",
220                inkEffectType::ColorFill => "effect (color fill)",
221                inkEffectType::InnerGlow => "effect (inner glow)",
222                inkEffectType::ColorCorrection => "effect (color correction)",
223                inkEffectType::Multisampling => "effect (multisampling)",
224                inkEffectType::Blackwall => "effect (blackwall)",
225            },
226            Self::inkanimAnchorInterpolator(_) => "anchor",
227            Self::inkanimPivotInterpolator(_) => "pivot",
228            Self::inkanimShearInterpolator(_) => "shear",
229            Self::inkanimRotationInterpolator(_) => "rotation",
230            Self::inkanimMarginInterpolator(_) => "margin",
231            Self::inkanimPaddingInterpolator(_) => "padding",
232            Self::inkanimTextReplaceInterpolator(_) => "text replace",
233            Self::inkanimTextOffsetInterpolator(_) => "text offset",
234        }
235    }
236    pub fn starts(&self) -> f32 {
237        self.as_ref().start_delay
238    }
239    pub fn ends(&self) -> f32 {
240        self.starts() + self.as_ref().duration
241    }
242    pub fn direction(&self) -> Direction {
243        self.as_ref().interpolation_direction
244    }
245    pub fn r#type(&self) -> Type {
246        self.as_ref().interpolation_type
247    }
248    pub fn mode(&self) -> Mode {
249        self.as_ref().interpolation_mode
250    }
251    pub fn duration(&self) -> f32 {
252        self.as_ref().duration
253    }
254    pub fn transformation(&self) -> Transformation {
255        Transformation {
256            from: self.as_ref().start_value.clone(),
257            to: self.as_ref().end_value.clone(),
258        }
259    }
260}
261
262impl PartialEq<InkAnimInterpolatorType> for InkAnimInterpolator {
263    fn eq(&self, other: &InkAnimInterpolatorType) -> bool {
264        match self {
265            Self::inkanimScaleInterpolator(_) => other == &InkAnimInterpolatorType::Scale,
266            Self::inkanimTranslationInterpolator(_) => {
267                other == &InkAnimInterpolatorType::Translation
268            }
269            Self::inkanimTransparencyInterpolator(interpolator) => match other {
270                InkAnimInterpolatorType::Transparency(None) => true,
271                InkAnimInterpolatorType::Transparency(Some(Fade::In)) => {
272                    interpolator.start_value < interpolator.end_value
273                }
274                InkAnimInterpolatorType::Transparency(Some(Fade::Out)) => {
275                    interpolator.start_value > interpolator.end_value
276                }
277                _ => false,
278            },
279            Self::inkanimSizeInterpolator(_) => other == &InkAnimInterpolatorType::Size,
280            Self::inkanimColorInterpolator(_) => other == &InkAnimInterpolatorType::Color,
281            Self::inkanimTextValueProgressInterpolator(_) => {
282                other == &InkAnimInterpolatorType::TextValueProgress
283            }
284            Self::inkanimEffectInterpolator(_) => other == &InkAnimInterpolatorType::Effect,
285            Self::inkanimAnchorInterpolator(_) => other == &InkAnimInterpolatorType::Anchor,
286            Self::inkanimPivotInterpolator(_) => other == &InkAnimInterpolatorType::Pivot,
287            Self::inkanimShearInterpolator(_) => other == &InkAnimInterpolatorType::Shear,
288            Self::inkanimRotationInterpolator(_) => other == &InkAnimInterpolatorType::Rotation,
289            Self::inkanimMarginInterpolator(_) => other == &InkAnimInterpolatorType::Margin,
290            Self::inkanimPaddingInterpolator(_) => other == &InkAnimInterpolatorType::Padding,
291            Self::inkanimTextReplaceInterpolator(_) => {
292                other == &InkAnimInterpolatorType::TextReplace
293            }
294            Self::inkanimTextOffsetInterpolator(_) => other == &InkAnimInterpolatorType::TextOffset,
295        }
296    }
297}
298
299/// a sequence of interpolators
300#[derive(Debug, Clone, Serialize, Deserialize)]
301#[serde(rename_all = "camelCase")]
302pub struct InkAnimDefinition {
303    pub interpolators: Vec<InkWrapper<InkAnimInterpolator>>,
304}
305
306/// a sequence of interpolations (interpolators and events)
307#[derive(Debug, Clone, Serialize, Deserialize)]
308#[serde(rename_all = "camelCase")]
309pub struct InkAnimSequence {
310    /// describe the interpolations played
311    ///
312    /// ⚠️ `definitions` size must always match `targets` size
313    pub definitions: Vec<InkWrapper<InkAnimDefinition>>,
314    pub name: Name,
315    /// describe the targets onto which the interpolations are played
316    ///
317    /// ⚠️ `targets` size must always match `definitions` size
318    pub targets: Vec<Target>,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
322#[serde(rename_all = "camelCase")]
323pub struct InkAnimAnimationLibraryResource {
324    pub sequences: Vec<InkWrapper<InkAnimSequence>>,
325}
326
327/// when related to interpolator(s),
328/// corresponding target is a sequence of digits indicating the path to the nested element
329///
330/// see [NativeDB](https://nativedb.red4ext.com/inkanimSequenceTargetInfo)
331#[allow(non_camel_case_types)]
332#[derive(Debug, Clone, Serialize, Deserialize)]
333#[serde(rename_all = "camelCase")]
334pub struct InkAnimSequenceTargetInfo {
335    /// path to the nested element (indexes)
336    ///
337    /// e.g. `[1,3,0,0,16]`
338    pub path: Vec<usize>,
339}
340
341/// when declaring interpolation event(s), corresponding target has a negative handle ref ID
342#[derive(Debug, Clone, Serialize, Deserialize)]
343#[serde(rename_all = "PascalCase")]
344pub struct BlankInkAnimSequenceTargetInfo {
345    /// typically here the value is `-1`
346    #[serde(deserialize_with = "deserialize_number_from_string")]
347    pub handle_ref_id: i32,
348}
349
350/// any target
351#[derive(Debug, Clone, Serialize, Deserialize)]
352#[serde(untagged)]
353pub enum Target {
354    /// a sequence of digits (path to nested element) : when related to interpolator(s)
355    WithHandleId(InkWrapper<InkAnimSequenceTargetInfo>),
356    /// a negative [handle ID](super::HandleId) (not element related) : when declaring interpolation event(s)
357    WithoutHandleId(BlankInkAnimSequenceTargetInfo),
358}
359
360impl InkAnimSequence {
361    /// find all interpolators matching filter
362    pub fn get_interpolators_matching(
363        &self,
364        filter: &InkAnimInterpolatorType,
365    ) -> Vec<InkWrapper<InkAnimInterpolator>> {
366        self.definitions
367            .first()
368            .expect("at least one ink anim definition")
369            .data
370            .interpolators
371            .clone()
372            .into_iter()
373            .filter(|x| x.data == *filter)
374            .collect()
375    }
376}
377
378impl InkWrapper<InkAnimSequence> {
379    pub fn name(&self) -> &str {
380        self.data.name.as_str()
381    }
382}