package engine // Profile is an ordered chain of propagators executed sequentially. Each // propagator picks up where the previous one finished. type Profile struct { // Stages are run in order. For Direction=Reverse they are still iterated // from index 0 onwards, but each propagator integrates with negative dt. Stages []*Propagator // Direction controls the sign of dt across the whole profile. Direction Direction // Globals are constraints evaluated alongside each stage's local Constraints. // Useful for profile-wide bounds like "stop after N hours total". Globals []Constraint } // Run executes the profile from the given launch point. Returns one Result // per executed stage, including any Fallback chains that were activated. func (p *Profile) Run(t0 float64, launch State) []Result { if p.Direction == 0 { p.Direction = Forward } results := make([]Result, 0, len(p.Stages)) t, s := t0, launch for i := 0; i < len(p.Stages); i++ { stage := p.Stages[i] res := stage.run(t, s, p.Direction, p.Globals) results = append(results, res) last := res.Points[len(res.Points)-1] t = last.Time s = State{Lat: last.Lat, Lng: last.Lng, Altitude: last.Altitude} // Follow Fallback chains until none remains. Each fallback consumes // from the same point the previous stage stopped at. for res.Outcome == OutcomeFallback && stage.Fallback != nil { stage = stage.Fallback res = stage.run(t, s, p.Direction, p.Globals) results = append(results, res) last = res.Points[len(res.Points)-1] t = last.Time s = State{Lat: last.Lat, Lng: last.Lng, Altitude: last.Altitude} } // If a propagator's stop fired (not a fallback), end the profile. if res.Outcome == OutcomeStopped { continue } } return results }