1use crate::drawing::*;
4use crate::{Path2D, Rect, ValidationError};
5
6mod primitives;
7
8pub use primitives::*;
9
10pub trait Shape: Send + Sync {
16 fn path(&self) -> Result<Path2D, ValidationError>;
18
19 fn bounding_box(&self) -> Result<Rect, ValidationError> {
21 self.path().map(|path| path.bounding_box())
22 }
23
24 fn as_rect(&self) -> Option<Rect> {
26 None
27 }
28
29 fn fill(self, paint: impl Into<Paint>) -> Styled<Self>
31 where
32 Self: Sized,
33 {
34 Styled {
35 shape: self,
36 fill: Some(paint.into()),
37 stroke: None,
38 stroke_style: StrokeStyle::default(),
39 rule: FillRule::default(),
40 trim: None,
41 dash: None,
42 }
43 }
44
45 fn stroke(self, width: f64, paint: impl Into<Paint>) -> Styled<Self>
47 where
48 Self: Sized,
49 {
50 Styled {
51 shape: self,
52 fill: None,
53 stroke: Some(paint.into()),
54 stroke_style: StrokeStyle {
55 width,
56 ..StrokeStyle::default()
57 },
58 rule: FillRule::default(),
59 trim: None,
60 dash: None,
61 }
62 }
63}
64
65impl Shape for Path2D {
66 fn path(&self) -> Result<Path2D, ValidationError> {
67 Ok(self.clone())
68 }
69
70 fn bounding_box(&self) -> Result<Rect, ValidationError> {
71 Ok(Path2D::bounding_box(self))
72 }
73}
74
75impl Shape for Rect {
76 fn path(&self) -> Result<Path2D, ValidationError> {
77 Path2D::from_rect(*self)
78 }
79
80 fn bounding_box(&self) -> Result<Rect, ValidationError> {
81 Ok(*self)
82 }
83
84 fn as_rect(&self) -> Option<Rect> {
85 Some(*self)
86 }
87}
88
89#[derive(Clone, Debug)]
94pub struct Styled<Geometry> {
95 shape: Geometry,
96 fill: Option<Paint>,
97 stroke: Option<Paint>,
98 stroke_style: StrokeStyle,
99 rule: FillRule,
100 trim: Option<(f64, f64)>,
101 dash: Option<(std::sync::Arc<[f64]>, f64)>,
102}
103
104impl<Geometry: Shape> Styled<Geometry> {
105 pub fn trim(mut self, start: f64, end: f64) -> Self {
108 self.trim = Some((start, end));
109 self
110 }
111
112 pub fn dash(mut self, pattern: &[f64], offset: f64) -> Self {
115 self.dash = Some((std::sync::Arc::from(pattern), offset));
116 self
117 }
118
119 pub fn fill(mut self, paint: impl Into<Paint>) -> Self {
121 self.fill = Some(paint.into());
122 self
123 }
124
125 pub fn stroke(mut self, width: f64, paint: impl Into<Paint>) -> Self {
127 self.stroke = Some(paint.into());
128 self.stroke_style.width = width;
129 self
130 }
131
132 pub fn stroke_style(mut self, style: StrokeStyle) -> Self {
134 self.stroke_style = style;
135 self
136 }
137
138 pub fn fill_rule(mut self, rule: FillRule) -> Self {
140 self.rule = rule;
141 self
142 }
143}
144
145impl<Geometry: Shape> Drawable for Styled<Geometry> {
146 fn bounds(&self, _: &DrawContext) -> Result<Rect, ValidationError> {
147 let bounds = self.shape.bounding_box()?;
148 if self.stroke.is_some() && validate_rect(bounds, "stroke", "bounds").is_ok() {
149 self.stroke_style.validate("stroke")?;
150 Ok(self.stroke_style.bounds(bounds))
151 } else {
152 Ok(bounds)
153 }
154 }
155
156 fn draw(&self, _: &DrawContext, canvas: &mut Canvas<'_>) -> Result<(), ValidationError> {
157 if self.trim.is_some() || self.dash.is_some() {
158 let mut geometry = self.shape.path()?;
159 if let Some((start, end)) = self.trim {
160 geometry = geometry.try_trim(start, end)?;
161 }
162 if let Some((pattern, offset)) = &self.dash {
163 geometry = geometry.dashed(pattern, *offset)?;
164 }
165 if let Some(paint) = &self.fill {
166 canvas.fill(&geometry, paint.clone(), self.rule)?;
167 }
168 if let Some(paint) = &self.stroke {
169 canvas.stroke(&geometry, &self.stroke_style, paint.clone())?;
170 }
171 return Ok(());
172 }
173 if let Some(paint) = &self.fill {
174 canvas.fill(&self.shape, paint.clone(), self.rule)?;
175 }
176 if let Some(paint) = &self.stroke {
177 canvas.stroke(&self.shape, &self.stroke_style, paint.clone())?;
178 }
179 Ok(())
180 }
181}