use crate::geom::{
self, Rect, Tri,
scalar::{self, Scalar},
};
use crate::math::{
self,
num_traits::{Float, NumCast},
};
pub trait EllipseScalar: Float + Scalar {
const TAU: Self;
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Ellipse<S = scalar::Default> {
pub rect: Rect<S>,
pub resolution: S,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Section<S = scalar::Default> {
pub ellipse: Ellipse<S>,
pub offset_radians: S,
pub section_radians: S,
}
#[derive(Clone, Debug)]
#[allow(missing_copy_implementations)]
pub struct Circumference<S = scalar::Default> {
index: S,
num_points: S,
middle: [S; 2],
rad_step: S,
rad_offset: S,
half_w: S,
half_h: S,
}
#[derive(Clone, Debug)]
#[allow(missing_copy_implementations)]
pub struct TriangleVertices<S = scalar::Default> {
middle: Option<[S; 2]>,
circumference: Circumference<S>,
}
#[derive(Clone, Debug)]
#[allow(missing_copy_implementations)]
pub struct TriangleIndices {
yield_middle: bool,
left: Option<usize>,
right: Option<usize>,
total: usize,
}
#[derive(Clone, Debug)]
pub struct Triangles<S = scalar::Default> {
last: [S; 2],
points: Circumference<S>,
}
impl<S> Ellipse<S>
where
S: EllipseScalar,
{
pub fn new(rect: Rect<S>, resolution: S) -> Self {
Ellipse { rect, resolution }
}
pub fn section(self, offset_radians: S, section_radians: S) -> Section<S> {
Section {
ellipse: self,
offset_radians,
section_radians,
}
}
pub fn circumference(self) -> Circumference<S> {
let Ellipse { rect, resolution } = self;
Circumference::new(rect, resolution)
}
pub fn triangles(self) -> Triangles<S> {
self.circumference().triangles()
}
pub fn triangle_indices(&self) -> (TriangleVertices<S>, TriangleIndices) {
self.circumference().triangle_indices()
}
}
impl<S> Section<S>
where
S: EllipseScalar,
{
pub fn circumference(self) -> Circumference<S> {
let Section {
ellipse,
offset_radians,
section_radians,
} = self;
let circ = Circumference::new_section(ellipse.rect, ellipse.resolution, section_radians);
circ.offset_radians(offset_radians)
}
pub fn triangles(self) -> Triangles<S> {
self.circumference().triangles()
}
pub fn triangle_indices(&self) -> (TriangleVertices<S>, TriangleIndices) {
self.circumference().triangle_indices()
}
}
impl<S> Circumference<S>
where
S: EllipseScalar,
{
fn new_inner(rect: Rect<S>, num_points: S, rad_step: S) -> Self {
let (x, y, w, h) = rect.x_y_w_h();
let two = math::two();
Circumference {
index: S::zero(),
num_points,
middle: [x, y],
half_w: w / two,
half_h: h / two,
rad_step,
rad_offset: S::zero(),
}
}
pub fn new(rect: Rect<S>, mut resolution: S) -> Self {
resolution = crate::math::partial_max(resolution, S::one());
Self::new_section(rect, resolution, S::TAU)
}
pub fn new_section(rect: Rect<S>, resolution: S, radians: S) -> Self {
Self::new_inner(rect, resolution + S::one(), radians / resolution)
}
pub fn section(mut self, radians: S) -> Self {
let resolution = self.num_points - S::one();
self.rad_step = radians / resolution;
self
}
pub fn offset_radians(mut self, radians: S) -> Self {
self.rad_offset = radians;
self
}
pub fn triangles(mut self) -> Triangles<S> {
let last = self.next().unwrap_or(self.middle);
Triangles { last, points: self }
}
pub fn triangle_indices(self) -> (TriangleVertices<S>, TriangleIndices) {
let middle = Some(self.middle);
let num_vertices = self.len();
let circumference = self;
let vertices = TriangleVertices {
middle,
circumference,
};
let indices = TriangleIndices {
yield_middle: true,
left: Some(1),
right: Some(2),
total: num_vertices,
};
(vertices, indices)
}
}
impl EllipseScalar for f32 {
const TAU: Self = core::f32::consts::TAU;
}
impl EllipseScalar for f64 {
const TAU: Self = core::f64::consts::TAU;
}
impl<S> Iterator for Circumference<S>
where
S: EllipseScalar,
{
type Item = [S; 2];
fn next(&mut self) -> Option<Self::Item> {
let Circumference {
ref mut index,
num_points,
middle: [mx, my],
rad_step,
rad_offset,
half_w,
half_h,
} = *self;
if *index >= num_points {
return None;
}
let x = mx + half_w * (rad_offset + rad_step * *index).cos();
let y = my + half_h * (rad_offset + rad_step * *index).sin();
*index += S::one();
Some([x, y])
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<S> ExactSizeIterator for Circumference<S>
where
S: EllipseScalar + NumCast,
{
fn len(&self) -> usize {
NumCast::from(self.num_points - self.index).unwrap()
}
}
impl<S> Iterator for TriangleVertices<S>
where
S: EllipseScalar,
{
type Item = [S; 2];
fn next(&mut self) -> Option<Self::Item> {
self.middle.take().or_else(|| self.circumference.next())
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<S> ExactSizeIterator for TriangleVertices<S>
where
S: EllipseScalar,
{
fn len(&self) -> usize {
(if self.middle.is_some() { 1 } else { 0 }) + self.circumference.len()
}
}
impl Iterator for TriangleIndices {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.yield_middle {
self.yield_middle = false;
Some(0)
} else if let Some(left) = self.left.take() {
Some(left)
} else if let Some(right) = self.right.take() {
if right < self.total {
self.yield_middle = true;
self.left = Some(right);
self.right = Some(right + 1);
}
Some(right)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl ExactSizeIterator for TriangleIndices {
fn len(&self) -> usize {
if let Some(right) = self.right {
let n_tris = self.total - right;
let remaining_middle = if self.yield_middle { 1 } else { 0 };
let remaining_left = if self.left.is_some() { 1 } else { 0 };
let remaining_right = 1;
n_tris * geom::tri::NUM_VERTICES as usize
+ remaining_middle
+ remaining_left
+ remaining_right
} else {
0
}
}
}
impl<S> Iterator for Triangles<S>
where
S: EllipseScalar,
{
type Item = Tri<[S; 2]>;
fn next(&mut self) -> Option<Self::Item> {
let Triangles {
ref mut points,
ref mut last,
} = *self;
points.next().map(|next| {
let triangle = Tri([points.middle, *last, next]);
*last = next;
triangle
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<S> ExactSizeIterator for Triangles<S>
where
S: EllipseScalar,
{
fn len(&self) -> usize {
self.points.len()
}
}