mirror of https://codeberg.org/topola/topola.git
feat(topola-egui): Show shape of infringee in addition to inflated infringer's
This commit is contained in:
parent
3078d9d546
commit
f4b78749b1
|
|
@ -67,11 +67,17 @@ impl fmt::Debug for DrawingException {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawingException {
|
impl DrawingException {
|
||||||
pub fn maybe_ghost_and_obstacle(&self) -> Option<(&PrimitiveShape, PrimitiveIndex)> {
|
pub fn maybe_ghosts_and_obstacle(
|
||||||
|
&self,
|
||||||
|
) -> Option<(&PrimitiveShape, &PrimitiveShape, PrimitiveIndex)> {
|
||||||
match self {
|
match self {
|
||||||
Self::NoTangents(_) => None,
|
Self::NoTangents(_) => None,
|
||||||
Self::Infringement(Infringement(ghost, obstacle)) => Some((ghost, *obstacle)),
|
Self::Infringement(Infringement(infringer_ghost, infringee_ghost, obstacle)) => {
|
||||||
Self::Collision(Collision(ghost, obstacle)) => Some((ghost, *obstacle)),
|
Some((infringer_ghost, infringee_ghost, *obstacle))
|
||||||
|
}
|
||||||
|
Self::Collision(Collision(collider_ghost, collidee_ghost, obstacle)) => {
|
||||||
|
Some((collider_ghost, collidee_ghost, *obstacle))
|
||||||
|
}
|
||||||
Self::AlreadyConnected(_) => None,
|
Self::AlreadyConnected(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +92,7 @@ impl DrawingException {
|
||||||
/// having the same net implies being connectable.
|
/// having the same net implies being connectable.
|
||||||
#[derive(Error, Debug, Clone, Copy)]
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
#[error("{0:?} infringes on {1:?}")]
|
#[error("{0:?} infringes on {1:?}")]
|
||||||
pub struct Infringement(pub PrimitiveShape, pub PrimitiveIndex);
|
pub struct Infringement(pub PrimitiveShape, pub PrimitiveShape, pub PrimitiveIndex);
|
||||||
|
|
||||||
/// A collision is a special case of infringement where the uninflated shapes of
|
/// A collision is a special case of infringement where the uninflated shapes of
|
||||||
/// two primitives themselves intersect. In other words, collision detection is
|
/// two primitives themselves intersect. In other words, collision detection is
|
||||||
|
|
@ -107,7 +113,7 @@ pub struct Infringement(pub PrimitiveShape, pub PrimitiveIndex);
|
||||||
/// net, making them connectable and thus uninfringable.
|
/// net, making them connectable and thus uninfringable.
|
||||||
#[derive(Error, Debug, Clone, Copy)]
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
#[error("{0:?} collides with {1:?}")]
|
#[error("{0:?} collides with {1:?}")]
|
||||||
pub struct Collision(pub PrimitiveShape, pub PrimitiveIndex);
|
pub struct Collision(pub PrimitiveShape, pub PrimitiveShape, pub PrimitiveIndex);
|
||||||
|
|
||||||
#[derive(Error, Debug, Clone, Copy)]
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
#[error("{1:?} is already connected to net {0}")]
|
#[error("{1:?} is already connected to net {0}")]
|
||||||
|
|
|
||||||
|
|
@ -190,9 +190,9 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
// Infringement with loose dots resulted in false positives for
|
// Infringement with loose dots resulted in false positives for
|
||||||
// line-of-sight paths.
|
// line-of-sight paths.
|
||||||
!matches!(infringer, PrimitiveIndex::LooseDot(..))
|
!matches!(infringer, PrimitiveIndex::LooseDot(..))
|
||||||
&& !matches!(infringement.1, PrimitiveIndex::LooseDot(..))
|
&& !matches!(infringement.2, PrimitiveIndex::LooseDot(..))
|
||||||
})
|
})
|
||||||
.filter(move |infringement| !self.are_connectable(infringer, infringement.1))
|
.filter(move |infringement| !self.are_connectable(infringer, infringement.2))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overlapees<'a>(
|
pub fn overlapees<'a>(
|
||||||
|
|
@ -224,8 +224,10 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
let infringee_conditions = primitive_node.primitive_ref(self).conditions();
|
let infringee_conditions = primitive_node.primitive_ref(self).conditions();
|
||||||
|
|
||||||
let epsilon = 1.0;
|
let epsilon = 1.0;
|
||||||
let inflated_shape = intersector.primitive_ref(self).shape().inflate(
|
let inflated_infringer_shape = intersector.primitive_ref(self).shape().inflate(match (
|
||||||
match (&conditions, infringee_conditions) {
|
&conditions,
|
||||||
|
infringee_conditions,
|
||||||
|
) {
|
||||||
(None, _) | (_, None) => 0.0,
|
(None, _) | (_, None) => 0.0,
|
||||||
(Some(lhs), Some(rhs)) => {
|
(Some(lhs), Some(rhs)) => {
|
||||||
// Note the epsilon comparison.
|
// Note the epsilon comparison.
|
||||||
|
|
@ -233,12 +235,17 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
// it be exactly then?
|
// it be exactly then?
|
||||||
(self.rules().clearance(lhs, &rhs) - epsilon).clamp(0.0, f64::INFINITY)
|
(self.rules().clearance(lhs, &rhs) - epsilon).clamp(0.0, f64::INFINITY)
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
inflated_shape
|
let infringee_shape = primitive_node.primitive_ref(self).shape();
|
||||||
.intersects(&primitive_node.primitive_ref(self).shape())
|
|
||||||
.then_some(Infringement(inflated_shape, primitive_node))
|
inflated_infringer_shape
|
||||||
|
.intersects(&infringee_shape)
|
||||||
|
.then_some(Infringement(
|
||||||
|
inflated_infringer_shape,
|
||||||
|
infringee_shape,
|
||||||
|
primitive_node,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,11 +273,11 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
collider: PrimitiveIndex,
|
collider: PrimitiveIndex,
|
||||||
predicate: &impl Fn(&Self, PrimitiveIndex, PrimitiveIndex) -> bool,
|
predicate: &impl Fn(&Self, PrimitiveIndex, PrimitiveIndex) -> bool,
|
||||||
) -> Option<Collision> {
|
) -> Option<Collision> {
|
||||||
let shape = collider.primitive_ref(self).shape();
|
let collider_shape = collider.primitive_ref(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(&collider_shape.full_height_envelope_3d(0.0, 2))
|
||||||
.filter_map(|wrapper| {
|
.filter_map(|wrapper| {
|
||||||
if let GenericNode::Primitive(collidee) = wrapper.data {
|
if let GenericNode::Primitive(collidee) = wrapper.data {
|
||||||
Some(collidee)
|
Some(collidee)
|
||||||
|
|
@ -290,8 +297,15 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
||||||
|| matches!(collidee, PrimitiveIndex::SeqLooseSeg(..))))
|
|| matches!(collidee, PrimitiveIndex::SeqLooseSeg(..))))
|
||||||
})
|
})
|
||||||
.filter(|collidee| predicate(&self, collider, *collidee))
|
.filter(|collidee| predicate(&self, collider, *collidee))
|
||||||
.find(|collidee| shape.intersects(&collidee.primitive_ref(self).shape()))
|
.find_map(|collidee| {
|
||||||
.map(|collidee| Collision(shape, collidee))
|
let collidee_shape = collidee.primitive_ref(self).shape();
|
||||||
|
|
||||||
|
if collider_shape.intersects(&collidee_shape) {
|
||||||
|
Some(Collision(collider_shape, collidee_shape, collidee))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn are_connectable(&self, node1: PrimitiveIndex, node2: PrimitiveIndex) -> bool {
|
fn are_connectable(&self, node1: PrimitiveIndex, node2: PrimitiveIndex) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
!drawing
|
!drawing
|
||||||
.overlapees(around.into())
|
.overlapees(around.into())
|
||||||
.find(|overlapee| {
|
.find(|overlapee| {
|
||||||
PrimitiveIndex::from(overlapee.1)
|
PrimitiveIndex::from(overlapee.2)
|
||||||
.primitive_ref(drawing)
|
.primitive_ref(drawing)
|
||||||
.limbs()
|
.limbs()
|
||||||
.contains(&infringee)
|
.contains(&infringee)
|
||||||
|
|
|
||||||
|
|
@ -337,14 +337,14 @@ impl Navmesh {
|
||||||
// Ignore overlaps with fillets.
|
// Ignore overlaps with fillets.
|
||||||
if layout
|
if layout
|
||||||
.drawing()
|
.drawing()
|
||||||
.compounds(GenericIndex::<()>::new(overlapee.1.index()))
|
.compounds(GenericIndex::<()>::new(overlapee.2.index()))
|
||||||
.find(|(label, _)| *label == CompoundEntryLabel::Fillet)
|
.find(|(label, _)| *label == CompoundEntryLabel::Fillet)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let PrimitiveIndex::FixedDot(overlapee) = overlapee.1 else {
|
let PrimitiveIndex::FixedDot(overlapee) = overlapee.2 else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -257,9 +257,11 @@ impl EvalException {
|
||||||
Self::Draw(DrawException::NoBitangents(_)) => (Vec::new(), Vec::new(), Vec::new()),
|
Self::Draw(DrawException::NoBitangents(_)) => (Vec::new(), Vec::new(), Vec::new()),
|
||||||
Self::Draw(DrawException::CannotFinishIn(_, dwxc))
|
Self::Draw(DrawException::CannotFinishIn(_, dwxc))
|
||||||
| Self::Draw(DrawException::CannotWrapAround(_, dwxc)) => {
|
| Self::Draw(DrawException::CannotWrapAround(_, dwxc)) => {
|
||||||
match dwxc.maybe_ghost_and_obstacle() {
|
match dwxc.maybe_ghosts_and_obstacle() {
|
||||||
None => (Vec::new(), Vec::new(), Vec::new()),
|
None => (Vec::new(), Vec::new(), Vec::new()),
|
||||||
Some((ghost, obstacle)) => (vec![*ghost], Vec::new(), vec![obstacle]),
|
Some((infringer_ghost, _, obstacle)) => {
|
||||||
|
(vec![*infringer_ghost], Vec::new(), vec![obstacle])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::ResolvingPathFailed { .. } => (Vec::new(), Vec::new(), Vec::new()),
|
Self::ResolvingPathFailed { .. } => (Vec::new(), Vec::new(), Vec::new()),
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ impl Prenavmesh {
|
||||||
layout
|
layout
|
||||||
.drawing()
|
.drawing()
|
||||||
// TODO: Add `.compounds()` method working on `PrimitiveIndex`.
|
// TODO: Add `.compounds()` method working on `PrimitiveIndex`.
|
||||||
.compounds(GenericIndex::<()>::new(overlapee.1.index()))
|
.compounds(GenericIndex::<()>::new(overlapee.2.index()))
|
||||||
.find(|(label, _)| *label == CompoundEntryLabel::Fillet)
|
.find(|(label, _)| *label == CompoundEntryLabel::Fillet)
|
||||||
.is_some()
|
.is_some()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -110,11 +110,13 @@ impl<R: AccessRules> ThetastarStrategy<Navmesh, f64, BandTermsegIndex>
|
||||||
DrawException::CannotWrapAround(.., layout_err) => layout_err,
|
DrawException::CannotWrapAround(.., layout_err) => layout_err,
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some((ghost, obstacle)) = layout_err.maybe_ghost_and_obstacle() else {
|
let Some((infringer_ghost, infringee_ghost, obstacle)) =
|
||||||
|
layout_err.maybe_ghosts_and_obstacle()
|
||||||
|
else {
|
||||||
return ControlFlow::Break(None);
|
return ControlFlow::Break(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.probe_ghosts = vec![*ghost];
|
self.probe_ghosts = vec![*infringer_ghost, *infringee_ghost];
|
||||||
self.probe_obstacles = vec![obstacle];
|
self.probe_obstacles = vec![obstacle];
|
||||||
|
|
||||||
let Some(initial_parent_navnode) = maybe_initial_parent_navnode else {
|
let Some(initial_parent_navnode) = maybe_initial_parent_navnode else {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue