Filter out bbox-located primitives for which hit-test fails

This commit is contained in:
Mikolaj Wielgus 2026-03-14 18:55:19 +01:00
parent 7aed12dda9
commit 6992369041
3 changed files with 65 additions and 28 deletions

View File

@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
use stable_vec::StableVec;
use undoredo::{ApplyDelta, Delta, FlushDelta, Recorder};
use crate::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId, Vector2};
use crate::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2, Via, ViaId};
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
@ -172,6 +172,19 @@ impl Layout {
]
}
pub fn segment_contains_point(&self, segment_id: SegmentId, point: Vector2<i64>) -> bool {
let endpoints = self.segment_endpoints(segment_id);
let segment = self.segments.get(&segment_id.id()).unwrap();
let vertices = crate::math::inflated_segment(
endpoints[0].x,
endpoints[0].y,
endpoints[1].x,
endpoints[1].y,
segment.half_width,
);
point.inside_polygon(&vertices)
}
pub fn segment_bbox(&self, segment_id: SegmentId) -> Rectangle<[i64; 3]> {
let endpoints = self.segment_endpoints(segment_id);
let layer = self.segments.get(&segment_id.id()).unwrap().layer as i64;
@ -188,23 +201,30 @@ impl Layout {
pub fn locate_joints_at_point(
&self,
layer: usize,
point: [i64; 2],
point: Vector2<i64>,
) -> impl Iterator<Item = JointId> {
self.joints_rtree
.as_ref()
.locate_all_at_point(&[point[0], point[1], layer as i64])
.locate_all_at_point(&[point.x, point.y, layer as i64])
.map(|geom_with_data| geom_with_data.data)
.filter(move |joint_id| {
self.joints
.get(&joint_id.id())
.unwrap()
.contains_point(point)
})
}
pub fn locate_segments_at_point(
&self,
layer: usize,
point: [i64; 2],
point: Vector2<i64>,
) -> impl Iterator<Item = SegmentId> {
self.segments_rtree
.as_ref()
.locate_all_at_point(&[point[0], point[1], layer as i64])
.locate_all_at_point(&[point.x, point.y, layer as i64])
.map(|geom_with_data| geom_with_data.data)
.filter(move |segment_id| self.segment_contains_point(*segment_id, point))
}
// TODO: vias.
@ -212,12 +232,18 @@ impl Layout {
pub fn locate_polygons_at_point(
&self,
layer: usize,
point: [i64; 2],
point: Vector2<i64>,
) -> impl Iterator<Item = PolygonId> {
self.polygons_rtree
.as_ref()
.locate_all_at_point(&[point[0], point[1], layer as i64])
.locate_all_at_point(&[point.x, point.y, layer as i64])
.map(|geom_with_data| geom_with_data.data)
.filter(move |polygon_id| {
self.polygons
.get(&polygon_id.id())
.unwrap()
.contains_point(point)
})
}
pub fn pin(&self, pin: PinId) -> &Pin {

View File

@ -57,6 +57,33 @@ impl_inside_polygon!(f64);
impl_inside_polygon!(i32);
impl_inside_polygon!(i64);
/// Returns the four vertices of a segment inflated by `half_width`, forming a convex
/// quadrilateral. The segment goes from (x1, y1) to (x2, y2).
pub fn inflated_segment(
x1: i64,
y1: i64,
x2: i64,
y2: i64,
half_width: u64,
) -> [Vector2<i64>; 4] {
let dx = x2 - x1;
let dy = y2 - y1;
let approx_len =
std::cmp::max(dx.abs(), dy.abs()) + 3 * std::cmp::min(dx.abs(), dy.abs()) / 8;
// Perpendicular vector scaled to half-width.
let px = -dy * (half_width as i64) / approx_len;
let py = dx * (half_width as i64) / approx_len;
[
Vector2::new(x1 + px, y1 + py),
Vector2::new(x2 + px, y2 + py),
Vector2::new(x2 - px, y2 - py),
Vector2::new(x1 - px, y1 - py),
]
}
macro_rules! impl_rotate_around_point {
($t:ty) => {
impl Vector2<$t> {

View File

@ -6,6 +6,7 @@ use dearcut::RecordingTriangulator;
use derive_getters::Getters;
use crate::{
math,
Board,
primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId},
};
@ -162,35 +163,18 @@ impl NavmesherBoard {
navmesher.insert_polygon(
segment.layer,
Self::inflated_segment(
math::inflated_segment(
endpoints[0].x,
endpoints[0].y,
endpoints[1].x,
endpoints[1].y,
segment.half_width,
),
)
.into_iter()
.map(Into::into),
)
}
fn inflated_segment(x1: i64, y1: i64, x2: i64, y2: i64, half_width: u64) -> [[i64; 2]; 4] {
let dx = x2 - x1;
let dy = y2 - y1;
let approx_len =
std::cmp::max(dx.abs(), dy.abs()) + 3 * std::cmp::min(dx.abs(), dy.abs()) / 8;
// Perpendicular vector scaled to half-width.
let px = -dy * (half_width as i64) / approx_len;
let py = dx * (half_width as i64) / approx_len;
[
[x1 + px, y1 + py],
[x2 + px, y2 + py],
[x2 - px, y2 - py],
[x1 - px, y1 - py],
]
}
pub fn insert_via(&mut self, via: Via) -> ViaId {
// TODO: Insert into navmesh.
self.board.add_via(via)