// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use std::ops::ControlFlow; use derive_getters::{Dissolve, Getters}; use crate::{ drawing::{ band::BandTermsegIndex, dot::FixedDotIndex, graph::PrimitiveIndex, rules::AccessRules, }, geometry::primitive::PrimitiveShape, layout::LayoutEdit, router::{ astar::{Astar, AstarError}, navcord::NavcordStepper, navcorder::Navcorder, navmesh::{Navmesh, NavmeshError}, Router, RouterAstarStrategy, }, stepper::Step, }; #[derive(Getters, Dissolve)] pub struct RouteStepper { #[getter(skip)] astar: Astar, navcord: NavcordStepper, ghosts: Vec, obstacles: Vec, } impl RouteStepper { pub fn new( router: &mut Router, recorder: LayoutEdit, from: FixedDotIndex, to: FixedDotIndex, width: f64, ) -> Result { let navmesh = Navmesh::new(router.layout(), from, to, *router.options())?; Ok(Self::new_from_navmesh(router, recorder, navmesh, width)) } pub fn new_from_navmesh( router: &mut Router, recorder: LayoutEdit, navmesh: Navmesh, width: f64, ) -> Self { let source = navmesh.origin(); let source_navvertex = navmesh.origin_navvertex(); let target = navmesh.destination(); let layout = router.layout_mut(); let mut navcord = layout.start(recorder, source, source_navvertex, width); let mut strategy = RouterAstarStrategy::new(layout, &mut navcord, target); let astar = Astar::new(navmesh, source_navvertex, &mut strategy); let ghosts = vec![]; let obstacles = vec![]; Self { astar, navcord, ghosts, obstacles, } } pub fn navmesh(&self) -> &Navmesh { &self.astar.graph } } impl<'a, R: AccessRules> Step, BandTermsegIndex> for RouteStepper { type Error = AstarError; fn step( &mut self, router: &mut Router, ) -> Result, AstarError> { let layout = router.layout_mut(); let target = self.astar.graph.destination(); let mut strategy = RouterAstarStrategy::new(layout, &mut self.navcord, target); let result = match self.astar.step(&mut strategy) { Ok(ControlFlow::Continue(..)) => Ok(ControlFlow::Continue(())), Ok(ControlFlow::Break((_cost, _path, band))) => Ok(ControlFlow::Break(band)), Err(e) => { // NOTE(fogti): The 1 instead 0 is because the first element in the path // is the source navvertex. See also: `NavcordStepper::new`. for _ in 1..self.navcord.path.len() { self.navcord.step_back(layout); } Err(e) } }; self.ghosts = strategy.probe_ghosts; self.obstacles = strategy.probe_obstacles; result } }