use std::collections::BTreeMap;
use crate::impl_mesh::ManifoldImpl;
use crate::linalg::{Vec3, dot};
use crate::types::{Halfedge, TriRef};
#[derive(Clone)]
pub(super) struct EdgePos {
edge_pos: f64,
vert: i32,
collision_id: i32,
is_start: bool,
}
impl EdgePos {
fn sort_key(&self) -> (OrderedF64, i32) {
(OrderedF64(self.edge_pos), self.collision_id)
}
}
#[derive(Clone, Copy, PartialEq)]
struct OrderedF64(f64);
impl Eq for OrderedF64 {}
impl PartialOrd for OrderedF64 {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OrderedF64 {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap_or(std::cmp::Ordering::Equal)
}
}
#[inline]
pub(super) fn abs_sum(a: i32, b: i32) -> i32 {
a.abs() + b.abs()
}
pub(super) fn exclusive_scan_abs(input: &[i32], init: i32) -> Vec<i32> {
let mut result = Vec::with_capacity(input.len());
let mut acc = init;
for &v in input {
result.push(acc);
acc = abs_sum(acc, v);
}
result
}
pub(super) fn size_output(
out_r: &mut ManifoldImpl,
in_p: &ManifoldImpl,
in_q: &ManifoldImpl,
i03: &[i32],
i30: &[i32],
i12: &[i32],
i21: &[i32],
p1q2: &[[i32; 2]],
p2q1: &[[i32; 2]],
invert_q: bool,
) -> (Vec<i32>, Vec<i32>) {
let num_tri_p = in_p.num_tri();
let num_tri_q = in_q.num_tri();
let mut sides_per_face = vec![0i32; num_tri_p + num_tri_q];
for face in 0..num_tri_p {
for j in 0..3 {
let v = in_p.halfedge[3 * face + j].start_vert as usize;
sides_per_face[face] += i03[v].abs();
}
}
for face in 0..num_tri_q {
for j in 0..3 {
let v = in_q.halfedge[3 * face + j].start_vert as usize;
sides_per_face[num_tri_p + face] += i30[v].abs();
}
}
for (idx, pair) in p1q2.iter().enumerate() {
let edge_p = pair[0] as usize;
let face_q = pair[1] as usize;
let inclusion = i12[idx].abs();
sides_per_face[num_tri_p + face_q] += inclusion;
let half = in_p.halfedge[edge_p];
sides_per_face[edge_p / 3] += inclusion;
sides_per_face[half.paired_halfedge as usize / 3] += inclusion;
}
for (idx, pair) in p2q1.iter().enumerate() {
let face_p = pair[0] as usize;
let edge_q = pair[1] as usize;
let inclusion = i21[idx].abs();
sides_per_face[face_p] += inclusion;
let half = in_q.halfedge[edge_q];
sides_per_face[num_tri_p + edge_q / 3] += inclusion;
sides_per_face[num_tri_p + half.paired_halfedge as usize / 3] += inclusion;
}
let mut face_pq2r = vec![0i32; num_tri_p + num_tri_q];
let mut count = 0i32;
for i in 0..sides_per_face.len() {
face_pq2r[i] = count;
if sides_per_face[i] > 0 {
count += 1;
}
}
let num_face_r = count as usize;
out_r.face_normal.resize(num_face_r, Vec3::splat(0.0));
let mut out_idx = 0;
for i in 0..num_tri_p {
if sides_per_face[i] > 0 {
out_r.face_normal[out_idx] = in_p.face_normal[i];
out_idx += 1;
}
}
for i in 0..num_tri_q {
if sides_per_face[num_tri_p + i] > 0 {
let normal = in_q.face_normal[i];
out_r.face_normal[out_idx] = if invert_q {
Vec3::new(-normal.x, -normal.y, -normal.z)
} else {
normal
};
out_idx += 1;
}
}
let active_sides: Vec<i32> = sides_per_face.iter().filter(|&&s| s > 0).copied().collect();
let mut face_edge = vec![0i32; active_sides.len() + 1];
for (i, &s) in active_sides.iter().enumerate() {
face_edge[i + 1] = face_edge[i] + s;
}
let num_halfedge = *face_edge.last().unwrap() as usize;
out_r.halfedge.resize(
num_halfedge,
Halfedge {
start_vert: -1,
end_vert: -1,
paired_halfedge: -1,
prop_vert: -1,
},
);
(face_edge, face_pq2r)
}
pub(super) fn add_new_edge_verts(
edges_p: &mut BTreeMap<i32, Vec<EdgePos>>,
edges_new: &mut BTreeMap<(i32, i32), Vec<EdgePos>>,
p1q2: &[[i32; 2]],
i12: &[i32],
v12r: &[i32],
halfedge_p: &[Halfedge],
forward: bool,
offset: usize,
) {
for i in 0..p1q2.len() {
let edge_p = p1q2[i][if forward { 0 } else { 1 }];
let face_q = p1q2[i][if forward { 1 } else { 0 }];
let vert = v12r[i];
let inclusion = i12[i];
let halfedge = halfedge_p[edge_p as usize];
let mut key_right = (halfedge.paired_halfedge / 3, face_q);
if !forward {
key_right = (key_right.1, key_right.0);
}
let mut key_left = (edge_p / 3, face_q);
if !forward {
key_left = (key_left.1, key_left.0);
}
let direction = inclusion < 0;
let collision_id = (i + offset) as i32;
let dir_p = direction;
let dir_right = direction ^ !forward;
let dir_left = direction ^ forward;
let ep = edges_p.entry(edge_p).or_default();
for j in 0..inclusion.abs() {
ep.push(EdgePos {
edge_pos: 0.0,
vert: vert + j,
collision_id,
is_start: dir_p,
});
}
let er = edges_new.entry(key_right).or_default();
for j in 0..inclusion.abs() {
er.push(EdgePos {
edge_pos: 0.0,
vert: vert + j,
collision_id,
is_start: dir_right,
});
}
let el = edges_new.entry(key_left).or_default();
for j in 0..inclusion.abs() {
el.push(EdgePos {
edge_pos: 0.0,
vert: vert + j,
collision_id,
is_start: dir_left,
});
}
}
}
fn cpp_partition<T, P: Fn(&T) -> bool>(v: &mut [T], pred: P) -> usize {
let mut first = 0usize;
let mut last = v.len();
loop {
loop {
if first == last {
return first;
}
if !pred(&v[first]) {
break;
}
first += 1;
}
loop {
last -= 1;
if first == last {
return first;
}
if pred(&v[last]) {
break;
}
}
v.swap(first, last);
first += 1;
}
}
fn pair_up<F: FnMut(Halfedge)>(edge_pos: &mut Vec<EdgePos>, mut f: F) {
assert!(
edge_pos.len() % 2 == 0,
"Non-manifold edge! Not an even number of points. Got {} points: starts={}, ends={}",
edge_pos.len(),
edge_pos.iter().filter(|e| e.is_start).count(),
edge_pos.iter().filter(|e| !e.is_start).count(),
);
let n_edges = edge_pos.len() / 2;
let middle = cpp_partition(edge_pos, |e| e.is_start);
debug_assert_eq!(middle, n_edges, "Non-manifold edge!");
edge_pos[..n_edges].sort_by_key(|e| e.sort_key());
edge_pos[n_edges..].sort_by_key(|e| e.sort_key());
for i in 0..n_edges {
f(Halfedge {
start_vert: edge_pos[i].vert,
end_vert: edge_pos[i + n_edges].vert,
paired_halfedge: -1,
prop_vert: -1,
});
}
}
pub(super) fn append_partial_edges(
out_r: &mut ManifoldImpl,
whole_halfedge_p: &mut Vec<bool>,
face_ptr_r: &mut Vec<i32>,
edges_p: &mut BTreeMap<i32, Vec<EdgePos>>,
halfedge_ref: &mut Vec<TriRef>,
in_p: &ManifoldImpl,
i03: &[i32],
vp2r: &[i32],
face_p2r: &[i32],
forward: bool,
) {
for (&edge_p, edge_pos_p) in edges_p.iter_mut() {
edge_pos_p.sort_by_key(|e| e.sort_key());
let halfedge = in_p.halfedge[edge_p as usize];
whole_halfedge_p[edge_p as usize] = false;
whole_halfedge_p[halfedge.paired_halfedge as usize] = false;
let v_start = halfedge.start_vert as usize;
let v_end = halfedge.end_vert as usize;
let edge_vec = in_p.vert_pos[v_end] - in_p.vert_pos[v_start];
for edge in edge_pos_p.iter_mut() {
edge.edge_pos = dot(out_r.vert_pos[edge.vert as usize], edge_vec);
}
let inclusion = i03[v_start];
if inclusion != 0 {
let start_pos = dot(out_r.vert_pos[vp2r[v_start] as usize], edge_vec);
for j in 0..inclusion.abs() {
edge_pos_p.push(EdgePos {
edge_pos: start_pos,
vert: vp2r[v_start] + j,
collision_id: i32::MAX,
is_start: inclusion > 0,
});
}
}
let inclusion = i03[v_end];
if inclusion != 0 {
let end_pos = dot(out_r.vert_pos[vp2r[v_end] as usize], edge_vec);
for j in 0..inclusion.abs() {
edge_pos_p.push(EdgePos {
edge_pos: end_pos,
vert: vp2r[v_end] + j,
collision_id: i32::MAX,
is_start: inclusion < 0,
});
}
}
let face_left_p = edge_p as usize / 3;
let face_left = face_p2r[face_left_p];
let face_right_p = halfedge.paired_halfedge as usize / 3;
let face_right = face_p2r[face_right_p];
let forward_ref = TriRef {
mesh_id: if forward { 0 } else { 1 },
original_id: -1,
face_id: face_left_p as i32,
coplanar_id: -1,
};
let backward_ref = TriRef {
mesh_id: if forward { 0 } else { 1 },
original_id: -1,
face_id: face_right_p as i32,
coplanar_id: -1,
};
pair_up(edge_pos_p, |mut e| {
let forward_edge = face_ptr_r[face_left as usize];
face_ptr_r[face_left as usize] += 1;
let backward_edge = face_ptr_r[face_right as usize];
face_ptr_r[face_right as usize] += 1;
e.paired_halfedge = backward_edge;
out_r.halfedge[forward_edge as usize] = e;
halfedge_ref[forward_edge as usize] = forward_ref;
let rev = Halfedge {
start_vert: e.end_vert,
end_vert: e.start_vert,
paired_halfedge: forward_edge,
prop_vert: -1,
};
out_r.halfedge[backward_edge as usize] = rev;
halfedge_ref[backward_edge as usize] = backward_ref;
});
}
}
pub(super) fn append_new_edges(
out_r: &mut ManifoldImpl,
face_ptr_r: &mut Vec<i32>,
edges_new: &mut BTreeMap<(i32, i32), Vec<EdgePos>>,
halfedge_ref: &mut Vec<TriRef>,
face_pq2r: &[i32],
num_face_p: usize,
) {
for (&(face_p, face_q), edge_pos) in edges_new.iter_mut() {
edge_pos.sort_by_key(|e| e.sort_key());
let mut min = Vec3::splat(f64::INFINITY);
let mut max = Vec3::splat(f64::NEG_INFINITY);
for edge in edge_pos.iter() {
let p = out_r.vert_pos[edge.vert as usize];
min.x = min.x.min(p.x);
min.y = min.y.min(p.y);
min.z = min.z.min(p.z);
max.x = max.x.max(p.x);
max.y = max.y.max(p.y);
max.z = max.z.max(p.z);
}
let size = max - min;
let dim = if size.x > size.y && size.x > size.z {
0
} else if size.y > size.z {
1
} else {
2
};
for edge in edge_pos.iter_mut() {
let p = out_r.vert_pos[edge.vert as usize];
edge.edge_pos = match dim {
0 => p.x,
1 => p.y,
_ => p.z,
};
}
let face_left = face_pq2r[face_p as usize];
let face_right = face_pq2r[num_face_p + face_q as usize];
let forward_ref = TriRef {
mesh_id: 0,
original_id: -1,
face_id: face_p,
coplanar_id: -1,
};
let backward_ref = TriRef {
mesh_id: 1,
original_id: -1,
face_id: face_q,
coplanar_id: -1,
};
pair_up(edge_pos, |mut e| {
let forward_edge = face_ptr_r[face_left as usize];
face_ptr_r[face_left as usize] += 1;
let backward_edge = face_ptr_r[face_right as usize];
face_ptr_r[face_right as usize] += 1;
e.paired_halfedge = backward_edge;
out_r.halfedge[forward_edge as usize] = e;
halfedge_ref[forward_edge as usize] = forward_ref;
let rev = Halfedge {
start_vert: e.end_vert,
end_vert: e.start_vert,
paired_halfedge: forward_edge,
prop_vert: -1,
};
out_r.halfedge[backward_edge as usize] = rev;
halfedge_ref[backward_edge as usize] = backward_ref;
});
}
}
pub(super) fn append_whole_edges(
out_r: &mut ManifoldImpl,
face_ptr_r: &mut Vec<i32>,
halfedge_ref: &mut Vec<TriRef>,
in_p: &ManifoldImpl,
whole_halfedge_p: &[bool],
i03: &[i32],
vp2r: &[i32],
face_p2r: &[i32],
forward: bool,
) {
for idx in 0..in_p.halfedge.len() {
if !whole_halfedge_p[idx] {
continue;
}
let mut halfedge = in_p.halfedge[idx];
if !halfedge.is_forward() {
continue;
}
let inclusion = i03[halfedge.start_vert as usize];
if inclusion == 0 {
continue;
}
if inclusion < 0 {
std::mem::swap(&mut halfedge.start_vert, &mut halfedge.end_vert);
}
halfedge.start_vert = vp2r[halfedge.start_vert as usize];
halfedge.end_vert = vp2r[halfedge.end_vert as usize];
let face_left_p = idx / 3;
let new_face = face_p2r[face_left_p];
let face_right_p = halfedge.paired_halfedge as usize / 3;
let face_right = face_p2r[face_right_p];
let forward_ref = TriRef {
mesh_id: if forward { 0 } else { 1 },
original_id: -1,
face_id: face_left_p as i32,
coplanar_id: -1,
};
let backward_ref = TriRef {
mesh_id: if forward { 0 } else { 1 },
original_id: -1,
face_id: face_right_p as i32,
coplanar_id: -1,
};
for i in 0..inclusion.abs() {
let forward_edge = face_ptr_r[new_face as usize];
face_ptr_r[new_face as usize] += 1;
let backward_edge = face_ptr_r[face_right as usize];
face_ptr_r[face_right as usize] += 1;
let he = Halfedge {
start_vert: halfedge.start_vert + i,
end_vert: halfedge.end_vert + i,
paired_halfedge: backward_edge,
prop_vert: -1,
};
out_r.halfedge[forward_edge as usize] = he;
halfedge_ref[forward_edge as usize] = forward_ref;
let rev = Halfedge {
start_vert: halfedge.end_vert + i,
end_vert: halfedge.start_vert + i,
paired_halfedge: forward_edge,
prop_vert: -1,
};
out_r.halfedge[backward_edge as usize] = rev;
halfedge_ref[backward_edge as usize] = backward_ref;
}
}
}
#[path = "boolean_result_assemble.rs"]
mod boolean_result_assemble;
pub use boolean_result_assemble::boolean_result;