pax_runtime_api/
lib.rs

1use std::collections::{HashMap, HashSet, VecDeque};
2use std::fmt::Display;
3use std::hash::Hasher;
4use std::ops::{Add, Deref, Mul, Neg, Sub};
5use std::time::Instant;
6
7use crate::math::Space;
8use kurbo::BezPath;
9pub use pax_message::*;
10pub use pax_value::numeric::Numeric;
11pub use pax_value::{CoercionRules, ImplToFromPaxAny, PaxValue, ToPaxValue};
12use piet::{PaintBrush, UnitPoint};
13use properties::{PropertyValue, UntypedProperty};
14pub mod cursor;
15
16/// Marker trait that needs to be implemented for a struct for insertion and
17/// deletion in a store
18/// NOTE: Stored objects need to be UNIQUE for any given stack. Do not insert
19/// values with types that could potentially be used in another use case,
20/// instead create a local new type (newtype pattern) only used for a single purpose
21pub trait Store: 'static {}
22
23use std::cell::Cell;
24use std::hash::Hash;
25use std::rc::{Rc, Weak};
26
27pub mod constants;
28pub mod math;
29pub mod pax_value;
30pub mod properties;
31
32pub use pax_value::functions;
33pub use properties::Property;
34
35pub use pax_value::functions::register_function;
36pub use pax_value::functions::Functions;
37pub use pax_value::functions::HelperFunctions;
38
39use crate::constants::COMMON_PROPERTIES_TYPE;
40pub use paste;
41pub use pax_message::serde;
42use serde::{Deserialize, Serialize};
43
44pub struct TransitionQueueEntry<T> {
45    pub duration_frames: u64,
46    pub curve: EasingCurve,
47    pub ending_value: T,
48}
49
50pub trait RenderContext {
51    fn fill(&mut self, layer: usize, path: BezPath, brush: &PaintBrush);
52    fn stroke(&mut self, layer: usize, path: BezPath, brush: &PaintBrush, width: f64);
53    fn save(&mut self, layer: usize);
54    fn restore(&mut self, layer: usize);
55    fn clip(&mut self, layer: usize, path: BezPath);
56    fn load_image(&mut self, path: &str, image: &[u8], width: usize, height: usize);
57    fn draw_image(&mut self, layer: usize, image_path: &str, rect: kurbo::Rect);
58    fn get_image_size(&mut self, image_path: &str) -> Option<(usize, usize)>;
59    fn transform(&mut self, layer: usize, affine: kurbo::Affine);
60    fn layers(&self) -> usize;
61}
62
63#[cfg(debug_assertions)]
64impl<T> std::fmt::Debug for TransitionQueueEntry<T> {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("TransitionQueueEntry")
67            .field("duration_frames", &self.duration_frames)
68            // .field("ending_value", &self.ending_value)
69            .finish()
70    }
71}
72
73#[derive(Default, Debug, Clone, Copy)]
74pub enum OS {
75    Mac,
76    Linux,
77    Windows,
78    Android,
79    IPhone,
80    #[default]
81    Unknown,
82}
83
84impl OS {
85    pub fn is_mobile(&self) -> bool {
86        match self {
87            OS::Android | OS::IPhone => true,
88            _ => false,
89        }
90    }
91
92    pub fn is_desktop(&self) -> bool {
93        match self {
94            OS::Mac | OS::Linux | OS::Windows => true,
95            _ => false,
96        }
97    }
98}
99
100#[derive(Default, Debug, Clone, Copy)]
101pub enum Platform {
102    Web,
103    Native,
104    #[default]
105    Unknown,
106}
107
108#[derive(Default, Debug, Clone, Copy)]
109pub struct Viewport {
110    pub width: f64,
111    pub height: f64,
112}
113
114impl ToPaxValue for Viewport {
115    fn to_pax_value(self) -> PaxValue {
116        PaxValue::Object(
117            vec![
118                ("width".to_string(), self.width.to_pax_value()),
119                ("height".to_string(), self.height.to_pax_value()),
120            ]
121            .into_iter()
122            .collect(),
123        )
124    }
125}
126
127impl Interpolatable for Viewport {
128    fn interpolate(&self, other: &Self, t: f64) -> Self {
129        Viewport {
130            width: self.width + (other.width - self.width) * t,
131            height: self.height + (other.height - self.height) * t,
132        }
133    }
134}
135
136pub struct Window;
137
138impl Space for Window {}
139
140// Unified events
141
142#[derive(Clone)]
143pub struct Event<T> {
144    pub args: T,
145    cancelled: Rc<Cell<bool>>,
146}
147
148impl<T: Clone + 'static> ImplToFromPaxAny for Event<T> {}
149
150impl<T> Event<T> {
151    pub fn new(args: T) -> Self {
152        Self {
153            args,
154            cancelled: Default::default(),
155        }
156    }
157
158    pub fn prevent_default(&self) {
159        self.cancelled.set(true);
160    }
161
162    pub fn cancelled(&self) -> bool {
163        self.cancelled.get()
164    }
165}
166
167impl<T> Deref for Event<T> {
168    type Target = T;
169
170    fn deref(&self) -> &Self::Target {
171        &self.args
172    }
173}
174
175/// A Clap describes either a "click" (mousedown followed by mouseup), OR a
176/// "tap" with one finger (singular fingerdown event).
177/// Claps are a useful alternative to most kinds of `Click` or `Tap` events,
178/// when you want the same behavior for both to be contained in one place.
179#[derive(Clone)]
180pub struct Clap {
181    pub x: f64,
182    pub y: f64,
183}
184
185/// Scroll occurs when a frame is translated vertically or horizontally
186/// Can be both by touch, mouse or keyboard
187/// The contained `delta_x` and `delta_y` describe the horizontal and vertical translation of
188/// the frame
189#[derive(Clone)]
190pub struct Scroll {
191    pub delta_x: f64,
192    pub delta_y: f64,
193}
194
195// Touch Events
196
197/// Represents a single touch point.
198#[derive(Clone)]
199pub struct Touch {
200    pub x: f64,
201    pub y: f64,
202    pub identifier: i64,
203    pub delta_x: f64,
204    pub delta_y: f64,
205}
206
207impl From<&TouchMessage> for Touch {
208    fn from(value: &TouchMessage) -> Self {
209        Touch {
210            x: value.x,
211            y: value.y,
212            identifier: value.identifier,
213            delta_x: value.delta_x,
214            delta_y: value.delta_x,
215        }
216    }
217}
218
219/// A TouchStart occurs when the user touches an element.
220/// The contained `touches` represent a list of touch points.
221#[derive(Clone)]
222pub struct TouchStart {
223    pub touches: Vec<Touch>,
224}
225
226/// A TouchMove occurs when the user moves while touching an element.
227/// The contained `touches` represent a list of touch points.
228#[derive(Clone)]
229pub struct TouchMove {
230    pub touches: Vec<Touch>,
231}
232
233/// A TouchEnd occurs when the user stops touching an element.
234/// The contained `touches` represent a list of touch points.
235#[derive(Clone)]
236pub struct TouchEnd {
237    pub touches: Vec<Touch>,
238}
239
240// Keyboard Events
241
242/// Common properties in keyboard events.
243#[derive(Clone)]
244pub struct KeyboardEventArgs {
245    pub key: String,
246    pub modifiers: Vec<ModifierKey>,
247    pub is_repeat: bool,
248}
249
250/// User is pressing a key.
251#[derive(Clone)]
252pub struct KeyDown {
253    pub keyboard: KeyboardEventArgs,
254}
255
256/// User is pressing a key.
257#[derive(Clone)]
258pub struct SelectStart {}
259
260/// User has released a key.
261#[derive(Clone)]
262pub struct KeyUp {
263    pub keyboard: KeyboardEventArgs,
264}
265
266/// User presses a key that displays a character (alphanumeric or symbol).
267#[derive(Clone)]
268pub struct KeyPress {
269    pub keyboard: KeyboardEventArgs,
270}
271
272// Window/component focused
273#[derive(Clone)]
274pub struct Focus {}
275
276// Mouse Events
277
278/// Common properties in mouse events.
279#[derive(Clone)]
280pub struct MouseEventArgs {
281    pub x: f64,
282    pub y: f64,
283    pub button: MouseButton,
284    pub modifiers: Vec<ModifierKey>,
285}
286
287#[derive(Clone, PartialEq)]
288pub enum MouseButton {
289    Left,
290    Right,
291    Middle,
292    Unknown,
293}
294
295impl From<MouseButtonMessage> for MouseButton {
296    fn from(value: MouseButtonMessage) -> Self {
297        match value {
298            MouseButtonMessage::Left => MouseButton::Left,
299            MouseButtonMessage::Right => MouseButton::Right,
300            MouseButtonMessage::Middle => MouseButton::Middle,
301            MouseButtonMessage::Unknown => MouseButton::Unknown,
302        }
303    }
304}
305
306#[derive(Clone)]
307pub enum ModifierKey {
308    Shift,
309    Control,
310    Alt,
311    Command,
312}
313
314impl From<&ModifierKeyMessage> for ModifierKey {
315    fn from(value: &ModifierKeyMessage) -> Self {
316        match value {
317            ModifierKeyMessage::Shift => ModifierKey::Shift,
318            ModifierKeyMessage::Control => ModifierKey::Control,
319            ModifierKeyMessage::Alt => ModifierKey::Alt,
320            ModifierKeyMessage::Command => ModifierKey::Command,
321        }
322    }
323}
324
325/// User clicks a mouse button over an element.
326#[derive(Clone)]
327pub struct Click {
328    pub mouse: MouseEventArgs,
329}
330
331/// User clicks a mouse button over an element.
332#[derive(Clone)]
333pub struct Drop {
334    pub x: f64,
335    pub y: f64,
336    pub name: String,
337    pub mime_type: String,
338    pub data: Vec<u8>,
339}
340
341/// User double-clicks a mouse button over an element.
342#[derive(Clone)]
343pub struct DoubleClick {
344    pub mouse: MouseEventArgs,
345}
346
347/// User moves the mouse while it is over an element.
348#[derive(Clone)]
349pub struct MouseMove {
350    pub mouse: MouseEventArgs,
351}
352
353/// User scrolls the mouse wheel over an element.
354#[derive(Clone)]
355pub struct Wheel {
356    pub x: f64,
357    pub y: f64,
358    pub delta_x: f64,
359    pub delta_y: f64,
360    pub modifiers: Vec<ModifierKey>,
361}
362
363#[derive(Clone)]
364pub struct CheckboxChange {
365    pub checked: bool,
366}
367
368#[derive(Clone)]
369pub struct TextInput {
370    pub text: String,
371}
372
373#[derive(Clone)]
374pub struct TextboxChange {
375    pub text: String,
376}
377
378#[derive(Clone)]
379pub struct TextboxInput {
380    pub text: String,
381}
382
383#[derive(Clone)]
384pub struct ButtonClick {}
385
386/// User presses a mouse button over an element.
387#[derive(Clone)]
388pub struct MouseDown {
389    pub mouse: MouseEventArgs,
390}
391
392/// User releases a mouse button over an element.
393#[derive(Clone)]
394pub struct MouseUp {
395    pub mouse: MouseEventArgs,
396}
397
398/// User moves the mouse onto an element.
399#[derive(Clone)]
400pub struct MouseOver {}
401
402/// User moves the mouse away from an element.
403#[derive(Clone)]
404pub struct MouseOut {}
405
406/// User right-clicks an element to open the context menu.
407#[derive(Clone)]
408pub struct ContextMenu {
409    pub mouse: MouseEventArgs,
410}
411
412/// A Size value that can be either a concrete pixel value
413/// or a percent of parent bounds.
414
415#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Hash)]
416#[serde(crate = "crate::serde")]
417pub enum Size {
418    Pixels(Numeric),
419    Percent(Numeric),
420    ///Pixel component, Percent component
421    Combined(Numeric, Numeric),
422}
423
424impl Display for Size {
425    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426        match self {
427            Size::Pixels(val) => write!(f, "{}px", val),
428            Size::Percent(val) => write!(f, "{}%", val),
429            Size::Combined(pix, per) => write!(f, "{}px + {}%", pix, per),
430        }
431    }
432}
433
434impl Neg for Size {
435    type Output = Size;
436    fn neg(self) -> Self::Output {
437        match self {
438            Size::Pixels(pix) => Size::Pixels(-pix),
439            Size::Percent(per) => Size::Percent(-per),
440            Size::Combined(pix, per) => Size::Combined(-pix, -per),
441        }
442    }
443}
444
445impl Add for Size {
446    type Output = Size;
447    fn add(self, rhs: Self) -> Self::Output {
448        let mut pixel_component: Numeric = Default::default();
449        let mut percent_component: Numeric = Default::default();
450
451        [self, rhs].iter().for_each(|size| match size {
452            Size::Pixels(s) => pixel_component = pixel_component + *s,
453            Size::Percent(s) => percent_component = percent_component + *s,
454            Size::Combined(s0, s1) => {
455                pixel_component = pixel_component + *s0;
456                percent_component = percent_component + *s1;
457            }
458        });
459
460        Size::Combined(pixel_component, percent_component)
461    }
462}
463
464impl Add<Percent> for Size {
465    type Output = Size;
466    fn add(self, rhs: Percent) -> Self::Output {
467        self + Size::Percent(rhs.0)
468    }
469}
470
471impl Sub<Percent> for Size {
472    type Output = Size;
473    fn sub(self, rhs: Percent) -> Self::Output {
474        self - Size::Percent(rhs.0)
475    }
476}
477
478impl Add<Size> for Percent {
479    type Output = Size;
480    fn add(self, rhs: Size) -> Self::Output {
481        Size::Percent(self.0) + rhs
482    }
483}
484
485impl Sub<Size> for Percent {
486    type Output = Size;
487    fn sub(self, rhs: Size) -> Self::Output {
488        Size::Percent(self.0) - rhs
489    }
490}
491
492impl Sub for Size {
493    type Output = Size;
494    fn sub(self, rhs: Self) -> Self::Output {
495        let mut pixel_component: Numeric = Default::default();
496        let mut percent_component: Numeric = Default::default();
497
498        let sizes = [(self, 1), (rhs, -1)];
499        for (size, multiplier) in sizes.iter() {
500            match size {
501                Size::Pixels(s) => {
502                    pixel_component = pixel_component + *s * Numeric::from(*multiplier)
503                }
504                Size::Percent(s) => {
505                    percent_component = percent_component + *s * Numeric::from(*multiplier)
506                }
507                Size::Combined(s0, s1) => {
508                    pixel_component = pixel_component + *s0 * Numeric::from(*multiplier);
509                    percent_component = percent_component + *s1 * Numeric::from(*multiplier);
510                }
511            }
512        }
513
514        Size::Combined(pixel_component, percent_component)
515    }
516}
517
518impl Size {
519    #[allow(non_snake_case)]
520    pub fn ZERO() -> Self {
521        Size::Pixels(Numeric::F64(0.0))
522    }
523
524    /// Returns the wrapped percent value normalized as a float, such that 100% => 1.0.
525    /// Panics if wrapped type is not a percentage.
526    pub fn expect_percent(&self) -> f64 {
527        match &self {
528            Size::Percent(val) => val.to_float() / 100.0,
529            Size::Pixels(val) => {
530                log::warn!("Percentage value expected but stored value was pixel.");
531                val.to_float() / 100.0
532            }
533            Size::Combined(_, percent) => {
534                log::warn!("Percentage value expected but stored value was a combination.");
535                percent.to_float() / 100.0
536            }
537        }
538    }
539
540    /// Returns the pixel value
541    /// Panics if wrapped type is not pixels.
542    pub fn expect_pixels(&self) -> Numeric {
543        match &self {
544            Size::Pixels(val) => val.clone(),
545            Size::Percent(val) => {
546                log::warn!("Pixel value expected but stored value was percentage.");
547                val.clone()
548            }
549            Size::Combined(pixels, _) => {
550                log::warn!("Pixel value expected but stored value was a combination.");
551                pixels.clone()
552            }
553        }
554    }
555}
556
557#[derive(Clone, Copy)]
558pub enum Axis {
559    X,
560    Y,
561}
562
563impl Size {
564    //Evaluate a Size in the context of `bounds` and a target `axis`.
565    //Returns a `Pixel` value as a simple f64; calculates `Percent` with respect to `bounds` & `axis`
566    pub fn evaluate(&self, bounds: (f64, f64), axis: Axis) -> f64 {
567        let target_bound = match axis {
568            Axis::X => bounds.0,
569            Axis::Y => bounds.1,
570        };
571        match &self {
572            Size::Pixels(num) => num.to_float(),
573            Size::Percent(num) => target_bound * (num.to_float() / 100.0),
574            Size::Combined(pixel_component, percent_component) => {
575                //first calc percent, then add pixel
576                (target_bound * (percent_component.to_float() / 100.0)) + pixel_component.to_float()
577            }
578        }
579    }
580}
581
582#[derive(Debug, Default, Serialize, Deserialize)]
583pub struct CommonProperty {
584    name: String,
585    property_type: String,
586    optional: bool,
587}
588
589// Struct containing fields shared by all RenderNodes.
590// Each property here is special-cased by the compiler when parsing element properties (e.g. `<SomeElement width={...} />`)
591// Retrieved via <dyn InstanceNode>#get_common_properties
592
593#[derive(Debug, Default, Clone)]
594pub struct CommonProperties {
595    pub id: Property<Option<String>>,
596    pub x: Property<Option<Size>>,
597    pub y: Property<Option<Size>>,
598    pub width: Property<Option<Size>>,
599    pub height: Property<Option<Size>>,
600    pub anchor_x: Property<Option<Size>>,
601    pub anchor_y: Property<Option<Size>>,
602    //TODO change scale to Percent (can't be px)
603    pub scale_x: Property<Option<Size>>,
604    pub scale_y: Property<Option<Size>>,
605    pub skew_x: Property<Option<Rotation>>,
606    pub skew_y: Property<Option<Rotation>>,
607    pub rotate: Property<Option<Rotation>>,
608    pub transform: Property<Option<Transform2D>>,
609    pub unclippable: Property<Option<bool>>,
610    pub _raycastable: Property<Option<bool>>,
611    pub _suspended: Property<Option<bool>>,
612}
613
614impl CommonProperties {
615    pub fn get_default_properties_literal() -> Vec<(String, String)> {
616        Self::get_property_identifiers()
617            .iter()
618            .map(|id| {
619                if id.0 == "transform" {
620                    (
621                        id.0.to_string(),
622                        "Transform2D::default_wrapped()".to_string(),
623                    )
624                } else {
625                    (id.0.to_string(), "Default::default()".to_string())
626                }
627            })
628            .collect()
629    }
630
631    pub fn get_property_identifiers() -> Vec<(String, String)> {
632        COMMON_PROPERTIES_TYPE
633            .iter()
634            .map(|(c, t)| (c.to_string(), t.to_string()))
635            .collect()
636    }
637
638    pub fn get_as_common_property() -> Vec<CommonProperty> {
639        Self::get_property_identifiers()
640            .iter()
641            .map(|id| CommonProperty {
642                name: id.0.to_string(),
643                property_type: id.1.to_string(),
644                optional: (id.0 == "transform" || id.0 == "width" || id.0 == "height"),
645            })
646            .collect()
647    }
648
649    pub fn retrieve_property_scope(&self) -> HashMap<String, Variable> {
650        let CommonProperties {
651            id,
652            x,
653            y,
654            width,
655            height,
656            anchor_x,
657            anchor_y,
658            scale_x,
659            scale_y,
660            skew_x,
661            skew_y,
662            rotate,
663            transform,
664            unclippable,
665            _raycastable,
666            _suspended,
667            // NOTE: remember to add an entry to the hashmap bellow as well
668        } = self;
669
670        HashMap::from([
671            (
672                "id".to_string(),
673                Variable::new_from_typed_property(id.clone()),
674            ),
675            (
676                "x".to_string(),
677                Variable::new_from_typed_property(x.clone()),
678            ),
679            (
680                "y".to_string(),
681                Variable::new_from_typed_property(y.clone()),
682            ),
683            (
684                "scale_x".to_string(),
685                Variable::new_from_typed_property(scale_x.clone()),
686            ),
687            (
688                "scale_y".to_string(),
689                Variable::new_from_typed_property(scale_y.clone()),
690            ),
691            (
692                "skew_x".to_string(),
693                Variable::new_from_typed_property(skew_x.clone()),
694            ),
695            (
696                "skew_y".to_string(),
697                Variable::new_from_typed_property(skew_y.clone()),
698            ),
699            (
700                "rotate".to_string(),
701                Variable::new_from_typed_property(rotate.clone()),
702            ),
703            (
704                "anchor_x".to_string(),
705                Variable::new_from_typed_property(anchor_x.clone()),
706            ),
707            (
708                "anchor_y".to_string(),
709                Variable::new_from_typed_property(anchor_y.clone()),
710            ),
711            (
712                "transform".to_string(),
713                Variable::new_from_typed_property(transform.clone()),
714            ),
715            (
716                "width".to_string(),
717                Variable::new_from_typed_property(width.clone()),
718            ),
719            (
720                "height".to_string(),
721                Variable::new_from_typed_property(height.clone()),
722            ),
723            (
724                "unclippable".to_string(),
725                Variable::new_from_typed_property(unclippable.clone()),
726            ),
727            (
728                "_raycastable".to_string(),
729                Variable::new_from_typed_property(_raycastable.clone()),
730            ),
731            (
732                "_suspended".to_string(),
733                Variable::new_from_typed_property(_suspended.clone()),
734            ),
735        ])
736    }
737}
738
739impl<T: Interpolatable> Interpolatable for Option<T> {
740    fn interpolate(&self, other: &Self, t: f64) -> Self {
741        match &self {
742            Self::Some(s) => match other {
743                Self::Some(o) => Some(s.interpolate(o, t)),
744                _ => None,
745            },
746            Self::None => None,
747        }
748    }
749}
750
751pub struct TransitionManager<T> {
752    queue: VecDeque<TransitionQueueEntry<T>>,
753    /// The value we are currently transitioning from
754    transition_checkpoint_value: T,
755    /// The time the current transition started
756    origin_frames_elapsed: u64,
757}
758
759#[cfg(debug_assertions)]
760impl<T> std::fmt::Debug for TransitionManager<T> {
761    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
762        f.debug_struct("TransitionManager")
763            .field("queue", &self.queue)
764            // .field("value", &self.transition_checkpoint_value)
765            .finish()
766    }
767}
768
769impl<T: Interpolatable> TransitionManager<T> {
770    pub fn new(value: T, current_time: u64) -> Self {
771        Self {
772            queue: VecDeque::new(),
773            transition_checkpoint_value: value,
774            origin_frames_elapsed: current_time,
775        }
776    }
777
778    pub fn push_transition(&mut self, transition: TransitionQueueEntry<T>) {
779        self.queue.push_back(transition);
780    }
781
782    pub fn reset_transitions(&mut self, current_time: u64) {
783        // update current value as to ease from this position
784        let eased_value = self.compute_eased_value(current_time);
785        self.transition_checkpoint_value =
786            eased_value.unwrap_or(self.transition_checkpoint_value.clone());
787        self.queue.clear();
788        self.origin_frames_elapsed = current_time;
789    }
790
791    pub fn compute_eased_value(&mut self, frames_elapsed: u64) -> Option<T> {
792        let global_fe = frames_elapsed;
793        let origin_fe = &mut self.origin_frames_elapsed;
794
795        // Fast-forward transitions that have already passed
796        while global_fe - *origin_fe > self.queue.front()?.duration_frames {
797            let curr = self.queue.pop_front()?;
798            *origin_fe += curr.duration_frames;
799            self.transition_checkpoint_value = curr.ending_value;
800        }
801        let current_transition = self.queue.front()?;
802        let local_fe = global_fe - *origin_fe;
803        let progress = local_fe as f64 / current_transition.duration_frames as f64;
804        let interpolated_val = current_transition.curve.interpolate(
805            &self.transition_checkpoint_value,
806            &current_transition.ending_value,
807            progress,
808        );
809        Some(interpolated_val)
810    }
811}
812
813pub enum EasingCurve {
814    Linear,
815    InQuad,
816    OutQuad,
817    InBack,
818    OutBack,
819    InOutBack,
820    Custom(Box<dyn Fn(f64) -> f64>),
821}
822
823struct EasingEvaluators {}
824impl EasingEvaluators {
825    fn linear(t: f64) -> f64 {
826        t
827    }
828    #[allow(dead_code)]
829    fn none(t: f64) -> f64 {
830        if t == 1.0 {
831            1.0
832        } else {
833            0.0
834        }
835    }
836    fn in_quad(t: f64) -> f64 {
837        t * t
838    }
839    fn out_quad(t: f64) -> f64 {
840        1.0 - (1.0 - t) * (1.0 - t)
841    }
842    fn in_back(t: f64) -> f64 {
843        const C1: f64 = 1.70158;
844        const C3: f64 = C1 + 1.00;
845        C3 * t * t * t - C1 * t * t
846    }
847    fn out_back(t: f64) -> f64 {
848        const C1: f64 = 1.70158;
849        const C3: f64 = C1 + 1.00;
850        1.0 + C3 * (t - 1.0).powi(3) + C1 * (t - 1.0).powi(2)
851    }
852
853    fn in_out_back(t: f64) -> f64 {
854        const C1: f64 = 1.70158;
855        const C2: f64 = C1 * 1.525;
856        if t < 0.5 {
857            ((2.0 * t).powi(2) * ((C2 + 1.0) * 2.0 * t - C2)) / 2.0
858        } else {
859            ((2.0 * t - 2.0).powi(2) * ((C2 + 1.0) * (t * 2.0 - 2.0) + C2) + 2.0) / 2.0
860        }
861    }
862}
863
864impl EasingCurve {
865    //for a time on the unit interval `t ∈ [0,1]`, given a value `t`,
866    // find the interpolated value `vt` between `v0` and `v1` given the self-contained easing curve
867    pub fn interpolate<T: Interpolatable>(&self, v0: &T, v1: &T, t: f64) -> T /*vt*/ {
868        let multiplier = match self {
869            EasingCurve::Linear => EasingEvaluators::linear(t),
870            EasingCurve::InQuad => EasingEvaluators::in_quad(t),
871            EasingCurve::OutQuad => EasingEvaluators::out_quad(t),
872            EasingCurve::InBack => EasingEvaluators::in_back(t),
873            EasingCurve::OutBack => EasingEvaluators::out_back(t),
874            EasingCurve::InOutBack => EasingEvaluators::in_out_back(t),
875            EasingCurve::Custom(evaluator) => (*evaluator)(t),
876        };
877
878        v0.interpolate(v1, multiplier)
879    }
880}
881
882impl<I: Clone + 'static> ImplToFromPaxAny for std::ops::Range<I> {}
883impl<T: 'static> ImplToFromPaxAny for Rc<T> {}
884impl<T: Clone + 'static> ImplToFromPaxAny for Weak<T> {}
885impl<T: Clone + 'static> ImplToFromPaxAny for Option<T> {}
886
887impl<T1: Clone + 'static, T2: Clone + 'static> ImplToFromPaxAny for (T1, T2) {}
888
889pub trait Interpolatable
890where
891    Self: Sized + Clone,
892{
893    //default implementation acts like a `None` ease — that is,
894    //the first value is simply returned.
895    fn interpolate(&self, _other: &Self, _t: f64) -> Self {
896        self.clone()
897    }
898}
899
900impl<I: Interpolatable> Interpolatable for std::ops::Range<I> {
901    fn interpolate(&self, _other: &Self, _t: f64) -> Self {
902        self.start.interpolate(&_other.start, _t)..self.end.interpolate(&_other.end, _t)
903    }
904}
905impl Interpolatable for () {}
906
907impl<T: ?Sized + Clone> Interpolatable for HashSet<T> {}
908impl<T: ?Sized + Clone> Interpolatable for VecDeque<T> {}
909impl<T: ?Sized> Interpolatable for Rc<T> {}
910impl<T: Interpolatable> Interpolatable for Weak<T> {}
911impl<T1: Interpolatable, T2: Interpolatable> Interpolatable for (T1, T2) {}
912
913impl<I: Interpolatable> Interpolatable for Vec<I> {
914    fn interpolate(&self, other: &Self, t: f64) -> Self {
915        //FUTURE: could revisit the following assertion/constraint, perhaps with a "don't-care" approach to disjoint vec elements
916        assert_eq!(
917            self.len(),
918            other.len(),
919            "cannot interpolate between vecs of different lengths"
920        );
921
922        self.iter()
923            .enumerate()
924            .map(|(i, elem)| elem.interpolate(other.get(i).unwrap(), t))
925            .collect()
926    }
927}
928
929impl Interpolatable for kurbo::BezPath {}
930
931impl Interpolatable for Instant {}
932
933impl Interpolatable for char {}
934
935impl Interpolatable for f64 {
936    fn interpolate(&self, other: &f64, t: f64) -> f64 {
937        self + (*other - self) * t
938    }
939}
940
941impl Interpolatable for bool {
942    fn interpolate(&self, _other: &bool, _t: f64) -> bool {
943        *self
944    }
945}
946
947impl Interpolatable for usize {
948    fn interpolate(&self, other: &usize, t: f64) -> usize {
949        (*self as f64 + (*other - self) as f64 * t) as usize
950    }
951}
952
953impl Interpolatable for isize {
954    fn interpolate(&self, other: &isize, t: f64) -> isize {
955        (*self as f64 + (*other - self) as f64 * t) as isize
956    }
957}
958
959impl Interpolatable for i64 {
960    fn interpolate(&self, other: &i64, t: f64) -> i64 {
961        (*self as f64 + (*other - self) as f64 * t) as i64
962    }
963}
964
965impl Interpolatable for i128 {
966    fn interpolate(&self, other: &i128, t: f64) -> i128 {
967        (*self as f64 + (*other - self) as f64 * t) as i128
968    }
969}
970
971impl Interpolatable for u128 {
972    fn interpolate(&self, other: &u128, t: f64) -> u128 {
973        (*self as f64 + (*other - self) as f64 * t) as u128
974    }
975}
976
977impl Interpolatable for u64 {
978    fn interpolate(&self, other: &u64, t: f64) -> u64 {
979        (*self as f64 + (*other - self) as f64 * t) as u64
980    }
981}
982
983impl Interpolatable for u8 {
984    fn interpolate(&self, other: &u8, t: f64) -> u8 {
985        (*self as f64 + (*other - *self) as f64 * t) as u8
986    }
987}
988
989impl Interpolatable for u16 {
990    fn interpolate(&self, other: &u16, t: f64) -> u16 {
991        (*self as f64 + (*other - *self) as f64 * t) as u16
992    }
993}
994
995impl Interpolatable for u32 {
996    fn interpolate(&self, other: &u32, t: f64) -> u32 {
997        (*self as f64 + (*other - *self) as f64 * t) as u32
998    }
999}
1000
1001impl Interpolatable for i8 {
1002    fn interpolate(&self, other: &i8, t: f64) -> i8 {
1003        (*self as f64 + (*other - *self) as f64 * t) as i8
1004    }
1005}
1006
1007impl Interpolatable for i16 {
1008    fn interpolate(&self, other: &i16, t: f64) -> i16 {
1009        (*self as f64 + (*other - *self) as f64 * t) as i16
1010    }
1011}
1012
1013impl Interpolatable for i32 {
1014    fn interpolate(&self, other: &i32, t: f64) -> i32 {
1015        (*self as f64 + (*other - *self) as f64 * t) as i32
1016    }
1017}
1018
1019impl Interpolatable for String {}
1020
1021pub struct Timeline {
1022    pub playhead_position: usize,
1023    pub frame_count: usize,
1024    pub is_playing: bool,
1025}
1026
1027#[derive(Debug, Clone, Copy, PartialEq)]
1028pub enum Layer {
1029    Native,
1030    Canvas,
1031    DontCare,
1032}
1033
1034/// Raw Percent type, which we use for serialization and dynamic traversal.  At the time
1035/// of authoring, this type is not used directly at runtime, but is intended for `into` coercion
1036/// into downstream types, e.g. ColorChannel, Rotation, and Size.  This allows us to be "dumb"
1037/// about how we parse `%`, and allow the context in which it is used to pull forward a specific
1038/// type through `into` inference.
1039#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
1040pub struct Percent(pub Numeric);
1041
1042impl Display for Percent {
1043    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1044        write!(f, "{}%", self.0)
1045    }
1046}
1047
1048impl Interpolatable for Percent {
1049    fn interpolate(&self, other: &Self, t: f64) -> Self {
1050        Self(self.0.interpolate(&other.0, t))
1051    }
1052}
1053
1054impl From<f64> for ColorChannel {
1055    fn from(value: f64) -> Self {
1056        Numeric::F64(value).into()
1057    }
1058}
1059
1060impl From<i32> for ColorChannel {
1061    fn from(value: i32) -> Self {
1062        Numeric::from(value).into()
1063    }
1064}
1065
1066impl Into<ColorChannel> for Percent {
1067    fn into(self) -> ColorChannel {
1068        ColorChannel::Percent(self.0)
1069    }
1070}
1071
1072impl Into<Size> for Percent {
1073    fn into(self) -> Size {
1074        Size::Percent(self.0)
1075    }
1076}
1077
1078impl Into<Rotation> for Percent {
1079    fn into(self) -> Rotation {
1080        Rotation::Percent(self.0)
1081    }
1082}
1083
1084#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1085pub enum ColorChannel {
1086    Rotation(Rotation),
1087    /// [0,255]
1088    Integer(u8),
1089    /// [0.0, 100.0]
1090    Percent(Numeric),
1091}
1092
1093impl Display for ColorChannel {
1094    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1095        match self {
1096            ColorChannel::Rotation(rot) => write!(f, "{}", rot),
1097            ColorChannel::Integer(int) => write!(f, "{}", int),
1098            ColorChannel::Percent(per) => write!(f, "{}%", per),
1099        }
1100    }
1101}
1102
1103impl Default for ColorChannel {
1104    fn default() -> Self {
1105        Self::Percent(Numeric::F64(50.0))
1106    }
1107}
1108
1109impl From<Numeric> for Rotation {
1110    fn from(value: Numeric) -> Self {
1111        Rotation::Degrees(value)
1112    }
1113}
1114
1115impl From<Numeric> for ColorChannel {
1116    fn from(value: Numeric) -> Self {
1117        Self::Integer(value.to_int().clamp(0, 255) as u8)
1118    }
1119}
1120
1121impl ColorChannel {
1122    ///Normalizes this ColorChannel as a float [0.0, 1.0]
1123    pub fn to_float_0_1(&self) -> f64 {
1124        match self {
1125            Self::Percent(per) => (per.to_float() / 100.0).clamp(0_f64, 1_f64),
1126            Self::Integer(zero_to_255) => {
1127                let f_zero = (*zero_to_255) as f64;
1128                (f_zero / 255.0_f64).clamp(0_f64, 1_f64)
1129            }
1130            Self::Rotation(rot) => rot.to_float_0_1(),
1131        }
1132    }
1133}
1134
1135#[allow(non_camel_case_types)]
1136#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq)]
1137pub enum Color {
1138    /// Models a color in the RGB space, with an alpha channel of 100%
1139    rgb(ColorChannel, ColorChannel, ColorChannel),
1140    /// Models a color in the RGBA space
1141    rgba(ColorChannel, ColorChannel, ColorChannel, ColorChannel),
1142
1143    /// Models a color in the HSL space.
1144    hsl(Rotation, ColorChannel, ColorChannel),
1145    /// Models a color in the HSLA space.
1146    hsla(Rotation, ColorChannel, ColorChannel, ColorChannel),
1147
1148    #[default]
1149    SLATE,
1150    GRAY,
1151    ZINC,
1152    NEUTRAL,
1153    STONE,
1154    RED,
1155    ORANGE,
1156    AMBER,
1157    YELLOW,
1158    LIME,
1159    GREEN,
1160    EMERALD,
1161    TEAL,
1162    CYAN,
1163    SKY,
1164    BLUE,
1165    INDIGO,
1166    VIOLET,
1167    PURPLE,
1168    FUCHSIA,
1169    PINK,
1170    ROSE,
1171    BLACK,
1172    WHITE,
1173    TRANSPARENT,
1174    NONE,
1175}
1176
1177impl Hash for Color {
1178    fn hash<H: Hasher>(&self, state: &mut H) {
1179        core::mem::discriminant(self).hash(state);
1180    }
1181}
1182
1183// implement display respecting all color enum variants, printing out their names
1184impl Display for Color {
1185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1186        match self {
1187            Self::rgb(r, g, b) => write!(f, "rgb({}, {}, {})", r, g, b),
1188            Self::rgba(r, g, b, a) => write!(f, "rgba({}, {}, {}, {})", r, g, b, a),
1189            Self::hsl(h, s, l) => write!(f, "hsl({}, {}, {})", h, s, l),
1190            Self::hsla(h, s, l, a) => write!(f, "hsla({}, {}, {}, {})", h, s, l, a),
1191            Self::SLATE => write!(f, "SLATE"),
1192            Self::GRAY => write!(f, "GRAY"),
1193            Self::ZINC => write!(f, "ZINC"),
1194            Self::NEUTRAL => write!(f, "NEUTRAL"),
1195            Self::STONE => write!(f, "STONE"),
1196            Self::RED => write!(f, "RED"),
1197            Self::ORANGE => write!(f, "ORANGE"),
1198            Self::AMBER => write!(f, "AMBER"),
1199            Self::YELLOW => write!(f, "YELLOW"),
1200            Self::LIME => write!(f, "LIME"),
1201            Self::GREEN => write!(f, "GREEN"),
1202            Self::EMERALD => write!(f, "EMERALD"),
1203            Self::TEAL => write!(f, "TEAL"),
1204            Self::CYAN => write!(f, "CYAN"),
1205            Self::SKY => write!(f, "SKY"),
1206            Self::BLUE => write!(f, "BLUE"),
1207            Self::INDIGO => write!(f, "INDIGO"),
1208            Self::VIOLET => write!(f, "VIOLET"),
1209            Self::PURPLE => write!(f, "PURPLE"),
1210            Self::FUCHSIA => write!(f, "FUCHSIA"),
1211            Self::PINK => write!(f, "PINK"),
1212            Self::ROSE => write!(f, "ROSE"),
1213            Self::BLACK => write!(f, "BLACK"),
1214            Self::WHITE => write!(f, "WHITE"),
1215            Self::TRANSPARENT => write!(f, "TRANSPARENT"),
1216            Self::NONE => write!(f, "NONE"),
1217        }
1218    }
1219}
1220
1221impl Color {
1222    //TODO: build out tint api and consider other color transforms
1223    //pub fn tint(tint_offset_amount) -> Self {...}
1224
1225    pub fn rgb(r: ColorChannel, g: ColorChannel, b: ColorChannel) -> Self {
1226        Self::rgb(r, g, b)
1227    }
1228
1229    pub fn rgba(r: ColorChannel, g: ColorChannel, b: ColorChannel, a: ColorChannel) -> Self {
1230        Self::rgba(r, g, b, a)
1231    }
1232
1233    pub fn hsl(h: Rotation, s: ColorChannel, l: ColorChannel) -> Self {
1234        Self::hsl(h, s, l)
1235    }
1236
1237    pub fn hsla(h: Rotation, s: ColorChannel, l: ColorChannel, a: ColorChannel) -> Self {
1238        Self::hsla(h, s, l, a)
1239    }
1240
1241    pub fn to_piet_color(&self) -> piet::Color {
1242        let rgba = self.to_rgba_0_1();
1243        piet::Color::rgba(rgba[0], rgba[1], rgba[2], rgba[3])
1244    }
1245
1246    pub fn from_rgba_0_1(rgba_0_1: [f64; 4]) -> Self {
1247        Self::rgba(
1248            ColorChannel::Percent(Numeric::F64(rgba_0_1[0] * 100.0)),
1249            ColorChannel::Percent(Numeric::F64(rgba_0_1[1] * 100.0)),
1250            ColorChannel::Percent(Numeric::F64(rgba_0_1[2] * 100.0)),
1251            ColorChannel::Percent(Numeric::F64(rgba_0_1[3] * 100.0)),
1252        )
1253    }
1254
1255    pub fn from_hex(hex: &str) -> Self {
1256        let r = u8::from_str_radix(&hex[0..2], 16).unwrap() as f64 / 255.0;
1257        let g = u8::from_str_radix(&hex[2..4], 16).unwrap() as f64 / 255.0;
1258        let b = u8::from_str_radix(&hex[4..6], 16).unwrap() as f64 / 255.0;
1259        let a = if hex.len() == 8 {
1260            u8::from_str_radix(&hex[6..8], 16).unwrap() as f64 / 255.0
1261        } else {
1262            1.0
1263        };
1264        Self::rgba(
1265            ColorChannel::Percent(Numeric::F64(r * 100.0)),
1266            ColorChannel::Percent(Numeric::F64(g * 100.0)),
1267            ColorChannel::Percent(Numeric::F64(b * 100.0)),
1268            ColorChannel::Percent(Numeric::F64(a * 100.0)),
1269        )
1270    }
1271
1272    // Returns a slice of four channels, normalized to [0,1]
1273    pub fn to_rgba_0_1(&self) -> [f64; 4] {
1274        match self {
1275            Self::hsla(h, s, l, a) => {
1276                let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
1277                [rgb[0], rgb[1], rgb[2], a.to_float_0_1()]
1278            }
1279            Self::hsl(h, s, l) => {
1280                let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
1281                [rgb[0], rgb[1], rgb[2], 1.0]
1282            }
1283            Self::rgba(r, g, b, a) => [
1284                r.to_float_0_1(),
1285                g.to_float_0_1(),
1286                b.to_float_0_1(),
1287                a.to_float_0_1(),
1288            ],
1289            Self::rgb(r, g, b) => [r.to_float_0_1(), g.to_float_0_1(), b.to_float_0_1(), 1.0],
1290
1291            //Color constants from TailwindCSS
1292            Self::SLATE => Self::rgb(
1293                Numeric::from(0x64).into(),
1294                Numeric::from(0x74).into(),
1295                Numeric::from(0x8b).into(),
1296            )
1297            .to_rgba_0_1(),
1298            Self::GRAY => Self::rgb(
1299                Numeric::from(0x6b).into(),
1300                Numeric::from(0x72).into(),
1301                Numeric::from(0x80).into(),
1302            )
1303            .to_rgba_0_1(),
1304            Self::ZINC => Self::rgb(
1305                Numeric::from(0x71).into(),
1306                Numeric::from(0x71).into(),
1307                Numeric::from(0x7a).into(),
1308            )
1309            .to_rgba_0_1(),
1310            Self::NEUTRAL => Self::rgb(
1311                Numeric::from(0x73).into(),
1312                Numeric::from(0x73).into(),
1313                Numeric::from(0x73).into(),
1314            )
1315            .to_rgba_0_1(),
1316            Self::STONE => Self::rgb(
1317                Numeric::from(0x78).into(),
1318                Numeric::from(0x71).into(),
1319                Numeric::from(0x6c).into(),
1320            )
1321            .to_rgba_0_1(),
1322            Self::RED => Self::rgb(
1323                Numeric::from(0xeF).into(),
1324                Numeric::from(0x44).into(),
1325                Numeric::from(0x44).into(),
1326            )
1327            .to_rgba_0_1(),
1328            Self::ORANGE => Self::rgb(
1329                Numeric::from(0xf9).into(),
1330                Numeric::from(0x73).into(),
1331                Numeric::from(0x16).into(),
1332            )
1333            .to_rgba_0_1(),
1334            Self::AMBER => Self::rgb(
1335                Numeric::from(0xf5).into(),
1336                Numeric::from(0x9e).into(),
1337                Numeric::from(0x0b).into(),
1338            )
1339            .to_rgba_0_1(),
1340            Self::YELLOW => Self::rgb(
1341                Numeric::from(0xea).into(),
1342                Numeric::from(0xb3).into(),
1343                Numeric::from(0x08).into(),
1344            )
1345            .to_rgba_0_1(),
1346            Self::LIME => Self::rgb(
1347                Numeric::from(0x84).into(),
1348                Numeric::from(0xcc).into(),
1349                Numeric::from(0x16).into(),
1350            )
1351            .to_rgba_0_1(),
1352            Self::GREEN => Self::rgb(
1353                Numeric::from(0x22).into(),
1354                Numeric::from(0xc5).into(),
1355                Numeric::from(0x5e).into(),
1356            )
1357            .to_rgba_0_1(),
1358            Self::EMERALD => Self::rgb(
1359                Numeric::from(0x10).into(),
1360                Numeric::from(0xb9).into(),
1361                Numeric::from(0x81).into(),
1362            )
1363            .to_rgba_0_1(),
1364            Self::TEAL => Self::rgb(
1365                Numeric::from(0x14).into(),
1366                Numeric::from(0xb8).into(),
1367                Numeric::from(0xa6).into(),
1368            )
1369            .to_rgba_0_1(),
1370            Self::CYAN => Self::rgb(
1371                Numeric::from(0x06).into(),
1372                Numeric::from(0xb6).into(),
1373                Numeric::from(0xd4).into(),
1374            )
1375            .to_rgba_0_1(),
1376            Self::SKY => Self::rgb(
1377                Numeric::from(0x0e).into(),
1378                Numeric::from(0xa5).into(),
1379                Numeric::from(0xe9).into(),
1380            )
1381            .to_rgba_0_1(),
1382            Self::BLUE => Self::rgb(
1383                Numeric::from(0x3b).into(),
1384                Numeric::from(0x82).into(),
1385                Numeric::from(0xf6).into(),
1386            )
1387            .to_rgba_0_1(),
1388            Self::INDIGO => Self::rgb(
1389                Numeric::from(0x63).into(),
1390                Numeric::from(0x66).into(),
1391                Numeric::from(0xf1).into(),
1392            )
1393            .to_rgba_0_1(),
1394            Self::VIOLET => Self::rgb(
1395                Numeric::from(0x8b).into(),
1396                Numeric::from(0x5c).into(),
1397                Numeric::from(0xf6).into(),
1398            )
1399            .to_rgba_0_1(),
1400            Self::PURPLE => Self::rgb(
1401                Numeric::from(0xa8).into(),
1402                Numeric::from(0x55).into(),
1403                Numeric::from(0xf7).into(),
1404            )
1405            .to_rgba_0_1(),
1406            Self::FUCHSIA => Self::rgb(
1407                Numeric::from(0xd9).into(),
1408                Numeric::from(0x46).into(),
1409                Numeric::from(0xef).into(),
1410            )
1411            .to_rgba_0_1(),
1412            Self::PINK => Self::rgb(
1413                Numeric::from(0xec).into(),
1414                Numeric::from(0x48).into(),
1415                Numeric::from(0x99).into(),
1416            )
1417            .to_rgba_0_1(),
1418            Self::ROSE => Self::rgb(
1419                Numeric::from(0xf4).into(),
1420                Numeric::from(0x3f).into(),
1421                Numeric::from(0x5e).into(),
1422            )
1423            .to_rgba_0_1(),
1424            Self::BLACK => Self::rgb(
1425                Numeric::from(0x00).into(),
1426                Numeric::from(0x00).into(),
1427                Numeric::from(0x00).into(),
1428            )
1429            .to_rgba_0_1(),
1430            Self::WHITE => Self::rgb(
1431                Numeric::from(0xff).into(),
1432                Numeric::from(0xff).into(),
1433                Numeric::from(0xff).into(),
1434            )
1435            .to_rgba_0_1(),
1436            Self::TRANSPARENT | Self::NONE => Self::rgba(
1437                Numeric::from(0xff).into(),
1438                Numeric::from(0xff).into(),
1439                Numeric::from(0xFF).into(),
1440                Numeric::from(0x00).into(),
1441            )
1442            .to_rgba_0_1(),
1443        }
1444    }
1445
1446    //Credit: Claude 3.5 Sonnet
1447    pub fn to_hsla_0_1(&self) -> [f64; 4] {
1448        let [r, g, b, a] = self.to_rgba_0_1();
1449
1450        let max = r.max(g).max(b);
1451        let min = r.min(g).min(b);
1452        let chroma = max - min;
1453
1454        let h = if chroma == 0.0 {
1455            0.0
1456        } else if max == r {
1457            ((g - b) / chroma + 6.0) % 6.0 / 6.0
1458        } else if max == g {
1459            ((b - r) / chroma + 2.0) / 6.0
1460        } else {
1461            ((r - g) / chroma + 4.0) / 6.0
1462        };
1463
1464        let l = (max + min) / 2.0;
1465
1466        let s = if l == 0.0 || l == 1.0 {
1467            0.0
1468        } else {
1469            (max - l) / l.min(1.0 - l)
1470        };
1471
1472        [h, s, l, a]
1473    }
1474}
1475
1476//hsl_to_rgb logic borrowed & modified from https://github.com/emgyrz/colorsys.rs, licensed MIT Copyright (c) 2019 mz <emgyrz@gmail.com>
1477const RGB_UNIT_MAX: f64 = 255.0;
1478fn hsl_to_rgb(h: f64, s: f64, l: f64) -> [f64; 3] {
1479    if s == 0.0 {
1480        let unit = RGB_UNIT_MAX * l;
1481        return [
1482            unit / RGB_UNIT_MAX,
1483            unit / RGB_UNIT_MAX,
1484            unit / RGB_UNIT_MAX,
1485        ];
1486    }
1487
1488    let temp1 = if l < 0.5 {
1489        l * (1.0 + s)
1490    } else {
1491        l + s - l * s
1492    };
1493
1494    let temp2 = 2.0 * l - temp1;
1495    let hue = h;
1496
1497    let temp_r = bound(hue + (1.0 / 3.0), 1.0);
1498    let temp_g = bound(hue, 1.0);
1499    let temp_b = bound(hue - (1.0 / 3.0), 1.0);
1500
1501    let r = calc_rgb_unit(temp_r, temp1, temp2);
1502    let g = calc_rgb_unit(temp_g, temp1, temp2);
1503    let b = calc_rgb_unit(temp_b, temp1, temp2);
1504    [r / RGB_UNIT_MAX, g / RGB_UNIT_MAX, b / RGB_UNIT_MAX]
1505}
1506
1507fn calc_rgb_unit(unit: f64, temp1: f64, temp2: f64) -> f64 {
1508    let mut result = temp2;
1509    if 6.0 * unit < 1.0 {
1510        result = temp2 + (temp1 - temp2) * 6.0 * unit
1511    } else if 2.0 * unit < 1.0 {
1512        result = temp1
1513    } else if 3.0 * unit < 2.0 {
1514        result = temp2 + (temp1 - temp2) * ((2.0 / 3.0) - unit) * 6.0
1515    }
1516    result * RGB_UNIT_MAX
1517}
1518
1519pub fn bound(r: f64, entire: f64) -> f64 {
1520    let mut n = r;
1521    loop {
1522        let less = n < 0.0;
1523        let bigger = n > entire;
1524        if !less && !bigger {
1525            break n;
1526        }
1527        if less {
1528            n += entire;
1529        } else {
1530            n -= entire;
1531        }
1532    }
1533}
1534
1535impl Into<ColorMessage> for &Color {
1536    fn into(self) -> ColorMessage {
1537        let rgba = self.to_rgba_0_1();
1538        ColorMessage::Rgba(rgba)
1539    }
1540}
1541impl PartialEq<ColorMessage> for Color {
1542    fn eq(&self, other: &ColorMessage) -> bool {
1543        let self_rgba = self.to_rgba_0_1();
1544
1545        match other {
1546            ColorMessage::Rgb(other_rgba) => {
1547                self_rgba[0] == other_rgba[0]
1548                    && self_rgba[1] == other_rgba[1]
1549                    && self_rgba[2] == other_rgba[2]
1550                    && self_rgba[3] == 1.0
1551            }
1552            ColorMessage::Rgba(other_rgba) => {
1553                self_rgba[0] == other_rgba[0]
1554                    && self_rgba[1] == other_rgba[1]
1555                    && self_rgba[2] == other_rgba[2]
1556                    && self_rgba[3] == other_rgba[3]
1557            }
1558        }
1559    }
1560}
1561impl Interpolatable for Color {
1562    fn interpolate(&self, other: &Self, t: f64) -> Self {
1563        let rgba_s = self.to_rgba_0_1();
1564        let rgba_o = other.to_rgba_0_1();
1565        let rgba_i = [
1566            rgba_s[0].interpolate(&rgba_o[0], t),
1567            rgba_s[1].interpolate(&rgba_o[1], t),
1568            rgba_s[2].interpolate(&rgba_o[2], t),
1569            rgba_s[3].interpolate(&rgba_o[3], t),
1570        ];
1571        Color::from_rgba_0_1(rgba_i)
1572    }
1573}
1574
1575#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Copy)]
1576pub enum Rotation {
1577    Radians(Numeric),
1578    Degrees(Numeric),
1579    Percent(Numeric),
1580}
1581
1582impl Display for Rotation {
1583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1584        match self {
1585            Rotation::Radians(rad) => write!(f, "{}rad", rad),
1586            Rotation::Degrees(deg) => write!(f, "{}deg", deg),
1587            Rotation::Percent(per) => write!(f, "{}%", per),
1588        }
1589    }
1590}
1591
1592impl Default for Rotation {
1593    fn default() -> Self {
1594        Self::Degrees(Numeric::F64(0.0))
1595    }
1596}
1597
1598impl Interpolatable for Rotation {
1599    fn interpolate(&self, other: &Self, t: f64) -> Self {
1600        Self::Percent(Numeric::F64(
1601            other.to_float_0_1() - self.to_float_0_1() * t / 100.0,
1602        ))
1603    }
1604}
1605
1606impl Rotation {
1607    #[allow(non_snake_case)]
1608    pub fn ZERO() -> Self {
1609        Self::Degrees(Numeric::F64(0.0))
1610    }
1611
1612    /// Returns a float proportional to `0deg : 0.0 :: 360deg :: 1.0`, in the domain 𝕗𝟞𝟜
1613    /// For example, 0rad maps to 0.0, 100% maps to 1.0, and 720deg maps to 2.0
1614    pub fn to_float_0_1(&self) -> f64 {
1615        match self {
1616            Self::Radians(rad) => rad.to_float() / (std::f64::consts::PI * 2.0),
1617            Self::Degrees(deg) => deg.to_float() / 360.0_f64,
1618            Self::Percent(per) => per.to_float(),
1619        }
1620    }
1621
1622    pub fn get_as_radians(&self) -> f64 {
1623        if let Self::Radians(num) = self {
1624            num.to_float()
1625        } else if let Self::Degrees(num) = self {
1626            num.to_float() * std::f64::consts::PI * 2.0 / 360.0
1627        } else if let Self::Percent(num) = self {
1628            num.to_float() * std::f64::consts::PI * 2.0 / 100.0
1629        } else {
1630            unreachable!()
1631        }
1632    }
1633
1634    pub fn get_as_degrees(&self) -> f64 {
1635        if let Self::Radians(num) = self {
1636            num.to_float() * 180.0 / std::f64::consts::PI
1637        } else if let Self::Degrees(num) = self {
1638            num.to_float()
1639        } else if let Self::Percent(num) = self {
1640            num.to_float() * 360.0 / 100.0
1641        } else {
1642            unreachable!()
1643        }
1644    }
1645}
1646impl Neg for Rotation {
1647    type Output = Rotation;
1648    fn neg(self) -> Self::Output {
1649        match self {
1650            Rotation::Degrees(deg) => Rotation::Degrees(-deg),
1651            Rotation::Radians(rad) => Rotation::Radians(-rad),
1652            Rotation::Percent(per) => Rotation::Percent(-per),
1653        }
1654    }
1655}
1656
1657impl Add for Rotation {
1658    type Output = Rotation;
1659
1660    fn add(self, rhs: Self) -> Self::Output {
1661        let self_rad = self.get_as_radians();
1662        let other_rad = rhs.get_as_radians();
1663        Rotation::Radians(Numeric::F64(self_rad + other_rad))
1664    }
1665}
1666
1667#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
1668#[serde(crate = "crate::serde")]
1669pub enum PathElement {
1670    #[default]
1671    Empty,
1672    Point(Size, Size),
1673    Line,
1674    Quadratic(Size, Size),
1675    Cubic(Box<(Size, Size, Size, Size)>),
1676    Close,
1677}
1678
1679impl Interpolatable for PathElement {}
1680impl HelperFunctions for PathElement {}
1681
1682#[derive(Debug, Clone, Serialize, Deserialize)]
1683#[serde(crate = "crate::serde")]
1684pub struct Stroke {
1685    pub color: Property<Color>,
1686    pub width: Property<Size>,
1687}
1688
1689impl Default for Stroke {
1690    fn default() -> Self {
1691        Self {
1692            color: Default::default(),
1693            width: Property::new(Size::Pixels(Numeric::F64(0.0))),
1694        }
1695    }
1696}
1697
1698impl PartialEq for Stroke {
1699    fn eq(&self, other: &Self) -> bool {
1700        self.color.get() == other.color.get() && self.width.get() == other.width.get()
1701    }
1702}
1703
1704impl Interpolatable for Stroke {
1705    fn interpolate(&self, _other: &Self, _t: f64) -> Self {
1706        // TODO interpolation
1707        self.clone()
1708    }
1709}
1710
1711pub enum NavigationTarget {
1712    Current,
1713    New,
1714}
1715
1716#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1717#[serde(crate = "crate::serde")]
1718pub enum Fill {
1719    Solid(Color),
1720    LinearGradient(LinearGradient),
1721    RadialGradient(RadialGradient),
1722}
1723
1724impl Hash for Fill {
1725    fn hash<H: Hasher>(&self, state: &mut H) {
1726        match self {
1727            Fill::Solid(color) => {
1728                state.write_u8(0);
1729                color.hash(state);
1730            }
1731            Fill::LinearGradient(linear) => {
1732                state.write_u8(1);
1733                linear.start.hash(state);
1734                linear.end.hash(state);
1735                linear.stops.hash(state);
1736            }
1737            Fill::RadialGradient(radial) => {
1738                state.write_u8(2);
1739                radial.start.hash(state);
1740                radial.end.hash(state);
1741                radial.radius.to_bits().hash(state);
1742                radial.stops.hash(state);
1743            }
1744        }
1745    }
1746}
1747
1748impl Hash for Stroke {
1749    fn hash<H: Hasher>(&self, state: &mut H) {
1750        self.width.get().hash(state);
1751        self.color.get().hash(state);
1752    }
1753}
1754
1755impl Interpolatable for Fill {
1756    fn interpolate(&self, _other: &Self, _t: f64) -> Self {
1757        // TODO interpolation
1758        self.clone()
1759    }
1760}
1761
1762#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash)]
1763#[serde(crate = "crate::serde")]
1764pub struct LinearGradient {
1765    pub start: (Size, Size),
1766    pub end: (Size, Size),
1767    pub stops: Vec<GradientStop>,
1768}
1769
1770#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1771#[serde(crate = "crate::serde")]
1772pub struct RadialGradient {
1773    pub end: (Size, Size),
1774    pub start: (Size, Size),
1775    pub radius: f64,
1776    pub stops: Vec<GradientStop>,
1777}
1778
1779#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash)]
1780#[serde(crate = "crate::serde")]
1781pub struct GradientStop {
1782    pub position: Size,
1783    pub color: Color,
1784}
1785
1786impl GradientStop {
1787    pub fn get(color: Color, position: Size) -> GradientStop {
1788        GradientStop { position, color }
1789    }
1790}
1791
1792impl Default for Fill {
1793    fn default() -> Self {
1794        Self::Solid(Color::default())
1795    }
1796}
1797
1798impl Fill {
1799    pub fn to_unit_point((x, y): (Size, Size), (width, height): (f64, f64)) -> UnitPoint {
1800        let normalized_x = match x {
1801            Size::Pixels(val) => val.to_float() / width,
1802            Size::Percent(val) => val.to_float() / 100.0,
1803            Size::Combined(pix, per) => (pix.to_float() / width) + (per.to_float() / 100.0),
1804        };
1805
1806        let normalized_y = match y {
1807            Size::Pixels(val) => val.to_float() / height,
1808            Size::Percent(val) => val.to_float() / 100.0,
1809            Size::Combined(pix, per) => (pix.to_float() / width) + (per.to_float() / 100.0),
1810        };
1811        UnitPoint::new(normalized_x, normalized_y)
1812    }
1813
1814    pub fn to_piet_gradient_stops(stops: Vec<GradientStop>) -> Vec<piet::GradientStop> {
1815        let mut ret = Vec::new();
1816        for gradient_stop in stops {
1817            match gradient_stop.position {
1818                Size::Pixels(_) => {
1819                    panic!("Gradient stops must be specified in percentages");
1820                }
1821                Size::Percent(p) => {
1822                    ret.push(piet::GradientStop {
1823                        pos: (p.to_float() / 100.0) as f32,
1824                        color: gradient_stop.color.to_piet_color(),
1825                    });
1826                }
1827                Size::Combined(_, _) => {
1828                    panic!("Gradient stops must be specified in percentages");
1829                }
1830            }
1831        }
1832        ret
1833    }
1834
1835    #[allow(non_snake_case)]
1836    pub fn linearGradient(
1837        start: (Size, Size),
1838        end: (Size, Size),
1839        stops: Vec<GradientStop>,
1840    ) -> Fill {
1841        Fill::LinearGradient(LinearGradient { start, end, stops })
1842    }
1843}
1844
1845impl Into<Rotation> for Size {
1846    fn into(self) -> Rotation {
1847        if let Size::Percent(pix) = self {
1848            Rotation::Percent(pix)
1849        } else {
1850            panic!("Tried to coerce a pixel value into a rotation value; try `%` or `rad` instead of `px`.")
1851        }
1852    }
1853}
1854
1855impl Size {
1856    pub fn get_pixels(&self, parent: f64) -> f64 {
1857        match &self {
1858            Self::Pixels(p) => p.to_float(),
1859            Self::Percent(p) => parent * (p.to_float() / 100.0),
1860            Self::Combined(pix, per) => (parent * (per.to_float() / 100.0)) + pix.to_float(),
1861        }
1862    }
1863}
1864
1865impl Interpolatable for Size {
1866    fn interpolate(&self, other: &Self, t: f64) -> Self {
1867        match &self {
1868            Self::Pixels(sp) => match other {
1869                Self::Pixels(op) => Self::Pixels(*sp + ((*op - *sp) * Numeric::F64(t))),
1870                Self::Percent(op) => Self::Percent(*op),
1871                Self::Combined(pix, per) => {
1872                    let pix = *sp + ((*pix - *sp) * Numeric::F64(t));
1873                    let per = *per;
1874                    Self::Combined(pix, per)
1875                }
1876            },
1877            Self::Percent(sp) => match other {
1878                Self::Pixels(op) => Self::Pixels(*op),
1879                Self::Percent(op) => Self::Percent(*sp + ((*op - *sp) * Numeric::F64(t))),
1880                Self::Combined(pix, per) => {
1881                    let pix = *pix;
1882                    let per = *sp + ((*per - *sp) * Numeric::F64(t));
1883                    Self::Combined(pix, per)
1884                }
1885            },
1886            Self::Combined(pix, per) => match other {
1887                Self::Pixels(op) => {
1888                    let pix = *pix + ((*op - *pix) * Numeric::F64(t));
1889                    Self::Combined(pix, *per)
1890                }
1891                Self::Percent(op) => {
1892                    let per = *per + ((*op - *per) * Numeric::F64(t));
1893                    Self::Combined(*pix, per)
1894                }
1895                Self::Combined(pix0, per0) => {
1896                    let pix = *pix + ((*pix0 - *pix) * Numeric::F64(t));
1897                    let per = *per + ((*per0 - *per) * Numeric::F64(t));
1898                    Self::Combined(pix, per)
1899                }
1900            },
1901        }
1902    }
1903}
1904
1905impl Default for Size {
1906    fn default() -> Self {
1907        Self::Percent(Numeric::F64(100.0))
1908    }
1909}
1910
1911impl Mul for Size {
1912    type Output = Size;
1913
1914    fn mul(self, rhs: Self) -> Self::Output {
1915        match self {
1916            Size::Pixels(pix0) => {
1917                match rhs {
1918                    //multiplying two pixel values adds them,
1919                    //in the sense of multiplying two affine translations.
1920                    //this might be wildly unexpected in some cases, so keep an eye on this and
1921                    //revisit whether to support Percent values in anchor calcs (could rescind)
1922                    Size::Pixels(pix1) => Size::Pixels(pix0 + pix1),
1923                    Size::Percent(per1) => Size::Pixels(pix0 * per1),
1924                    Size::Combined(pix1, per1) => Size::Pixels((pix0 * per1) + pix0 + pix1),
1925                }
1926            }
1927            Size::Percent(per0) => match rhs {
1928                Size::Pixels(pix1) => Size::Pixels(per0 * pix1),
1929                Size::Percent(per1) => Size::Percent(per0 * per1),
1930                Size::Combined(pix1, per1) => Size::Pixels((per0 * pix1) + (per0 * per1)),
1931            },
1932            Size::Combined(pix0, per0) => match rhs {
1933                Size::Pixels(pix1) => Size::Pixels((pix0 * per0) + pix1),
1934                Size::Percent(per1) => Size::Percent(pix0 * per0 * per1),
1935                Size::Combined(pix1, per1) => Size::Pixels((pix0 * per0) + (pix1 * per1)),
1936            },
1937        }
1938    }
1939}
1940
1941/// A sugary representation of an Affine transform+, including
1942/// `anchor` and `align` as layout-computed properties.
1943///
1944/// `translate` represents an (x,y) affine translation
1945/// `scale`     represents an (x,y) non-uniform affine scale
1946/// `rotate`    represents a (z) affine rotation (intuitive 2D rotation)
1947/// `anchor`    represents the "(0,0)" point of the render node as it relates to its own bounding box.
1948///             By default that's the top-left of the element, but `anchor` allows that
1949///             to be offset either by a pixel or percentage-of-element-size
1950///             for each of (x,y)
1951#[derive(Debug, Default, Clone, Deserialize, Serialize)]
1952pub struct Transform2D {
1953    /// Keeps track of a linked list of previous Transform2Ds, assembled e.g. via multiplication
1954    pub previous: Option<Box<Transform2D>>,
1955    /// Rotation is single-dimensional for 2D rendering, representing rotation over z axis
1956    pub rotate: Option<Rotation>,
1957    pub translate: Option<[Size; 2]>,
1958    pub anchor: Option<[Size; 2]>,
1959    pub scale: Option<[Size; 2]>,
1960    pub skew: Option<[Rotation; 2]>,
1961}
1962
1963impl Interpolatable for Transform2D {}
1964
1965impl Mul for Transform2D {
1966    type Output = Transform2D;
1967
1968    fn mul(self, rhs: Self) -> Self::Output {
1969        let mut ret = rhs.clone();
1970        ret.previous = Some(Box::new(self));
1971        ret
1972    }
1973}
1974
1975impl Transform2D {
1976    ///Scale coefficients (1.0 == 100%) over x-y plane
1977    pub fn scale(x: Size, y: Size) -> Self {
1978        let mut ret = Transform2D::default();
1979        ret.scale = Some([x, y]);
1980        ret
1981    }
1982    ///Rotation over z axis
1983    pub fn rotate(z: Rotation) -> Self {
1984        let mut ret = Transform2D::default();
1985        ret.rotate = Some(z);
1986        ret
1987    }
1988    ///Translation across x-y plane, pixels
1989    pub fn translate(x: Size, y: Size) -> Self {
1990        let mut ret = Transform2D::default();
1991        ret.translate = Some([x, y]);
1992        ret
1993    }
1994    ///Describe alignment of the (0,0) position of this element as it relates to its own bounding box
1995    pub fn anchor(x: Size, y: Size) -> Self {
1996        let mut ret = Transform2D::default();
1997        ret.anchor = Some([x, y]);
1998        ret
1999    }
2000}
2001
2002#[derive(Clone)]
2003pub struct Variable {
2004    untyped_property: UntypedProperty,
2005    convert_to_pax_value: Rc<dyn Fn(UntypedProperty) -> PaxValue>,
2006}
2007
2008impl Variable {
2009    pub fn new<T: PropertyValue + ToPaxValue>(untyped_property: UntypedProperty) -> Self {
2010        let closure = |untyped_property: UntypedProperty| {
2011            let property: Property<T> = Property::new_from_untyped(untyped_property.clone());
2012            property.get().to_pax_value()
2013        };
2014
2015        Variable {
2016            untyped_property,
2017            convert_to_pax_value: Rc::new(closure),
2018        }
2019    }
2020
2021    pub fn new_from_typed_property<T: PropertyValue + ToPaxValue>(property: Property<T>) -> Self {
2022        let untyped_property = property.untyped();
2023        let closure = |untyped_property: UntypedProperty| {
2024            let property: Property<T> = Property::new_from_untyped(untyped_property.clone());
2025            property.get().to_pax_value()
2026        };
2027
2028        Variable {
2029            untyped_property,
2030            convert_to_pax_value: Rc::new(closure),
2031        }
2032    }
2033
2034    pub fn get_untyped_property(&self) -> &UntypedProperty {
2035        &self.untyped_property
2036    }
2037    pub fn get_as_pax_value(&self) -> PaxValue {
2038        (self.convert_to_pax_value)(self.untyped_property.clone())
2039    }
2040}