use crate::error::{Error, Result};
use crate::gltf;
use crate::mesh::{Face, Mesh, MeshBuilder};
use crate::ring::{Branch, Degrees, Point, Pt, Ring, Shading};
use glam::Vec3;
use std::collections::HashMap;
use std::io::Write;
pub struct Husk {
builder: MeshBuilder,
surface: u16,
ring: Option<Ring>,
branches: HashMap<String, Branch>,
}
impl Default for Husk {
fn default() -> Self {
Husk::new()
}
}
impl Husk {
pub fn new() -> Self {
Husk {
builder: Mesh::builder(),
surface: 0,
ring: None,
branches: HashMap::new(),
}
}
fn push_branch_internal(&mut self, label: &str, pos: Vec3) {
if !self.branches.contains_key(label) {
self.branches.insert(label.to_string(), Branch::default());
}
if let Some(branch) = self.branches.get_mut(label) {
branch.push_internal(pos);
}
}
fn push_branch_edge(&mut self, label: &str, v0: usize, v1: usize) {
if !self.branches.contains_key(label) {
self.branches.insert(label.to_string(), Branch::default());
}
if let Some(branch) = self.branches.get_mut(label) {
branch.push_edge(v0, v1);
}
}
fn add_branch_points(&mut self, ring: &Ring) {
for point in ring.points() {
if let Pt::Branch(label, pos) = &point.pt {
self.push_branch_internal(label, *pos);
}
}
}
pub fn ring(&mut self, ring: Ring) -> Result<()> {
let pring = self.ring.take();
let mut ring = match &pring {
Some(pr) => pr.with_ring(&ring),
None => ring,
};
if ring.points().len() == 0 {
ring.make_points(&mut self.builder);
self.add_branch_points(&ring);
}
if let Some(pring) = &pring {
self.make_band(pring, &ring)?;
}
self.ring = Some(ring);
Ok(())
}
fn cap(&mut self) -> Result<()> {
match self.ring.take() {
Some(ring) => self.cap_ring(ring),
None => Ok(()),
}
}
fn cap_ring(&mut self, ring: Ring) -> Result<()> {
let mut pts = ring.points_offset(Degrees(0));
let last = pts.pop().unwrap();
if pts.len() < 2 {
return Ok(());
}
let (order, pos) = ring.make_hub();
let vid = self.builder.push_vtx(pos);
let hub = Point::new(Pt::Vertex(vid), order);
let mut prev = last.clone();
for pt in pts.drain(..) {
self.add_face([&pt, &prev, &hub])?;
prev = pt;
if ring.shading_or_default() == Shading::Flat {
self.surface += 1;
}
}
self.add_face([&last, &prev, &hub])?;
if ring.shading_or_default() == Shading::Flat {
self.surface += 1;
}
Ok(())
}
pub fn branch(&mut self, label: impl AsRef<str>) -> Result<Ring> {
self.cap()?;
let branch = self.take_branch(label.as_ref())?;
Ok(Ring::with_branch(branch, &self.builder))
}
fn take_branch(&mut self, label: &str) -> Result<Branch> {
self.branches
.remove(label)
.ok_or_else(|| Error::UnknownBranchLabel(label.to_string()))
}
fn make_band(&mut self, ring0: &Ring, ring1: &Ring) -> Result<()> {
if ring0.shading_or_default() != Shading::Smooth {
self.surface += 1;
}
let mut pts0 = ring0.points_offset(ring1.half_step());
let mut pts1 = ring1.points_offset(ring0.half_step());
let first0 = pts0.pop().unwrap();
let first1 = pts1.pop().unwrap();
let (mut pt0, mut pt1) = (first0.clone(), first1.clone());
let mut band = Vec::with_capacity(pts0.len() + pts1.len());
band.extend_from_slice(&pts0[..]);
band.append(&mut pts1);
band.sort_by(|a, b| b.order.partial_cmp(&a.order).unwrap());
while let Some(pt) = band.pop() {
self.add_face([&pt1, &pt0, &pt])?;
if pts0.contains(&pt) {
pt0 = pt;
} else {
pt1 = pt;
}
if ring0.shading_or_default() == Shading::Flat {
self.surface += 1;
}
}
if pt1 != first1 {
self.add_face([&pt1, &pt0, &first1])?;
if ring0.shading_or_default() == Shading::Flat {
self.surface += 1;
}
}
if pt0 != first0 {
self.add_face([&first0, &first1, &pt0])?;
if ring0.shading_or_default() == Shading::Flat {
self.surface += 1;
}
}
Ok(())
}
fn add_face(&mut self, pts: [&Point; 3]) -> Result<()> {
match (&pts[0].pt, &pts[1].pt, &pts[2].pt) {
(Pt::Vertex(v0), Pt::Vertex(v1), Pt::Vertex(v2)) => {
let face = Face::new([*v0, *v1, *v2], self.surface);
self.builder.push_face(face);
}
(Pt::Branch(lbl, _), Pt::Vertex(v0), Pt::Vertex(v1))
| (Pt::Vertex(v1), Pt::Branch(lbl, _), Pt::Vertex(v0))
| (Pt::Vertex(v0), Pt::Vertex(v1), Pt::Branch(lbl, _)) => {
self.push_branch_edge(lbl, *v0, *v1);
}
(Pt::Vertex(_v), Pt::Branch(b0, _), Pt::Branch(b1, _))
| (Pt::Branch(b0, _), Pt::Vertex(_v), Pt::Branch(b1, _))
| (Pt::Branch(b0, _), Pt::Branch(b1, _), Pt::Vertex(_v)) => {
if b0 != b1 {
return Err(Error::InvalidBranches(format!(
"{b0} != {b1}"
)));
}
}
(Pt::Branch(b0, _), Pt::Branch(b1, _), Pt::Branch(b2, _)) => {
if b0 != b1 {
return Err(Error::InvalidBranches(format!(
"{b0} != {b1}"
)));
}
if b0 != b2 {
return Err(Error::InvalidBranches(format!(
"{b0} != {b2}"
)));
}
}
}
Ok(())
}
pub fn write_gltf<W: Write>(mut self, writer: W) -> Result<()> {
self.cap()?;
let mesh = self.builder.build();
gltf::export(writer, &mesh)?;
Ok(())
}
}