mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Mesh: a point outside the mesh locates on the nearest boundary edge
locate_within maps a point outside the outline to the nearest point of the outer edges, so a patch folded past the outline does not take an edge click as an interior one.
This commit is contained in:
@@ -253,8 +253,9 @@ mod tests {
|
||||
assert_eq!(m.locate_within(p, 1.0), None);
|
||||
let (r, c, u, v) = m.locate_within(p, 3.0).unwrap();
|
||||
assert_eq!((r, c), (0, 1));
|
||||
assert_eq!(u, 1.0, "on the right edge");
|
||||
let q = m.eval(r, c, u, v);
|
||||
assert!(q.distance(Point::new(100.0, 30.0)) < 0.5, "{q:?}");
|
||||
assert!(q.distance(Point::new(100.0, 30.0)) < 1e-6, "{q:?}");
|
||||
assert_eq!(m.locate_within(Point::new(30.0, 60.0), 3.0), m.locate(Point::new(30.0, 60.0)));
|
||||
}
|
||||
|
||||
|
||||
@@ -598,13 +598,12 @@ impl GradientMesh {
|
||||
/// As [`Self::locate`], and for a point outside the mesh within `tol` of its outline, the
|
||||
/// patch and local (u, v) of the nearest point of the mesh's edge.
|
||||
pub fn locate_within(&self, p: Point, tol: f64) -> Option<(usize, usize, f64, f64)> {
|
||||
use vectorcraft_geom::kurbo::ParamCurveNearest;
|
||||
if !self.is_valid() {
|
||||
return None;
|
||||
}
|
||||
let bp = self.outline().to_bezpath();
|
||||
if bp.winding(p) == 0 && !(tol > 0.0 && bp.segments().any(|s| s.nearest(p, 1e-9).distance_sq <= tol * tol)) {
|
||||
return None;
|
||||
if bp.winding(p) == 0 {
|
||||
return self.nearest_edge(p).filter(|e| e.0 <= tol * tol).map(|(_, r, c, u, v)| (r, c, u, v));
|
||||
}
|
||||
const N: usize = 16;
|
||||
let mut best = (f64::INFINITY, 0, 0, 0.0, 0.0);
|
||||
@@ -641,6 +640,30 @@ impl GradientMesh {
|
||||
Some((r, c, u, v))
|
||||
}
|
||||
|
||||
/// The point of the mesh's outer edge nearest `p`: its squared distance from `p`, and its
|
||||
/// boundary patch and local (u, v).
|
||||
fn nearest_edge(&self, p: Point) -> Option<(f64, usize, usize, f64, f64)> {
|
||||
use vectorcraft_geom::kurbo::ParamCurveNearest;
|
||||
let (rows, cols) = (self.rows as usize, self.cols as usize);
|
||||
let mut best: Option<(f64, usize, usize, f64, f64)> = None;
|
||||
let mut take = |e: CubicBez, at: (usize, usize, Option<f64>, Option<f64>)| {
|
||||
let n = e.nearest(p, 1e-9);
|
||||
if best.is_none_or(|b| n.distance_sq < b.0) {
|
||||
let (r, c, u, v) = at;
|
||||
best = Some((n.distance_sq, r, c, u.unwrap_or(n.t), v.unwrap_or(n.t)));
|
||||
}
|
||||
};
|
||||
for c in 0..cols {
|
||||
take(self.edge_u(0, c), (0, c, None, Some(0.0)));
|
||||
take(self.edge_u(rows, c), (rows - 1, c, None, Some(1.0)));
|
||||
}
|
||||
for r in 0..rows {
|
||||
take(self.edge_v(r, 0), (r, 0, Some(0.0), None));
|
||||
take(self.edge_v(r, cols), (r, cols - 1, Some(1.0), None));
|
||||
}
|
||||
best
|
||||
}
|
||||
|
||||
/// Index of the mesh point within `tol` of `p`.
|
||||
pub fn point_near(&self, p: Point, tol: f64) -> Option<usize> {
|
||||
self.points
|
||||
|
||||
Reference in New Issue
Block a user