Skip to main content

rustmotion_core/engine/renderer/
shapes.rs

1use skia_safe::{Canvas, Paint, Rect};
2
3use crate::schema::ShapeType;
4
5/// Build a `Path` for the given shape type without drawing it.
6pub fn build_shape_path(
7    shape_type: &ShapeType,
8    x: f32,
9    y: f32,
10    w: f32,
11    h: f32,
12    corner_radius: Option<f32>,
13) -> Option<skia_safe::Path> {
14    match shape_type {
15        ShapeType::Rect => {
16            let mut path = skia_safe::PathBuilder::new();
17            path.add_rect(Rect::from_xywh(x, y, w, h), None, None);
18            Some(path.detach())
19        }
20        ShapeType::RoundedRect => {
21            let r = corner_radius.unwrap_or(8.0);
22            let rrect = skia_safe::RRect::new_rect_xy(Rect::from_xywh(x, y, w, h), r, r);
23            let mut path = skia_safe::PathBuilder::new();
24            path.add_rrect(rrect, None, None);
25            Some(path.detach())
26        }
27        ShapeType::Circle => {
28            let radius = w.min(h) / 2.0;
29            let mut path = skia_safe::PathBuilder::new();
30            path.add_circle((x + w / 2.0, y + h / 2.0), radius, None);
31            Some(path.detach())
32        }
33        ShapeType::Ellipse => {
34            let mut path = skia_safe::PathBuilder::new();
35            path.add_oval(Rect::from_xywh(x, y, w, h), None, None);
36            Some(path.detach())
37        }
38        ShapeType::Triangle => {
39            let mut path = skia_safe::PathBuilder::new();
40            path.move_to((x + w / 2.0, y));
41            path.line_to((x + w, y + h));
42            path.line_to((x, y + h));
43            path.close();
44            Some(path.detach())
45        }
46        ShapeType::Star { points } => {
47            let cx = x + w / 2.0;
48            let cy = y + h / 2.0;
49            let outer_r = w.min(h) / 2.0;
50            let inner_r = outer_r * 0.4;
51            let n = *points as usize;
52            let mut path = skia_safe::PathBuilder::new();
53            for i in 0..(n * 2) {
54                let angle =
55                    (i as f32) * std::f32::consts::PI / n as f32 - std::f32::consts::FRAC_PI_2;
56                let r = if i % 2 == 0 { outer_r } else { inner_r };
57                let px = cx + r * angle.cos();
58                let py = cy + r * angle.sin();
59                if i == 0 {
60                    path.move_to((px, py));
61                } else {
62                    path.line_to((px, py));
63                }
64            }
65            path.close();
66            Some(path.detach())
67        }
68        ShapeType::Polygon { sides } => {
69            let cx = x + w / 2.0;
70            let cy = y + h / 2.0;
71            let r = w.min(h) / 2.0;
72            let n = *sides as usize;
73            let mut path = skia_safe::PathBuilder::new();
74            for i in 0..n {
75                let angle = (i as f32) * 2.0 * std::f32::consts::PI / n as f32
76                    - std::f32::consts::FRAC_PI_2;
77                let px = cx + r * angle.cos();
78                let py = cy + r * angle.sin();
79                if i == 0 {
80                    path.move_to((px, py));
81                } else {
82                    path.line_to((px, py));
83                }
84            }
85            path.close();
86            Some(path.detach())
87        }
88        ShapeType::Path { data } => skia_safe::Path::from_svg(data),
89    }
90}
91
92pub fn draw_shape_path(
93    canvas: &Canvas,
94    shape_type: &ShapeType,
95    x: f32,
96    y: f32,
97    w: f32,
98    h: f32,
99    corner_radius: Option<f32>,
100    paint: &Paint,
101) {
102    let rect = Rect::from_xywh(x, y, w, h);
103    match shape_type {
104        ShapeType::Rect => {
105            canvas.draw_rect(rect, paint);
106        }
107        ShapeType::RoundedRect => {
108            let r = corner_radius.unwrap_or(8.0);
109            let rrect = skia_safe::RRect::new_rect_xy(rect, r, r);
110            canvas.draw_rrect(rrect, paint);
111        }
112        ShapeType::Circle => {
113            let radius = w.min(h) / 2.0;
114            canvas.draw_circle((x + w / 2.0, y + h / 2.0), radius, paint);
115        }
116        ShapeType::Ellipse => {
117            canvas.draw_oval(rect, paint);
118        }
119        ShapeType::Triangle => {
120            let mut path = skia_safe::PathBuilder::new();
121            path.move_to((x + w / 2.0, y));
122            path.line_to((x + w, y + h));
123            path.line_to((x, y + h));
124            path.close();
125            canvas.draw_path(&path.detach(), paint);
126        }
127        ShapeType::Star { points } => {
128            let cx = x + w / 2.0;
129            let cy = y + h / 2.0;
130            let outer_r = w.min(h) / 2.0;
131            let inner_r = outer_r * 0.4;
132            let n = *points as usize;
133            let mut path = skia_safe::PathBuilder::new();
134            for i in 0..(n * 2) {
135                let angle =
136                    (i as f32) * std::f32::consts::PI / n as f32 - std::f32::consts::FRAC_PI_2;
137                let r = if i % 2 == 0 { outer_r } else { inner_r };
138                let px = cx + r * angle.cos();
139                let py = cy + r * angle.sin();
140                if i == 0 {
141                    path.move_to((px, py));
142                } else {
143                    path.line_to((px, py));
144                }
145            }
146            path.close();
147            canvas.draw_path(&path.detach(), paint);
148        }
149        ShapeType::Polygon { sides } => {
150            let cx = x + w / 2.0;
151            let cy = y + h / 2.0;
152            let r = w.min(h) / 2.0;
153            let n = *sides as usize;
154            let mut path = skia_safe::PathBuilder::new();
155            for i in 0..n {
156                let angle = (i as f32) * 2.0 * std::f32::consts::PI / n as f32
157                    - std::f32::consts::FRAC_PI_2;
158                let px = cx + r * angle.cos();
159                let py = cy + r * angle.sin();
160                if i == 0 {
161                    path.move_to((px, py));
162                } else {
163                    path.line_to((px, py));
164                }
165            }
166            path.close();
167            canvas.draw_path(&path.detach(), paint);
168        }
169        ShapeType::Path { data } => {
170            if let Some(path) = skia_safe::Path::from_svg(data) {
171                canvas.draw_path(&path, paint);
172            }
173        }
174    }
175}