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