mirror of https://codeberg.org/topola/topola.git
fix(drawing/drawing): Prevent self-intersecting band loops from forming
Fixes https://codeberg.org/topola/topola/issues/205
This commit is contained in:
parent
6175f7aec2
commit
680a813a33
|
|
@ -606,8 +606,29 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Segs must not cross.
|
// Check if the cane's seg collides with:
|
||||||
if let Some(collision) = self.detect_collision(cane.seg.into()) {
|
// - Different-net primitives,
|
||||||
|
// - Same-net loose segs.
|
||||||
|
//
|
||||||
|
// This solves two problems:
|
||||||
|
//
|
||||||
|
// It prevents the currently routed band from forming a
|
||||||
|
// self-intersecting loop.
|
||||||
|
//
|
||||||
|
// And it prevents the currently routed band from infringing already
|
||||||
|
// routed bands wrapped around the same core. This can happen because
|
||||||
|
// when the band is squeezed through under bends the outward bows
|
||||||
|
// of these bends are excluded from infringement detection to avoid
|
||||||
|
// false positives (the code where this exception is made is in
|
||||||
|
// `.update_this_and_outward_bows_intern(...)`).
|
||||||
|
//
|
||||||
|
// XXX: Possible problem: What if there could be a collision due
|
||||||
|
// to bend's length being zero? We may or may not want to create an
|
||||||
|
// exception for this case, at least until we switch to our exact
|
||||||
|
// lineocircular kernel Cyrk.
|
||||||
|
if let Some(collision) =
|
||||||
|
self.detect_collision_except(cane.seg.into(), &|_drawing, _collider, _collidee| true)
|
||||||
|
{
|
||||||
let joint = self.primitive(cane.bend).other_joint(cane.dot);
|
let joint = self.primitive(cane.bend).other_joint(cane.dot);
|
||||||
self.remove_cane(recorder, &cane, joint);
|
self.remove_cane(recorder, &cane, joint);
|
||||||
Err(collision.into())
|
Err(collision.into())
|
||||||
|
|
@ -924,21 +945,36 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_collision(&self, node: PrimitiveIndex) -> Option<Collision> {
|
fn detect_collision_except(
|
||||||
let shape = node.primitive(self).shape();
|
&self,
|
||||||
|
collider: PrimitiveIndex,
|
||||||
|
predicate: &impl Fn(&Self, PrimitiveIndex, PrimitiveIndex) -> bool,
|
||||||
|
) -> Option<Collision> {
|
||||||
|
let shape = collider.primitive(self).shape();
|
||||||
|
|
||||||
self.recording_geometry_with_rtree
|
self.recording_geometry_with_rtree
|
||||||
.rtree()
|
.rtree()
|
||||||
.locate_in_envelope_intersecting(&shape.full_height_envelope_3d(0.0, 2))
|
.locate_in_envelope_intersecting(&shape.full_height_envelope_3d(0.0, 2))
|
||||||
.filter_map(|wrapper| {
|
.filter_map(|wrapper| {
|
||||||
if let GenericNode::Primitive(primitive_node) = wrapper.data {
|
if let GenericNode::Primitive(collidee) = wrapper.data {
|
||||||
Some(primitive_node)
|
Some(collidee)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(|primitive_node| !self.are_connectable(node, *primitive_node))
|
// NOTE: Collisions can happen between two same-net loose
|
||||||
.find(|primitive_node| shape.intersects(&primitive_node.primitive(self).shape()))
|
// segs, so these cases in particular are not filtered out
|
||||||
|
// here, unlike what is done in infringement code.
|
||||||
|
.filter(|collidee| {
|
||||||
|
collider != *collidee
|
||||||
|
&& (!self.are_connectable(collider, *collidee)
|
||||||
|
|| ((matches!(collider, PrimitiveIndex::LoneLooseSeg(..))
|
||||||
|
|| matches!(collider, PrimitiveIndex::SeqLooseSeg(..)))
|
||||||
|
&& (matches!(collidee, PrimitiveIndex::LoneLooseSeg(..))
|
||||||
|
|| matches!(collidee, PrimitiveIndex::SeqLooseSeg(..)))))
|
||||||
|
})
|
||||||
|
.filter(|collidee| predicate(&self, collider, *collidee))
|
||||||
|
.find(|collidee| shape.intersects(&collidee.primitive(self).shape()))
|
||||||
.map(|collidee| Collision(shape, collidee))
|
.map(|collidee| Collision(shape, collidee))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1069,18 +1105,18 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
|
|
||||||
fn find_infringement(
|
fn find_infringement(
|
||||||
&self,
|
&self,
|
||||||
node: PrimitiveIndex,
|
infringer: PrimitiveIndex,
|
||||||
it: impl Iterator<Item = PrimitiveIndex>,
|
it: impl Iterator<Item = PrimitiveIndex>,
|
||||||
) -> Option<Infringement> {
|
) -> Option<Infringement> {
|
||||||
let mut inflated_shape = node.primitive(self).shape(); // Unused temporary value just for initialization.
|
let mut inflated_shape = infringer.primitive(self).shape(); // Unused temporary value just for initialization.
|
||||||
let conditions = node.primitive(self).conditions();
|
let conditions = infringer.primitive(self).conditions();
|
||||||
|
|
||||||
it.filter(|primitive_node| !self.are_connectable(node, *primitive_node))
|
it.filter(|infringee| !self.are_connectable(infringer, *infringee))
|
||||||
.find_map(|primitive_node| {
|
.find_map(|primitive_node| {
|
||||||
let infringee_conditions = primitive_node.primitive(self).conditions();
|
let infringee_conditions = primitive_node.primitive(self).conditions();
|
||||||
|
|
||||||
let epsilon = 1.0;
|
let epsilon = 1.0;
|
||||||
inflated_shape = node.primitive(self).shape().inflate(
|
inflated_shape = infringer.primitive(self).shape().inflate(
|
||||||
match (&conditions, infringee_conditions) {
|
match (&conditions, infringee_conditions) {
|
||||||
(None, _) | (_, None) => 0.0,
|
(None, _) | (_, None) => 0.0,
|
||||||
(Some(lhs), Some(rhs)) => {
|
(Some(lhs), Some(rhs)) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue