1use crate::drawing::*;
4use crate::{Path2D, Rect, ValidationError};
5
6mod primitives;
7
8pub use primitives::*;
9
10pub trait Shape: Send + Sync {
16 fn validate(&self) -> Result<(), ValidationError> {
18 Ok(())
19 }
20
21 fn path(&self) -> Path2D;
23
24 fn bounding_box(&self) -> Rect {
26 self.path().bounding_box()
27 }
28
29 fn as_rect(&self) -> Option<Rect> {
31 None
32 }
33
34 fn fill(self, paint: impl Into<Paint>) -> Styled<Self>
36 where
37 Self: Sized,
38 {
39 Styled {
40 shape: self,
41 fill: Some(paint.into()),
42 stroke: None,
43 stroke_style: StrokeStyle::default(),
44 rule: FillRule::default(),
45 trim: None,
46 dash: None,
47 }
48 }
49
50 fn stroke(self, width: f64, paint: impl Into<Paint>) -> Styled<Self>
52 where
53 Self: Sized,
54 {
55 Styled {
56 shape: self,
57 fill: None,
58 stroke: Some(paint.into()),
59 stroke_style: StrokeStyle {
60 width,
61 ..StrokeStyle::default()
62 },
63 rule: FillRule::default(),
64 trim: None,
65 dash: None,
66 }
67 }
68}
69
70impl Shape for Path2D {
71 fn validate(&self) -> Result<(), ValidationError> {
72 Path2D::validate(self)
73 }
74
75 fn path(&self) -> Path2D {
76 self.clone()
77 }
78
79 fn bounding_box(&self) -> Rect {
80 Path2D::bounding_box(self)
81 }
82}
83
84impl Shape for Rect {
85 fn path(&self) -> Path2D {
86 Path2D::from_rect(*self).expect("Shape::path requires valid rectangle geometry")
87 }
88
89 fn bounding_box(&self) -> Rect {
90 *self
91 }
92
93 fn as_rect(&self) -> Option<Rect> {
94 Some(*self)
95 }
96}
97
98#[derive(Clone, Debug)]
103pub struct Styled<Geometry> {
104 shape: Geometry,
105 fill: Option<Paint>,
106 stroke: Option<Paint>,
107 stroke_style: StrokeStyle,
108 rule: FillRule,
109 trim: Option<(f64, f64)>,
110 dash: Option<(std::sync::Arc<[f64]>, f64)>,
111}
112
113impl<Geometry: Shape> Styled<Geometry> {
114 pub fn trim(mut self, start: f64, end: f64) -> Self {
117 self.trim = Some((start, end));
118 self
119 }
120
121 pub fn dash(mut self, pattern: &[f64], offset: f64) -> Self {
124 self.dash = Some((std::sync::Arc::from(pattern), offset));
125 self
126 }
127
128 pub fn fill(mut self, paint: impl Into<Paint>) -> Self {
130 self.fill = Some(paint.into());
131 self
132 }
133
134 pub fn stroke(mut self, width: f64, paint: impl Into<Paint>) -> Self {
136 self.stroke = Some(paint.into());
137 self.stroke_style.width = width;
138 self
139 }
140
141 pub fn stroke_style(mut self, style: StrokeStyle) -> Self {
143 self.stroke_style = style;
144 self
145 }
146
147 pub fn fill_rule(mut self, rule: FillRule) -> Self {
149 self.rule = rule;
150 self
151 }
152}
153
154impl Rect {
155 pub fn fill(self, paint: impl Into<Paint>) -> Styled<Self> {
159 Shape::fill(self, paint)
160 }
161
162 pub fn stroke(self, width: f64, paint: impl Into<Paint>) -> Styled<Self> {
164 Shape::stroke(self, width, paint)
165 }
166}
167
168impl<Geometry: Shape> Drawable for Styled<Geometry> {
169 fn try_bounds(&self, context: &DrawContext) -> Result<Rect, ValidationError> {
170 self.shape.validate()?;
171 Ok(self.bounds(context))
172 }
173
174 fn bounds(&self, _: &DrawContext) -> Rect {
175 let bounds = self.shape.bounding_box();
176 if self.stroke.is_some() && validate_rect(bounds, "stroke", "bounds").is_ok() {
177 if self.stroke_style.validate("stroke").is_err() {
178 return Rect::new(f64::NAN, 0.0, 0.0, 0.0);
179 }
180 self.stroke_style.bounds(bounds)
181 } else {
182 bounds
183 }
184 }
185
186 fn draw(&self, _: &DrawContext, canvas: &mut Canvas<'_>) -> Result<(), ValidationError> {
187 self.shape.validate()?;
188 if self.trim.is_some() || self.dash.is_some() {
189 let mut geometry = self.shape.path();
190 if let Some((start, end)) = self.trim {
191 geometry = geometry.try_trim(start, end)?;
192 }
193 if let Some((pattern, offset)) = &self.dash {
194 geometry = geometry.dashed(pattern, *offset)?;
195 }
196 if let Some(paint) = &self.fill {
197 canvas.fill(&geometry, paint.clone(), self.rule)?;
198 }
199 if let Some(paint) = &self.stroke {
200 canvas.stroke(&geometry, &self.stroke_style, paint.clone())?;
201 }
202 return Ok(());
203 }
204 if let Some(paint) = &self.fill {
205 canvas.fill(&self.shape, paint.clone(), self.rule)?;
206 }
207 if let Some(paint) = &self.stroke {
208 canvas.stroke(&self.shape, &self.stroke_style, paint.clone())?;
209 }
210 Ok(())
211 }
212}