primase_core/
clip_transition.rs1use crate::{Component, Entity, Geometry, JsonError, Linear, World};
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
8pub struct IncomingTransition {
9 pub duration: Option<f64>,
10 pub refusal: Option<String>,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
15pub struct EffectBlend {
16 pub authored: f64,
17 pub event_end: Option<f64>,
18}
19impl EffectBlend {
20 pub fn seconds(&self) -> f64 {
21 self.event_end.unwrap_or(self.authored)
22 }
23}
24impl IncomingTransition {
25 pub fn resolve(&self, authored: f64) -> Result<EffectBlend, String> {
27 if !authored.is_finite() || authored < 0.0 {
28 return Err(format!(
29 "the effect's authored duration {authored} is not seconds"
30 ));
31 }
32 if let Some(reason) = &self.refusal {
33 return Err(reason.clone());
34 }
35 Ok(EffectBlend {
36 authored,
37 event_end: self.duration,
38 })
39 }
40}
41impl Component for IncomingTransition {
42 const NAME: &'static str = "core.incoming_transition";
43 const GEOMETRY: Geometry = Geometry::None;
44 fn verify(&self, _: Entity, _: &World) -> Result<(), String> {
45 if self
46 .duration
47 .is_some_and(|value| !value.is_finite() || value < 0.)
48 {
49 return Err("incoming transition duration must be finite and non-negative".into());
50 }
51 if self
52 .refusal
53 .as_ref()
54 .is_some_and(|reason| reason.trim().is_empty())
55 {
56 return Err("empty incoming transition refusal".into());
57 }
58 if self.duration.is_some() && self.refusal.is_some() {
59 return Err("incoming transition cannot be both resolved and refused".into());
60 }
61 Ok(())
62 }
63 fn to_json(&self) -> Result<Option<serde_json::Value>, JsonError> {
64 crate::json::to_value(self).map(Some)
65 }
66 fn apply_linear(&self, _: &Linear) -> crate::Linearity<Self> {
67 crate::Linearity::Invariant
68 }
69}