use crate::geom::{self, ellipse, quad, scalar, tri, vec2, Point2, Rect};
use crate::math::{two, BaseFloat, EuclideanSpace, InnerSpace};
pub mod cap;
pub mod join;
pub type Quad<S = scalar::Default> = quad::Quad<Point2<S>>;
pub type Tri<S = scalar::Default> = tri::Tri<Point2<S>>;
pub type Vertices<S = scalar::Default> = quad::Vertices<Point2<S>>;
pub type Triangles<S = scalar::Default> = quad::Triangles<Point2<S>>;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Line<S = scalar::Default> {
pub start: Point2<S>,
pub end: Point2<S>,
pub half_thickness: S,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Cap {
Butt,
Round {
resolution: usize,
},
Square,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Capped<S = scalar::Default> {
pub line: Line<S>,
pub cap: Cap,
}
#[derive(Clone, Debug)]
pub struct CappedVertices<S = scalar::Default> {
start: Option<cap::Vertices<S>>,
line: Option<Vertices<S>>,
end: Option<cap::Vertices<S>>,
}
impl<S> Line<S>
where
S: BaseFloat,
{
pub fn new(start: Point2<S>, end: Point2<S>, half_thickness: S) -> Self {
Line {
start,
end,
half_thickness,
}
}
pub fn centroid(&self) -> Point2<S> {
EuclideanSpace::midpoint(self.start, self.end)
}
pub fn bounding_rect(&self) -> Rect<S> {
self.quad_corners().bounding_rect()
}
pub fn quad_corners(&self) -> Quad<S> {
quad_corners(self.start, self.end, self.half_thickness)
}
pub fn quad_corners_iter(&self) -> Vertices<S> {
quad::vertices(self.quad_corners())
}
pub fn triangles(&self) -> (Tri<S>, Tri<S>) {
triangles(self.start, self.end, self.half_thickness)
}
pub fn triangles_iter(&self) -> Triangles<S> {
triangles_iter(self.start, self.end, self.half_thickness)
}
pub fn contains(&self, point: &Point2<S>) -> Option<Tri<S>> {
contains(self.start, self.end, self.half_thickness, point)
}
pub fn start_edge(&self) -> (Point2<S>, Point2<S>) {
let (a, b, _, _) = self.quad_corners().into();
(a, b)
}
pub fn end_edge(&self) -> (Point2<S>, Point2<S>) {
let (_, _, c, d) = self.quad_corners().into();
(c, d)
}
pub fn start_cap_round(&self, resolution: usize) -> ellipse::Section<S> {
let (a, b) = self.start_edge();
cap::round::ellipse_section(a, b, resolution)
}
pub fn end_cap_round(&self, resolution: usize) -> ellipse::Section<S> {
let (c, d) = self.end_edge();
cap::round::ellipse_section(c, d, resolution)
}
pub fn start_cap_square(&self) -> Quad<S> {
let (a, b) = self.start_edge();
cap::square::quad(a, b, self.half_thickness)
}
pub fn end_cap_square(&self) -> Quad<S> {
let (c, d) = self.end_edge();
cap::square::quad(c, d, self.half_thickness)
}
pub fn caps_round(&self, resolution: usize) -> (ellipse::Section<S>, ellipse::Section<S>) {
let (a, b, c, d) = self.quad_corners().into();
let start = cap::round::ellipse_section(a, b, resolution);
let end = cap::round::ellipse_section(c, d, resolution);
(start, end)
}
pub fn caps_square(&self) -> (Quad<S>, Quad<S>) {
let (a, b, c, d) = self.quad_corners().into();
let start = cap::square::quad(a, b, self.half_thickness);
let end = cap::square::quad(c, d, self.half_thickness);
(start, end)
}
pub fn capped_butt(self) -> Capped<S> {
let line = self;
let cap = Cap::Butt;
Capped { line, cap }
}
pub fn capped_round(self, resolution: usize) -> Capped<S> {
let line = self;
let cap = Cap::Round { resolution };
Capped { line, cap }
}
pub fn capped_square(self) -> Capped<S> {
let line = self;
let cap = Cap::Square;
Capped { line, cap }
}
}
impl<S> Capped<S>
where
S: BaseFloat,
{
pub fn vertices(&self) -> CappedVertices<S> {
let (start, end) = match self.cap {
Cap::Butt => (cap::Vertices::Butt, cap::Vertices::Butt),
Cap::Round { resolution } => {
let (start_cap, end_cap) = self.line.caps_round(resolution);
let start = cap::Vertices::Round(start_cap.circumference());
let end = cap::Vertices::Round(end_cap.circumference());
(start, end)
}
Cap::Square => {
let (start_cap, end_cap) = self.line.caps_square();
let start = cap::Vertices::Square(start_cap.vertices());
let end = cap::Vertices::Square(end_cap.vertices());
(start, end)
}
};
let line = self.line.quad_corners_iter();
CappedVertices {
start: Some(start),
line: Some(line),
end: Some(end),
}
}
pub fn polygon(&self) -> geom::Polygon<CappedVertices<S>> {
let vertices = self.vertices();
geom::Polygon { points: vertices }
}
}
pub fn quad_corners<S>(a: Point2<S>, b: Point2<S>, half_thickness: S) -> Quad<S>
where
S: BaseFloat,
{
let direction = b - a;
let unit = direction.normalize();
let neg_1 = S::from(-1).unwrap();
let normal = vec2(unit.y * neg_1, unit.x);
let n = normal.normalize_to(half_thickness);
let neg_n = n * neg_1;
let r1 = a + neg_n;
let r2 = a + n;
let r3 = b + n;
let r4 = b + neg_n;
let quad = Quad::from([r1, r2, r3, r4]);
quad
}
pub fn triangles<S>(a: Point2<S>, b: Point2<S>, half_thickness: S) -> (Tri<S>, Tri<S>)
where
S: BaseFloat,
{
let q = quad_corners(a, b, half_thickness);
quad::triangles(&q)
}
pub fn triangles_iter<S>(a: Point2<S>, b: Point2<S>, half_thickness: S) -> Triangles<S>
where
S: BaseFloat,
{
let q = quad_corners(a, b, half_thickness);
let tris = quad::triangles_iter(&q);
tris
}
pub fn contains<S>(a: Point2<S>, b: Point2<S>, thickness: S, point: &Point2<S>) -> Option<Tri<S>>
where
S: BaseFloat,
{
let half_thickness = thickness / two::<S>();
let tris = triangles_iter(a, b, half_thickness);
tri::iter_contains(tris, point)
}
impl Default for Cap {
fn default() -> Self {
Cap::Butt
}
}
impl<S> Iterator for CappedVertices<S>
where
S: BaseFloat,
{
type Item = Point2<S>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(start) = self.start.as_mut() {
if let Some(next) = start.next() {
return Some(next);
}
}
if let Some(line) = self.line.as_mut() {
if let Some(next) = line.next() {
return Some(next);
}
}
if let Some(end) = self.end.as_mut() {
if let Some(next) = end.next() {
return Some(next);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<S> ExactSizeIterator for CappedVertices<S>
where
S: BaseFloat,
{
fn len(&self) -> usize {
let start = self.start.as_ref().map(|s| s.len()).unwrap_or(0);
let line = self.line.as_ref().map(|l| l.len()).unwrap_or(0);
let end = self.end.as_ref().map(|e| e.len()).unwrap_or(0);
start + line + end
}
}