tawasuyu

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 660Markdown 302TOML 157RON 312382 Sutichaykuna

⎇ main shared/foreign-lottie/src/schema/animated_properties/shape_property.rs Rust · 77 siq'ikuna · 2.6 KB Ñawpa willay Pi imata llamkarqan ch'uya

chore: refresco desde el monorepo — 27 crates nuevos (anim, lottie, svg, mesh, image, layer, test, wire-view, widgets rag-sidebar/router/lazy-list/rive-button) y sync de los 95 existentes 93253cc22 · Sergio ·

1// Copyright 2024 the Velato Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::schema::helpers::bezier::Bezier;
5use crate::schema::helpers::int_boolean::BoolInt;
6use serde::de::{self, Deserializer};
7use serde::{Deserialize, Serialize};
8
9use super::shape_keyframe::ShapeKeyframe;
10
11/// An animatable property that holds a Bezier
12#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
13pub struct ShapeProperty {
14    /// The index of the property.
15    #[serde(rename = "ix")]
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub property_index: Option<f64>,
18    /// Whether the property is animated
19    #[serde(rename = "a")]
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub animated: Option<BoolInt>,
22    /// The expression for the property.
23    #[serde(rename = "x")]
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub expression: Option<String>,
26    /// See [`ShapePropertyK`]
27    #[serde(rename = "k")]
28    pub value: ShapePropertyK,
29}
30
31/// The possible values of "k" in a [`ShapeProperty`].
32/// Either a static bezier held, or an animation with keyframes defined.
33#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
34#[serde(untagged)]
35pub enum ShapePropertyK {
36    #[serde(deserialize_with = "deserialize_keyframes")]
37    Animated(Vec<ShapeKeyframe>),
38    Static(Bezier),
39}
40
41/// Custom deserializer ensures that every keyframe has a "s" start, except only the last keyframe which may omit this.
42pub fn deserialize_keyframes<'de, D>(deserializer: D) -> Result<Vec<ShapeKeyframe>, D::Error>
43where
44    D: Deserializer<'de>,
45{
46    // Deserialize the keyframes & handle empty case
47    let keyframes = Vec::<ShapeKeyframe>::deserialize(deserializer)?;
48    if keyframes.is_empty() {
49        return Ok(keyframes);
50    }
51
52    // Validate that all keyframes until the last have a start property
53    if !keyframes[0..(keyframes.len() - 1)]
54        .iter()
55        .all(|keyframe| keyframe.start.is_some())
56    {
57        return Err(de::Error::custom(
58            "Animated Shape Keyframe found with missing 's' start property. Only the last keyframe may omit this.",
59        ));
60    }
61
62    // Early return if last keyframe has a start value.
63    if let Some(last_keyframe) = keyframes.last()
64        && last_keyframe.start.is_some()
65    {
66        return Ok(keyframes);
67    }
68
69    // The last keyframe has no start value - so there must be at least one other keyframe present.
70    if keyframes.len() < 2 {
71        Err(de::Error::custom(
72            "Last Animated Shape Keyframe 's' was omitted where only one keyframe is present.",
73        ))
74    } else {
75        Ok(keyframes)
76    }
77}