3D and Materials: the edge index's search from outside its grid looks along the side
A point outside the grid can be nearer an edge further along the grid's side than one straight ahead of it; the search stopped a ring early there. The bucket lookup is one function.
This commit is contained in:
parent
b3ada24da7
commit
c3c036daf2
|
|
@ -701,14 +701,18 @@ impl Edges {
|
||||||
Self { edges, origin: bounds.origin(), size, n: N, buckets }
|
Self { edges, origin: bounds.origin(), size, n: N, buckets }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The bucket row or column of coordinate `v`, from the grid's origin `o` on that axis.
|
||||||
|
fn cell(&self, v: f64, o: f64) -> isize {
|
||||||
|
(((v - o) / self.size).floor() as isize).clamp(0, self.n as isize - 1)
|
||||||
|
}
|
||||||
|
|
||||||
/// Is `p` within `tolerance` of an edge? Only the buckets `p` could share an edge with are
|
/// Is `p` within `tolerance` of an edge? Only the buckets `p` could share an edge with are
|
||||||
/// looked at.
|
/// looked at.
|
||||||
fn near(&self, p: Point, tolerance: f64) -> bool {
|
fn near(&self, p: Point, tolerance: f64) -> bool {
|
||||||
self.touch(Rect::new(p.x - tolerance, p.y - tolerance, p.x + tolerance, p.y + tolerance)) && {
|
self.touch(Rect::new(p.x - tolerance, p.y - tolerance, p.x + tolerance, p.y + tolerance)) && {
|
||||||
let n = self.n as isize;
|
let n = self.n as isize;
|
||||||
let index = |v: f64, o: f64| (((v - o) / self.size).floor() as isize).clamp(0, n - 1);
|
let (i0, i1) = (self.cell(p.x - tolerance, self.origin.x), self.cell(p.x + tolerance, self.origin.x));
|
||||||
let (i0, i1) = (index(p.x - tolerance, self.origin.x), index(p.x + tolerance, self.origin.x));
|
let (j0, j1) = (self.cell(p.y - tolerance, self.origin.y), self.cell(p.y + tolerance, self.origin.y));
|
||||||
let (j0, j1) = (index(p.y - tolerance, self.origin.y), index(p.y + tolerance, self.origin.y));
|
|
||||||
(j0..=j1).any(|j| {
|
(j0..=j1).any(|j| {
|
||||||
(i0..=i1).any(|i| {
|
(i0..=i1).any(|i| {
|
||||||
self.buckets[(j * n + i) as usize]
|
self.buckets[(j * n + i) as usize]
|
||||||
|
|
@ -724,9 +728,8 @@ impl Edges {
|
||||||
/// buckets `r` covers comes more than once).
|
/// buckets `r` covers comes more than once).
|
||||||
fn each(&self, r: Rect, mut f: impl FnMut(Point, Point)) {
|
fn each(&self, r: Rect, mut f: impl FnMut(Point, Point)) {
|
||||||
let n = self.n as isize;
|
let n = self.n as isize;
|
||||||
let index = |v: f64, o: f64| (((v - o) / self.size).floor() as isize).clamp(0, n - 1);
|
for j in self.cell(r.y0, self.origin.y)..=self.cell(r.y1, self.origin.y) {
|
||||||
for j in index(r.y0, self.origin.y)..=index(r.y1, self.origin.y) {
|
for i in self.cell(r.x0, self.origin.x)..=self.cell(r.x1, self.origin.x) {
|
||||||
for i in index(r.x0, self.origin.x)..=index(r.x1, self.origin.x) {
|
|
||||||
for (a, b) in self.buckets[(j * n + i) as usize].iter().filter_map(|k| self.edges.get(*k as usize)) {
|
for (a, b) in self.buckets[(j * n + i) as usize].iter().filter_map(|k| self.edges.get(*k as usize)) {
|
||||||
if a.x.min(b.x) <= r.x1 && a.x.max(b.x) >= r.x0 && a.y.min(b.y) <= r.y1 && a.y.max(b.y) >= r.y0 {
|
if a.x.min(b.x) <= r.x1 && a.x.max(b.x) >= r.x0 && a.y.min(b.y) <= r.y1 && a.y.max(b.y) >= r.y0 {
|
||||||
f(*a, *b);
|
f(*a, *b);
|
||||||
|
|
@ -739,8 +742,8 @@ impl Edges {
|
||||||
/// Does any edge pass through `r` (its bounding box, at least, overlapping it)?
|
/// Does any edge pass through `r` (its bounding box, at least, overlapping it)?
|
||||||
fn touch(&self, r: Rect) -> bool {
|
fn touch(&self, r: Rect) -> bool {
|
||||||
let n = self.n as isize;
|
let n = self.n as isize;
|
||||||
let index = |v: f64, o: f64| (((v - o) / self.size).floor() as isize).clamp(0, n - 1);
|
let (i0, i1) = (self.cell(r.x0, self.origin.x), self.cell(r.x1, self.origin.x));
|
||||||
let (i0, i1, j0, j1) = (index(r.x0, self.origin.x), index(r.x1, self.origin.x), index(r.y0, self.origin.y), index(r.y1, self.origin.y));
|
let (j0, j1) = (self.cell(r.y0, self.origin.y), self.cell(r.y1, self.origin.y));
|
||||||
(j0..=j1).any(|j| {
|
(j0..=j1).any(|j| {
|
||||||
(i0..=i1).any(|i| {
|
(i0..=i1).any(|i| {
|
||||||
self.buckets[(j * n + i) as usize]
|
self.buckets[(j * n + i) as usize]
|
||||||
|
|
@ -760,7 +763,7 @@ impl Edges {
|
||||||
}
|
}
|
||||||
let n = self.n as isize;
|
let n = self.n as isize;
|
||||||
let (ci, cj) = ((fx.floor() as isize).clamp(0, n - 1), (fy.floor() as isize).clamp(0, n - 1));
|
let (ci, cj) = ((fx.floor() as isize).clamp(0, n - 1), (fy.floor() as isize).clamp(0, n - 1));
|
||||||
// How far `p` is outside the grid, which every bucket's distance bound must add.
|
// How far `p` is outside the grid: the nearest point of the grid to it lies in `p`'s bucket.
|
||||||
let outside = (fx.clamp(0.0, n as f64) - fx).hypot(fy.clamp(0.0, n as f64) - fy) * self.size;
|
let outside = (fx.clamp(0.0, n as f64) - fx).hypot(fy.clamp(0.0, n as f64) - fy) * self.size;
|
||||||
let mut best = f64::INFINITY;
|
let mut best = f64::INFINITY;
|
||||||
for r in 0..n {
|
for r in 0..n {
|
||||||
|
|
@ -776,8 +779,9 @@ impl Edges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Buckets r + 1 away are at least r buckets from any point of `p`'s own.
|
// Buckets r + 1 away are at least r buckets from any point of `p`'s own, and that
|
||||||
if best <= outside + r as f64 * self.size {
|
// far across the grid from a `p` outside it (at right angles to its way in, at worst).
|
||||||
|
if best <= outside.hypot(r as f64 * self.size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,3 +349,16 @@ fn the_edge_index_finds_the_nearest_edge() {
|
||||||
assert!((fast - slow).abs() < 1e-9, "{q:?}: {fast} against {slow}");
|
assert!((fast - slow).abs() < 1e-9, "{q:?}: {fast} against {slow}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// From a point outside the grid, an edge further along the grid's side can be nearer than one
|
||||||
|
/// straight ahead: the search doesn't stop before it has looked there.
|
||||||
|
#[test]
|
||||||
|
fn the_edge_index_looks_along_the_side_from_outside() {
|
||||||
|
let ring = |b: BezPath| rings(&shape(&b).0, 64.0, MAX_SOLID_POINTS).unwrap().remove(0);
|
||||||
|
// Straight ahead of the point, a little further up its side, and the far corner (a 64 pt grid).
|
||||||
|
let rings = vec![ring(square(2.5, 0.0, 1.0)), ring(square(0.0, 4.2, 0.8)), ring(square(63.0, 63.0, 1.0))];
|
||||||
|
let edges = Edges::new(&rings);
|
||||||
|
let q = Point::new(-10.0, 0.5);
|
||||||
|
let (fast, slow) = (edges.distance(q), edge_distance(&rings, q));
|
||||||
|
assert!((fast - slow).abs() < 1e-9, "{fast} against {slow}");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue