nannou/draw/primitive/
ellipse.rs

1use crate::color::conv::IntoLinSrgba;
2use crate::draw;
3use crate::draw::primitive::polygon::{self, PolygonInit, PolygonOptions, SetPolygon};
4use crate::draw::primitive::Primitive;
5use crate::draw::properties::spatial::{dimension, orientation, position};
6use crate::draw::properties::{
7    spatial, ColorScalar, LinSrgba, SetColor, SetDimensions, SetOrientation, SetPosition, SetStroke,
8};
9use crate::draw::Drawing;
10use crate::geom;
11use crate::glam::Vec2;
12use lyon::tessellation::StrokeOptions;
13
14/// Properties related to drawing an **Ellipse**.
15#[derive(Clone, Debug, Default)]
16pub struct Ellipse {
17    dimensions: spatial::dimension::Properties,
18    resolution: Option<f32>,
19    polygon: PolygonInit,
20}
21
22/// The drawing context for an ellipse.
23pub type DrawingEllipse<'a> = Drawing<'a, Ellipse>;
24
25// Ellipse-specific methods.
26
27impl Ellipse {
28    /// Stroke the outline with the given color.
29    pub fn stroke<C>(self, color: C) -> Self
30    where
31        C: IntoLinSrgba<ColorScalar>,
32    {
33        self.stroke_color(color)
34    }
35
36    /// Specify the width and height of the **Ellipse** via a given **radius**.
37    pub fn radius(self, radius: f32) -> Self {
38        let side = radius * 2.0;
39        self.w_h(side, side)
40    }
41
42    /// The number of sides used to draw the ellipse.
43    ///
44    /// By default, ellipse does not use a resolution, but rather uses a stroke tolerance to
45    /// determine how many vertices to use during tessellation.
46    pub fn resolution(mut self, resolution: f32) -> Self {
47        self.resolution = Some(resolution);
48        self
49    }
50}
51
52// Trait implementations.
53
54impl draw::renderer::RenderPrimitive for Ellipse {
55    fn render_primitive(
56        self,
57        ctxt: draw::renderer::RenderContext,
58        mesh: &mut draw::Mesh,
59    ) -> draw::renderer::PrimitiveRender {
60        let Ellipse {
61            dimensions,
62            polygon,
63            resolution,
64        } = self;
65
66        // First get the dimensions of the ellipse.
67        let (maybe_x, maybe_y, maybe_z) = (dimensions.x, dimensions.y, dimensions.z);
68        assert!(
69            maybe_z.is_none(),
70            "z dimension support for ellipse is unimplemented"
71        );
72
73        let w = maybe_x.map(f32::abs).unwrap_or(100.0);
74        let h = maybe_y.map(f32::abs).unwrap_or(100.0);
75        match resolution {
76            None => {
77                // Determine the transform to apply to all points.
78                let radii = lyon::math::vector(w * 0.5, h * 0.5);
79                if radii.square_length() > 0.0 {
80                    let centre = lyon::math::point(0.0, 0.0);
81                    let mut builder = lyon::path::Path::svg_builder();
82                    let sweep_angle = lyon::math::Angle::radians(std::f32::consts::PI * 2.0);
83                    let x_rotation = lyon::math::Angle::radians(0.0);
84                    let start = lyon::math::point(w * 0.5, 0.0);
85                    builder.move_to(start);
86                    builder.arc(centre, radii, sweep_angle, x_rotation);
87                    let path = builder.build();
88                    polygon::render_events_themed(
89                        polygon.opts,
90                        || (&path).into_iter(),
91                        ctxt,
92                        &draw::theme::Primitive::Ellipse,
93                        mesh,
94                    );
95                }
96            }
97            Some(resolution) => {
98                let rect = geom::Rect::from_w_h(w, h);
99                let ellipse = geom::Ellipse::new(rect, resolution);
100                let points = ellipse.circumference().map(Vec2::from);
101                polygon::render_points_themed(
102                    polygon.opts,
103                    points,
104                    ctxt,
105                    &draw::theme::Primitive::Ellipse,
106                    mesh,
107                );
108            }
109        }
110
111        draw::renderer::PrimitiveRender::default()
112    }
113}
114
115impl SetOrientation for Ellipse {
116    fn properties(&mut self) -> &mut orientation::Properties {
117        SetOrientation::properties(&mut self.polygon)
118    }
119}
120
121impl SetPosition for Ellipse {
122    fn properties(&mut self) -> &mut position::Properties {
123        SetPosition::properties(&mut self.polygon)
124    }
125}
126
127impl SetDimensions for Ellipse {
128    fn properties(&mut self) -> &mut dimension::Properties {
129        SetDimensions::properties(&mut self.dimensions)
130    }
131}
132
133impl SetColor<ColorScalar> for Ellipse {
134    fn rgba_mut(&mut self) -> &mut Option<LinSrgba> {
135        SetColor::rgba_mut(&mut self.polygon)
136    }
137}
138
139impl SetStroke for Ellipse {
140    fn stroke_options_mut(&mut self) -> &mut StrokeOptions {
141        SetStroke::stroke_options_mut(&mut self.polygon)
142    }
143}
144
145impl SetPolygon for Ellipse {
146    fn polygon_options_mut(&mut self) -> &mut PolygonOptions {
147        SetPolygon::polygon_options_mut(&mut self.polygon)
148    }
149}
150
151// Primitive conversion.
152
153impl From<Ellipse> for Primitive {
154    fn from(prim: Ellipse) -> Self {
155        Primitive::Ellipse(prim)
156    }
157}
158
159impl Into<Option<Ellipse>> for Primitive {
160    fn into(self) -> Option<Ellipse> {
161        match self {
162            Primitive::Ellipse(prim) => Some(prim),
163            _ => None,
164        }
165    }
166}
167
168// Drawing methods.
169
170impl<'a> DrawingEllipse<'a> {
171    /// Stroke the outline with the given color.
172    pub fn stroke<C>(self, color: C) -> Self
173    where
174        C: IntoLinSrgba<ColorScalar>,
175    {
176        self.map_ty(|ty| ty.stroke(color))
177    }
178
179    /// Specify the width and height of the **Ellipse** via a given **radius**.
180    pub fn radius(self, radius: f32) -> Self {
181        self.map_ty(|ty| ty.radius(radius))
182    }
183
184    /// The number of sides used to draw the ellipse.
185    pub fn resolution(self, resolution: f32) -> Self {
186        self.map_ty(|ty| ty.resolution(resolution))
187    }
188}