mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-27 04:16:48 -06:00
This commit is contained in:
+44
-17
@@ -60,6 +60,21 @@ struct Graph {
|
||||
edge_src: HashMap<(usize, usize), usize>,
|
||||
}
|
||||
|
||||
/// Records the crossing of segments `i` and `j`, if any, as split parameters on both.
|
||||
fn split_pair(segs: &[(Vec2, Vec2)], splits: &mut [Vec<f64>], i: usize, j: usize) {
|
||||
let (Some(si), Some(sj)) = (segs.get(i), segs.get(j)) else { return };
|
||||
let li = Line::new(si.0, si.1);
|
||||
let lj = Line::new(sj.0, sj.1);
|
||||
if let Some(x) = line_line(&li, &lj) {
|
||||
if let Some(v) = splits.get_mut(i) {
|
||||
v.push(li.param_of(x).clamp(0.0, 1.0));
|
||||
}
|
||||
if let Some(v) = splits.get_mut(j) {
|
||||
v.push(lj.param_of(x).clamp(0.0, 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build(polys: &[Vec<Vec2>], tol: f64) -> Option<Graph> {
|
||||
let mut segs: Vec<(Vec2, Vec2)> = Vec::new();
|
||||
let mut owner: Vec<usize> = Vec::new();
|
||||
@@ -94,16 +109,13 @@ fn build(polys: &[Vec<Vec2>], tol: f64) -> Option<Graph> {
|
||||
)
|
||||
};
|
||||
let mut grid: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
|
||||
let mut long: Vec<usize> = Vec::new();
|
||||
for (i, (a, b)) in segs.iter().enumerate() {
|
||||
let (x0, y0) = cell(a.min(*b));
|
||||
let (x1, y1) = cell(a.max(*b));
|
||||
if (x1 - x0 + 1) * (y1 - y0 + 1) > 4096 {
|
||||
// Very long segment: register in a coarse band.
|
||||
for gx in (x0..=x1).step_by(((x1 - x0) / 64).max(1)) {
|
||||
for gy in (y0..=y1).step_by(((y1 - y0) / 64).max(1)) {
|
||||
grid.entry((gx, gy)).or_default().push(i);
|
||||
}
|
||||
}
|
||||
// Very long segment: too many cells to register in; it is tested against every segment below.
|
||||
long.push(i);
|
||||
continue;
|
||||
}
|
||||
for gx in x0..=x1 {
|
||||
@@ -130,23 +142,24 @@ fn build(polys: &[Vec<Vec2>], tol: f64) -> Option<Graph> {
|
||||
if i == j || !tested.insert((i.min(j), i.max(j))) {
|
||||
continue;
|
||||
}
|
||||
let (Some(si), Some(sj)) = (segs.get(i), segs.get(j)) else { continue };
|
||||
let li = Line::new(si.0, si.1);
|
||||
let lj = Line::new(sj.0, sj.1);
|
||||
if let Some(x) = line_line(&li, &lj) {
|
||||
if let Some(v) = splits.get_mut(i) {
|
||||
v.push(li.param_of(x).clamp(0.0, 1.0));
|
||||
}
|
||||
if let Some(v) = splits.get_mut(j) {
|
||||
v.push(lj.param_of(x).clamp(0.0, 1.0));
|
||||
}
|
||||
}
|
||||
split_pair(&segs, &mut splits, i, j);
|
||||
if tested.len() > 20_000_000 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for &i in &long {
|
||||
for j in 0..n {
|
||||
if i == j || !tested.insert((i.min(j), i.max(j))) {
|
||||
continue;
|
||||
}
|
||||
split_pair(&segs, &mut splits, i, j);
|
||||
if tested.len() > 20_000_000 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut g = Graph { pts: Vec::new(), adj: Vec::new(), src: Vec::with_capacity(n), edge_src: HashMap::new() };
|
||||
let mut index: HashMap<(i64, i64), usize> = HashMap::new();
|
||||
let mut vid = |p: Vec2, g: &mut Graph| -> usize {
|
||||
@@ -609,6 +622,20 @@ mod tests {
|
||||
vec![Vec2::new(x0, y0), Vec2::new(x0 + s, y0), Vec2::new(x0 + s, y0 + s), Vec2::new(x0, y0 + s), Vec2::new(x0, y0)]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_segment_crossing_in_unsampled_cell_is_split() {
|
||||
// A diagonal long enough to skip the grid, and a short segment crossing it in a cell the old coarse sampling missed.
|
||||
let mut polys = vec![vec![Vec2::new(0.0, 0.0), Vec2::new(1000.0, 1000.0)], vec![Vec2::new(11.0, 12.0), Vec2::new(12.0, 11.0)]];
|
||||
for i in 0..17_000usize {
|
||||
let x = 600.0 + (i % 130) as f64 * 2.0;
|
||||
let y = 100.0 + (i / 130) as f64 * 2.0;
|
||||
polys.push(vec![Vec2::new(x, y), Vec2::new(x + 1.0, y)]);
|
||||
}
|
||||
let g = build(&polys, 1e-6).unwrap();
|
||||
let hit = g.pts.iter().position(|p| p.near(Vec2::new(11.5, 11.5), 1e-6)).unwrap();
|
||||
assert_eq!(g.adj[hit].len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_square_around_point() {
|
||||
let l = enclosing_loop(&[square(0.0, 0.0, 10.0)], Vec2::new(5.0, 5.0), 1e-9).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user