1use alloc::string::String;
6use crate::corety::{AzString, OptionF32};
7
8fn cmp_f32(a: f32, b: f32) -> core::cmp::Ordering {
10 a.partial_cmp(&b).unwrap_or(core::cmp::Ordering::Equal)
11}
12
13#[derive(Debug, Copy, Clone, PartialEq)]
15#[repr(C)]
16pub struct ShapePoint {
17 pub x: f32,
18 pub y: f32,
19}
20
21impl_option!(
22 ShapePoint,
23 OptionShapePoint,
24 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
25);
26
27impl ShapePoint {
28 #[must_use] pub const fn new(x: f32, y: f32) -> Self {
29 Self { x, y }
30 }
31
32 #[must_use] pub const fn zero() -> Self {
33 Self { x: 0.0, y: 0.0 }
34 }
35}
36
37impl Eq for ShapePoint {}
38
39impl PartialOrd for ShapePoint {
41 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
42 Some(self.cmp(other))
43 }
44}
45
46impl Ord for ShapePoint {
47 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
48 match self.x.partial_cmp(&other.x) {
49 Some(core::cmp::Ordering::Equal) => self
50 .y
51 .partial_cmp(&other.y)
52 .unwrap_or(core::cmp::Ordering::Equal),
53 other => other.unwrap_or(core::cmp::Ordering::Equal),
54 }
55 }
56}
57
58impl core::hash::Hash for ShapePoint {
59 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
60 self.x.to_bits().hash(state);
61 self.y.to_bits().hash(state);
62 }
63}
64
65impl_vec!(ShapePoint, ShapePointVec, ShapePointVecDestructor, ShapePointVecDestructorType, ShapePointVecSlice, OptionShapePoint);
66impl_vec_debug!(ShapePoint, ShapePointVec);
67impl_vec_partialord!(ShapePoint, ShapePointVec);
68impl_vec_ord!(ShapePoint, ShapePointVec);
69impl_vec_clone!(ShapePoint, ShapePointVec, ShapePointVecDestructor);
70impl_vec_partialeq!(ShapePoint, ShapePointVec);
71impl_vec_eq!(ShapePoint, ShapePointVec);
72impl_vec_hash!(ShapePoint, ShapePointVec);
73
74#[derive(Debug, Copy, Clone, PartialEq)]
76#[repr(C)]
77pub struct ShapeCircle {
78 pub center: ShapePoint,
79 pub radius: f32,
80}
81
82impl Eq for ShapeCircle {}
83impl core::hash::Hash for ShapeCircle {
84 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
85 self.center.hash(state);
86 self.radius.to_bits().hash(state);
87 }
88}
89impl PartialOrd for ShapeCircle {
90 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
91 Some(self.cmp(other))
92 }
93}
94impl Ord for ShapeCircle {
95 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
96 match self.center.cmp(&other.center) {
97 core::cmp::Ordering::Equal => self
98 .radius
99 .partial_cmp(&other.radius)
100 .unwrap_or(core::cmp::Ordering::Equal),
101 other => other,
102 }
103 }
104}
105
106#[derive(Debug, Copy, Clone, PartialEq)]
108#[repr(C)]
109pub struct ShapeEllipse {
110 pub center: ShapePoint,
111 pub radius_x: f32,
112 pub radius_y: f32,
113}
114
115impl Eq for ShapeEllipse {}
116impl core::hash::Hash for ShapeEllipse {
117 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
118 self.center.hash(state);
119 self.radius_x.to_bits().hash(state);
120 self.radius_y.to_bits().hash(state);
121 }
122}
123impl PartialOrd for ShapeEllipse {
124 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
125 Some(self.cmp(other))
126 }
127}
128impl Ord for ShapeEllipse {
129 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
130 match self.center.cmp(&other.center) {
131 core::cmp::Ordering::Equal => match self.radius_x.partial_cmp(&other.radius_x) {
132 Some(core::cmp::Ordering::Equal) | None => self
133 .radius_y
134 .partial_cmp(&other.radius_y)
135 .unwrap_or(core::cmp::Ordering::Equal),
136 Some(other) => other,
137 },
138 other => other,
139 }
140 }
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
145#[repr(C)]
146pub struct ShapePolygon {
147 pub points: ShapePointVec,
148}
149
150#[derive(Debug, Copy, Clone, PartialEq)]
153#[repr(C)]
154pub struct ShapeInset {
155 pub inset_top: f32,
156 pub inset_right: f32,
157 pub inset_bottom: f32,
158 pub inset_left: f32,
159 pub border_radius: OptionF32,
160}
161
162impl Eq for ShapeInset {}
163impl core::hash::Hash for ShapeInset {
164 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
165 self.inset_top.to_bits().hash(state);
166 self.inset_right.to_bits().hash(state);
167 self.inset_bottom.to_bits().hash(state);
168 self.inset_left.to_bits().hash(state);
169 self.border_radius.hash(state);
170 }
171}
172impl PartialOrd for ShapeInset {
173 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
174 Some(self.cmp(other))
175 }
176}
177impl Ord for ShapeInset {
178 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
179 cmp_f32(self.inset_top, other.inset_top)
180 .then_with(|| cmp_f32(self.inset_right, other.inset_right))
181 .then_with(|| cmp_f32(self.inset_bottom, other.inset_bottom))
182 .then_with(|| cmp_f32(self.inset_left, other.inset_left))
183 .then_with(|| self.border_radius.cmp(&other.border_radius))
184 }
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
190#[repr(C)]
191pub struct ShapePath {
192 pub data: AzString,
193}
194
195#[derive(Debug, Clone, PartialEq)]
198#[repr(C, u8)]
199pub enum CssShape {
200 Circle(ShapeCircle),
201 Ellipse(ShapeEllipse),
202 Polygon(ShapePolygon),
203 Inset(ShapeInset),
204 Path(ShapePath),
205}
206
207impl Eq for CssShape {}
208
209impl core::hash::Hash for CssShape {
210 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
211 core::mem::discriminant(self).hash(state);
212 match self {
213 Self::Circle(c) => c.hash(state),
214 Self::Ellipse(e) => e.hash(state),
215 Self::Polygon(p) => p.hash(state),
216 Self::Inset(i) => i.hash(state),
217 Self::Path(p) => p.hash(state),
218 }
219 }
220}
221
222impl PartialOrd for CssShape {
223 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
224 Some(self.cmp(other))
225 }
226}
227
228impl Ord for CssShape {
229 #[allow(clippy::match_same_arms)]
233 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
234 match (self, other) {
235 (Self::Circle(a), Self::Circle(b)) => a.cmp(b),
236 (Self::Ellipse(a), Self::Ellipse(b)) => a.cmp(b),
237 (Self::Polygon(a), Self::Polygon(b)) => a.cmp(b),
238 (Self::Inset(a), Self::Inset(b)) => a.cmp(b),
239 (Self::Path(a), Self::Path(b)) => a.cmp(b),
240 (Self::Circle(_), _) => core::cmp::Ordering::Less,
242 (_, Self::Circle(_)) => core::cmp::Ordering::Greater,
243 (Self::Ellipse(_), _) => core::cmp::Ordering::Less,
244 (_, Self::Ellipse(_)) => core::cmp::Ordering::Greater,
245 (Self::Polygon(_), _) => core::cmp::Ordering::Less,
246 (_, Self::Polygon(_)) => core::cmp::Ordering::Greater,
247 (Self::Inset(_), Self::Path(_)) => core::cmp::Ordering::Less,
248 (Self::Path(_), Self::Inset(_)) => core::cmp::Ordering::Greater,
249 }
250 }
251}
252
253impl CssShape {
254 #[must_use] pub const fn circle(center: ShapePoint, radius: f32) -> Self {
256 Self::Circle(ShapeCircle { center, radius })
257 }
258
259 #[must_use] pub const fn ellipse(center: ShapePoint, radius_x: f32, radius_y: f32) -> Self {
261 Self::Ellipse(ShapeEllipse {
262 center,
263 radius_x,
264 radius_y,
265 })
266 }
267
268 #[must_use] pub const fn polygon(points: ShapePointVec) -> Self {
270 Self::Polygon(ShapePolygon { points })
271 }
272
273 #[must_use] pub const fn inset(top: f32, right: f32, bottom: f32, left: f32) -> Self {
275 Self::Inset(ShapeInset {
276 inset_top: top,
277 inset_right: right,
278 inset_bottom: bottom,
279 inset_left: left,
280 border_radius: OptionF32::None,
281 })
282 }
283
284 #[must_use] pub const fn inset_rounded(top: f32, right: f32, bottom: f32, left: f32, radius: f32) -> Self {
286 Self::Inset(ShapeInset {
287 inset_top: top,
288 inset_right: right,
289 inset_bottom: bottom,
290 inset_left: left,
291 border_radius: OptionF32::Some(radius),
292 })
293 }
294
295 #[must_use] pub fn print_as_css_value(&self) -> String {
296 use alloc::format;
297 match self {
298 Self::Circle(ShapeCircle { center, radius }) => {
299 format!("circle({}px at {}px {}px)", radius, center.x, center.y)
300 }
301 Self::Ellipse(ShapeEllipse { center, radius_x, radius_y }) => {
302 format!("ellipse({}px {}px at {}px {}px)", radius_x, radius_y, center.x, center.y)
303 }
304 Self::Polygon(ShapePolygon { points }) => {
305 let pts: Vec<String> = points.as_ref().iter()
306 .map(|p| format!("{}px {}px", p.x, p.y))
307 .collect();
308 format!("polygon({})", pts.join(", "))
309 }
310 Self::Inset(ShapeInset { inset_top, inset_right, inset_bottom, inset_left, border_radius }) => {
311 let base = format!("inset({inset_top}px {inset_right}px {inset_bottom}px {inset_left}px");
312 match border_radius {
313 OptionF32::Some(r) => format!("{base} round {r}px)"),
314 OptionF32::None => format!("{base})"),
315 }
316 }
317 Self::Path(ShapePath { data }) => {
318 format!("path(\"{}\")", data.as_str())
319 }
320 }
321 }
322
323 #[must_use] pub fn format_as_rust_code(&self) -> String {
324 use alloc::format;
325 match self {
326 Self::Circle(ShapeCircle { center, radius }) => {
327 format!(
328 "CssShape::Circle(ShapeCircle {{ center: ShapePoint::new({}_f32, {}_f32), radius: {}_f32 }})",
329 center.x, center.y, radius
330 )
331 }
332 Self::Ellipse(ShapeEllipse { center, radius_x, radius_y }) => {
333 format!(
334 "CssShape::Ellipse(ShapeEllipse {{ center: ShapePoint::new({}_f32, {}_f32), radius_x: {}_f32, radius_y: {}_f32 }})",
335 center.x, center.y, radius_x, radius_y
336 )
337 }
338 Self::Polygon(ShapePolygon { points }) => {
339 let pts: Vec<String> = points.as_ref().iter()
340 .map(|p| format!("ShapePoint::new({}_f32, {}_f32)", p.x, p.y))
341 .collect();
342 format!("CssShape::Polygon(ShapePolygon {{ points: vec![{}].into() }})", pts.join(", "))
343 }
344 Self::Inset(ShapeInset { inset_top, inset_right, inset_bottom, inset_left, border_radius }) => {
345 let br = match border_radius {
346 OptionF32::Some(r) => format!("OptionF32::Some({r}_f32)"),
347 OptionF32::None => String::from("OptionF32::None"),
348 };
349 format!(
350 "CssShape::Inset(ShapeInset {{ inset_top: {inset_top}_f32, inset_right: {inset_right}_f32, inset_bottom: {inset_bottom}_f32, inset_left: {inset_left}_f32, border_radius: {br} }})"
351 )
352 }
353 Self::Path(ShapePath { data }) => {
354 format!("CssShape::Path(ShapePath {{ data: AzString::from_const_str(\"{}\") }})", data.as_str())
355 }
356 }
357 }
358}
359
360impl_option!(
361 CssShape,
362 OptionCssShape,
363 copy = false,
364 [Debug, Clone, PartialEq, Eq]
365);
366