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 (an unbounded maximum is a constraint, not a size: use `Constraints::at_most` or `Constraints::new` with `f32::INFINITY`)"
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]
388 pub fn coefficients(self) -> [f64; 6] {
389 self.0.as_coeffs()
390 }
391
392 #[must_use]
393 pub const fn from_kurbo(affine: Affine) -> Self {
394 Self(affine)
395 }
396
397 #[must_use]
398 pub const fn to_kurbo(self) -> Affine {
399 self.0
400 }
401
402 #[must_use]
404 pub fn then(self, other: Self) -> Self {
405 Self(self.0 * other.0)
406 }
407
408 #[must_use]
409 pub fn inverse(self) -> Option<Self> {
410 let inverse = self.0.inverse();
411 inverse.is_finite().then_some(Self(inverse))
412 }
413
414 #[must_use]
415 pub fn inverse_translation(self) -> Self {
416 self.inverse().unwrap_or(Self::IDENTITY)
417 }
418
419 #[must_use]
420 pub fn translation_offset(self) -> Offset {
421 let coefficients = self.0.as_coeffs();
422 Offset::new(coefficients[4] as f32, coefficients[5] as f32)
423 }
424
425 #[must_use]
426 pub fn is_translation(self) -> bool {
427 let [a, b, c, d, _, _] = self.0.as_coeffs();
428 a == 1. && b == 0. && c == 0. && d == 1.
429 }
430
431 #[must_use]
442 pub fn magnification_bound(self) -> f32 {
443 let [a, b, c, d, _, _] = self.0.as_coeffs();
444 let bound = a.hypot(b).hypot(c.hypot(d));
445 if !bound.is_finite() {
446 f32::INFINITY
447 } else if bound == 0.0 {
448 0.0
451 } else {
452 (bound as f32).next_up()
453 }
454 }
455
456 #[must_use]
457 pub fn transform_point(self, point: Offset) -> Offset {
458 let point = self.0 * Point::new(f64::from(point.x), f64::from(point.y));
459 Offset::new(point.x as f32, point.y as f32)
460 }
461
462 #[must_use]
463 pub fn inverse_transform_point(self, point: Offset) -> Option<Offset> {
464 self.inverse().map(|inverse| inverse.transform_point(point))
465 }
466
467 #[must_use]
468 pub fn transform_rect_bbox(self, rect: Rect) -> Rect {
469 let rect = kurbo::Rect::new(
470 f64::from(rect.origin.x),
471 f64::from(rect.origin.y),
472 f64::from(rect.origin.x + rect.size.width),
473 f64::from(rect.origin.y + rect.size.height),
474 );
475 let rect = self.0.transform_rect_bbox(rect);
476 Rect::from_origin_size(
477 Offset::new(rect.x0 as f32, rect.y0 as f32),
478 Size::new((rect.x1 - rect.x0) as f32, (rect.y1 - rect.y0) as f32),
479 )
480 }
481}
482
483bitflags::bitflags! {
484 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
486 pub struct Invalidation: u16 {
487 const NONE = 0;
489 const BUILD = 1 << 0;
491 const LAYOUT = 1 << 1;
493 const PAINT = 1 << 2;
495 const COMPOSITE = 1 << 3;
497 const SEMANTICS = 1 << 4;
499 const HIT_TEST = 1 << 5;
501 }
502}
503
504pub type DirtyFlags = Invalidation;
506
507pub trait Lerp {
509 #[must_use]
511 fn lerp(&self, other: &Self, t: f32) -> Self;
512}
513
514impl Lerp for f32 {
515 fn lerp(&self, other: &Self, t: f32) -> Self {
516 self + (other - self) * t
517 }
518}
519
520impl Lerp for f64 {
521 fn lerp(&self, other: &Self, t: f32) -> Self {
522 self + (other - self) * f64::from(t)
523 }
524}
525
526impl<A: Lerp, B: Lerp> Lerp for (A, B) {
529 fn lerp(&self, other: &Self, t: f32) -> Self {
530 (self.0.lerp(&other.0, t), self.1.lerp(&other.1, t))
531 }
532}
533
534impl Lerp for Offset {
535 fn lerp(&self, other: &Self, t: f32) -> Self {
536 Self {
537 x: self.x.lerp(&other.x, t),
538 y: self.y.lerp(&other.y, t),
539 }
540 }
541}
542
543impl Lerp for Size {
544 fn lerp(&self, other: &Self, t: f32) -> Self {
545 Self {
546 width: self.width.lerp(&other.width, t).max(0.0),
547 height: self.height.lerp(&other.height, t).max(0.0),
548 }
549 }
550}
551
552impl Lerp for Transform {
558 fn lerp(&self, other: &Self, t: f32) -> Self {
559 let t = f64::from(t);
560 let (begin_translation, begin_rotation, begin_scale) = decompose(self.0);
561 let (end_translation, end_rotation, end_scale) = decompose(other.0);
562 let mix = |a: f64, b: f64| a * (1.0 - t) + b * t;
563 let translation = [
564 mix(begin_translation[0], end_translation[0]),
565 mix(begin_translation[1], end_translation[1]),
566 ];
567 let mut rotation = [0.0; 4];
568 for (index, value) in rotation.iter_mut().enumerate() {
569 *value = mix(begin_rotation[index], end_rotation[index]);
570 }
571 let length = rotation
572 .iter()
573 .map(|value| value * value)
574 .sum::<f64>()
575 .sqrt();
576 if length != 0.0 {
577 for value in &mut rotation {
578 *value /= length;
579 }
580 }
581 let scale = [
582 mix(begin_scale[0], end_scale[0]),
583 mix(begin_scale[1], end_scale[1]),
584 ];
585 Self(compose(translation, rotation, scale))
586 }
587}
588
589fn decompose(affine: Affine) -> ([f64; 2], [f64; 4], [f64; 2]) {
592 let [a, b, c, d, e, f] = affine.as_coeffs();
593 let mut sx = a.hypot(b);
594 let sy = c.hypot(d);
595 if a * d - b * c < 0.0 {
596 sx = -sx;
597 }
598 let m = [
601 [a / sx, c / sy, 0.0],
602 [b / sx, d / sy, 0.0],
603 [0.0, 0.0, 1.0],
604 ];
605 ([e, f], quaternion_from_rotation(m), [sx, sy])
606}
607
608fn quaternion_from_rotation(m: [[f64; 3]; 3]) -> [f64; 4] {
610 let mut q = [0.0; 4];
611 let trace = m[0][0] + m[1][1] + m[2][2];
612 if trace > 0.0 {
613 let mut s = (trace + 1.0).sqrt();
614 q[3] = s * 0.5;
615 s = 0.5 / s;
616 q[0] = (m[2][1] - m[1][2]) * s;
617 q[1] = (m[0][2] - m[2][0]) * s;
618 q[2] = (m[1][0] - m[0][1]) * s;
619 } else {
620 let i = if m[0][0] < m[1][1] {
621 if m[1][1] < m[2][2] { 2 } else { 1 }
622 } else if m[0][0] < m[2][2] {
623 2
624 } else {
625 0
626 };
627 let j = (i + 1) % 3;
628 let k = (i + 2) % 3;
629 let mut s = (m[i][i] - m[j][j] - m[k][k] + 1.0).sqrt();
630 q[i] = s * 0.5;
631 s = 0.5 / s;
632 q[3] = (m[k][j] - m[j][k]) * s;
633 q[j] = (m[j][i] + m[i][j]) * s;
634 q[k] = (m[k][i] + m[i][k]) * s;
635 }
636 q
637}
638
639fn compose(translation: [f64; 2], rotation: [f64; 4], scale: [f64; 2]) -> Affine {
641 let [x, y, z, w] = rotation;
642 let (x2, y2, z2) = (x + x, y + y, z + z);
643 let (xx, xy) = (x * x2, x * y2);
644 let (yy, zz) = (y * y2, z * z2);
645 let wz = w * z2;
646 Affine::new([
647 (1.0 - (yy + zz)) * scale[0],
648 (xy + wz) * scale[0],
649 (xy - wz) * scale[1],
650 (1.0 - (xx + zz)) * scale[1],
651 translation[0],
652 translation[1],
653 ])
654}
655
656impl Lerp for Rect {
657 fn lerp(&self, other: &Self, t: f32) -> Self {
658 Self {
659 origin: self.origin.lerp(&other.origin, t),
660 size: self.size.lerp(&other.size, t),
661 }
662 }
663}
664
665impl Lerp for Color {
666 fn lerp(&self, other: &Self, t: f32) -> Self {
667 let t = t.clamp(0.0, 1.0);
668 let mix = |a: u8, b: u8| -> u8 {
669 (f32::from(a) + (f32::from(b) - f32::from(a)) * t)
670 .round()
671 .clamp(0.0, 255.0) as u8
672 };
673 Self::rgba(
674 mix(self.red, other.red),
675 mix(self.green, other.green),
676 mix(self.blue, other.blue),
677 mix(self.alpha, other.alpha),
678 )
679 }
680}
681
682impl Lerp for HslColor {
683 fn lerp(&self, other: &Self, t: f32) -> Self {
684 let t = t.clamp(0.0, 1.0);
685 let mix = |a: f32, b: f32| a + (b - a) * t;
686 Self::new(
687 mix(self.hue, other.hue),
688 mix(self.saturation, other.saturation),
689 mix(self.lightness, other.lightness),
690 mix(self.alpha, other.alpha),
691 )
692 }
693}
694
695impl Lerp for HsvColor {
696 fn lerp(&self, other: &Self, t: f32) -> Self {
697 let t = t.clamp(0.0, 1.0);
698 let mix = |a: f32, b: f32| a + (b - a) * t;
699 Self::new(
700 mix(self.hue, other.hue),
701 mix(self.saturation, other.saturation),
702 mix(self.value, other.value),
703 mix(self.alpha, other.alpha),
704 )
705 }
706}
707
708#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
710pub enum ChangeImpact {
711 #[default]
713 None,
714 Composite,
716 Paint,
718 Layout,
720 Build,
722}