fix(topola-egui): Fix click-based selection of pins

- Do intersection calculation between DotShape and Rect=AABB properly
This commit is contained in:
Ellen Emilia Anna Zscheile 2025-05-15 19:42:23 +02:00 committed by mikolaj
parent 464e8abb89
commit 8d0681c07d
3 changed files with 34 additions and 33 deletions

View File

@ -12,7 +12,6 @@ use topola::{
selection::{BboxSelectionKind, Selection}, selection::{BboxSelectionKind, Selection},
}, },
board::{AccessMesadata, Board}, board::{AccessMesadata, Board},
geometry::shape::AccessShape,
layout::NodeIndex, layout::NodeIndex,
router::planar_incr_embed, router::planar_incr_embed,
}; };
@ -191,29 +190,14 @@ impl Overlay {
return; return;
} }
let geoms: Vec<_> = board let old_selection = self.take_selection();
.layout() self.select_all_in_bbox(
.drawing() board,
.rtree() appearance_panel,
.locate_in_envelope_intersecting(&AABB::<[f64; 3]>::from_corners( &AABB::from_point([at.x(), at.y()]),
[at.x(), at.y(), -f64::INFINITY], BboxSelectionKind::MerelyIntersects,
[at.x(), at.y(), f64::INFINITY], );
)) self.selection ^= &old_selection;
.collect();
if let Some(geom) = geoms.iter().find(|&&geom| {
board.layout().node_shape(geom.data).contains_point(at)
// TODO: fix which layers to query
&& board
.layout()
.drawing()
// This should use:
// `.is_node_in_any_layer_of(geom.data, &appearance_panel.visible[..])`
// instead, but that doesn't work reliably
.is_node_in_layer(geom.data, 0)
}) {
self.selection.toggle_at_node(board, geom.data);
}
} }
pub fn select_all_in_bbox( pub fn select_all_in_bbox(

View File

@ -5,10 +5,7 @@
use geo::Line; use geo::Line;
use crate::{ use crate::{
geometry::{ geometry::{primitive::PrimitiveShape, shape::AccessShape, GetWidth},
primitive::{AccessPrimitiveShape, PrimitiveShape},
shape::AccessShape,
},
math::{self, Circle, NoTangents, RotationSense}, math::{self, Circle, NoTangents, RotationSense},
}; };

View File

@ -6,20 +6,22 @@ use std::f64::consts::TAU;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use geo::algorithm::line_measures::{Distance, Euclidean}; use geo::algorithm::line_measures::{Distance, Euclidean};
use geo::{point, polygon, Contains, Intersects, Line, Point, Polygon, Rotate}; use geo::{point, polygon, Contains, Intersects, Line, Point, Polygon, Rect, Rotate};
use rstar::{RTreeObject, AABB}; use rstar::{RTreeObject, AABB};
use crate::{ use crate::{
geometry::shape::{AccessShape, MeasureLength}, geometry::{
shape::{AccessShape, MeasureLength},
GetWidth,
},
math::{self, Circle}, math::{self, Circle},
}; };
#[enum_dispatch] #[enum_dispatch]
pub trait AccessPrimitiveShape: AccessShape { pub trait AccessPrimitiveShape: AccessShape + GetWidth {
fn priority(&self) -> usize; fn priority(&self) -> usize;
fn inflate(&self, margin: f64) -> PrimitiveShape; fn inflate(&self, margin: f64) -> PrimitiveShape;
fn intersects(&self, other: &PrimitiveShape) -> bool; fn intersects(&self, other: &PrimitiveShape) -> bool;
fn width(&self) -> f64;
fn envelope_3d(&self, margin: f64, layer: usize) -> AABB<[f64; 3]> { fn envelope_3d(&self, margin: f64, layer: usize) -> AABB<[f64; 3]> {
let envelope = self.bbox(margin); let envelope = self.bbox(margin);
@ -42,7 +44,7 @@ pub trait AccessPrimitiveShape: AccessShape {
} }
} }
#[enum_dispatch(MeasureLength, AccessShape, AccessPrimitiveShape)] #[enum_dispatch(AccessShape, AccessPrimitiveShape, GetWidth, MeasureLength)]
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum PrimitiveShape { pub enum PrimitiveShape {
// Intentionally in different order to reorder `self.intersects(...)` properly. // Intentionally in different order to reorder `self.intersects(...)` properly.
@ -74,6 +76,11 @@ impl AccessShape for DotShape {
fn bbox_without_margin(&self) -> AABB<[f64; 2]> { fn bbox_without_margin(&self) -> AABB<[f64; 2]> {
self.circle.bbox(0.0) self.circle.bbox(0.0)
} }
fn intersects_with_bbox(&self, bbox: &AABB<[f64; 2]>) -> bool {
let bbox = Rect::new(bbox.lower(), bbox.upper());
Euclidean::distance(&self.circle.pos, &bbox.to_polygon()) < self.circle.r
}
} }
impl AccessPrimitiveShape for DotShape { impl AccessPrimitiveShape for DotShape {
@ -120,7 +127,9 @@ impl AccessPrimitiveShape for DotShape {
} }
} }
} }
}
impl GetWidth for DotShape {
fn width(&self) -> f64 { fn width(&self) -> f64 {
self.circle.r * 2.0 self.circle.r * 2.0
} }
@ -157,6 +166,13 @@ impl SegShape {
} }
} }
impl From<SegShape> for Polygon {
#[inline(always)]
fn from(x: SegShape) -> Polygon {
x.polygon()
}
}
impl MeasureLength for SegShape { impl MeasureLength for SegShape {
fn length(&self) -> f64 { fn length(&self) -> f64 {
Euclidean::distance(&self.to, &self.from) Euclidean::distance(&self.to, &self.from)
@ -227,7 +243,9 @@ impl AccessPrimitiveShape for SegShape {
} }
} }
} }
}
impl GetWidth for SegShape {
fn width(&self) -> f64 { fn width(&self) -> f64 {
self.width self.width
} }
@ -382,7 +400,9 @@ impl AccessPrimitiveShape for BendShape {
} }
} }
} }
}
impl GetWidth for BendShape {
fn width(&self) -> f64 { fn width(&self) -> f64 {
self.width self.width
} }