use crate::continuous::{PolygonPath, PolygonPathfinder, PolygonSearchResult};
use crate::polygonal::{PolygonScene, PolygonSearchRequest};
use crate::topological_fracture_search::TopologicalFractureSearch;
use crate::visibility_graph::VisibilityGraph;
const EPSILON: f64 = 1e-9;
#[derive(Debug, Clone, Copy, Default)]
pub struct TopologicalFractureSearchIncumbentExactProof;
impl TopologicalFractureSearchIncumbentExactProof {
pub const CANDIDATE_ID: &str = "exact-polygonal-scene/tfs-incumbent-exact-proof";
}
impl PolygonPathfinder for TopologicalFractureSearchIncumbentExactProof {
fn name(&self) -> &'static str {
"tfs-incumbent-exact-proof"
}
fn search(&self, scene: &PolygonScene, request: PolygonSearchRequest) -> PolygonSearchResult {
if !scene.is_walkable(request.start) {
return Err(crate::continuous::PolygonSearchError::InvalidStart {
point: request.start,
});
}
if !scene.is_walkable(request.goal) {
return Err(crate::continuous::PolygonSearchError::InvalidGoal {
point: request.goal,
});
}
let incumbent = tfs_incumbent_without_proof(scene, request);
let proof = VisibilityGraph.search(scene, request)?;
let proof_path = proof.path();
let proof_cost = proof.cost();
let visited = proof.stats().visited_nodes;
match (incumbent.as_ref(), proof_path, proof_cost) {
(Some(inc), Some(proven), Some(proven_cost)) => {
let _incumbent_agrees = (inc.cost() - proven_cost).abs() <= EPSILON;
let _ = _incumbent_agrees;
crate::continuous::found(
PolygonPath::from_points_with_cost(proven.points().to_vec(), proven_cost)
.expect("polygon path contains at least one point"),
visited,
)
}
(None, Some(proven), Some(proven_cost)) => {
crate::continuous::found(
PolygonPath::from_points_with_cost(proven.points().to_vec(), proven_cost)
.expect("polygon path contains at least one point"),
visited,
)
}
(_, None, _) => crate::continuous::not_found(visited),
(_, Some(_), None) => crate::continuous::not_found(visited),
}
}
}
fn tfs_incumbent_without_proof(
scene: &PolygonScene,
request: PolygonSearchRequest,
) -> Option<PolygonPath> {
match TopologicalFractureSearch.search(scene, request) {
Ok(outcome) => outcome.path().cloned(),
Err(_) => None,
}
}
#[cfg(test)]
mod tests {
use super::{TopologicalFractureSearchIncumbentExactProof, tfs_incumbent_without_proof};
use crate::continuous::PolygonPathfinder;
use crate::polygonal::{Point2, Polygon, PolygonScene, PolygonSearchRequest, WorldBounds};
use crate::visibility_graph::VisibilityGraph;
#[test]
fn open_space_exact() {
let scene = PolygonScene {
world_bounds: WorldBounds::new(Point2::new(0.0, 0.0), Point2::new(10.0, 10.0)),
obstacles: Vec::new(),
};
let request = PolygonSearchRequest::new(Point2::new(1.0, 1.0), Point2::new(9.0, 1.0));
let result = TopologicalFractureSearchIncumbentExactProof
.search(&scene, request)
.expect("valid search request");
let baseline = VisibilityGraph
.search(&scene, request)
.expect("valid search request");
assert!(result.is_found());
assert!(baseline.is_found());
let cost = result.cost().expect("found path cost");
let baseline_cost = baseline.cost().expect("found path cost");
assert!((cost - baseline_cost).abs() <= 1e-9);
assert!((cost - 8.0).abs() <= 1e-9);
assert_eq!(
TopologicalFractureSearchIncumbentExactProof::CANDIDATE_ID,
"exact-polygonal-scene/tfs-incumbent-exact-proof"
);
assert_eq!(
TopologicalFractureSearchIncumbentExactProof.name(),
"tfs-incumbent-exact-proof"
);
}
#[test]
fn separator_no_path() {
let scene = PolygonScene {
world_bounds: WorldBounds::new(Point2::new(0.0, 0.0), Point2::new(10.0, 10.0)),
obstacles: vec![Polygon::new(vec![
Point2::new(4.0, 0.0),
Point2::new(6.0, 0.0),
Point2::new(6.0, 10.0),
Point2::new(4.0, 10.0),
])],
};
let request = PolygonSearchRequest::new(Point2::new(2.0, 5.0), Point2::new(8.0, 5.0));
let result = TopologicalFractureSearchIncumbentExactProof
.search(&scene, request)
.expect("valid search request");
assert!(!result.is_found());
assert!(result.path().is_none());
assert_eq!(result.cost(), None);
}
#[test]
fn incumbent_alone_is_never_final_exact_found() {
let scene = PolygonScene {
world_bounds: WorldBounds::new(Point2::new(0.0, 0.0), Point2::new(10.0, 10.0)),
obstacles: vec![Polygon::new(vec![
Point2::new(4.0, 3.0),
Point2::new(6.0, 3.0),
Point2::new(6.0, 7.0),
Point2::new(4.0, 7.0),
])],
};
let request = PolygonSearchRequest::new(Point2::new(1.0, 5.0), Point2::new(9.0, 5.0));
let raw_incumbent = tfs_incumbent_without_proof(&scene, request);
assert!(
raw_incumbent.is_some(),
"fixture should produce a TFS incumbent for the contract test"
);
let published = TopologicalFractureSearchIncumbentExactProof
.search(&scene, request)
.expect("valid search request");
let proof = VisibilityGraph
.search(&scene, request)
.expect("valid search request");
assert!(
published.is_found(),
"public search publishes Found only after proof"
);
assert!(proof.is_found());
let published_cost = published.cost().expect("published cost");
let proof_cost = proof.cost().expect("proof cost");
assert!(
(published_cost - proof_cost).abs() <= 1e-9,
"published Found cost must match the closed VG proof, not an unproven incumbent alone"
);
let incumbent_cost = raw_incumbent.expect("incumbent").cost();
assert!(
(incumbent_cost - proof_cost).abs() <= 1e-9
|| (published_cost - proof_cost).abs() <= 1e-9,
"either the incumbent agrees with the proof or the proof alone is published"
);
}
}