Skip to main content

pax_runtime_api/
color.rs

1//! Color and opacity types used by Pax style properties and render backends.
2
3use super::*;
4
5/// Raw Percent type, which we use for serialization and dynamic traversal.  At the time
6/// of authoring, this type is not used directly at runtime, but is intended for `into` coercion
7/// into downstream types, e.g. ColorChannel, Rotation, and Size.  This allows us to be "dumb"
8/// about how we parse `%`, and allow the context in which it is used to pull forward a specific
9/// type through `into` inference.
10#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
11pub struct Percent(pub Numeric);
12
13impl Display for Percent {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        write!(f, "{}%", self.0)
16    }
17}
18
19impl Interpolatable for Percent {
20    fn interpolate(&self, other: &Self, t: f64) -> Self {
21        Self(self.0.interpolate(&other.0, t))
22    }
23}
24
25/// Describes an opacity value either as normalized alpha or as a percent.
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub enum Opacity {
28    /// Unitless alpha in the normalized [0.0, 1.0] range.
29    Alpha(Numeric),
30    /// Percent alpha in the [0.0, 100.0] range.
31    Percent(Numeric),
32}
33
34impl Display for Opacity {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Opacity::Alpha(value) => write!(f, "{}", value),
38            Opacity::Percent(value) => write!(f, "{}%", value),
39        }
40    }
41}
42
43impl Default for Opacity {
44    fn default() -> Self {
45        Self::Alpha(Numeric::F64(1.0))
46    }
47}
48
49impl Interpolatable for Opacity {
50    fn interpolate(&self, other: &Self, t: f64) -> Self {
51        Opacity::Alpha(Numeric::F64(
52            self.to_float_0_1().interpolate(&other.to_float_0_1(), t),
53        ))
54    }
55}
56
57impl From<f64> for Opacity {
58    fn from(value: f64) -> Self {
59        Numeric::F64(value).into()
60    }
61}
62
63impl From<i32> for Opacity {
64    fn from(value: i32) -> Self {
65        Numeric::from(value).into()
66    }
67}
68
69impl From<Numeric> for Opacity {
70    fn from(value: Numeric) -> Self {
71        Opacity::Alpha(value)
72    }
73}
74
75impl From<Percent> for Opacity {
76    fn from(value: Percent) -> Self {
77        Opacity::Percent(value.0)
78    }
79}
80
81impl Opacity {
82    /// Normalizes this Opacity as a float in [0.0, 1.0].
83    pub fn to_float_0_1(&self) -> f64 {
84        match self {
85            Opacity::Alpha(value) => value.to_float().clamp(0.0, 1.0),
86            Opacity::Percent(value) => (value.to_float() / 100.0).clamp(0.0, 1.0),
87        }
88    }
89}
90
91impl From<f64> for ColorChannel {
92    fn from(value: f64) -> Self {
93        Numeric::F64(value).into()
94    }
95}
96
97impl From<i32> for ColorChannel {
98    fn from(value: i32) -> Self {
99        Numeric::from(value).into()
100    }
101}
102
103impl Into<ColorChannel> for Percent {
104    fn into(self) -> ColorChannel {
105        ColorChannel::Percent(self.0)
106    }
107}
108
109impl Into<Size> for Percent {
110    fn into(self) -> Size {
111        Size::Percent(self.0)
112    }
113}
114
115impl Into<Rotation> for Percent {
116    fn into(self) -> Rotation {
117        Rotation::Percent(self.0)
118    }
119}
120
121/// Describes a color channel in a unit appropriate to the surrounding color model.
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
123pub enum ColorChannel {
124    /// Used, for example, to express hue in HSL or other rotational color models.
125    Rotation(Rotation),
126    /// Integer color channel in the `[0, 255]` range.
127    Integer(u8),
128    /// Percent color channel in the `[0.0, 100.0]` range.
129    Percent(Numeric),
130}
131
132impl Display for ColorChannel {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        match self {
135            ColorChannel::Rotation(rot) => write!(f, "{}", rot),
136            ColorChannel::Integer(int) => write!(f, "{}", int),
137            ColorChannel::Percent(per) => write!(f, "{}%", per),
138        }
139    }
140}
141
142impl Default for ColorChannel {
143    fn default() -> Self {
144        Self::Percent(Numeric::F64(50.0))
145    }
146}
147
148impl From<Numeric> for Rotation {
149    fn from(value: Numeric) -> Self {
150        Rotation::Degrees(value)
151    }
152}
153
154impl From<Numeric> for ColorChannel {
155    fn from(value: Numeric) -> Self {
156        Self::Integer(value.to_int().clamp(0, 255) as u8)
157    }
158}
159
160impl ColorChannel {
161    /// Normalizes this color channel as a float in the `[0.0, 1.0]` range.
162    pub fn to_float_0_1(&self) -> f64 {
163        match self {
164            Self::Percent(per) => (per.to_float() / 100.0).clamp(0_f64, 1_f64),
165            Self::Integer(zero_to_255) => {
166                let f_zero = (*zero_to_255) as f64;
167                (f_zero / 255.0_f64).clamp(0_f64, 1_f64)
168            }
169            Self::Rotation(rot) => rot.to_float_0_1(),
170        }
171    }
172}
173
174/// Entrypoint for specifying and representing colors in Pax.
175#[allow(non_camel_case_types)]
176#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq)]
177pub enum Color {
178    /// Models a color in the RGB space, with an alpha channel of 100%
179    rgb(ColorChannel, ColorChannel, ColorChannel),
180    /// Models a color in the RGBA space
181    rgba(ColorChannel, ColorChannel, ColorChannel, ColorChannel),
182    /// Models a color in the HSL space, with an alpha channel of 100%
183    hsl(Rotation, ColorChannel, ColorChannel),
184    /// Models a color in the HSLA space.
185    hsla(Rotation, ColorChannel, ColorChannel, ColorChannel),
186
187    /// Cool blue-gray, RGB (100, 116, 139).
188    #[default]
189    SLATE,
190    /// Balanced gray, RGB (107, 114, 128).
191    GRAY,
192    /// Crisp cool gray, RGB (113, 113, 122).
193    ZINC,
194    /// Plain neutral gray, RGB (115, 115, 115).
195    NEUTRAL,
196    /// Warm stone gray, RGB (120, 113, 108).
197    STONE,
198    /// Bright warm red, RGB (239, 68, 68).
199    RED,
200    /// Vivid citrus orange, RGB (249, 115, 22).
201    ORANGE,
202    /// Golden amber, RGB (245, 158, 11).
203    AMBER,
204    /// Sunny yellow, RGB (234, 179, 8).
205    YELLOW,
206    /// Electric lime, RGB (132, 204, 22).
207    LIME,
208    /// Fresh green, RGB (34, 197, 94).
209    GREEN,
210    /// Jewel emerald, RGB (16, 185, 129).
211    EMERALD,
212    /// Deep aquatic teal, RGB (20, 184, 166).
213    TEAL,
214    /// Bright clean cyan, RGB (6, 182, 212).
215    CYAN,
216    /// Open sky blue, RGB (14, 165, 233).
217    SKY,
218    /// Saturated primary blue, RGB (59, 130, 246).
219    BLUE,
220    /// Cool electric indigo, RGB (99, 102, 241).
221    INDIGO,
222    /// Soft vivid violet, RGB (139, 92, 246).
223    VIOLET,
224    /// Rich playful purple, RGB (168, 85, 247).
225    PURPLE,
226    /// Brilliant magenta fuchsia, RGB (217, 70, 239).
227    FUCHSIA,
228    /// Bright candy pink, RGB (236, 72, 153).
229    PINK,
230    /// Warm rosy red, RGB (244, 63, 94).
231    ROSE,
232    /// Pure black, RGB (0, 0, 0).
233    BLACK,
234    /// Pure white, RGB (255, 255, 255).
235    WHITE,
236    /// Fully transparent white, RGB (255, 255, 255).
237    TRANSPARENT,
238    /// Non-rendering transparent, RGB (255, 255, 255).
239    NONE,
240}
241
242impl Hash for Color {
243    fn hash<H: Hasher>(&self, state: &mut H) {
244        core::mem::discriminant(self).hash(state);
245    }
246}
247
248// implement display respecting all color enum variants, printing out their names
249impl Display for Color {
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        match self {
252            Self::rgb(r, g, b) => write!(f, "rgb({}, {}, {})", r, g, b),
253            Self::rgba(r, g, b, a) => write!(f, "rgba({}, {}, {}, {})", r, g, b, a),
254            Self::hsl(h, s, l) => write!(f, "hsl({}, {}, {})", h, s, l),
255            Self::hsla(h, s, l, a) => write!(f, "hsla({}, {}, {}, {})", h, s, l, a),
256            Self::SLATE => write!(f, "SLATE"),
257            Self::GRAY => write!(f, "GRAY"),
258            Self::ZINC => write!(f, "ZINC"),
259            Self::NEUTRAL => write!(f, "NEUTRAL"),
260            Self::STONE => write!(f, "STONE"),
261            Self::RED => write!(f, "RED"),
262            Self::ORANGE => write!(f, "ORANGE"),
263            Self::AMBER => write!(f, "AMBER"),
264            Self::YELLOW => write!(f, "YELLOW"),
265            Self::LIME => write!(f, "LIME"),
266            Self::GREEN => write!(f, "GREEN"),
267            Self::EMERALD => write!(f, "EMERALD"),
268            Self::TEAL => write!(f, "TEAL"),
269            Self::CYAN => write!(f, "CYAN"),
270            Self::SKY => write!(f, "SKY"),
271            Self::BLUE => write!(f, "BLUE"),
272            Self::INDIGO => write!(f, "INDIGO"),
273            Self::VIOLET => write!(f, "VIOLET"),
274            Self::PURPLE => write!(f, "PURPLE"),
275            Self::FUCHSIA => write!(f, "FUCHSIA"),
276            Self::PINK => write!(f, "PINK"),
277            Self::ROSE => write!(f, "ROSE"),
278            Self::BLACK => write!(f, "BLACK"),
279            Self::WHITE => write!(f, "WHITE"),
280            Self::TRANSPARENT => write!(f, "TRANSPARENT"),
281            Self::NONE => write!(f, "NONE"),
282        }
283    }
284}
285
286impl Color {
287    //TODO: build out tint api and consider other color transforms
288    //pub fn tint(tint_offset_amount) -> Self {...}
289
290    /// Constructs an RGB color with 100% alpha.
291    pub fn rgb(r: ColorChannel, g: ColorChannel, b: ColorChannel) -> Self {
292        Self::rgb(r, g, b)
293    }
294
295    /// Constructs an RGBA color.
296    pub fn rgba(r: ColorChannel, g: ColorChannel, b: ColorChannel, a: ColorChannel) -> Self {
297        Self::rgba(r, g, b, a)
298    }
299
300    /// Constructs an HSL color with 100% alpha.
301    pub fn hsl(h: Rotation, s: ColorChannel, l: ColorChannel) -> Self {
302        Self::hsl(h, s, l)
303    }
304
305    /// Constructs an HSLA color.
306    pub fn hsla(h: Rotation, s: ColorChannel, l: ColorChannel, a: ColorChannel) -> Self {
307        Self::hsla(h, s, l, a)
308    }
309
310    // Converts to the Piet color type used by canvas renderers.
311    pub fn to_piet_color(&self) -> piet::Color {
312        let rgba = self.to_rgba_0_1();
313        piet::Color::rgba(rgba[0], rgba[1], rgba[2], rgba[3])
314    }
315
316    /// Constructs a color from normalized RGBA channels in the `[0.0, 1.0]` range.
317    pub fn from_rgba_0_1(rgba_0_1: [f64; 4]) -> Self {
318        Self::rgba(
319            ColorChannel::Percent(Numeric::F64(rgba_0_1[0] * 100.0)),
320            ColorChannel::Percent(Numeric::F64(rgba_0_1[1] * 100.0)),
321            ColorChannel::Percent(Numeric::F64(rgba_0_1[2] * 100.0)),
322            ColorChannel::Percent(Numeric::F64(rgba_0_1[3] * 100.0)),
323        )
324    }
325
326    /// Constructs a color from a six- or eight-character RGB/RGBA hex string.
327    pub fn from_hex(hex: &str) -> Self {
328        let r = u8::from_str_radix(&hex[0..2], 16).unwrap() as f64 / 255.0;
329        let g = u8::from_str_radix(&hex[2..4], 16).unwrap() as f64 / 255.0;
330        let b = u8::from_str_radix(&hex[4..6], 16).unwrap() as f64 / 255.0;
331        let a = if hex.len() == 8 {
332            u8::from_str_radix(&hex[6..8], 16).unwrap() as f64 / 255.0
333        } else {
334            1.0
335        };
336        Self::rgba(
337            ColorChannel::Percent(Numeric::F64(r * 100.0)),
338            ColorChannel::Percent(Numeric::F64(g * 100.0)),
339            ColorChannel::Percent(Numeric::F64(b * 100.0)),
340            ColorChannel::Percent(Numeric::F64(a * 100.0)),
341        )
342    }
343
344    /// Returns this color's alpha channel normalized to the `[0.0, 1.0]` range.
345    pub fn alpha_0_1(&self) -> f64 {
346        self.to_rgba_0_1()[3]
347    }
348
349    /// Multiplies this color's alpha channel by `factor`.
350    pub fn with_alpha_factor(&self, factor: f64) -> Self {
351        let mut rgba = self.to_rgba_0_1();
352        rgba[3] = (rgba[3] * factor.clamp(0.0, 1.0)).clamp(0.0, 1.0);
353        Self::from_rgba_0_1(rgba)
354    }
355
356    /// Returns RGBA channels normalized to the `[0.0, 1.0]` range.
357    pub fn to_rgba_0_1(&self) -> [f64; 4] {
358        match self {
359            Self::hsla(h, s, l, a) => {
360                let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
361                [rgb[0], rgb[1], rgb[2], a.to_float_0_1()]
362            }
363            Self::hsl(h, s, l) => {
364                let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
365                [rgb[0], rgb[1], rgb[2], 1.0]
366            }
367            Self::rgba(r, g, b, a) => [
368                r.to_float_0_1(),
369                g.to_float_0_1(),
370                b.to_float_0_1(),
371                a.to_float_0_1(),
372            ],
373            Self::rgb(r, g, b) => [r.to_float_0_1(), g.to_float_0_1(), b.to_float_0_1(), 1.0],
374
375            //Color constants from TailwindCSS
376            Self::SLATE => Self::rgb(
377                Numeric::from(0x64).into(),
378                Numeric::from(0x74).into(),
379                Numeric::from(0x8b).into(),
380            )
381            .to_rgba_0_1(),
382            Self::GRAY => Self::rgb(
383                Numeric::from(0x6b).into(),
384                Numeric::from(0x72).into(),
385                Numeric::from(0x80).into(),
386            )
387            .to_rgba_0_1(),
388            Self::ZINC => Self::rgb(
389                Numeric::from(0x71).into(),
390                Numeric::from(0x71).into(),
391                Numeric::from(0x7a).into(),
392            )
393            .to_rgba_0_1(),
394            Self::NEUTRAL => Self::rgb(
395                Numeric::from(0x73).into(),
396                Numeric::from(0x73).into(),
397                Numeric::from(0x73).into(),
398            )
399            .to_rgba_0_1(),
400            Self::STONE => Self::rgb(
401                Numeric::from(0x78).into(),
402                Numeric::from(0x71).into(),
403                Numeric::from(0x6c).into(),
404            )
405            .to_rgba_0_1(),
406            Self::RED => Self::rgb(
407                Numeric::from(0xeF).into(),
408                Numeric::from(0x44).into(),
409                Numeric::from(0x44).into(),
410            )
411            .to_rgba_0_1(),
412            Self::ORANGE => Self::rgb(
413                Numeric::from(0xf9).into(),
414                Numeric::from(0x73).into(),
415                Numeric::from(0x16).into(),
416            )
417            .to_rgba_0_1(),
418            Self::AMBER => Self::rgb(
419                Numeric::from(0xf5).into(),
420                Numeric::from(0x9e).into(),
421                Numeric::from(0x0b).into(),
422            )
423            .to_rgba_0_1(),
424            Self::YELLOW => Self::rgb(
425                Numeric::from(0xea).into(),
426                Numeric::from(0xb3).into(),
427                Numeric::from(0x08).into(),
428            )
429            .to_rgba_0_1(),
430            Self::LIME => Self::rgb(
431                Numeric::from(0x84).into(),
432                Numeric::from(0xcc).into(),
433                Numeric::from(0x16).into(),
434            )
435            .to_rgba_0_1(),
436            Self::GREEN => Self::rgb(
437                Numeric::from(0x22).into(),
438                Numeric::from(0xc5).into(),
439                Numeric::from(0x5e).into(),
440            )
441            .to_rgba_0_1(),
442            Self::EMERALD => Self::rgb(
443                Numeric::from(0x10).into(),
444                Numeric::from(0xb9).into(),
445                Numeric::from(0x81).into(),
446            )
447            .to_rgba_0_1(),
448            Self::TEAL => Self::rgb(
449                Numeric::from(0x14).into(),
450                Numeric::from(0xb8).into(),
451                Numeric::from(0xa6).into(),
452            )
453            .to_rgba_0_1(),
454            Self::CYAN => Self::rgb(
455                Numeric::from(0x06).into(),
456                Numeric::from(0xb6).into(),
457                Numeric::from(0xd4).into(),
458            )
459            .to_rgba_0_1(),
460            Self::SKY => Self::rgb(
461                Numeric::from(0x0e).into(),
462                Numeric::from(0xa5).into(),
463                Numeric::from(0xe9).into(),
464            )
465            .to_rgba_0_1(),
466            Self::BLUE => Self::rgb(
467                Numeric::from(0x3b).into(),
468                Numeric::from(0x82).into(),
469                Numeric::from(0xf6).into(),
470            )
471            .to_rgba_0_1(),
472            Self::INDIGO => Self::rgb(
473                Numeric::from(0x63).into(),
474                Numeric::from(0x66).into(),
475                Numeric::from(0xf1).into(),
476            )
477            .to_rgba_0_1(),
478            Self::VIOLET => Self::rgb(
479                Numeric::from(0x8b).into(),
480                Numeric::from(0x5c).into(),
481                Numeric::from(0xf6).into(),
482            )
483            .to_rgba_0_1(),
484            Self::PURPLE => Self::rgb(
485                Numeric::from(0xa8).into(),
486                Numeric::from(0x55).into(),
487                Numeric::from(0xf7).into(),
488            )
489            .to_rgba_0_1(),
490            Self::FUCHSIA => Self::rgb(
491                Numeric::from(0xd9).into(),
492                Numeric::from(0x46).into(),
493                Numeric::from(0xef).into(),
494            )
495            .to_rgba_0_1(),
496            Self::PINK => Self::rgb(
497                Numeric::from(0xec).into(),
498                Numeric::from(0x48).into(),
499                Numeric::from(0x99).into(),
500            )
501            .to_rgba_0_1(),
502            Self::ROSE => Self::rgb(
503                Numeric::from(0xf4).into(),
504                Numeric::from(0x3f).into(),
505                Numeric::from(0x5e).into(),
506            )
507            .to_rgba_0_1(),
508            Self::BLACK => Self::rgb(
509                Numeric::from(0x00).into(),
510                Numeric::from(0x00).into(),
511                Numeric::from(0x00).into(),
512            )
513            .to_rgba_0_1(),
514            Self::WHITE => Self::rgb(
515                Numeric::from(0xff).into(),
516                Numeric::from(0xff).into(),
517                Numeric::from(0xff).into(),
518            )
519            .to_rgba_0_1(),
520            Self::TRANSPARENT | Self::NONE => Self::rgba(
521                Numeric::from(0xff).into(),
522                Numeric::from(0xff).into(),
523                Numeric::from(0xFF).into(),
524                Numeric::from(0x00).into(),
525            )
526            .to_rgba_0_1(),
527        }
528    }
529
530    /// Returns HSLA channels normalized to the `[0.0, 1.0]` range.
531    // Credit: Claude 3.5 Sonnet.
532    pub fn to_hsla_0_1(&self) -> [f64; 4] {
533        let [r, g, b, a] = self.to_rgba_0_1();
534
535        let max = r.max(g).max(b);
536        let min = r.min(g).min(b);
537        let chroma = max - min;
538
539        let h = if chroma == 0.0 {
540            0.0
541        } else if max == r {
542            ((g - b) / chroma + 6.0) % 6.0 / 6.0
543        } else if max == g {
544            ((b - r) / chroma + 2.0) / 6.0
545        } else {
546            ((r - g) / chroma + 4.0) / 6.0
547        };
548
549        let l = (max + min) / 2.0;
550
551        let s = if l == 0.0 || l == 1.0 {
552            0.0
553        } else {
554            (max - l) / l.min(1.0 - l)
555        };
556
557        [h, s, l, a]
558    }
559}
560
561//hsl_to_rgb logic borrowed & modified from https://github.com/emgyrz/colorsys.rs, licensed MIT Copyright (c) 2019 mz <emgyrz@gmail.com>
562const RGB_UNIT_MAX: f64 = 255.0;
563fn hsl_to_rgb(h: f64, s: f64, l: f64) -> [f64; 3] {
564    if s == 0.0 {
565        let unit = RGB_UNIT_MAX * l;
566        return [
567            unit / RGB_UNIT_MAX,
568            unit / RGB_UNIT_MAX,
569            unit / RGB_UNIT_MAX,
570        ];
571    }
572
573    let temp1 = if l < 0.5 {
574        l * (1.0 + s)
575    } else {
576        l + s - l * s
577    };
578
579    let temp2 = 2.0 * l - temp1;
580    let hue = h;
581
582    let temp_r = bound(hue + (1.0 / 3.0), 1.0);
583    let temp_g = bound(hue, 1.0);
584    let temp_b = bound(hue - (1.0 / 3.0), 1.0);
585
586    let r = calc_rgb_unit(temp_r, temp1, temp2);
587    let g = calc_rgb_unit(temp_g, temp1, temp2);
588    let b = calc_rgb_unit(temp_b, temp1, temp2);
589    [r / RGB_UNIT_MAX, g / RGB_UNIT_MAX, b / RGB_UNIT_MAX]
590}
591
592fn calc_rgb_unit(unit: f64, temp1: f64, temp2: f64) -> f64 {
593    let mut result = temp2;
594    if 6.0 * unit < 1.0 {
595        result = temp2 + (temp1 - temp2) * 6.0 * unit
596    } else if 2.0 * unit < 1.0 {
597        result = temp1
598    } else if 3.0 * unit < 2.0 {
599        result = temp2 + (temp1 - temp2) * ((2.0 / 3.0) - unit) * 6.0
600    }
601    result * RGB_UNIT_MAX
602}
603
604// Wraps a scalar into the `[0, entire]` interval, used by HSL conversion.
605pub fn bound(r: f64, entire: f64) -> f64 {
606    let mut n = r;
607    loop {
608        let less = n < 0.0;
609        let bigger = n > entire;
610        if !less && !bigger {
611            break n;
612        }
613        if less {
614            n += entire;
615        } else {
616            n -= entire;
617        }
618    }
619}
620
621impl Into<ColorMessage> for &Color {
622    fn into(self) -> ColorMessage {
623        let rgba = self.to_rgba_0_1();
624        ColorMessage::Rgba(rgba)
625    }
626}
627impl PartialEq<ColorMessage> for Color {
628    fn eq(&self, other: &ColorMessage) -> bool {
629        let self_rgba = self.to_rgba_0_1();
630
631        match other {
632            ColorMessage::Rgb(other_rgba) => {
633                self_rgba[0] == other_rgba[0]
634                    && self_rgba[1] == other_rgba[1]
635                    && self_rgba[2] == other_rgba[2]
636                    && self_rgba[3] == 1.0
637            }
638            ColorMessage::Rgba(other_rgba) => {
639                self_rgba[0] == other_rgba[0]
640                    && self_rgba[1] == other_rgba[1]
641                    && self_rgba[2] == other_rgba[2]
642                    && self_rgba[3] == other_rgba[3]
643            }
644        }
645    }
646}
647impl Interpolatable for Color {
648    fn interpolate(&self, other: &Self, t: f64) -> Self {
649        let rgba_s = self.to_rgba_0_1();
650        let rgba_o = other.to_rgba_0_1();
651        let rgba_i = [
652            rgba_s[0].interpolate(&rgba_o[0], t),
653            rgba_s[1].interpolate(&rgba_o[1], t),
654            rgba_s[2].interpolate(&rgba_o[2], t),
655            rgba_s[3].interpolate(&rgba_o[3], t),
656        ];
657        Color::from_rgba_0_1(rgba_i)
658    }
659}