Files
omath/docs/projectile_prediction/projectile_engine.md
2025-11-01 09:54:08 +00:00

163 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `omath::projectile_prediction::ProjPredEngineInterface` — Aim-point solver interface
> Header: your projects `projectile_prediction/proj_pred_engine_interface.hpp`
> Namespace: `omath::projectile_prediction`
> Depends on: `Vector3<float>`, `Projectile`, `Target`
> Purpose: **contract** for engines that compute a lead/aim point to hit a moving target.
---
## Overview
`ProjPredEngineInterface` defines a single pure-virtual method that attempts to compute the **world-space aim point** where a projectile should be launched to intersect a target under the engines physical model (e.g., constant projectile speed, gravity, drag, max flight time, etc.).
If a valid solution exists, the engine returns the 3D aim point. Otherwise, it returns `std::nullopt` (no feasible intercept).
---
## API
```cpp
namespace omath::projectile_prediction {
class ProjPredEngineInterface {
public:
[[nodiscard]]
virtual std::optional<Vector3<float>>
maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const = 0;
virtual ~ProjPredEngineInterface() = default;
};
} // namespace omath::projectile_prediction
```
### Semantics
* **Input**
* `Projectile` — engine-specific projectile properties (typical: muzzle speed, gravity vector, drag flag/coeff, max range / flight time).
* `Target` — target state (typical: position, velocity, possibly acceleration).
* **Output**
* `std::optional<Vector3<float>>`
* `value()` — world-space point to aim at **now** so that the projectile intersects the target under the model.
* `std::nullopt` — no solution (e.g., target outruns projectile, blocked by constraints, numerical failure).
* **No side effects**: method is `const` and should not modify inputs.
---
## Typical usage
```cpp
using namespace omath::projectile_prediction;
std::unique_ptr<ProjPredEngineInterface> engine = /* your implementation */;
Projectile proj = /* fill from weapon config */;
Target tgt = /* read from tracking system */;
if (auto aim = engine->maybe_calculate_aim_point(proj, tgt)) {
// Rotate/steer to (*aim)
} else {
// Fall back: no-lead, predictive UI, or do not fire
}
```
---
## Implementation guidance (for engine authors)
**Common models:**
1. **No gravity, constant speed**
Closed form intersect time `t` solves `‖p_t + v_t t p_0‖ = v_p t`.
Choose the smallest non-negative real root; aim point = `p_t + v_t t`.
2. **Gravity (constant g), constant speed**
Solve ballistics with vertical drop: either numerical (NewtonRaphson on time) or 2D elevation + azimuth decomposition. Ensure convergence caps and time bounds.
3. **Drag**
Typically requires numeric integration (e.g., RK4) wrapped in a root find on time-of-flight.
**Robustness tips:**
* **Feasibility checks:** return `nullopt` when:
* projectile speed ≤ 0; target too fast in receding direction; solution time outside `[0, t_max]`.
* **Bounds:** clamp search time to reasonable `[t_min, t_max]` (e.g., `[0, max_flight_time]` or by range).
* **Tolerances:** use epsilons for convergence (e.g., `|f(t)| < 1e-4`, `|Δt| < 1e-4 s`).
* **Determinism:** fix iteration counts or seeds if needed for replayability.
---
## Example: constant-speed, no-gravity intercept (closed form)
```cpp
// Solve ||p + v t|| = s t where p = target_pos - shooter_pos, v = target_vel, s = projectile_speed
// Quadratic: (v·v - s^2) t^2 + 2 (p·v) t + (p·p) = 0
inline std::optional<float> intercept_time_no_gravity(const Vector3<float>& p,
const Vector3<float>& v,
float s) {
const float a = v.dot(v) - s*s;
const float b = 2.f * p.dot(v);
const float c = p.dot(p);
if (std::abs(a) < 1e-6f) { // near linear
if (std::abs(b) < 1e-6f) return std::nullopt;
float t = -c / b;
return t >= 0.f ? std::optional{t} : std::nullopt;
}
const float disc = b*b - 4.f*a*c;
if (disc < 0.f) return std::nullopt;
const float sqrtD = std::sqrt(disc);
float t1 = (-b - sqrtD) / (2.f*a);
float t2 = (-b + sqrtD) / (2.f*a);
float t = (t1 >= 0.f ? t1 : t2);
return t >= 0.f ? std::optional{t} : std::nullopt;
}
```
Aim point (given shooter origin `S`, target pos `T`, vel `V`):
```
p = T - S
t* = intercept_time_no_gravity(p, V, speed)
aim = T + V * t*
```
Return `nullopt` if `t*` is absent.
---
## Testing checklist
* **Stationary target**: aim point equals target position when `s > 0`.
* **Target perpendicular motion**: lead equals lateral displacement `V⊥ * t`.
* **Receding too fast**: expect `nullopt`.
* **Gravity model**: verify arc solutions exist for short & long trajectories (if implemented).
* **Numerics**: convergence within max iterations; monotonic improvement of residuals.
---
## Notes
* This is an **interface** only; concrete engines (e.g., `SimpleNoGravityEngine`, `BallisticGravityEngine`) should document their assumptions (gravity, drag, wind, bounds) and units (meters, seconds).
* The coordinate system and handedness should be consistent with `Vector3<float>` and the rest of your math stack.
---
## See Also
- [Projectile Documentation](projectile.md) - Projectile properties
- [Target Documentation](target.md) - Target state representation
- [Legacy Implementation](proj_pred_engine_legacy.md) - Standard projectile prediction engine
- [AVX2 Implementation](proj_pred_engine_avx2.md) - Optimized AVX2 engine
- [Tutorials - Projectile Prediction](../tutorials.md#tutorial-3-projectile-prediction-aim-bot) - Complete aim-bot tutorial
---
*Last updated: 1 Nov 2025*