// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use core::ops::ControlFlow; use enum_dispatch::enum_dispatch; use geo::geometry::{LineString, Point}; use thiserror::Error; use crate::{ autorouter::{ execution::ExecutionStepper, invoker::{GetDebugOverlayData, Invoker, InvokerError}, }, board::AccessMesadata, drawing::graph::PrimitiveIndex, geometry::primitive::PrimitiveShape, graph::GenericIndex, interactor::interaction::{InteractionError, InteractionStepper}, layout::poly::PolyWeight, router::{ navcord::Navcord, navmesh::{Navmesh, NavnodeIndex}, ng, thetastar::ThetastarStepper, }, stepper::{Abort, OnEvent, Step}, }; /// Stores the interactive input data from the user. pub struct InteractiveInput { pub active_layer: Option, pub pointer_pos: Point, pub dt: f32, } #[derive(Clone, Copy, Debug)] pub enum InteractiveEventKind { PointerPrimaryButtonClicked, PointerPrimaryButtonDragStarted, PointerPrimaryButtonDragStopped, PointerSecondaryButtonClicked, } /// An event received from the user. pub struct InteractiveEvent { pub kind: InteractiveEventKind, /// `true` if the `Ctrl` key is pressed during the event pub ctrl: bool, /// `true` if the `Shift` key is pressed during the event pub shift: bool, } /// This is the execution context passed to the stepper on each step. pub struct ActivityContext<'a, M> { pub interactive_input: &'a InteractiveInput, pub invoker: &'a mut Invoker, } #[derive(Error, Debug, Clone)] pub enum ActivityError { #[error(transparent)] Interaction(#[from] InteractionError), #[error(transparent)] Invoker(#[from] InvokerError), } /// An activity is either an interaction or an execution. #[enum_dispatch(GetDebugOverlayData)] pub enum ActivityStepper { Interaction(InteractionStepper), Execution(ExecutionStepper), } impl Step, String> for ActivityStepper { type Error = ActivityError; fn step( &mut self, context: &mut ActivityContext, ) -> Result, ActivityError> { match self { ActivityStepper::Interaction(interaction) => Ok(interaction.step(context)?), ActivityStepper::Execution(execution) => Ok(execution.step(context.invoker)?), } } } impl Abort> for ActivityStepper { fn abort(&mut self, context: &mut Invoker) { match self { ActivityStepper::Interaction(interaction) => interaction.abort(context), ActivityStepper::Execution(execution) => execution.abort(context), } } } impl OnEvent, InteractiveEvent> for ActivityStepper { type Output = Result<(), InteractionError>; fn on_event( &mut self, context: &mut ActivityContext, event: InteractiveEvent, ) -> Result<(), InteractionError> { match self { ActivityStepper::Interaction(interaction) => interaction.on_event(context, event), ActivityStepper::Execution(_) => Ok(()), } } } /// An ActivityStepper that preserves its status pub struct ActivityStepperWithStatus { activity: ActivityStepper, maybe_status: Option>, } impl ActivityStepperWithStatus { pub fn new_execution(execution: ExecutionStepper) -> Self { Self { activity: ActivityStepper::Execution(execution), maybe_status: None, } } pub fn new_interaction(interaction: InteractionStepper) -> Self { Self { activity: ActivityStepper::Interaction(interaction), maybe_status: None, } } pub fn activity(&self) -> &ActivityStepper { &self.activity } pub fn maybe_status(&self) -> Option> { self.maybe_status.clone() } } impl Step, String> for ActivityStepperWithStatus { type Error = ActivityError; fn step( &mut self, context: &mut ActivityContext, ) -> Result, ActivityError> { let status = self.activity.step(context)?; self.maybe_status = Some(status.clone()); Ok(status) } } impl Abort> for ActivityStepperWithStatus { fn abort(&mut self, context: &mut Invoker) { self.maybe_status = Some(ControlFlow::Break(String::from("aborted"))); self.activity.abort(context); } } impl OnEvent, InteractiveEvent> for ActivityStepperWithStatus { type Output = Result<(), InteractionError>; fn on_event( &mut self, context: &mut ActivityContext, event: InteractiveEvent, ) -> Result<(), InteractionError> { self.activity.on_event(context, event) } } impl GetDebugOverlayData for ActivityStepperWithStatus { fn maybe_thetastar(&self) -> Option<&ThetastarStepper> { self.activity.maybe_thetastar() } fn maybe_topo_navmesh(&self) -> Option> { self.activity.maybe_topo_navmesh() } fn maybe_navcord(&self) -> Option<&Navcord> { self.activity.maybe_navcord() } fn active_polygons(&self) -> &[GenericIndex] { self.activity.active_polygons() } fn ghosts(&self) -> &[PrimitiveShape] { self.activity.ghosts() } fn obstacles(&self) -> &[PrimitiveIndex] { self.activity.obstacles() } fn polygonal_blockers(&self) -> &[LineString] { self.activity.polygonal_blockers() } fn navnode_debug_text(&self, navnode: NavnodeIndex) -> Option<&str> { self.activity.navnode_debug_text(navnode) } fn navedge_debug_text(&self, navedge: (NavnodeIndex, NavnodeIndex)) -> Option<&str> { self.activity.navedge_debug_text(navedge) } }