1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
mod batch;
mod circle;
mod fill;
mod geometry;
mod path;
mod stroke;

pub use self::{batch::*, fill::*, path::*, stroke::*};
use crate::{
    math::{Point, Points, Raw, Rect, Scaled},
    scene::{Element, SceneTarget},
    KludgineResult,
};
use circle::Circle;
use geometry::ShapeGeometry;

#[derive(Default, Clone)]
pub struct Shape<S> {
    geometry: ShapeGeometry<S>,
    stroke: Option<Stroke>,
    fill: Option<Fill>,
}

impl Shape<Scaled> {
    pub fn rect(rect: impl Into<Rect<f32, Scaled>>) -> Self {
        let rect = rect.into();
        let path = PathBuilder::new(Point::new(rect.min_x(), rect.min_y()))
            .line_to(Point::new(rect.max_x(), rect.min_y()))
            .line_to(Point::new(rect.max_x(), rect.max_y()))
            .line_to(Point::new(rect.min_x(), rect.max_y()))
            .close()
            .build();

        Self {
            geometry: ShapeGeometry::Path(path),
            stroke: None,
            fill: None,
        }
    }

    pub fn circle(center: Point<f32, Scaled>, radius: Points) -> Self {
        Self {
            geometry: ShapeGeometry::Circle(Circle { center, radius }),
            stroke: None,
            fill: None,
        }
    }

    pub fn polygon(points: impl IntoIterator<Item = Point<f32, Scaled>>) -> Self {
        let mut points = points.into_iter();
        if let Some(start) = points.next() {
            let mut builder = PathBuilder::new(start);
            for point in points {
                builder = builder.line_to(point);
            }

            Self {
                geometry: ShapeGeometry::Path(builder.close().build()),
                stroke: None,
                fill: None,
            }
        } else {
            Self::default()
        }
    }

    pub fn fill(mut self, fill: Fill) -> Self {
        self.fill = Some(fill);
        self
    }

    pub fn stroke(mut self, stroke: Stroke) -> Self {
        self.stroke = Some(stroke);
        self
    }

    pub async fn draw_at(&self, location: Point<f32, Scaled>, scene: &SceneTarget) {
        let translated = self.convert_from_user_to_device(location, scene).await;
        scene.push_element(Element::Shape(translated)).await
    }

    async fn convert_from_user_to_device(
        &self,
        location: Point<f32, Scaled>,
        scene: &SceneTarget,
    ) -> Shape<Raw> {
        Shape {
            geometry: self
                .geometry
                .translate_and_convert_to_device(location, scene)
                .await,
            fill: self.fill.clone(),
            stroke: self.stroke.clone(),
        }
    }
}

impl Shape<Raw> {
    pub(crate) fn build(&self, builder: &mut rgx_lyon::ShapeBuilder) -> KludgineResult<()> {
        self.geometry.build(builder, &self.stroke, &self.fill)
    }
}