use crate::drawing::*;
use crate::{Path2D, Rect, ValidationError};
mod primitives;
pub use primitives::*;
pub trait Shape: Send + Sync {
fn path(&self) -> Result<Path2D, ValidationError>;
fn bounding_box(&self) -> Result<Rect, ValidationError> {
self.path().map(|path| path.bounding_box())
}
fn as_rect(&self) -> Option<Rect> {
None
}
fn fill(self, paint: impl Into<Paint>) -> Styled<Self>
where
Self: Sized,
{
Styled {
shape: self,
fill: Some(paint.into()),
stroke: None,
stroke_style: StrokeStyle::default(),
rule: FillRule::default(),
trim: None,
dash: None,
}
}
fn stroke(self, width: f64, paint: impl Into<Paint>) -> Styled<Self>
where
Self: Sized,
{
Styled {
shape: self,
fill: None,
stroke: Some(paint.into()),
stroke_style: StrokeStyle {
width,
..StrokeStyle::default()
},
rule: FillRule::default(),
trim: None,
dash: None,
}
}
}
impl Shape for Path2D {
fn path(&self) -> Result<Path2D, ValidationError> {
Ok(self.clone())
}
fn bounding_box(&self) -> Result<Rect, ValidationError> {
Ok(Path2D::bounding_box(self))
}
}
impl Shape for Rect {
fn path(&self) -> Result<Path2D, ValidationError> {
Path2D::from_rect(*self)
}
fn bounding_box(&self) -> Result<Rect, ValidationError> {
Ok(*self)
}
fn as_rect(&self) -> Option<Rect> {
Some(*self)
}
}
#[derive(Clone, Debug)]
pub struct Styled<Geometry> {
shape: Geometry,
fill: Option<Paint>,
stroke: Option<Paint>,
stroke_style: StrokeStyle,
rule: FillRule,
trim: Option<(f64, f64)>,
dash: Option<(std::sync::Arc<[f64]>, f64)>,
}
impl<Geometry: Shape> Styled<Geometry> {
pub fn trim(mut self, start: f64, end: f64) -> Self {
self.trim = Some((start, end));
self
}
pub fn dash(mut self, pattern: &[f64], offset: f64) -> Self {
self.dash = Some((std::sync::Arc::from(pattern), offset));
self
}
pub fn fill(mut self, paint: impl Into<Paint>) -> Self {
self.fill = Some(paint.into());
self
}
pub fn stroke(mut self, width: f64, paint: impl Into<Paint>) -> Self {
self.stroke = Some(paint.into());
self.stroke_style.width = width;
self
}
pub fn stroke_style(mut self, style: StrokeStyle) -> Self {
self.stroke_style = style;
self
}
pub fn fill_rule(mut self, rule: FillRule) -> Self {
self.rule = rule;
self
}
}
impl<Geometry: Shape> Drawable for Styled<Geometry> {
fn bounds(&self, _: &DrawContext) -> Result<Rect, ValidationError> {
let bounds = self.shape.bounding_box()?;
if self.stroke.is_some() && validate_rect(bounds, "stroke", "bounds").is_ok() {
self.stroke_style.validate("stroke")?;
Ok(self.stroke_style.bounds(bounds))
} else {
Ok(bounds)
}
}
fn draw(&self, _: &DrawContext, canvas: &mut Canvas<'_>) -> Result<(), ValidationError> {
if self.trim.is_some() || self.dash.is_some() {
let mut geometry = self.shape.path()?;
if let Some((start, end)) = self.trim {
geometry = geometry.try_trim(start, end)?;
}
if let Some((pattern, offset)) = &self.dash {
geometry = geometry.dashed(pattern, *offset)?;
}
if let Some(paint) = &self.fill {
canvas.fill(&geometry, paint.clone(), self.rule)?;
}
if let Some(paint) = &self.stroke {
canvas.stroke(&geometry, &self.stroke_style, paint.clone())?;
}
return Ok(());
}
if let Some(paint) = &self.fill {
canvas.fill(&self.shape, paint.clone(), self.rule)?;
}
if let Some(paint) = &self.stroke {
canvas.stroke(&self.shape, &self.stroke_style, paint.clone())?;
}
Ok(())
}
}