fix(geometry/recording_with_rtree): Do not inadvertedly invalidate bend bboxes

This fixes a crash that was happening when undoing autoroutings.

Bends are still not restored correctly, I will fix that soon.
This commit is contained in:
Mikolaj Wielgus 2025-07-16 00:21:25 +02:00
parent 48fe63a387
commit 1f8ace9c77
2 changed files with 116 additions and 76 deletions

View File

@ -361,63 +361,6 @@ impl<
for RecordingGeometryWithRtree<PW, DW, SW, BW, CW, Cel, PI, DI, SI, BI> for RecordingGeometryWithRtree<PW, DW, SW, BW, CW, Cel, PI, DI, SI, BI>
{ {
fn apply(&mut self, edit: &GeometryEdit<DW, SW, BW, CW, Cel, PI, DI, SI, BI>) { fn apply(&mut self, edit: &GeometryEdit<DW, SW, BW, CW, Cel, PI, DI, SI, BI>) {
for (compound, (maybe_old_data, ..)) in &edit.compounds { self.geometry_with_rtree.apply(edit);
if maybe_old_data.is_some() {
self.geometry_with_rtree.remove_compound(*compound);
}
}
for (bend, (maybe_old_data, ..)) in &edit.bends {
if maybe_old_data.is_some() {
self.geometry_with_rtree.remove_bend(*bend);
}
}
for (seg, (maybe_old_data, ..)) in &edit.segs {
if maybe_old_data.is_some() {
self.geometry_with_rtree.remove_seg(*seg);
}
}
for (dot, (maybe_old_data, ..)) in &edit.dots {
if maybe_old_data.is_some() {
self.geometry_with_rtree.remove_dot(*dot);
}
}
for (dot, (.., maybe_new_data)) in &edit.dots {
if let Some(weight) = maybe_new_data {
self.geometry_with_rtree.add_dot_at_index(*dot, *weight);
}
}
for (seg, (.., maybe_new_data)) in &edit.segs {
if let Some(((from, to), weight)) = maybe_new_data {
self.geometry_with_rtree
.add_seg_at_index(*seg, *from, *to, *weight);
}
}
for (bend, (.., maybe_new_data)) in &edit.bends {
if let Some(((from, to, core), weight)) = maybe_new_data {
self.geometry_with_rtree
.add_bend_at_index(*bend, *from, *to, *core, *weight);
}
}
for (compound, (.., maybe_new_data)) in &edit.compounds {
if let Some((members, weight)) = maybe_new_data {
self.geometry_with_rtree
.add_compound_at_index(*compound, weight.clone());
for (entry_label, member) in members {
self.geometry_with_rtree.add_to_compound(
GenericIndex::<PW>::new(member.petgraph_index()),
*entry_label,
*compound,
);
}
}
}
} }
} }

View File

@ -18,6 +18,8 @@ use crate::{
graph::{GenericIndex, GetPetgraphIndex}, graph::{GenericIndex, GetPetgraphIndex},
}; };
use super::edit::{ApplyGeometryEdit, GeometryEdit};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Bbox { pub struct Bbox {
pub aabb: AABB<[f64; 3]>, pub aabb: AABB<[f64; 3]>,
@ -155,24 +157,6 @@ impl<
bend bend
} }
pub(super) fn add_bend_at_index<W: AccessBendWeight + Into<PW> + GetLayer>(
&mut self,
bend: BI,
from: DI,
to: DI,
core: DI,
weight: W,
) {
self.geometry.add_bend_at_index(
GenericIndex::<W>::new(bend.petgraph_index()),
from,
to,
core,
weight,
);
self.init_bend_bbox(bend);
}
pub(super) fn add_compound_at_index(&mut self, compound: GenericIndex<CW>, weight: CW) { pub(super) fn add_compound_at_index(&mut self, compound: GenericIndex<CW>, weight: CW) {
self.geometry self.geometry
.add_compound_at_index(GenericIndex::<CW>::new(compound.petgraph_index()), weight); .add_compound_at_index(GenericIndex::<CW>::new(compound.petgraph_index()), weight);
@ -491,3 +475,116 @@ impl<
self.geometry.compounds(node) self.geometry.compounds(node)
} }
} }
impl<
PW: GetWidth + GetLayer + TryInto<DW> + TryInto<SW> + TryInto<BW> + Retag<Index = PI> + Copy,
DW: AccessDotWeight + Into<PW> + GetLayer,
SW: AccessSegWeight + Into<PW> + GetLayer,
BW: AccessBendWeight + Into<PW> + GetLayer,
CW: Clone,
Cel: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Ord + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
> ApplyGeometryEdit<DW, SW, BW, CW, Cel, PI, DI, SI, BI>
for GeometryWithRtree<PW, DW, SW, BW, CW, Cel, PI, DI, SI, BI>
{
fn apply(&mut self, edit: &GeometryEdit<DW, SW, BW, CW, Cel, PI, DI, SI, BI>) {
// First we remove every node that is in the edit. But we have to do
// this in a correct order, because otherwise some removals of some
// nodes may fail due to inadvertent invalidation of the bboxes of these
// nodes.
//
// We chose to first remove compounds, then bends and segs, and only
// then dots.
for (compound, (maybe_old_data, ..)) in &edit.compounds {
if maybe_old_data.is_some() {
self.remove_compound(*compound);
}
}
// Because removal of a bend will invalidate bboxes of the bends wrapped
// around it, we first remove the bends from the R-tree, and their nodes
// from the graph only afterwards.
for (bend, (maybe_old_data, ..)) in &edit.bends {
if maybe_old_data.is_some() {
Self::rtree_remove_must_be_successful(
self.rtree.remove(&self.make_bend_bbox(*bend)),
);
}
}
for (bend, (maybe_old_data, ..)) in &edit.bends {
if maybe_old_data.is_some() {
self.geometry.remove_primitive((*bend).into());
}
}
for (seg, (maybe_old_data, ..)) in &edit.segs {
if maybe_old_data.is_some() {
self.remove_seg(*seg);
}
}
for (dot, (maybe_old_data, ..)) in &edit.dots {
if maybe_old_data.is_some() {
self.remove_dot(*dot);
}
}
// Now we add every node that is created or modified (but not removed)
// in the edit. This also has to be done in a right order, which we
// chose to be exactly the opposite of the order of the removal which we
// just did.
for (dot, (.., maybe_new_data)) in &edit.dots {
if let Some(weight) = maybe_new_data {
self.add_dot_at_index(*dot, *weight);
}
}
for (seg, (.., maybe_new_data)) in &edit.segs {
if let Some(((from, to), weight)) = maybe_new_data {
self.add_seg_at_index(*seg, *from, *to, *weight);
}
}
// Just as with removal, we handle bend layout nodes and their bboxes
// separately to prevent failures from inadvertent bbox invalidation.
for (bend, (.., maybe_new_data)) in &edit.bends {
if let Some(((from, to, core), weight)) = maybe_new_data {
self.geometry.add_bend_at_index(
GenericIndex::<BW>::new(bend.petgraph_index()),
*from,
*to,
*core,
*weight,
);
}
}
for (bend, (.., maybe_new_data)) in &edit.bends {
if let Some(..) = maybe_new_data {
self.init_bend_bbox(*bend);
}
}
for (compound, (.., maybe_new_data)) in &edit.compounds {
if let Some((members, weight)) = maybe_new_data {
self.add_compound_at_index(*compound, weight.clone());
for (entry_label, member) in members {
self.geometry.add_to_compound(
GenericIndex::<PW>::new(member.petgraph_index()),
*entry_label,
*compound,
);
}
}
}
}
}