use bevy::prelude::*;
use lyon::tessellation::StrokeOptions;
use nannou_core::geom;
use crate::draw;
use crate::draw::Drawing;
use crate::draw::primitive::Primitive;
use crate::draw::primitive::polygon::{self, PolygonInit, PolygonOptions, SetPolygon};
use crate::draw::properties::spatial::{dimension, orientation, position};
use crate::draw::properties::{
SetColor, SetDimensions, SetOrientation, SetPosition, SetStroke, spatial,
};
#[derive(Clone, Debug, Default)]
pub struct Ellipse {
dimensions: spatial::dimension::Properties,
resolution: Option<f32>,
polygon: PolygonInit,
}
pub type DrawingEllipse<'a> = Drawing<'a, Ellipse>;
impl Ellipse {
pub fn stroke<C>(self, color: C) -> Self
where
C: Into<Color>,
{
self.stroke_color(color)
}
pub fn radius(self, radius: f32) -> Self {
let side = radius * 2.0;
self.w_h(side, side)
}
pub fn resolution(mut self, resolution: f32) -> Self {
self.resolution = Some(resolution);
self
}
}
impl draw::render::RenderPrimitive for Ellipse {
fn render_primitive(self, ctxt: draw::render::RenderContext, mesh: &mut Mesh) {
let Ellipse {
dimensions,
polygon,
resolution,
} = self;
let (maybe_x, maybe_y, maybe_z) = (dimensions.x, dimensions.y, dimensions.z);
assert!(
maybe_z.is_none(),
"z dimension support for ellipse is unimplemented"
);
let w = maybe_x.map(f32::abs).unwrap_or(100.0);
let h = maybe_y.map(f32::abs).unwrap_or(100.0);
match resolution {
None => {
let radii = lyon::math::vector(w * 0.5, h * 0.5);
if radii.square_length() > 0.0 {
let centre = lyon::math::point(0.0, 0.0);
let mut builder = lyon::path::Path::svg_builder();
let sweep_angle = lyon::math::Angle::radians(std::f32::consts::PI * 2.0);
let x_rotation = lyon::math::Angle::radians(0.0);
let start = lyon::math::point(w * 0.5, 0.0);
builder.move_to(start);
builder.arc(centre, radii, sweep_angle, x_rotation);
let path = builder.build();
polygon::render_events_themed(
polygon.opts,
|| (&path).into_iter(),
ctxt,
&draw::theme::Primitive::Ellipse,
mesh,
);
}
}
Some(resolution) => {
let rect = geom::Rect::from_w_h(w, h);
let ellipse = geom::Ellipse::new(rect, resolution);
let top_left = rect.top_left();
let bottom_right = rect.bottom_right();
let center = Vec2::new(
(top_left.x + bottom_right.x) / 2.0,
(top_left.y + bottom_right.y) / 2.0,
);
let radii = Vec2::new(
(bottom_right.x - top_left.x) / 2.0,
(bottom_right.y - top_left.y) / 2.0,
);
let points = ellipse.circumference().map(|p| {
let p = Vec2::from(p);
let tex_coords = calculate_tex_coords(&p, ¢er, &radii);
(p, tex_coords)
});
polygon::render_points_themed(
polygon.opts,
true,
points,
ctxt,
&draw::theme::Primitive::Ellipse,
mesh,
);
}
}
}
}
fn calculate_tex_coords(position: &Vec2, center: &Vec2, radius: &Vec2) -> Vec2 {
Vec2::new(
(position.x - center.x) / (2.0 * radius.x) + 0.5,
(position.y - center.y) / (2.0 * radius.y) + 0.5,
)
}
impl SetOrientation for Ellipse {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.polygon)
}
}
impl SetPosition for Ellipse {
fn properties(&mut self) -> &mut position::Properties {
SetPosition::properties(&mut self.polygon)
}
}
impl SetDimensions for Ellipse {
fn properties(&mut self) -> &mut dimension::Properties {
SetDimensions::properties(&mut self.dimensions)
}
}
impl SetColor for Ellipse {
fn color_mut(&mut self) -> &mut Option<Color> {
SetColor::color_mut(&mut self.polygon)
}
}
impl SetStroke for Ellipse {
fn stroke_options_mut(&mut self) -> &mut StrokeOptions {
SetStroke::stroke_options_mut(&mut self.polygon)
}
}
impl SetPolygon for Ellipse {
fn polygon_options_mut(&mut self) -> &mut PolygonOptions {
SetPolygon::polygon_options_mut(&mut self.polygon)
}
}
impl From<Ellipse> for Primitive {
fn from(prim: Ellipse) -> Self {
Primitive::Ellipse(prim)
}
}
impl<'a> DrawingEllipse<'a> {
pub fn stroke<C>(self, color: C) -> Self
where
C: Into<Color>,
{
self.stroke_color(color)
}
pub fn radius(self, radius: f32) -> Self {
let side = radius * 2.0;
self.w_h(side, side)
}
pub fn resolution(self, resolution: f32) -> Self {
update_ellipse(&self.draw, self.index, |ellipse| {
ellipse.resolution = Some(resolution)
});
self
}
}
fn update_ellipse(draw: &crate::draw::Draw, index: usize, f: impl FnOnce(&mut Ellipse)) {
crate::draw::drawing::with_primitive(draw, index, |prim| match prim {
Primitive::Ellipse(ellipse) => f(ellipse),
_ => bevy::log::warn_once!("expected an `Ellipse` primitive"),
})
}