1use std::ops::{Add, Sub};
4
5use palette::{FromColor, Hsl, Hsv, LinSrgba, Srgba, WithAlpha};
6
7pub use kurbo::Affine;
8use kurbo::{Point, Vec2};
9
10#[derive(Clone, Copy, Debug, Default, PartialEq)]
11pub struct Size {
12 pub width: f32,
13 pub height: f32,
14}
15
16impl Size {
17 pub const ZERO: Self = Self {
18 width: 0.0,
19 height: 0.0,
20 };
21
22 #[must_use]
23 pub fn new(width: f32, height: f32) -> Self {
24 assert!(
25 width.is_finite() && height.is_finite() && width >= 0.0 && height >= 0.0,
26 "sizes must be finite and non-negative"
27 );
28 Self { width, height }
29 }
30}
31
32#[derive(Clone, Copy, Debug, Default, PartialEq)]
33pub struct Offset {
34 pub x: f32,
35 pub y: f32,
36}
37
38impl Offset {
39 pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
40
41 #[must_use]
42 pub const fn new(x: f32, y: f32) -> Self {
43 Self { x, y }
44 }
45
46 #[must_use]
48 pub fn finite_or_zero(self) -> Self {
49 Self::new(finite_or_zero(self.x), finite_or_zero(self.y))
50 }
51}
52
53#[inline]
56#[must_use]
57pub fn finite_or_zero(value: f32) -> f32 {
58 if value.is_finite() { value } else { 0.0 }
59}
60
61#[inline]
63#[must_use]
64pub fn finite_non_negative(value: f32) -> f32 {
65 finite_or_zero(value).max(0.0)
66}
67
68impl Add for Offset {
69 type Output = Self;
70
71 fn add(self, rhs: Self) -> Self {
72 Self::new(self.x + rhs.x, self.y + rhs.y)
73 }
74}
75
76impl Sub for Offset {
77 type Output = Self;
78
79 fn sub(self, rhs: Self) -> Self {
80 Self::new(self.x - rhs.x, self.y - rhs.y)
81 }
82}
83
84#[derive(Clone, Copy, Debug, Default, PartialEq)]
85pub struct Rect {
86 pub origin: Offset,
87 pub size: Size,
88}
89
90impl Rect {
91 #[must_use]
92 pub fn from_origin_size(origin: Offset, size: Size) -> Self {
93 Self { origin, size }
94 }
95
96 #[must_use]
97 pub fn contains(self, point: Offset) -> bool {
98 point.x >= self.origin.x
99 && point.x <= self.origin.x + self.size.width
100 && point.y >= self.origin.y
101 && point.y <= self.origin.y + self.size.height
102 }
103
104 #[must_use]
105 pub fn intersects(self, other: Self) -> bool {
106 self.origin.x < other.origin.x + other.size.width
107 && other.origin.x < self.origin.x + self.size.width
108 && self.origin.y < other.origin.y + other.size.height
109 && other.origin.y < self.origin.y + self.size.height
110 }
111
112 #[must_use]
113 pub fn intersection(self, other: Self) -> Option<Self> {
114 let left = self.origin.x.max(other.origin.x);
115 let top = self.origin.y.max(other.origin.y);
116 let right = (self.origin.x + self.size.width).min(other.origin.x + other.size.width);
117 let bottom = (self.origin.y + self.size.height).min(other.origin.y + other.size.height);
118 (right >= left && bottom >= top).then(|| {
119 Self::from_origin_size(
120 Offset::new(left, top),
121 Size::new(right - left, bottom - top),
122 )
123 })
124 }
125}
126
127#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
128pub struct Color {
129 pub red: u8,
130 pub green: u8,
131 pub blue: u8,
132 pub alpha: u8,
133}
134
135impl Color {
136 pub const TRANSPARENT: Self = Self::rgba(0, 0, 0, 0);
137 pub const WHITE: Self = Self::rgba(255, 255, 255, 255);
138 pub const BLACK: Self = Self::rgba(0, 0, 0, 255);
139
140 #[must_use]
141 pub const fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
142 Self {
143 red,
144 green,
145 blue,
146 alpha,
147 }
148 }
149
150 #[must_use]
154 pub fn to_linear_rgba(self) -> [f32; 4] {
155 let linear = Srgba::new(
156 self.red as f32 / 255.0,
157 self.green as f32 / 255.0,
158 self.blue as f32 / 255.0,
159 self.alpha as f32 / 255.0,
160 )
161 .into_linear();
162 [
163 linear.color.red,
164 linear.color.green,
165 linear.color.blue,
166 linear.alpha,
167 ]
168 }
169
170 #[must_use]
175 pub fn from_linear_rgba([red, green, blue, alpha]: [f32; 4]) -> Self {
176 let encoded = Srgba::<u8>::from_linear(LinSrgba::new(
177 unit(red),
178 unit(green),
179 unit(blue),
180 unit(alpha),
181 ));
182 Self::rgba(
183 encoded.color.red,
184 encoded.color.green,
185 encoded.color.blue,
186 encoded.alpha,
187 )
188 }
189
190 #[must_use]
193 pub fn to_hsl(self) -> HslColor {
194 HslColor::from_color(self)
195 }
196
197 #[must_use]
200 pub fn to_hsv(self) -> HsvColor {
201 HsvColor::from_color(self)
202 }
203}
204
205#[derive(Clone, Copy, Debug, PartialEq)]
211pub struct HslColor {
212 pub hue: f32,
213 pub saturation: f32,
214 pub lightness: f32,
215 pub alpha: f32,
216}
217
218impl HslColor {
219 #[must_use]
220 pub fn new(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Self {
221 Self {
222 hue: normalize_hue(hue),
223 saturation: unit(saturation),
224 lightness: unit(lightness),
225 alpha: unit(alpha),
226 }
227 }
228
229 #[must_use]
230 pub fn from_color(color: Color) -> Self {
231 let source = palette_color(color);
232 let hsl = Hsl::from_color(source.color);
233 Self::new(
234 hsl.hue.into_degrees(),
235 hsl.saturation,
236 hsl.lightness,
237 source.alpha,
238 )
239 }
240
241 #[must_use]
242 pub fn to_color(self) -> Color {
243 let hsl = Hsl::new_srgb(
244 normalize_hue(self.hue),
245 unit(self.saturation),
246 unit(self.lightness),
247 );
248 color_from_palette(Srgba::from_color(hsl).with_alpha(unit(self.alpha)))
249 }
250}
251
252#[derive(Clone, Copy, Debug, PartialEq)]
254pub struct HsvColor {
255 pub hue: f32,
256 pub saturation: f32,
257 pub value: f32,
258 pub alpha: f32,
259}
260
261impl HsvColor {
262 #[must_use]
263 pub fn new(hue: f32, saturation: f32, value: f32, alpha: f32) -> Self {
264 Self {
265 hue: normalize_hue(hue),
266 saturation: unit(saturation),
267 value: unit(value),
268 alpha: unit(alpha),
269 }
270 }
271
272 #[must_use]
273 pub fn from_color(color: Color) -> Self {
274 let source = palette_color(color);
275 let hsv = Hsv::from_color(source.color);
276 Self::new(
277 hsv.hue.into_degrees(),
278 hsv.saturation,
279 hsv.value,
280 source.alpha,
281 )
282 }
283
284 #[must_use]
285 pub fn to_color(self) -> Color {
286 let hsv = Hsv::new_srgb(
287 normalize_hue(self.hue),
288 unit(self.saturation),
289 unit(self.value),
290 );
291 color_from_palette(Srgba::from_color(hsv).with_alpha(unit(self.alpha)))
292 }
293}
294
295fn unit(value: f32) -> f32 {
296 if value.is_finite() {
297 value.clamp(0.0, 1.0)
298 } else {
299 0.0
300 }
301}
302
303fn normalize_hue(hue: f32) -> f32 {
304 if hue.is_finite() {
305 hue.rem_euclid(360.0)
306 } else {
307 0.0
308 }
309}
310
311fn palette_color(color: Color) -> Srgba {
312 Srgba::new(
313 color.red as f32 / 255.0,
314 color.green as f32 / 255.0,
315 color.blue as f32 / 255.0,
316 color.alpha as f32 / 255.0,
317 )
318}
319
320fn color_from_palette(color: Srgba) -> Color {
321 let byte = |value: f32| (unit(value) * 255.0).round() as u8;
322 Color::rgba(
323 byte(color.color.red),
324 byte(color.color.green),
325 byte(color.color.blue),
326 byte(color.alpha),
327 )
328}
329
330#[derive(Clone, Copy, Debug, PartialEq)]
337pub struct Transform(Affine);
338
339impl Default for Transform {
340 fn default() -> Self {
341 Self::IDENTITY
342 }
343}
344
345impl Transform {
346 pub const IDENTITY: Self = Self(Affine::IDENTITY);
347
348 #[must_use]
349 pub fn translation(offset: Offset) -> Self {
350 Self(Affine::translate(Vec2::new(
351 f64::from(offset.x),
352 f64::from(offset.y),
353 )))
354 }
355
356 #[must_use]
357 pub const fn scale(scale: f32) -> Self {
358 Self(Affine::scale(scale as f64))
359 }
360
361 #[must_use]
362 pub const fn scale_non_uniform(x: f32, y: f32) -> Self {
363 Self(Affine::scale_non_uniform(x as f64, y as f64))
364 }
365
366 #[must_use]
368 pub fn rotation(radians: f32) -> Self {
369 Self(Affine::rotate(f64::from(radians)))
370 }
371
372 #[must_use]
373 pub const fn skew(x: f32, y: f32) -> Self {
374 Self(Affine::skew(x as f64, y as f64))
375 }
376
377 #[must_use]
381 pub const fn from_coefficients(coefficients: [f64; 6]) -> Self {
382 Self(Affine::new(coefficients))
383 }
384
385 #[must_use]
386 pub const fn from_kurbo(affine: Affine) -> Self {
387 Self(affine)
388 }
389
390 #[must_use]
391 pub const fn to_kurbo(self) -> Affine {
392 self.0
393 }
394
395 #[must_use]
397 pub fn then(self, other: Self) -> Self {
398 Self(self.0 * other.0)
399 }
400
401 #[must_use]
402 pub fn inverse(self) -> Option<Self> {
403 let inverse = self.0.inverse();
404 inverse.is_finite().then_some(Self(inverse))
405 }
406
407 #[must_use]
408 pub fn inverse_translation(self) -> Self {
409 self.inverse().unwrap_or(Self::IDENTITY)
410 }
411
412 #[must_use]
413 pub fn translation_offset(self) -> Offset {
414 let coefficients = self.0.as_coeffs();
415 Offset::new(coefficients[4] as f32, coefficients[5] as f32)
416 }
417
418 #[must_use]
419 pub fn is_translation(self) -> bool {
420 let [a, b, c, d, _, _] = self.0.as_coeffs();
421 a == 1. && b == 0. && c == 0. && d == 1.
422 }
423
424 #[must_use]
435 pub fn magnification_bound(self) -> f32 {
436 let [a, b, c, d, _, _] = self.0.as_coeffs();
437 let bound = a.hypot(b).hypot(c.hypot(d));
438 if !bound.is_finite() {
439 f32::INFINITY
440 } else if bound == 0.0 {
441 0.0
444 } else {
445 (bound as f32).next_up()
446 }
447 }
448
449 #[must_use]
450 pub fn transform_point(self, point: Offset) -> Offset {
451 let point = self.0 * Point::new(f64::from(point.x), f64::from(point.y));
452 Offset::new(point.x as f32, point.y as f32)
453 }
454
455 #[must_use]
456 pub fn inverse_transform_point(self, point: Offset) -> Option<Offset> {
457 self.inverse().map(|inverse| inverse.transform_point(point))
458 }
459
460 #[must_use]
461 pub fn transform_rect_bbox(self, rect: Rect) -> Rect {
462 let rect = kurbo::Rect::new(
463 f64::from(rect.origin.x),
464 f64::from(rect.origin.y),
465 f64::from(rect.origin.x + rect.size.width),
466 f64::from(rect.origin.y + rect.size.height),
467 );
468 let rect = self.0.transform_rect_bbox(rect);
469 Rect::from_origin_size(
470 Offset::new(rect.x0 as f32, rect.y0 as f32),
471 Size::new((rect.x1 - rect.x0) as f32, (rect.y1 - rect.y0) as f32),
472 )
473 }
474}
475
476bitflags::bitflags! {
477 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
479 pub struct Invalidation: u16 {
480 const NONE = 0;
482 const BUILD = 1 << 0;
484 const LAYOUT = 1 << 1;
486 const PAINT = 1 << 2;
488 const COMPOSITE = 1 << 3;
490 const SEMANTICS = 1 << 4;
492 const HIT_TEST = 1 << 5;
494 }
495}
496
497pub type DirtyFlags = Invalidation;
499
500pub trait Lerp {
502 #[must_use]
504 fn lerp(&self, other: &Self, t: f32) -> Self;
505}
506
507impl Lerp for f32 {
508 fn lerp(&self, other: &Self, t: f32) -> Self {
509 self + (other - self) * t
510 }
511}
512
513impl Lerp for f64 {
514 fn lerp(&self, other: &Self, t: f32) -> Self {
515 self + (other - self) * f64::from(t)
516 }
517}
518
519impl Lerp for Offset {
520 fn lerp(&self, other: &Self, t: f32) -> Self {
521 Self {
522 x: self.x.lerp(&other.x, t),
523 y: self.y.lerp(&other.y, t),
524 }
525 }
526}
527
528impl Lerp for Size {
529 fn lerp(&self, other: &Self, t: f32) -> Self {
530 Self {
531 width: self.width.lerp(&other.width, t).max(0.0),
532 height: self.height.lerp(&other.height, t).max(0.0),
533 }
534 }
535}
536
537impl Lerp for Transform {
543 fn lerp(&self, other: &Self, t: f32) -> Self {
544 let t = f64::from(t);
545 let (begin_translation, begin_rotation, begin_scale) = decompose(self.0);
546 let (end_translation, end_rotation, end_scale) = decompose(other.0);
547 let mix = |a: f64, b: f64| a * (1.0 - t) + b * t;
548 let translation = [
549 mix(begin_translation[0], end_translation[0]),
550 mix(begin_translation[1], end_translation[1]),
551 ];
552 let mut rotation = [0.0; 4];
553 for (index, value) in rotation.iter_mut().enumerate() {
554 *value = mix(begin_rotation[index], end_rotation[index]);
555 }
556 let length = rotation
557 .iter()
558 .map(|value| value * value)
559 .sum::<f64>()
560 .sqrt();
561 if length != 0.0 {
562 for value in &mut rotation {
563 *value /= length;
564 }
565 }
566 let scale = [
567 mix(begin_scale[0], end_scale[0]),
568 mix(begin_scale[1], end_scale[1]),
569 ];
570 Self(compose(translation, rotation, scale))
571 }
572}
573
574fn decompose(affine: Affine) -> ([f64; 2], [f64; 4], [f64; 2]) {
577 let [a, b, c, d, e, f] = affine.as_coeffs();
578 let mut sx = a.hypot(b);
579 let sy = c.hypot(d);
580 if a * d - b * c < 0.0 {
581 sx = -sx;
582 }
583 let m = [
586 [a / sx, c / sy, 0.0],
587 [b / sx, d / sy, 0.0],
588 [0.0, 0.0, 1.0],
589 ];
590 ([e, f], quaternion_from_rotation(m), [sx, sy])
591}
592
593fn quaternion_from_rotation(m: [[f64; 3]; 3]) -> [f64; 4] {
595 let mut q = [0.0; 4];
596 let trace = m[0][0] + m[1][1] + m[2][2];
597 if trace > 0.0 {
598 let mut s = (trace + 1.0).sqrt();
599 q[3] = s * 0.5;
600 s = 0.5 / s;
601 q[0] = (m[2][1] - m[1][2]) * s;
602 q[1] = (m[0][2] - m[2][0]) * s;
603 q[2] = (m[1][0] - m[0][1]) * s;
604 } else {
605 let i = if m[0][0] < m[1][1] {
606 if m[1][1] < m[2][2] { 2 } else { 1 }
607 } else if m[0][0] < m[2][2] {
608 2
609 } else {
610 0
611 };
612 let j = (i + 1) % 3;
613 let k = (i + 2) % 3;
614 let mut s = (m[i][i] - m[j][j] - m[k][k] + 1.0).sqrt();
615 q[i] = s * 0.5;
616 s = 0.5 / s;
617 q[3] = (m[k][j] - m[j][k]) * s;
618 q[j] = (m[j][i] + m[i][j]) * s;
619 q[k] = (m[k][i] + m[i][k]) * s;
620 }
621 q
622}
623
624fn compose(translation: [f64; 2], rotation: [f64; 4], scale: [f64; 2]) -> Affine {
626 let [x, y, z, w] = rotation;
627 let (x2, y2, z2) = (x + x, y + y, z + z);
628 let (xx, xy) = (x * x2, x * y2);
629 let (yy, zz) = (y * y2, z * z2);
630 let wz = w * z2;
631 Affine::new([
632 (1.0 - (yy + zz)) * scale[0],
633 (xy + wz) * scale[0],
634 (xy - wz) * scale[1],
635 (1.0 - (xx + zz)) * scale[1],
636 translation[0],
637 translation[1],
638 ])
639}
640
641impl Lerp for Rect {
642 fn lerp(&self, other: &Self, t: f32) -> Self {
643 Self {
644 origin: self.origin.lerp(&other.origin, t),
645 size: self.size.lerp(&other.size, t),
646 }
647 }
648}
649
650impl Lerp for Color {
651 fn lerp(&self, other: &Self, t: f32) -> Self {
652 let t = t.clamp(0.0, 1.0);
653 let mix = |a: u8, b: u8| -> u8 {
654 (f32::from(a) + (f32::from(b) - f32::from(a)) * t)
655 .round()
656 .clamp(0.0, 255.0) as u8
657 };
658 Self::rgba(
659 mix(self.red, other.red),
660 mix(self.green, other.green),
661 mix(self.blue, other.blue),
662 mix(self.alpha, other.alpha),
663 )
664 }
665}
666
667impl Lerp for HslColor {
668 fn lerp(&self, other: &Self, t: f32) -> Self {
669 let t = t.clamp(0.0, 1.0);
670 let mix = |a: f32, b: f32| a + (b - a) * t;
671 Self::new(
672 mix(self.hue, other.hue),
673 mix(self.saturation, other.saturation),
674 mix(self.lightness, other.lightness),
675 mix(self.alpha, other.alpha),
676 )
677 }
678}
679
680impl Lerp for HsvColor {
681 fn lerp(&self, other: &Self, t: f32) -> Self {
682 let t = t.clamp(0.0, 1.0);
683 let mix = |a: f32, b: f32| a + (b - a) * t;
684 Self::new(
685 mix(self.hue, other.hue),
686 mix(self.saturation, other.saturation),
687 mix(self.value, other.value),
688 mix(self.alpha, other.alpha),
689 )
690 }
691}
692
693#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
695pub enum ChangeImpact {
696 #[default]
698 None,
699 Composite,
701 Paint,
703 Layout,
705 Build,
707}