incular_rendering/
geometry.rs1use incular_core::finite_non_negative;
2use incular_core::{Offset, Rect, Size};
3
4#[derive(Clone, Copy, Debug, Default, PartialEq)]
6pub struct CornerRadii {
7 pub top_left: f32,
8 pub top_right: f32,
9 pub bottom_right: f32,
10 pub bottom_left: f32,
11}
12impl CornerRadii {
13 pub const ZERO: Self = Self::uniform(0.0);
14
15 #[must_use]
16 pub const fn uniform(radius: f32) -> Self {
17 Self {
18 top_left: radius,
19 top_right: radius,
20 bottom_right: radius,
21 bottom_left: radius,
22 }
23 }
24
25 #[must_use]
26 pub fn is_zero(&self) -> bool {
27 self.top_left <= 0.0
28 && self.top_right <= 0.0
29 && self.bottom_right <= 0.0
30 && self.bottom_left <= 0.0
31 }
32
33 #[must_use]
37 pub fn scaled(self, scale: f32) -> Self {
38 let scale = scale.abs();
39 Self {
40 top_left: self.top_left * scale,
41 top_right: self.top_right * scale,
42 bottom_right: self.bottom_right * scale,
43 bottom_left: self.bottom_left * scale,
44 }
45 }
46
47 #[must_use]
50 pub fn normalized(self, size: incular_core::Size) -> Self {
51 let clean = |v: f32| finite_non_negative(v);
52 let mut r = Self {
53 top_left: clean(self.top_left),
54 top_right: clean(self.top_right),
55 bottom_right: clean(self.bottom_right),
56 bottom_left: clean(self.bottom_left),
57 };
58 let ratio = |edge: f32, sum: f32| if sum > 0. { edge / sum } else { 1. };
59 let scale = ratio(size.width, r.top_left + r.top_right)
60 .min(ratio(size.width, r.bottom_left + r.bottom_right))
61 .min(ratio(size.height, r.top_left + r.bottom_left))
62 .min(ratio(size.height, r.top_right + r.bottom_right))
63 .min(1.);
64 r.top_left *= scale;
65 r.top_right *= scale;
66 r.bottom_right *= scale;
67 r.bottom_left *= scale;
68 r
69 }
70 #[must_use]
71 pub fn inset(self, amount: f32) -> Self {
72 Self {
73 top_left: (self.top_left - amount).max(0.),
74 top_right: (self.top_right - amount).max(0.),
75 bottom_right: (self.bottom_right - amount).max(0.),
76 bottom_left: (self.bottom_left - amount).max(0.),
77 }
78 }
79}
80
81impl From<f32> for CornerRadii {
82 fn from(v: f32) -> Self {
83 Self::uniform(v)
84 }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq)]
88pub struct RRect {
89 pub rect: Rect,
90 pub radii: CornerRadii,
91}
92impl RRect {
93 #[must_use]
94 pub fn new(rect: Rect, radii: CornerRadii) -> Self {
95 Self {
96 rect,
97 radii: radii.normalized(rect.size),
98 }
99 }
100 #[must_use]
101 pub fn uniform(rect: Rect, radius: f32) -> Self {
102 Self::new(rect, CornerRadii::uniform(radius))
103 }
104 #[must_use]
105 pub fn inset(self, amount: f32) -> Self {
106 let rect = Rect::from_origin_size(
107 Offset::new(self.rect.origin.x + amount, self.rect.origin.y + amount),
108 incular_core::Size::new(
109 (self.rect.size.width - 2. * amount).max(0.),
110 (self.rect.size.height - 2. * amount).max(0.),
111 ),
112 );
113 Self::new(rect, self.radii.inset(amount))
114 }
115 #[must_use]
118 pub fn contains(self, point: Offset) -> bool {
119 if !self.rect.contains(point) {
120 return false;
121 }
122 let local = point - self.rect.origin;
123 let size = self.rect.size;
124 let corner = if local.x < self.radii.top_left && local.y < self.radii.top_left {
125 Some((
126 self.radii.top_left,
127 self.radii.top_left,
128 self.radii.top_left,
129 ))
130 } else if local.x > size.width - self.radii.top_right && local.y < self.radii.top_right {
131 Some((
132 size.width - self.radii.top_right,
133 self.radii.top_right,
134 self.radii.top_right,
135 ))
136 } else if local.x > size.width - self.radii.bottom_right
137 && local.y > size.height - self.radii.bottom_right
138 {
139 Some((
140 size.width - self.radii.bottom_right,
141 size.height - self.radii.bottom_right,
142 self.radii.bottom_right,
143 ))
144 } else if local.x < self.radii.bottom_left && local.y > size.height - self.radii.bottom_left
145 {
146 Some((
147 self.radii.bottom_left,
148 size.height - self.radii.bottom_left,
149 self.radii.bottom_left,
150 ))
151 } else {
152 None
153 };
154 corner.is_none_or(|(cx, cy, radius)| {
155 radius <= 0.
156 || (local.x - cx).mul_add(local.x - cx, (local.y - cy) * (local.y - cy))
157 <= radius * radius
158 })
159 }
160}
161
162pub(crate) fn union_rect(left: Rect, right: Rect) -> Rect {
163 let x0 = left.origin.x.min(right.origin.x);
164 let y0 = left.origin.y.min(right.origin.y);
165 let x1 = (left.origin.x + left.size.width).max(right.origin.x + right.size.width);
166 let y1 = (left.origin.y + left.size.height).max(right.origin.y + right.size.height);
167 Rect::from_origin_size(Offset::new(x0, y0), Size::new(x1 - x0, y1 - y0))
168}