1mod batch;
2mod circle;
3mod fill;
4mod geometry;
5mod path;
6mod stroke;
7
8use circle::Circle;
9use easygpu_lyon::lyon_tessellation;
10use figures::{Displayable, Figure, Points, Rectlike, Scale};
11use geometry::ShapeGeometry;
12
13pub use self::batch::*;
14pub use self::fill::*;
15pub use self::path::*;
16pub use self::stroke::*;
17use crate::math::{Pixels, Point, Rect, Scaled};
18use crate::scene::{Element, Target};
19
20#[derive(Debug, Clone)]
22pub struct Shape<Unit> {
23 geometry: ShapeGeometry<Unit>,
24 stroke: Option<Stroke>,
25 fill: Option<Fill>,
26}
27
28impl<Unit> Default for Shape<Unit> {
29 fn default() -> Self {
30 Self {
31 geometry: ShapeGeometry::Empty,
32 stroke: None,
33 fill: None,
34 }
35 }
36}
37
38impl<Unit> Shape<Unit> {
39 pub fn rect(rect: impl Into<Rect<f32, Unit>>) -> Self {
41 let rect = rect.into().as_extents();
42 let path = PathBuilder::new(Point::from_figures(rect.origin.x(), rect.origin.y()))
43 .line_to(Point::from_figures(rect.extent.x(), rect.origin.y()))
44 .line_to(Point::from_figures(rect.extent.x(), rect.extent.y()))
45 .line_to(Point::from_figures(rect.origin.x(), rect.extent.y()))
46 .close()
47 .build();
48
49 Self {
50 geometry: ShapeGeometry::Path(path),
51 stroke: None,
52 fill: None,
53 }
54 }
55
56 #[must_use]
58 pub const fn circle(center: Point<f32, Unit>, radius: Figure<f32, Unit>) -> Self {
59 Self {
60 geometry: ShapeGeometry::Circle(Circle { center, radius }),
61 stroke: None,
62 fill: None,
63 }
64 }
65
66 #[must_use]
68 pub fn polygon(points: impl IntoIterator<Item = Point<f32, Unit>>) -> Self {
69 let mut points = points.into_iter();
70 if let Some(start) = points.next() {
71 let mut builder = PathBuilder::new(start);
72 for point in points {
73 builder = builder.line_to(point);
74 }
75
76 Self {
77 geometry: ShapeGeometry::Path(builder.close().build()),
78 stroke: None,
79 fill: None,
80 }
81 } else {
82 Self::default()
83 }
84 }
85
86 #[must_use]
88 pub const fn fill(mut self, fill: Fill) -> Self {
89 self.fill = Some(fill);
90 self
91 }
92
93 #[must_use]
95 pub const fn stroke(mut self, stroke: Stroke) -> Self {
96 self.stroke = Some(stroke);
97 self
98 }
99
100 #[must_use]
103 pub fn cast_unit<U>(self) -> Shape<U> {
104 Shape {
105 geometry: self.geometry.cast_unit(),
106 fill: self.fill,
107 stroke: self.stroke,
108 }
109 }
110}
111
112impl<Unit> Shape<Unit>
113where
114 Self: Displayable<f32, Pixels = Shape<Pixels>>,
115{
116 pub fn render(&self, scene: &Target) {
119 self.render_at(&Point::<f32, Pixels>::default(), scene);
120 }
121
122 pub fn render_at(
124 &self,
125 location: &impl Displayable<f32, Pixels = Point<f32, Pixels>>,
126 scene: &Target,
127 ) {
128 let location = location.to_pixels(scene.scale());
129 let pixel_shape = self.to_pixels(scene.scale());
130 let translated = pixel_shape.convert_from_user_to_device(location, scene);
131 scene.push_element(Element::Shape(translated));
132 }
133}
134
135impl Shape<Pixels> {
136 fn convert_from_user_to_device(&self, location: Point<f32, Pixels>, scene: &Target) -> Self {
137 Self {
138 geometry: self
139 .geometry
140 .translate_and_convert_to_device(location, scene),
141 fill: self.fill.clone(),
142 stroke: self.stroke.clone(),
143 }
144 }
145}
146
147impl Shape<Pixels> {
148 pub(crate) fn build(&self, builder: &mut easygpu_lyon::ShapeBuilder) -> crate::Result<()> {
149 self.geometry.build(builder, &self.stroke, &self.fill)
150 }
151}
152
153impl<Src, Dst> std::ops::Mul<Scale<f32, Src, Dst>> for Shape<Src> {
154 type Output = Shape<Dst>;
155
156 fn mul(self, scale: Scale<f32, Src, Dst>) -> Self::Output {
157 Self::Output {
158 geometry: self.geometry * scale,
159 fill: self.fill,
160 stroke: self.stroke,
161 }
162 }
163}
164
165impl Displayable<f32> for Shape<Pixels> {
166 type Pixels = Self;
167 type Points = Shape<Points>;
168 type Scaled = Shape<Scaled>;
169
170 fn to_pixels(&self, _scale: &figures::DisplayScale<f32>) -> Self::Pixels {
171 self.clone()
172 }
173
174 fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
175 Shape {
176 geometry: self.geometry.to_points(scale),
177 stroke: self.stroke.clone(),
178 fill: self.fill.clone(),
179 }
180 }
181
182 fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
183 Shape {
184 geometry: self.geometry.to_scaled(scale),
185 stroke: self.stroke.clone(),
186 fill: self.fill.clone(),
187 }
188 }
189}
190
191impl Displayable<f32> for Shape<Points> {
192 type Pixels = Shape<Pixels>;
193 type Points = Self;
194 type Scaled = Shape<Scaled>;
195
196 fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
197 Shape {
198 geometry: self.geometry.to_pixels(scale),
199 stroke: self.stroke.clone(),
200 fill: self.fill.clone(),
201 }
202 }
203
204 fn to_points(&self, _scale: &figures::DisplayScale<f32>) -> Self::Points {
205 self.clone()
206 }
207
208 fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
209 Shape {
210 geometry: self.geometry.to_scaled(scale),
211 stroke: self.stroke.clone(),
212 fill: self.fill.clone(),
213 }
214 }
215}
216
217impl Displayable<f32> for Shape<Scaled> {
218 type Pixels = Shape<Pixels>;
219 type Points = Shape<Points>;
220 type Scaled = Self;
221
222 fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
223 Shape {
224 geometry: self.geometry.to_pixels(scale),
225 stroke: self.stroke.clone(),
226 fill: self.fill.clone(),
227 }
228 }
229
230 fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
231 Shape {
232 geometry: self.geometry.to_points(scale),
233 stroke: self.stroke.clone(),
234 fill: self.fill.clone(),
235 }
236 }
237
238 fn to_scaled(&self, _scale: &figures::DisplayScale<f32>) -> Self::Scaled {
239 self.clone()
240 }
241}
242
243const fn lyon_point<T>(pt: Point<f32, T>) -> lyon_tessellation::math::Point {
244 lyon_tessellation::math::Point::new(pt.x, pt.y)
245}