mirror of https://codeberg.org/topola/topola.git
feat(layout): cache convex hull on polygon initialization
This commit is contained in:
parent
f416958936
commit
8a66ece14c
|
|
@ -5,7 +5,7 @@
|
||||||
use contracts_try::debug_ensures;
|
use contracts_try::debug_ensures;
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use geo::Point;
|
use geo::{algorithm::ConvexHull, IsConvex, Point};
|
||||||
use rstar::AABB;
|
use rstar::AABB;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -52,7 +52,11 @@ pub enum CompoundWeight {
|
||||||
Via(ViaWeight),
|
Via(ViaWeight),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CompoundEntryKind = ();
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum CompoundEntryKind {
|
||||||
|
Normal,
|
||||||
|
NotInConvexHull,
|
||||||
|
}
|
||||||
|
|
||||||
/// The alias to differ node types
|
/// The alias to differ node types
|
||||||
pub type NodeIndex = GenericNode<PrimitiveIndex, GenericIndex<CompoundWeight>>;
|
pub type NodeIndex = GenericNode<PrimitiveIndex, GenericIndex<CompoundWeight>>;
|
||||||
|
|
@ -119,7 +123,12 @@ impl<R: AccessRules> Layout<R> {
|
||||||
}),
|
}),
|
||||||
) {
|
) {
|
||||||
Ok(dot) => {
|
Ok(dot) => {
|
||||||
self.drawing.add_to_compound(recorder, dot, (), compound);
|
self.drawing.add_to_compound(
|
||||||
|
recorder,
|
||||||
|
dot,
|
||||||
|
CompoundEntryKind::Normal,
|
||||||
|
compound,
|
||||||
|
);
|
||||||
dots.push(dot);
|
dots.push(dot);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -233,7 +242,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
self.drawing.add_to_compound(
|
self.drawing.add_to_compound(
|
||||||
recorder,
|
recorder,
|
||||||
GenericIndex::<()>::new(i.petgraph_index()),
|
GenericIndex::<()>::new(i.petgraph_index()),
|
||||||
(),
|
CompoundEntryKind::Normal,
|
||||||
poly_compound,
|
poly_compound,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -251,9 +260,64 @@ impl<R: AccessRules> Layout<R> {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if !shape.exterior().is_convex() {
|
||||||
|
// create a temporary RTree to speed up lookups from O(n²) to O(n log n)
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Rto {
|
||||||
|
pt: Point,
|
||||||
|
idx: PrimitiveIndex,
|
||||||
|
}
|
||||||
|
impl rstar::RTreeObject for Rto {
|
||||||
|
type Envelope = rstar::AABB<[f64; 2]>;
|
||||||
|
fn envelope(&self) -> rstar::AABB<[f64; 2]> {
|
||||||
|
rstar::AABB::from_point([self.pt.x(), self.pt.y()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl rstar::PointDistance for Rto {
|
||||||
|
fn distance_2(&self, point: &[f64; 2]) -> f64 {
|
||||||
|
let d_x = self.pt.0.x - point[0];
|
||||||
|
let d_y = self.pt.0.y - point[1];
|
||||||
|
d_x * d_x + d_y * d_y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut temp_rtree = rstar::RTree::<Rto>::new();
|
||||||
|
|
||||||
|
for &idx in nodes {
|
||||||
|
let PrimitiveIndex::FixedDot(dot) = idx else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let pt = self.drawing.geometry().dot_weight(dot.into()).pos();
|
||||||
|
temp_rtree.insert(Rto { pt, idx });
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute the convex hull
|
||||||
|
let convex_hull_exterior = shape.exterior().convex_hull().into_inner().0;
|
||||||
|
for pt in convex_hull_exterior {
|
||||||
|
// note that the convex hull should be a strict subset of the points
|
||||||
|
// on the exterior of `shape`
|
||||||
|
assert!(temp_rtree.pop_nearest_neighbor(&[pt.x, pt.y]).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark all elements which aren't in the convex hull
|
||||||
|
for Rto { idx, .. } in temp_rtree {
|
||||||
|
self.drawing.add_to_compound(
|
||||||
|
recorder,
|
||||||
|
GenericIndex::<()>::new(idx.petgraph_index()),
|
||||||
|
CompoundEntryKind::NotInConvexHull,
|
||||||
|
poly_compound,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// maybe this should be a different edge kind
|
// maybe this should be a different edge kind
|
||||||
self.drawing
|
self.drawing.add_to_compound(
|
||||||
.add_to_compound(recorder, apex, (), poly_compound);
|
recorder,
|
||||||
|
apex,
|
||||||
|
CompoundEntryKind::NotInConvexHull,
|
||||||
|
poly_compound,
|
||||||
|
);
|
||||||
|
|
||||||
assert!(is_apex(&self.drawing, apex));
|
assert!(is_apex(&self.drawing, apex));
|
||||||
|
|
||||||
|
|
@ -304,7 +368,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
pub fn poly_members(
|
pub fn poly_members(
|
||||||
&self,
|
&self,
|
||||||
poly: GenericIndex<PolyWeight>,
|
poly: GenericIndex<PolyWeight>,
|
||||||
) -> impl Iterator<Item = ((), PrimitiveIndex)> + '_ {
|
) -> impl Iterator<Item = (CompoundEntryKind, PrimitiveIndex)> + '_ {
|
||||||
self.drawing
|
self.drawing
|
||||||
.geometry()
|
.geometry()
|
||||||
.compound_members(GenericIndex::new(poly.petgraph_index()))
|
.compound_members(GenericIndex::new(poly.petgraph_index()))
|
||||||
|
|
|
||||||
|
|
@ -61,20 +61,18 @@ impl<'a, R> PolyRef<'a, R> {
|
||||||
Self { index, drawing }
|
Self { index, drawing }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_apex(&self, dot: FixedDotIndex) -> bool {
|
|
||||||
is_apex(self.drawing, dot)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apex(&self) -> FixedDotIndex {
|
pub fn apex(&self) -> FixedDotIndex {
|
||||||
self.drawing
|
self.drawing
|
||||||
.geometry()
|
.geometry()
|
||||||
.compound_members(self.index.into())
|
.compound_members(self.index.into())
|
||||||
.find_map(|(_kind, primitive_node)| {
|
.find_map(|(kind, primitive_node)| {
|
||||||
|
if kind == CompoundEntryKind::NotInConvexHull {
|
||||||
if let PrimitiveIndex::FixedDot(dot) = primitive_node {
|
if let PrimitiveIndex::FixedDot(dot) = primitive_node {
|
||||||
if self.is_apex(dot) {
|
if is_apex(self.drawing, dot) {
|
||||||
return Some(dot);
|
return Some(dot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
|
|
@ -110,7 +108,7 @@ impl<R> MakePolygon for PolyRef<'_, R> {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.is_apex(dot) {
|
if is_apex(self.drawing, dot) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.drawing.geometry().dot_weight(dot.into()).pos())
|
Some(self.drawing.geometry().dot_weight(dot.into()).pos())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue