diff --git a/crates/doc/src/geometry.rs b/crates/doc/src/geometry.rs index d0ed4f2b..b4f89dbc 100644 --- a/crates/doc/src/geometry.rs +++ b/crates/doc/src/geometry.rs @@ -72,7 +72,16 @@ fn hits_node_itself(doc: &Document, id: NodeId, n: &Node, pt: Point, tol: f64, o NodeKind::Frame | NodeKind::Component { .. } | NodeKind::ComponentSet { .. } | NodeKind::Instance { .. } | NodeKind::Section { .. } => { n.local_rect().inflate(tol_l, tol_l).contains(lp) } - _ => shape_path(n).is_some_and(|path| hits_path(n, &path, lp, tol_l, !outline && is_closed(n) && path.winding(lp) != 0)), + _ => shape_path(n).is_some_and(|path| hits_path(n, &path, lp, tol_l, !outline && is_closed(n) && fills_point(n, &path, lp))), + } +} + +/// Whether the fill of `n`, drawn as `path`, covers `lp` (vectors follow the rule of +/// [`VectorNetwork::fill_contains`](crate::VectorNetwork::fill_contains): even-odd when any region asks for it). +fn fills_point(n: &Node, path: &BezPath, lp: Point) -> bool { + match &n.kind { + NodeKind::Vector { network } => network.fill_contains(lp), + _ => path.winding(lp) != 0, } } diff --git a/crates/doc/src/tests.rs b/crates/doc/src/tests.rs index cc1f240c..0ec0b0f2 100644 --- a/crates/doc/src/tests.rs +++ b/crates/doc/src/tests.rs @@ -499,3 +499,31 @@ fn stale_next_id_never_overwrites_existing_nodes() { assert_eq!(bad.node(p).unwrap().name, snapshot.node(p).unwrap().name); assert!(bad.validate().is_empty(), "{:?}", bad.validate()); } + +#[test] +fn hit_test_even_odd_vector_hole_misses() { + let ring = |rule| { + let sq = |o: f64, s: f64, base: usize| { + ( + [(o, o), (o + s, o), (o + s, o + s), (o, o + s)].map(|(x, y)| Vertex::new(x, y)), + [Segment::line(base, base + 1), Segment::line(base + 1, base + 2), Segment::line(base + 2, base + 3), Segment::line(base + 3, base)], + ) + }; + let (v1, s1) = sq(0.0, 200.0, 0); + let (v2, s2) = sq(60.0, 80.0, 4); + VectorNetwork { + vertices: v1.into_iter().chain(v2).collect(), + segments: s1.into_iter().chain(s2).collect(), + regions: vec![Region { loops: vec![vec![0, 1, 2, 3], vec![4, 5, 6, 7]], winding: rule }], + } + }; + let mut d = Document::new("x"); + let p = page(&d); + let eo = d.create(p, NodeKind::Vector { network: ring(WindingRule::EvenOdd) }, "V", 0.0, 0.0, 200.0, 200.0).unwrap(); + assert_eq!(d.hit_test(p, Point::new(20.0, 100.0), 0.0, None), Some(eo)); + assert_eq!(d.hit_test(p, Point::new(100.0, 100.0), 0.0, None), None, "the hole is not filled"); + let mut d = Document::new("x"); + let p = page(&d); + let nz = d.create(p, NodeKind::Vector { network: ring(WindingRule::NonZero) }, "V", 0.0, 0.0, 200.0, 200.0).unwrap(); + assert_eq!(d.hit_test(p, Point::new(100.0, 100.0), 0.0, None), Some(nz)); +}