Oracle: 25/29 (14 and 15 now match Fusion). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
118 lines
3.3 KiB
Rust
118 lines
3.3 KiB
Rust
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
|
use std::collections::VecDeque;
|
|
use truck_base::{cgmath64::*, tolerance::*};
|
|
use truck_meshalgo::prelude::PolylineCurve;
|
|
|
|
pub fn construct_polylines(lines: &[(Point3, Point3)]) -> Vec<PolylineCurve<Point3>> {
|
|
let mut graph: Graph = lines.iter().collect();
|
|
let mut res = Vec::new();
|
|
while !graph.is_empty() {
|
|
let (mut idx, node) = graph.get_one();
|
|
let mut wire: VecDeque<_> = vec![node.coord].into();
|
|
while let Some((idx0, pt)) = graph.get_a_next_node(idx) {
|
|
idx = idx0;
|
|
wire.push_back(pt);
|
|
}
|
|
let mut idx = PointIndex::from(wire[0]);
|
|
while let Some((idx0, pt)) = graph.get_a_next_node(idx) {
|
|
idx = idx0;
|
|
wire.push_front(pt);
|
|
}
|
|
res.push(PolylineCurve(wire.into()));
|
|
}
|
|
res
|
|
}
|
|
|
|
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
|
|
struct PointIndex([i64; 3]);
|
|
|
|
impl From<Point3> for PointIndex {
|
|
#[inline(always)]
|
|
fn from(pt: Point3) -> PointIndex {
|
|
let idx = pt.add_element_wise(TOLERANCE) / (2.0 * TOLERANCE);
|
|
PointIndex(idx.cast::<i64>().unwrap().into())
|
|
}
|
|
}
|
|
|
|
struct Node {
|
|
coord: Point3,
|
|
adjacency: HashSet<PointIndex>,
|
|
}
|
|
|
|
impl Node {
|
|
#[inline(always)]
|
|
fn new(coord: Point3, adjacency: HashSet<PointIndex>) -> Node { Node { coord, adjacency } }
|
|
|
|
fn pop_one_adjacency(&mut self) -> PointIndex {
|
|
let idx = *self.adjacency.iter().next().unwrap();
|
|
self.adjacency.remove(&idx);
|
|
idx
|
|
}
|
|
}
|
|
|
|
struct Graph(HashMap<PointIndex, Node>);
|
|
|
|
impl std::ops::Deref for Graph {
|
|
type Target = HashMap<PointIndex, Node>;
|
|
#[inline(always)]
|
|
fn deref(&self) -> &Self::Target { &self.0 }
|
|
}
|
|
|
|
impl std::ops::DerefMut for Graph {
|
|
#[inline(always)]
|
|
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
|
|
}
|
|
|
|
impl Graph {
|
|
fn add_half_edge(&mut self, pt0: Point3, pt1: Point3) {
|
|
let idx0 = pt0.into();
|
|
let idx1 = pt1.into();
|
|
if let Some(node) = self.get_mut(&idx0) {
|
|
node.adjacency.insert(idx1);
|
|
} else {
|
|
let mut set = HashSet::default();
|
|
set.insert(idx1);
|
|
self.insert(idx0, Node::new(pt0, set));
|
|
}
|
|
}
|
|
|
|
fn add_edge(&mut self, line: (Point3, Point3)) {
|
|
if !line.0.near(&line.1) {
|
|
self.add_half_edge(line.0, line.1);
|
|
self.add_half_edge(line.1, line.0);
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn get_one(&self) -> (PointIndex, &Node) {
|
|
let (idx, node) = self.iter().next().unwrap();
|
|
(*idx, node)
|
|
}
|
|
|
|
fn get_a_next_node(&mut self, idx: PointIndex) -> Option<(PointIndex, Point3)> {
|
|
let node = self.get_mut(&idx)?;
|
|
let idx0 = node.pop_one_adjacency();
|
|
if node.adjacency.is_empty() {
|
|
self.remove(&idx);
|
|
}
|
|
let node = self.get_mut(&idx0)?;
|
|
node.adjacency.remove(&idx);
|
|
let pt = node.coord;
|
|
if node.adjacency.is_empty() {
|
|
self.remove(&idx0);
|
|
}
|
|
Some((idx0, pt))
|
|
}
|
|
}
|
|
|
|
impl<'a> FromIterator<&'a (Point3, Point3)> for Graph {
|
|
fn from_iter<I: IntoIterator<Item = &'a (Point3, Point3)>>(iter: I) -> Graph {
|
|
let mut res = Graph(HashMap::default());
|
|
iter.into_iter().for_each(|line| res.add_edge(*line));
|
|
res
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|