use crate::InnerAtom;
use lyon::path::Path;
use lyon::tessellation::geometry_builder::simple_builder;
use lyon::tessellation::math::point;
use lyon::tessellation::{FillOptions, FillTessellator};
use std::collections::HashMap;
pub use lyon::tessellation::{geometry_builder::VertexBuffers, math::Point, TessellationError};
pub fn tessellate_2d(
poly: geo::Polygon<f64>,
interior: Vec<InnerAtom>,
) -> Result<VertexBuffers<Point, u16>, TessellationError> {
let mut buffers: VertexBuffers<Point, u16> = VertexBuffers::new();
let mut vertex_builder = simple_builder(&mut buffers);
let mut tessellator = FillTessellator::new();
let options = FillOptions::default();
let mut path_builder = Path::builder();
let mut last: Option<geo::Point<f64>> = None;
for p in poly.exterior().points_iter() {
let (x, y) = (p.x() as f32, p.y() as f32);
if last.is_none() {
path_builder.begin(point(x, y));
} else {
path_builder.line_to(point(x, y));
}
last = Some(p);
}
path_builder.end(true);
for hole in poly.interiors() {
let mut last: Option<geo::Point<f64>> = None;
for p in hole.points_iter() {
let (x, y) = (p.x() as f32, p.y() as f32);
if last.is_none() {
path_builder.begin(point(x, y));
} else {
path_builder.line_to(point(x, y));
}
last = Some(p);
}
path_builder.end(true);
}
for f in interior {
if let InnerAtom::Drill {
center,
radius,
plated: _,
} = f
{
use geo::{algorithm::rotate::RotatePoint, Point};
let right_edge: Point<_> = (center.x + radius, center.y).into();
let start = right_edge.rotate_around_point(0.0, center.into());
path_builder.begin(point(start.x() as f32, start.y() as f32));
for i in (0..=360).step_by(8) {
let p = right_edge.rotate_around_point(i as f64, center.into());
path_builder.line_to(point(p.x() as f32, p.y() as f32));
}
path_builder.end(true);
}
}
let path = path_builder.build();
tessellator.tessellate_path(&path, &options, &mut vertex_builder)?;
Ok(buffers)
}
pub fn tessellate_3d(buffer: VertexBuffers<Point, u16>) -> (Vec<[f64; 3]>, Vec<u16>) {
let mut lines: HashMap<(u16, u16), Vec<(usize, bool)>> =
HashMap::with_capacity(buffer.indices.len());
for (i, in3) in buffer.indices.chunks_exact(3).enumerate() {
for (i, verts) in &[
(i * 3 + 0, &[in3[0], in3[1]]),
(i * 3 + 1, &[in3[1], in3[2]]),
(i * 3 + 2, &[in3[2], in3[0]]),
] {
let key = (verts[0].min(verts[1]), verts[1].max(verts[0]));
match lines.get_mut(&key) {
Some(v) => v.push((*i, verts[0] < verts[1])),
None => {
lines.insert(key, vec![(*i, verts[0] < verts[1])]);
}
}
}
}
let mut boundary_lines: Vec<_> = lines
.into_iter()
.filter(|(_k, v)| v.len() == 1)
.map(|(k, v)| (k, v[0])) .collect();
boundary_lines.sort_by(|a, b| a.1 .0.cmp(&b.1 .0));
let mut vertices: Vec<[f64; 3]> =
Vec::with_capacity(2 * buffer.vertices.len() + 6 * boundary_lines.len());
for v in &buffer.vertices {
vertices.push([v.x.into(), v.y.into(), -0.8]);
}
for v in &buffer.vertices {
vertices.push([v.x.into(), v.y.into(), 0.8]);
}
let c = buffer.vertices.len() as u16;
let mut indices: Vec<u16> =
Vec::with_capacity((buffer.indices.len() * 2) + (buffer.vertices.len() * 6));
for i in &buffer.indices {
indices.push(*i);
}
for i3 in buffer.indices.chunks_exact(3) {
indices.push(i3[2] + c); indices.push(i3[1] + c);
indices.push(i3[0] + c);
}
for ((v_low, v_high), (_, original_order)) in boundary_lines {
if !original_order {
indices.push(v_low);
indices.push(v_high);
indices.push(v_low + c);
indices.push(v_high);
indices.push(v_high + c);
indices.push(v_low + c);
} else {
indices.push(v_high);
indices.push(v_low);
indices.push(v_high + c);
indices.push(v_low);
indices.push(v_low + c);
indices.push(v_high + c);
}
}
(vertices, indices)
}