use super::{EdgeSamples, FaceRecord, HashMap};
use crate::mass_properties::{hash_face, TwoLane};
use crate::Mesh;
use rustc_hash::FxHashMap;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
const TRIANGLE_BUDGET: usize = 500_000;
const ENTRY_BUDGET: usize = 8_192;
thread_local! {
static MEMO: RefCell<FxHashMap<[u64; 2], Rc<Mesh>>> = RefCell::new(FxHashMap::default());
static STORED_TRIANGLES: Cell<usize> = const { Cell::new(0) };
static HITS: Cell<u64> = const { Cell::new(0) };
static MISSES: Cell<u64> = const { Cell::new(0) };
static ENABLED: Cell<Option<bool>> = const { Cell::new(None) };
}
#[cfg(not(target_arch = "wasm32"))]
fn enabled_from_env() -> bool {
switch!(live "BREP_TESS_MEMO", true)
}
#[cfg(target_arch = "wasm32")]
fn enabled_from_env() -> bool {
true
}
pub(in crate::watertight_tessellation) fn enabled() -> bool {
ENABLED.with(|cell| match cell.get() {
Some(enabled) => enabled,
None => {
let enabled = enabled_from_env();
cell.set(Some(enabled));
enabled
}
})
}
pub(in crate::watertight_tessellation) fn key(
face: &FaceRecord,
samples: &HashMap<u64, EdgeSamples>,
chord_tolerance: f64,
) -> [u64; 2] {
let mut lane = TwoLane::new();
hash_face(&mut lane, face);
lane.f64(chord_tolerance);
lane.bool(super::triangulate::ruling_seed_enabled());
for loop_record in &face.loops {
for coedge in &loop_record.coedges {
match samples.get(&coedge.edge_id) {
None => lane.bool(false),
Some(edge) => {
lane.bool(true);
lane.floats(&edge.fractions);
lane.usize(edge.positions.len());
for point in &edge.positions {
lane.f64(point.x);
lane.f64(point.y);
lane.f64(point.z);
}
lane.f64(edge.worst_sag);
}
}
}
}
if super::scan_mesh::face_bound(face).is_some() {
lane.bool(true);
}
lane.finish()
}
pub(super) fn trace_key(face_id: u32, face: &FaceRecord, samples: &HashMap<u64, EdgeSamples>, chord: f64, key: [u64; 2]) {
let mut geometry = TwoLane::new();
hash_face(&mut geometry, face);
let geometry = geometry.finish();
let mut boundary = TwoLane::new();
for loop_record in &face.loops {
for coedge in &loop_record.coedges {
match samples.get(&coedge.edge_id) {
None => boundary.bool(false),
Some(edge) => {
boundary.bool(true);
boundary.floats(&edge.fractions);
boundary.usize(edge.positions.len());
for point in &edge.positions {
boundary.f64(point.x); boundary.f64(point.y); boundary.f64(point.z);
}
boundary.f64(edge.worst_sag);
}
}
}
}
let boundary = boundary.finish();
eprintln!("tess.key face={face_id} geometry={:016x}{:016x} samples={:016x}{:016x} chord_bits={:016x} rule={} full={:016x}{:016x}",
geometry[0], geometry[1], boundary[0], boundary[1], chord.to_bits(), super::triangulate::ruling_seed_enabled(), key[0], key[1]);
}
pub(in crate::watertight_tessellation) fn lookup(key: [u64; 2], face_id: u32) -> Option<Mesh> {
let stored = MEMO.with(|memo| memo.borrow().get(&key).cloned());
match stored {
Some(fragment) => {
HITS.with(|cell| cell.set(cell.get() + 1));
let mut mesh = Mesh::clone(&fragment);
mesh.face_ids.fill(face_id);
Some(mesh)
}
None => {
MISSES.with(|cell| cell.set(cell.get() + 1));
None
}
}
}
pub(in crate::watertight_tessellation) fn store(key: [u64; 2], fragment: &Mesh) {
let triangles = fragment.indices.len() / 3;
if triangles > TRIANGLE_BUDGET {
return;
}
MEMO.with(|memo| {
let mut memo = memo.borrow_mut();
let stored = STORED_TRIANGLES.with(|cell| cell.get());
if memo.len() >= ENTRY_BUDGET || stored + triangles > TRIANGLE_BUDGET {
memo.clear();
STORED_TRIANGLES.with(|cell| cell.set(0));
}
if memo.insert(key, Rc::new(fragment.clone())).is_none() {
STORED_TRIANGLES.with(|cell| cell.set(cell.get() + triangles));
}
});
}
pub fn clear() {
MEMO.with(|memo| memo.borrow_mut().clear());
STORED_TRIANGLES.with(|cell| cell.set(0));
}
pub(in crate::watertight_tessellation) fn take_counts() -> (u64, u64, usize, usize) {
let hits = HITS.with(|cell| cell.replace(0));
let misses = MISSES.with(|cell| cell.replace(0));
let entries = MEMO.with(|memo| memo.borrow().len());
let triangles = STORED_TRIANGLES.with(|cell| cell.get());
(hits, misses, entries, triangles)
}