use super::exact::backend::{rat_one, Rational};
use crate::linalg::Vec3;
use crate::manifold::Manifold;
use crate::types::MeshGL64;
use super::cells::VertTables;
use super::exact::rational::{rat_to_f64, R3};
use super::intersection_graph::Piece;
use super::tri_tri::dominant_axis;
pub struct PropCtx<'a> {
pub num_prop: [usize; 2],
pub tris: [&'a [[Vec3; 3]]; 2],
pub props: [&'a [f64]; 2],
}
impl<'a> PropCtx<'a> {
pub fn out_num_prop(&self) -> usize {
self.num_prop[0].max(self.num_prop[1])
}
}
fn barycentric_r(p: &R3, tri: &[R3; 3]) -> [Rational; 3] {
use super::exact::predicates::tri_normal_r;
let n = tri_normal_r(&tri[0], &tri[1], &tri[2]);
let axis = dominant_axis(&n);
let p2 = p.project_drop(axis);
let a = tri[0].project_drop(axis);
let b = tri[1].project_drop(axis);
let c = tri[2].project_drop(axis);
let total = b.sub(&a).cross(&c.sub(&a));
let w0 = b.sub(&p2).cross(&c.sub(&p2)) / &total;
let w1 = c.sub(&p2).cross(&a.sub(&p2)) / &total;
let w2 = rat_one() - &w0 - &w1;
[w0, w1, w2]
}
fn interpolate_props(ctx: &PropCtx, piece: &Piece, v: &R3, out: usize) -> Vec<f64> {
let m = piece.mesh as usize;
let np = ctx.num_prop[m];
let mut result = vec![0.0f64; out];
if np == 0 {
return result;
}
let base = 3 * piece.tri * np;
let corner = |i: usize| &ctx.props[m][base + i * np..base + (i + 1) * np];
let (c0, c1, c2) = (corner(0), corner(1), corner(2));
let all_const = (0..np).all(|k| c0[k] == c1[k] && c0[k] == c2[k]);
if all_const {
result[..np].copy_from_slice(c0);
return result;
}
let t = ctx.tris[m][piece.tri];
let corners = [
R3::from_vec3(t[0]),
R3::from_vec3(t[1]),
R3::from_vec3(t[2]),
];
let w = barycentric_r(v, &corners);
let wf = [rat_to_f64(&w[0]), rat_to_f64(&w[1]), rat_to_f64(&w[2])];
for k in 0..np {
result[k] = if c0[k] == c1[k] && c0[k] == c2[k] {
c0[k]
} else {
wf[0] * c0[k] + wf[1] * c1[k] + wf[2] * c2[k]
};
}
result
}
pub fn assemble<F: Fn(usize) -> bool>(
pieces: &[Piece],
verts: &[R3],
verts_f64: &[Vec3],
select: F,
props: Option<&PropCtx>,
) -> Manifold {
let out_prop = props.map_or(0, |p| p.out_num_prop());
let selected: Vec<&Piece> = pieces
.iter()
.enumerate()
.filter(|(pi, _)| select(*pi))
.map(|(_, piece)| piece)
.collect();
if selected.is_empty() {
return Manifold::empty();
}
let tris: Vec<[u32; 3]> = selected.iter().map(|piece| piece.vi).collect();
let plan = super::pairing::plan_vertex_splits(&tris, VertTables { verts, verts_f64 });
type Key = (u32, u32, Vec<u64>);
let mut vert_index: rustc_hash::FxHashMap<Key, u64> = rustc_hash::FxHashMap::default();
let mut vert_order: Vec<(u32, u32, Vec<f64>)> = Vec::new();
let mut tri_verts: Vec<u64> = Vec::new();
for (t, piece) in selected.iter().enumerate() {
for (c, &vid) in piece.vi.iter().enumerate() {
let split = plan.as_ref().map_or(0, |p| p[3 * t + c]);
let pvals = match props {
Some(ctx) if out_prop > 0 => {
interpolate_props(ctx, piece, &verts[vid as usize], out_prop)
}
_ => Vec::new(),
};
let key = (vid, split, pvals.iter().map(|x| x.to_bits()).collect());
let next = vert_order.len() as u64;
let id = *vert_index.entry(key).or_insert_with(|| {
vert_order.push((vid, split, pvals));
next
});
tri_verts.push(id);
}
}
let stride = 3 + out_prop;
let mut mesh = MeshGL64::default();
mesh.num_prop = stride as u64;
mesh.vert_properties = Vec::with_capacity(stride * vert_order.len());
for (vid, _, pvals) in &vert_order {
let p = verts_f64[*vid as usize];
mesh.vert_properties.extend([p.x, p.y, p.z]);
mesh.vert_properties.extend(pvals.iter());
}
mesh.tri_verts = tri_verts;
if out_prop > 0 {
let mut by_pos: rustc_hash::FxHashMap<(u32, u32), u64> = rustc_hash::FxHashMap::default();
for (i, (vid, split, _)) in vert_order.iter().enumerate() {
match by_pos.get(&(*vid, *split)) {
Some(&first) => {
mesh.merge_from_vert.push(i as u64);
mesh.merge_to_vert.push(first);
}
None => {
by_pos.insert((*vid, *split), i as u64);
}
}
}
}
let out = Manifold::from_mesh_gl64_robust_assembled(&mesh);
if out.status() == crate::types::Error::NoError && !out.as_impl().is_soup && !out.is_empty() {
let mut imp = out.into_impl();
crate::edge_op::cleanup_topology(&mut imp);
crate::edge_op::collapse_short_edges(&mut imp, 0);
crate::edge_op::collapse_colinear_edges(&mut imp, 0);
crate::face_op::calculate_vert_normals(&mut imp);
imp.remove_unreferenced_verts();
imp.calculate_bbox();
imp.sort_geometry();
return Manifold::from_impl(imp);
}
out
}