Skip to main content

subtr_actor/stats/analysis_graph/nodes/
positioning.rs

1use super::*;
2use crate::stats::calculators::*;
3use crate::*;
4
5/// Tracks per-player field positioning (thirds/halves/roles/proximity) from frame and possession state.
6pub struct PositioningNode {
7    calculator: PositioningCalculator,
8}
9
10impl PositioningNode {
11    pub fn new() -> Self {
12        Self::with_config(PositioningCalculatorConfig::default())
13    }
14
15    pub fn with_config(config: PositioningCalculatorConfig) -> Self {
16        Self {
17            calculator: PositioningCalculator::with_config(config),
18        }
19    }
20}
21
22impl Default for PositioningNode {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl AnalysisNode for PositioningNode {
29    type State = PositioningCalculator;
30
31    fn name(&self) -> &'static str {
32        "positioning"
33    }
34
35    fn emitted_events(&self) -> &'static [crate::stats::calculators::EmittedEvent] {
36        crate::stats::calculators::POSITIONING_EMITTED_EVENTS
37    }
38
39    fn dependencies(&self) -> NodeDependencies {
40        vec![
41            frame_info_dependency(),
42            gameplay_state_dependency(),
43            ball_frame_state_dependency(),
44            player_frame_state_dependency(),
45            frame_events_state_dependency(),
46            possession_state_dependency(),
47            live_play_dependency(),
48        ]
49    }
50
51    fn evaluate(&mut self, ctx: &AnalysisStateContext<'_>) -> SubtrActorResult<()> {
52        let possession_state = ctx.get::<PossessionState>()?;
53        self.calculator.update(
54            ctx.get::<FrameInfo>()?,
55            ctx.get::<GameplayState>()?,
56            ctx.get::<BallFrameState>()?,
57            ctx.get::<PlayerFrameState>()?,
58            ctx.get::<FrameEventsState>()?,
59            ctx.get::<LivePlayState>()?,
60            possession_state.active_player_before_sample.as_ref(),
61        )
62    }
63
64    fn finish(&mut self, _ctx: &AnalysisStateContext<'_>) -> SubtrActorResult<()> {
65        self.calculator.flush_pending_events();
66        Ok(())
67    }
68
69    fn state(&self) -> &Self::State {
70        &self.calculator
71    }
72}
73
74pub(crate) fn boxed_default() -> Box<dyn AnalysisNodeDyn> {
75    Box::new(PositioningNode::new())
76}