use alloc::vec;
use alloc::vec::Vec;
use i_triangle::float::triangulation::Triangulation;
use i_triangle::i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_triangle::i_overlay::i_float::float::number::FloatNumber;
use i_triangle::int::triangulation::IndexType;
use crate::path::style::StrokeStyle;
struct Segment<P> {
a: P,
b: P,
direction: P
}
impl<P> Segment<P>
where
P: FloatPointCompatible,
{
fn new(a: P, b: P) -> Segment<P> {
let dx = b.x() - a.x();
let dy = b.y() - a.y();
let length = (dx * dx + dy * dy).sqrt();
let x = dx / length;
let y = dy / length;
let direction = P::from_xy(x, y);
Self {
a,
b,
direction,
}
}
}
pub struct ButtStrokeBuilder<P: FloatPointCompatible> {
stroke_style: StrokeStyle<P::Scalar>,
}
impl<P: FloatPointCompatible> ButtStrokeBuilder<P> {
pub fn new(stroke_style: StrokeStyle<P::Scalar>) -> ButtStrokeBuilder<P> {
Self { stroke_style }
}
pub fn build_closed_path_mesh<I: IndexType>(&self, path: &[P]) -> Triangulation<P, I> {
let n = path.len();
if n < 2 {
return Triangulation { points: vec![], indices: vec![] };
}
let mut points = Vec::with_capacity(4 * n);
let mut indices = Vec::with_capacity(12 * n);
let r = P::Scalar::from_float(0.5) * self.stroke_style.width;
let a = path[(n + n - 2) % n];
let b = path[n - 1];
let mut seg0 = Segment::new(a, b);
for i in 0..n {
let c = path[i];
let seg1 = Segment::new(seg0.b, c);
self.join_butt_segment(&mut points, &mut indices, &seg0, r);
let is_close = i + 1 == n;
self.join_butt_joint(&mut points, &mut indices, &seg0, &seg1, is_close);
seg0 = seg1;
}
Triangulation { points, indices }
}
pub fn build_open_path_mesh<I: IndexType>(&self, path: &[P]) -> Triangulation<P, I> {
let n = path.len();
if n < 2 {
return Triangulation { points: vec![], indices: vec![] };
}
let mut points = Vec::with_capacity(4 * n);
let mut indices = Vec::with_capacity(12 * n);
let r = P::Scalar::from_float(0.5) * self.stroke_style.width;
let a = path[0];
let b = path[1];
let mut seg0 = Segment::new(a, b);
self.join_butt_segment(&mut points, &mut indices, &seg0, r);
for &c in path.iter().skip(2) {
let seg1 = Segment::new(seg0.b, c);
self.join_butt_joint(&mut points, &mut indices, &seg0, &seg1, false);
self.join_butt_segment(&mut points, &mut indices, &seg1, r);
seg0 = seg1;
}
Triangulation { points, indices }
}
fn join_butt_joint<I: IndexType>(&self, points: &mut Vec<P>, indices: &mut Vec<I>, seg0: &Segment<P>, seg1: &Segment<P>, is_last: bool) {
let v0 = Self::cw_rotate_90(&seg0.direction);
let v1 = Self::cw_rotate_90(&seg1.direction);
let cross = Self::cross_product(&v0, &v1).to_f64();
if cross.abs() < 1e-7 {
return;
}
let m = points.len(); points.push(seg0.b);
let (a, b) = if cross > 0.0 {
let a = points.len() - 2;
let b = if is_last { 1 } else { points.len() + 1 };
(a, b)
} else {
let a = points.len() - 3;
let b = if is_last { 0 } else { points.len() };
(a, b)
};
let i0 = I::try_from(a).unwrap_or(I::ZERO);
let i1 = I::try_from(m).unwrap_or(I::ZERO);
let i2 = I::try_from(b).unwrap_or(I::ZERO);
indices.extend(&[i0, i1, i2]);
}
fn join_butt_segment<I: IndexType>(&self, points: &mut Vec<P>, indices: &mut Vec<I>, segment: &Segment<P>, r: P::Scalar) {
let ortho = Self::cw_rotate_90(&segment.direction);
let vr = Self::mul(r, &ortho);
let s_top = Self::add(&segment.a, &vr);
let s_bot = Self::sub(&segment.a, &vr);
let e_top = Self::add(&segment.b, &vr);
let e_bot = Self::sub(&segment.b, &vr);
let start_index = points.len();
points.push(s_top);
points.push(s_bot);
points.push(e_top);
points.push(e_bot);
let i0 = I::try_from(start_index).unwrap_or(I::ZERO);
let i1 = I::try_from(start_index + 1).unwrap_or(I::ZERO);
let i2 = I::try_from(start_index + 2).unwrap_or(I::ZERO);
let i3 = I::try_from(start_index + 3).unwrap_or(I::ZERO);
indices.extend(&[
i0, i1, i2,
i1, i3, i2,
]);
}
#[inline]
fn cw_rotate_90(vector: &P) -> P {
P::from_xy(-vector.y(), vector.x())
}
#[inline]
fn cross_product(a: &P, b: &P) -> P::Scalar {
a.x() * b.y() - a.y() * b.x()
}
#[inline]
fn add(a: &P, b: &P) -> P {
P::from_xy(a.x() + b.x(), a.y() + b.y())
}
#[inline]
fn sub(a: &P, b: &P) -> P {
P::from_xy(a.x() - b.x(), a.y() - b.y())
}
#[inline]
fn mul(s: P::Scalar, a: &P) -> P {
P::from_xy(s * a.x(), s * a.y())
}
}
#[cfg(test)]
mod tests {
use i_triangle::float::triangulation::Triangulation;
use i_triangle::i_overlay::i_float::float::point::FloatPoint;
use crate::path::butt::ButtStrokeBuilder;
use crate::path::style::StrokeStyle;
#[test]
fn test_0() {
let path = [
FloatPoint::new(0.0, 0.0),
FloatPoint::new(0.0, 10.0),
FloatPoint::new(10.0, 10.0),
FloatPoint::new(10.0, 0.0),
];
let stroke_builder = ButtStrokeBuilder::new(StrokeStyle::with_width(2.0));
let triangulation: Triangulation<FloatPoint<f64>, u16> = stroke_builder.build_closed_path_mesh(&path);
}
}