diff --git a/drone_simulation/CMakeLists.txt b/drone_simulation/CMakeLists.txt index 5456da2..0c0a32d 100644 --- a/drone_simulation/CMakeLists.txt +++ b/drone_simulation/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(drone_simulation_lib src/solvers/analytical_solver.cpp src/solvers/table_solver.cpp src/providers/json_target_provider.cpp + src/providers/thread_safe_target_provider.cpp src/config_loaders/file_config_loader.cpp src/config_loaders/factory.cpp src/mission_processor.cpp @@ -14,7 +15,12 @@ add_library(drone_simulation_lib src/drone_states/state_decelerating.cpp src/drone_states/state_turning.cpp src/drone_states/state_moving.cpp + src/drone_physics.cpp ) + +find_package(Threads REQUIRED) +target_link_libraries(drone_simulation_lib PUBLIC Threads::Threads) + target_include_directories(drone_simulation_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_compile_options(drone_simulation_lib PRIVATE -Wall -Wextra -pedantic) diff --git a/drone_simulation/data/config.json b/drone_simulation/data/config.json index 1a5e88d..8d6ffc6 100644 --- a/drone_simulation/data/config.json +++ b/drone_simulation/data/config.json @@ -14,7 +14,10 @@ "ammo": "VOG-17", "simulation": { "timeStep": 0.1, - "hitRadius": 2 + "hitRadius": 2, + "targetTimeStep": 0.05, + "physicsTimeStep": 0.01, + "timeScale": 10.0 }, "targetArrayTimeStep": 1 } \ No newline at end of file diff --git a/drone_simulation/include/config_loaders/factory.hpp b/drone_simulation/include/config_loaders/factory.hpp index 7b5a077..6fb38c8 100644 --- a/drone_simulation/include/config_loaders/factory.hpp +++ b/drone_simulation/include/config_loaders/factory.hpp @@ -6,13 +6,10 @@ #include "interfaces/target_provider.hpp" #include "interfaces/config_loader.hpp" -enum class SolverType { - ANALYTICAL, - TABLE -}; -enum class ProviderType { JSON }; -enum class LoaderType { FILE }; +enum class SolverType { ANALYTICAL, TABLE }; +enum class ProviderType { JSON, THREAD_SAFE }; +enum class LoaderType { FILE }; std::unique_ptr createSolver(SolverType type, const DroneConfig& config, const std::string tablePath = ""); -std::unique_ptr createProvider(ProviderType type, const std::string path); +std::unique_ptr createProvider(ProviderType type, const std::string path, float arrayTimeStep, float timeScale); std::unique_ptr createLoader(LoaderType type, const std::string c, const std::string a); \ No newline at end of file diff --git a/drone_simulation/include/drone_physics.hpp b/drone_simulation/include/drone_physics.hpp new file mode 100644 index 0000000..2de968d --- /dev/null +++ b/drone_simulation/include/drone_physics.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include +#include "simulation.hpp" +#include "thread_safe_queue.hpp" + +class DronePhysics { +public: + DronePhysics(const DroneConfig& config); + + auto sendCommand(const DroneCommand& cmd) -> void; + auto getTelemetry() const -> DroneTelemetry; + auto run() -> void; + auto start() -> void; + auto stop() -> void; + auto isThreadReady() const -> bool; + +private: + auto stepPhysics(float dt) -> void; + auto processState(float dt) -> void; + + DroneContext droneContext; + DroneConfig config; + ThreadSafeQueue commandQueue; + mutable std::mutex mtx; + float elapsedTime = 0.0f; + float angleSpeed = 0.0f; + DroneStates currentState = DroneStates::Stopped; + std::atomic ready{false}; + std::atomic running{false}; + std::atomic stopFlag{false}; +}; diff --git a/drone_simulation/include/drone_states/state_accelerating.hpp b/drone_simulation/include/drone_states/state_accelerating.hpp index f35e879..2c6b747 100644 --- a/drone_simulation/include/drone_states/state_accelerating.hpp +++ b/drone_simulation/include/drone_states/state_accelerating.hpp @@ -2,10 +2,9 @@ #include "interfaces/drone_state.hpp" class StateAccelerating : public IDroneState { - public: - static constexpr const char* NAME = "ACCELERATING"; - StateAccelerating(); +public: + StateAccelerating(); - auto execute(DroneContext& ctx) -> std::unique_ptr override; - auto name() const -> std::string override; + auto execute(const DroneStateInput& input) -> std::unique_ptr override; + auto name() const -> DroneStates override; }; \ No newline at end of file diff --git a/drone_simulation/include/drone_states/state_decelerating.hpp b/drone_simulation/include/drone_states/state_decelerating.hpp index 583889b..e22d9a7 100644 --- a/drone_simulation/include/drone_states/state_decelerating.hpp +++ b/drone_simulation/include/drone_states/state_decelerating.hpp @@ -2,10 +2,9 @@ #include "interfaces/drone_state.hpp" class StateDecelerating : public IDroneState { - public: - static constexpr const char* NAME = "DECELERATING"; - StateDecelerating(); +public: + StateDecelerating(); - auto execute(DroneContext& context) -> std::unique_ptr override; - auto name() const -> std::string override; + auto execute(const DroneStateInput& input) -> std::unique_ptr override; + auto name() const -> DroneStates override; }; \ No newline at end of file diff --git a/drone_simulation/include/drone_states/state_moving.hpp b/drone_simulation/include/drone_states/state_moving.hpp index bd95d1c..2ce0f4b 100644 --- a/drone_simulation/include/drone_states/state_moving.hpp +++ b/drone_simulation/include/drone_states/state_moving.hpp @@ -2,10 +2,9 @@ #include "interfaces/drone_state.hpp" class StateMoving : public IDroneState { - public: - static constexpr const char* NAME = "MOVING"; - StateMoving(); +public: + StateMoving(); - auto execute(DroneContext& context) -> std::unique_ptr override; - auto name() const -> std::string override; + auto execute(const DroneStateInput& input) -> std::unique_ptr override; + auto name() const -> DroneStates override; }; \ No newline at end of file diff --git a/drone_simulation/include/drone_states/state_stopped.hpp b/drone_simulation/include/drone_states/state_stopped.hpp index 20f1223..f69b3ad 100644 --- a/drone_simulation/include/drone_states/state_stopped.hpp +++ b/drone_simulation/include/drone_states/state_stopped.hpp @@ -2,10 +2,9 @@ #include "interfaces/drone_state.hpp" class StateStopped : public IDroneState { - public: - static constexpr const char* NAME = "STOPPED"; - StateStopped(); +public: + StateStopped(); - auto execute(DroneContext& context) -> std::unique_ptr override; - auto name() const -> std::string override; + auto execute(const DroneStateInput& input) -> std::unique_ptr override; + auto name() const -> DroneStates override; }; \ No newline at end of file diff --git a/drone_simulation/include/drone_states/state_turning.hpp b/drone_simulation/include/drone_states/state_turning.hpp index 84a060b..87e54b9 100644 --- a/drone_simulation/include/drone_states/state_turning.hpp +++ b/drone_simulation/include/drone_states/state_turning.hpp @@ -2,10 +2,9 @@ #include "interfaces/drone_state.hpp" class StateTurning : public IDroneState { - public: - static constexpr const char* NAME = "TURNING"; - StateTurning(); +public: + StateTurning(); - auto execute(DroneContext& context) -> std::unique_ptr override; - auto name() const -> std::string override; + auto execute(const DroneStateInput& input) -> std::unique_ptr override; + auto name() const -> DroneStates override; }; \ No newline at end of file diff --git a/drone_simulation/include/interfaces/ballistic_solver.hpp b/drone_simulation/include/interfaces/ballistic_solver.hpp index d2088c3..9d00630 100644 --- a/drone_simulation/include/interfaces/ballistic_solver.hpp +++ b/drone_simulation/include/interfaces/ballistic_solver.hpp @@ -1,12 +1,13 @@ #pragma once -#include "../simulation.hpp" +#include "simulation.hpp" + +struct BallisticResult { + float flightTime; + float hDist; +}; class IBallisticSolver { public: - virtual Coord solve( - const Coord& dronePosition, - const Coord& targetPosition, - const AmmoParams& ammmo - ) = 0; - virtual ~IBallisticSolver() {} + virtual BallisticResult solve(const AmmoParams& ammo) = 0; + virtual ~IBallisticSolver() {} }; \ No newline at end of file diff --git a/drone_simulation/include/interfaces/config_loader.hpp b/drone_simulation/include/interfaces/config_loader.hpp index b6b93d2..385223f 100644 --- a/drone_simulation/include/interfaces/config_loader.hpp +++ b/drone_simulation/include/interfaces/config_loader.hpp @@ -1,10 +1,10 @@ #pragma once -#include "../simulation.hpp" +#include "simulation.hpp" class IConfigLoader { public: - virtual void load() = 0; - virtual DroneConfig getConfig() = 0; - virtual AmmoParams getAmmoParams() = 0; - virtual ~IConfigLoader() {} + virtual void load() = 0; + virtual DroneConfig getConfig() = 0; + virtual AmmoParams getAmmoParams() = 0; + virtual ~IConfigLoader() {} }; \ No newline at end of file diff --git a/drone_simulation/include/interfaces/drone_state.hpp b/drone_simulation/include/interfaces/drone_state.hpp index 462522a..b67a562 100644 --- a/drone_simulation/include/interfaces/drone_state.hpp +++ b/drone_simulation/include/interfaces/drone_state.hpp @@ -5,13 +5,12 @@ class IDroneState { public: - virtual ~IDroneState() = default; - - // Виконати логіку стану, повернути наступний стан. - // Якщо стан не змінився — повернути nullptr - // (головний цикл залишить поточний). - virtual std::unique_ptr execute(DroneContext& context) = 0; - - virtual std::string name() const = 0; -}; + virtual ~IDroneState() = default; + + // Виконати логіку стану, повернути наступний стан. + // Якщо стан не змінився — повернути nullptr + // (головний цикл залишить поточний). + virtual std::unique_ptr execute(const DroneStateInput& input) = 0; + virtual DroneStates name() const = 0; +}; diff --git a/drone_simulation/include/interfaces/target_provider.hpp b/drone_simulation/include/interfaces/target_provider.hpp index 5b14470..055970c 100644 --- a/drone_simulation/include/interfaces/target_provider.hpp +++ b/drone_simulation/include/interfaces/target_provider.hpp @@ -1,9 +1,9 @@ #pragma once -#include "../simulation.hpp" +#include "simulation.hpp" class ITargetProvider { public: - virtual int getTargetCount() = 0; - virtual std::vector& getTarget(int index) = 0; - virtual ~ITargetProvider() {} + virtual int getTargetCount() = 0; + virtual Target getTarget(int index) = 0; + virtual ~ITargetProvider() {} }; \ No newline at end of file diff --git a/drone_simulation/include/mission_processor.hpp b/drone_simulation/include/mission_processor.hpp index 80ff1b0..bc79998 100644 --- a/drone_simulation/include/mission_processor.hpp +++ b/drone_simulation/include/mission_processor.hpp @@ -1,36 +1,48 @@ #pragma once +#include +#include +#include #include "interfaces/ballistic_solver.hpp" #include "interfaces/config_loader.hpp" #include "interfaces/target_provider.hpp" #include "interfaces/drone_state.hpp" - +#include "drone_physics.hpp" class MissionProcessor { - const int MAX_STEPS = 10000; - std::unique_ptr targets; - std::unique_ptr ballisticSolver; - DroneConfig droneConfig; - AmmoParams ammo; - DroneContext droneContext; - int currentStep = 1; - int prevTargetIndex = -1; - float currentTime = 0.0f; - float ammoFlightTime; - float ammoHorizontalFlightDistance; - bool hit = false; - std::unique_ptr state; +public: + MissionProcessor(std::unique_ptr targetProvider, std::unique_ptr solver, DronePhysics* physics); - public: - MissionProcessor(std::unique_ptr targetProvider, std::unique_ptr solver); + auto init(std::unique_ptr configLoader) -> void; + auto getCurrentStep() -> int; + auto step() -> SimulationStep; + auto reset() -> void; + auto changeSolver(std::unique_ptr ballisticSolver) -> void; + auto run() -> void; + auto start() -> void; + auto isThreadReady() const -> bool; + auto getSteps() const -> const std::vector&; - auto init(std::unique_ptr configLoader) -> void; - auto hasNext() -> bool; - auto getCurrentStep() -> int; - auto step() -> SimulationStep; - auto reset() -> void; - auto changeSolver(std::unique_ptr ballisticSolver) -> void; +private: + auto evaluateTarget(int targetIndex, const DroneTelemetry& telemetry, Coord& outFireCoord) -> float; + auto getSimulationStep(int targetIndex, const Coord& fireCoord, const DroneTelemetry& telemetry) -> SimulationStep; + auto hasNext() -> bool; - private: - auto evaluateTarget(int targetIndex, float currentTime, Coord& outFireCoord) -> float; - auto getSimulationStep(int targetIndex, const Coord& fireCoord) -> SimulationStep; -}; \ No newline at end of file + const int MAX_STEPS = 10000; + std::unique_ptr targets; + std::unique_ptr ballisticSolver; + DroneConfig droneConfig; + AmmoParams ammo; + int currentStep = 1; + int prevTargetIndex = -1; + float currentTime = 0.0f; + float ammoFlightTime; + float ammoHorizontalFlightDistance; + float acceleration; + float prevDirectionToTarget = 0.0f; + bool hit = false; + DronePhysics* dronePhysics; + std::atomic ready{false}; + std::atomic running{false}; + std::vector simulationSteps; + std::unique_ptr droneState; +}; diff --git a/drone_simulation/include/providers/json_target_provider.hpp b/drone_simulation/include/providers/json_target_provider.hpp index eebef6a..f35a284 100644 --- a/drone_simulation/include/providers/json_target_provider.hpp +++ b/drone_simulation/include/providers/json_target_provider.hpp @@ -4,13 +4,13 @@ #include "interfaces/target_provider.hpp" class JsonTargetProvider : public ITargetProvider { - std::vector> targets; - int count; + std::vector> targets; + int count; - public: - JsonTargetProvider(const std::string path); - ~JsonTargetProvider() override; - - auto getTargetCount() -> int override; - auto getTarget(int i) -> std::vector& override; +public: + JsonTargetProvider(const std::string path); + ~JsonTargetProvider() override; + + auto getTargetCount() -> int override; + auto getTarget(int i) -> Target override; }; \ No newline at end of file diff --git a/drone_simulation/include/providers/thread_safe_target_provider.hpp b/drone_simulation/include/providers/thread_safe_target_provider.hpp new file mode 100644 index 0000000..7d18792 --- /dev/null +++ b/drone_simulation/include/providers/thread_safe_target_provider.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include "interfaces/target_provider.hpp" + +class ThreadSafeTargetProvider : public ITargetProvider { +public: + ThreadSafeTargetProvider(const std::string path, float arrayTimeStep, float timeScale); + ~ThreadSafeTargetProvider() override; + + auto getTargetCount() -> int override; + auto getTarget(int i) -> Target override; + auto run() -> void; + auto start() -> void; + auto stop() -> void; + auto isThreadReady() const -> bool; + +private: + auto updateTargets() -> void; + + std::vector> trajectories; + std::vector currentTargets; + std::vector currentIndex; + float arrayTimeStep; + int count; + float timeScale; + mutable std::mutex mtx; + std::atomic ready{false}; + std::atomic running{false}; + std::atomic stopFlag{false}; +}; diff --git a/drone_simulation/include/simulation.hpp b/drone_simulation/include/simulation.hpp index 9b449f3..3a3ac75 100644 --- a/drone_simulation/include/simulation.hpp +++ b/drone_simulation/include/simulation.hpp @@ -7,145 +7,146 @@ using json = nlohmann::json; -struct Coord { - float x; - float y; - - Coord operator+(const Coord& other) const { - Coord result; - result.x = x + other.x; - result.y = y + other.y; - return result; - } - - Coord operator-(const Coord& other) const { - Coord result; - result.x = x - other.x; - result.y = y - other.y; - return result; - } - - Coord operator*(float s) const { - Coord result; - result.x = x * s; - result.y = y * s; - return result; - } - - Coord operator/(float s) const { - Coord result; - result.x = x / s; - result.y = y / s; - return result; - } - - bool operator==(const Coord& coordToCompare) const { - const float eps = 1e-6f; - - return fabs(x - coordToCompare.x) < eps && fabs(y - coordToCompare.y) < eps; - } +enum class DroneStates { Stopped, Turning, Accelerating, Moving, Decelerating }; +struct DroneCommand { + DroneStates state; + float angleSpeed; +}; + +struct Coord { + float x; + float y; + + Coord operator+(const Coord& other) const + { + Coord result; + result.x = x + other.x; + result.y = y + other.y; + return result; + } + + Coord operator-(const Coord& other) const + { + Coord result; + result.x = x - other.x; + result.y = y - other.y; + return result; + } + + Coord operator*(float s) const + { + Coord result; + result.x = x * s; + result.y = y * s; + return result; + } + + Coord operator/(float s) const + { + Coord result; + result.x = x / s; + result.y = y / s; + return result; + } + + bool operator==(const Coord& coordToCompare) const + { + const float eps = 1e-6f; + + return fabs(x - coordToCompare.x) < eps && fabs(y - coordToCompare.y) < eps; + } }; struct AmmoParams { - std::string name; - float mass; - float drag; - float lift; + std::string name; + float mass; + float drag; + float lift; }; struct DroneConfig { - Coord startPos; - float altitude; - float initialDir; - float attackSpeed; - float accelPath; - std::string ammoName; - float arrayTimeStep; - float simTimeStep; - float hitRadius; - float angularSpeed; - float turnThreshold; + Coord startPos; + float altitude; + float initialDir; + float attackSpeed; + float accelPath; + std::string ammoName; + float arrayTimeStep; + float simTimeStep; + float hitRadius; + float angularSpeed; + float turnThreshold; + float physicsTimeStep; + float timeScale; +}; + +struct DroneStateInput { + float deltaAngle; + float speed; + const DroneConfig& config; }; struct DroneContext { - float directionToTarget; - float prevDirectionToTarget; - float speed = 0.0f; - float direction; - float acceleration; - float timeToStop; - Coord position; - const DroneConfig* config; + float speed = 0.0f; + float direction; + float acceleration; + Coord position; + const DroneConfig* config; }; struct SimulationStep { - bool hit = false; - int target; - float droneSpeed = 0.0f; - float droneDirection; - Coord dropPoint; - Coord aimPoint; - Coord predictedTarget; - Coord dronePosition; - std::string droneState; + bool hit = false; + int target; + float droneSpeed = 0.0f; + float droneDirection; + float timeSecSinceStart = 0.0f; + Coord dropPoint; + Coord aimPoint; + Coord predictedTarget; + Coord dronePosition; + DroneStates droneState = DroneStates::Stopped; +}; + +struct Target { + Coord pos; + Coord velocity; +}; +struct DroneTelemetry { + Coord pos; + float speed; + float direction; + float timeSecSinceStart; }; -float getDistanceToTarget( - const Coord& target, const Coord& posiiton -); +float getDistanceToTarget(const Coord& target, const Coord& posiiton); -Coord getFireCoords( - const Coord& targetCoord, const Coord& dronePos, const float& ratio -); +Coord getFireCoords(const Coord& targetCoord, const Coord& dronePos, const float& ratio); -Coord getInterpolatedCoords( - const float& currentTime, - const float& arrayTimeStep, - const std::vector& coordInTime -); +Coord getInterpolatedCoords(const float& currentTime, const float& arrayTimeStep, const std::vector& coordInTime); -Coord getPredictedTargetCoords ( - float& currentTime, - float& timePeriod, - const std::vector& coordInTime, - const Coord& target, - float& totalTime - ); +Coord getPredictedTargetCoords( + float& currentTime, float& timePeriod, const std::vector& coordInTime, const Coord& target, float& totalTime); float normalizeAngle(float angle); -float turnDrone( - const float& deltaAngle, - const float& angularSpeed, - const float& simTimeStep, - float droneDir -); +float turnDrone(const float& deltaAngle, const float& angularSpeed, const float& simTimeStep, float droneDir); float getRatio(const float& distanceToTarget, const float& ammoHorizontalFlightDistance); auto updateDronePosition(const DroneContext& context) -> Coord; -float getManeuveringTime( - const float& distanceToTarget, - const float& ammoHorizontalFlightDistance, - const float& attackSpeed, - const float& acceleration, - const float& angularSpeed -); +float getManeuveringTime(const float& distanceToTarget, + const float& ammoHorizontalFlightDistance, + const float& attackSpeed, + const float& acceleration, + const float& angularSpeed); -Coord getBombLandCoord( - const DroneContext& droneContext, - const float& ammoHorizontalFlightDistance -); +Coord getBombLandCoord(const DroneTelemetry& telemetry, const float& ammoHorizontalFlightDistance); void writeSimulationJSONFile(const std::vector& simSteps); -float getAmmoHorizontalFlightDistance( - const float& attackSpeed, - const float& ammoFlightTime, - const AmmoParams& ammo -); +float getAmmoHorizontalFlightDistance(const float& attackSpeed, const float& ammoFlightTime, const AmmoParams& ammo); json parseJSONfile(const std::string& path); @@ -157,10 +158,7 @@ AmmoParams findAmmo(const std::unordered_map& ammoMap, std::vector> fillTargets(json& targetsJSON); -float getAmmoFlightTime( - const DroneConfig& droneConfig, - const AmmoParams& ammo -); +float getAmmoFlightTime(const DroneConfig& droneConfig, const AmmoParams& ammo); float length(const Coord& coord); diff --git a/drone_simulation/include/solvers/analytical_solver.hpp b/drone_simulation/include/solvers/analytical_solver.hpp index 7b17729..4cbaed9 100644 --- a/drone_simulation/include/solvers/analytical_solver.hpp +++ b/drone_simulation/include/solvers/analytical_solver.hpp @@ -3,14 +3,10 @@ #include "interfaces/ballistic_solver.hpp" class AnalyticalSolver : public IBallisticSolver { - DroneConfig droneConfig; + DroneConfig droneConfig; - public: - AnalyticalSolver(const DroneConfig& config); +public: + AnalyticalSolver(const DroneConfig& config); - auto solve( - const Coord& dronePosition, - const Coord& targetPosition, - const AmmoParams& ammo - ) -> Coord override; + auto solve(const AmmoParams& ammo) -> BallisticResult override; }; \ No newline at end of file diff --git a/drone_simulation/include/solvers/table_solver.hpp b/drone_simulation/include/solvers/table_solver.hpp index 75e19b5..e85d239 100644 --- a/drone_simulation/include/solvers/table_solver.hpp +++ b/drone_simulation/include/solvers/table_solver.hpp @@ -1,18 +1,14 @@ #pragma once #include "interfaces/ballistic_solver.hpp" -#include "ballistic_table.hpp" +#include "solvers/ballistic_table.hpp" class TableSolver : public IBallisticSolver { - DroneConfig droneConfig; - BallisticTable table; + DroneConfig droneConfig; + BallisticTable table; - public: - TableSolver(const DroneConfig& config, std::string table_path); +public: + TableSolver(const DroneConfig& config, std::string table_path); - auto solve( - const Coord& dronePosition, - const Coord& targetPosition, - const AmmoParams& ammo - ) -> Coord override; + auto solve(const AmmoParams& ammo) -> BallisticResult override; }; \ No newline at end of file diff --git a/drone_simulation/include/thread_safe_queue.hpp b/drone_simulation/include/thread_safe_queue.hpp new file mode 100644 index 0000000..8947fd5 --- /dev/null +++ b/drone_simulation/include/thread_safe_queue.hpp @@ -0,0 +1,28 @@ +#pragma once +#include +#include + +template +class ThreadSafeQueue { + std::queue queue; + mutable std::mutex mtx; + +public: + auto push(const T& item) -> void + { + std::lock_guard lock(mtx); + queue.push(item); + } + + auto tryPop(T& out) -> bool + { + std::lock_guard lock(mtx); + if (queue.empty()) { + return false; + } + out = queue.front(); + queue.pop(); + + return true; + } +}; \ No newline at end of file diff --git a/drone_simulation/src/config_loaders/factory.cpp b/drone_simulation/src/config_loaders/factory.cpp index 6ac190f..a98e4a0 100644 --- a/drone_simulation/src/config_loaders/factory.cpp +++ b/drone_simulation/src/config_loaders/factory.cpp @@ -3,34 +3,41 @@ #include "solvers/analytical_solver.hpp" #include "solvers/table_solver.hpp" #include "providers/json_target_provider.hpp" +#include "providers/thread_safe_target_provider.hpp" #include "config_loaders/file_config_loader.hpp" -auto createSolver(SolverType type, const DroneConfig& config, const std::string tablePath) -> std::unique_ptr { - switch (type) { - case SolverType::ANALYTICAL: - return std::make_unique(config); +auto createSolver(SolverType type, const DroneConfig& config, const std::string tablePath) -> std::unique_ptr +{ + switch (type) { + case SolverType::ANALYTICAL: + return std::make_unique(config); - case SolverType::TABLE: - return std::make_unique(config, tablePath); - } + case SolverType::TABLE: + return std::make_unique(config, tablePath); + } - return nullptr; + return nullptr; } -auto createProvider(ProviderType type, const std::string param) -> std::unique_ptr { - switch (type) { - case ProviderType::JSON: - return std::make_unique(param); - } +auto createProvider(ProviderType type, const std::string param, float arrayTimeStep, float timeScale) -> std::unique_ptr +{ + switch (type) { + case ProviderType::JSON: + return std::make_unique(param); - return nullptr; + case ProviderType::THREAD_SAFE: + return std::make_unique(param, arrayTimeStep, timeScale); + } + + return nullptr; } -auto createLoader(LoaderType type, const std::string droneConfigPath, const std::string ammoConfigPath) -> std::unique_ptr { - switch (type) { - case LoaderType::FILE: - return std::make_unique(droneConfigPath, ammoConfigPath); - } +auto createLoader(LoaderType type, const std::string droneConfigPath, const std::string ammoConfigPath) -> std::unique_ptr +{ + switch (type) { + case LoaderType::FILE: + return std::make_unique(droneConfigPath, ammoConfigPath); + } - return nullptr; + return nullptr; } \ No newline at end of file diff --git a/drone_simulation/src/drone_physics.cpp b/drone_simulation/src/drone_physics.cpp new file mode 100644 index 0000000..330e7d3 --- /dev/null +++ b/drone_simulation/src/drone_physics.cpp @@ -0,0 +1,109 @@ +#include +#include "drone_physics.hpp" + +DronePhysics::DronePhysics(const DroneConfig& config) + : config(config) +{ + this->droneContext = {.speed = 0.0f, + .direction = config.initialDir, + .acceleration = config.attackSpeed * config.attackSpeed / (2.0f * config.accelPath), + .position = config.startPos, + .config = &this->config}; +} + +auto DronePhysics::sendCommand(const DroneCommand& cmd) -> void +{ + this->commandQueue.push(cmd); +} + +auto DronePhysics::getTelemetry() const -> DroneTelemetry +{ + std::lock_guard lock(mtx); + + return {.pos = this->droneContext.position, + .speed = this->droneContext.speed, + .direction = this->droneContext.direction, + .timeSecSinceStart = elapsedTime}; +} + +auto DronePhysics::run() -> void +{ + this->ready.store(true); + + while (!this->running.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + while (!this->stopFlag.load()) { + this->stepPhysics(this->config.physicsTimeStep); + std::this_thread::sleep_for(std::chrono::duration(this->config.physicsTimeStep / this->config.timeScale)); + } +} + +auto DronePhysics::start() -> void +{ + this->running.store(true); +} + +auto DronePhysics::stop() -> void +{ + this->stopFlag.store(true); +} + +auto DronePhysics::isThreadReady() const -> bool +{ + return this->ready.load(); +} + +auto DronePhysics::stepPhysics(float dt) -> void +{ + std::lock_guard lock(mtx); + DroneCommand cmd; + if (this->commandQueue.tryPop(cmd)) { + this->currentState = cmd.state; + this->angleSpeed = cmd.angleSpeed; + } + + this->processState(dt); + + this->elapsedTime += dt; +} + +auto DronePhysics::processState(float dt) -> void +{ + switch (this->currentState) { + case DroneStates::Stopped: + break; + + case DroneStates::Turning: + this->droneContext.direction += this->angleSpeed * dt; + this->droneContext.direction = normalizeAngle(this->droneContext.direction); + break; + + case DroneStates::Accelerating: + this->droneContext.speed += this->droneContext.acceleration * dt; + if (this->droneContext.speed > this->config.attackSpeed) + this->droneContext.speed = this->config.attackSpeed; + if (std::fabs(this->angleSpeed) > 1e-6f) { + this->droneContext.direction += this->angleSpeed * dt; + this->droneContext.direction = normalizeAngle(this->droneContext.direction); + } + this->droneContext.position = updateDronePosition(this->droneContext); + break; + + case DroneStates::Moving: + if (std::fabs(this->angleSpeed) > 1e-6f) { + this->droneContext.direction += this->angleSpeed * dt; + this->droneContext.direction = normalizeAngle(this->droneContext.direction); + } + this->droneContext.position = updateDronePosition(this->droneContext); + break; + + case DroneStates::Decelerating: + this->droneContext.speed -= this->droneContext.acceleration * dt; + if (this->droneContext.speed < 0) + this->droneContext.speed = 0; + this->droneContext.position = updateDronePosition(this->droneContext); + break; + } +} \ No newline at end of file diff --git a/drone_simulation/src/drone_states/state_accelerating.cpp b/drone_simulation/src/drone_states/state_accelerating.cpp index bc0916e..4f20d23 100644 --- a/drone_simulation/src/drone_states/state_accelerating.cpp +++ b/drone_simulation/src/drone_states/state_accelerating.cpp @@ -3,33 +3,18 @@ #include "drone_states/state_moving.hpp" #include "simulation.hpp" - StateAccelerating::StateAccelerating() {} -auto StateAccelerating::execute(DroneContext& context) -> std::unique_ptr { - - float deltaAngle = normalizeAngle(context.directionToTarget - context.direction); - context.speed += context.acceleration * context.config->simTimeStep; - - if (context.speed > context.config->attackSpeed) { - context.speed = context.config->attackSpeed; - } - - context.timeToStop = context.speed / context.acceleration; - - if (fabs(deltaAngle) > 1e-3f && fabs(deltaAngle) <= context.config->turnThreshold) { - context.direction = turnDrone(deltaAngle, context.config->angularSpeed, context.config->simTimeStep, context.direction); - } - - context.position = updateDronePosition(context); - - if (context.speed >= context.config->attackSpeed) { - return std::make_unique(); - } +auto StateAccelerating::execute(const DroneStateInput& input) -> std::unique_ptr +{ + if (input.speed >= input.config.attackSpeed) { + return std::make_unique(); + } - return nullptr; -} + return nullptr; +} -auto StateAccelerating::name() const -> std::string { - return NAME; +auto StateAccelerating::name() const -> DroneStates +{ + return DroneStates::Accelerating; } \ No newline at end of file diff --git a/drone_simulation/src/drone_states/state_decelerating.cpp b/drone_simulation/src/drone_states/state_decelerating.cpp index e85e4cd..5fa670a 100644 --- a/drone_simulation/src/drone_states/state_decelerating.cpp +++ b/drone_simulation/src/drone_states/state_decelerating.cpp @@ -6,26 +6,20 @@ StateDecelerating::StateDecelerating() {} -auto StateDecelerating::execute(DroneContext& context) -> std::unique_ptr { - float deltaAngle = normalizeAngle(context.directionToTarget - context.direction); - context.position = updateDronePosition(context); - context.speed -= context.acceleration * context.config->simTimeStep; - context.timeToStop = context.speed / context.acceleration; +auto StateDecelerating::execute(const DroneStateInput& input) -> std::unique_ptr +{ + if (input.speed <= 1e-3f) { + return std::make_unique(); + } - if (context.speed <= 1e-3f) { - context.speed = 0; - context.timeToStop = 0; + if (std::fabs(input.deltaAngle) <= input.config.turnThreshold) { + return std::make_unique(); + } - return std::make_unique(); - } + return nullptr; +} - if (std::fabs(deltaAngle) <= context.config->turnThreshold) { - return std::make_unique(); - } - - return nullptr; -} - -auto StateDecelerating::name() const -> std::string { - return NAME; +auto StateDecelerating::name() const -> DroneStates +{ + return DroneStates::Decelerating; } \ No newline at end of file diff --git a/drone_simulation/src/drone_states/state_moving.cpp b/drone_simulation/src/drone_states/state_moving.cpp index bfa1a10..6b7178e 100644 --- a/drone_simulation/src/drone_states/state_moving.cpp +++ b/drone_simulation/src/drone_states/state_moving.cpp @@ -3,26 +3,18 @@ #include "drone_states/state_decelerating.hpp" #include "simulation.hpp" - StateMoving::StateMoving() {} -auto StateMoving::execute(DroneContext& context) -> std::unique_ptr { - float deltaAngle = normalizeAngle(context.directionToTarget - context.direction); - - if (fabs(deltaAngle) > 1e-3f && fabs(deltaAngle) <= context.config->turnThreshold) { - context.direction = turnDrone(deltaAngle, context.config->angularSpeed, context.config->simTimeStep, context.direction); - } - - context.timeToStop = context.config->attackSpeed / context.acceleration; - context.position = updateDronePosition(context); - - if (fabs(deltaAngle) > context.config->turnThreshold) { - return std::make_unique(); - } +auto StateMoving::execute(const DroneStateInput& input) -> std::unique_ptr +{ + if (fabs(input.deltaAngle) > input.config.turnThreshold) { + return std::make_unique(); + } - return nullptr; -} + return nullptr; +} -auto StateMoving::name() const -> std::string { - return NAME; +auto StateMoving::name() const -> DroneStates +{ + return DroneStates::Moving; } \ No newline at end of file diff --git a/drone_simulation/src/drone_states/state_stopped.cpp b/drone_simulation/src/drone_states/state_stopped.cpp index fe67266..4968821 100644 --- a/drone_simulation/src/drone_states/state_stopped.cpp +++ b/drone_simulation/src/drone_states/state_stopped.cpp @@ -6,21 +6,16 @@ StateStopped::StateStopped() {} -auto StateStopped::execute(DroneContext& context) -> std::unique_ptr +auto StateStopped::execute(const DroneStateInput& input) -> std::unique_ptr { - float deltaAngle = normalizeAngle(context.directionToTarget - context.direction); - context.timeToStop = 0; + if (std::fabs(input.deltaAngle) > input.config.turnThreshold) { + return std::make_unique(); + } - if (std::fabs(deltaAngle) > context.config->turnThreshold) { - - return std::make_unique(); - } - - return std::make_unique(); + return std::make_unique(); } - -auto StateStopped::name() const -> std::string +auto StateStopped::name() const -> DroneStates { - return NAME; + return DroneStates::Stopped; } \ No newline at end of file diff --git a/drone_simulation/src/drone_states/state_turning.cpp b/drone_simulation/src/drone_states/state_turning.cpp index 85697bc..4487f35 100644 --- a/drone_simulation/src/drone_states/state_turning.cpp +++ b/drone_simulation/src/drone_states/state_turning.cpp @@ -3,23 +3,18 @@ #include "drone_states/state_accelerating.hpp" #include "simulation.hpp" - StateTurning::StateTurning() {} -auto StateTurning::execute(DroneContext& context) -> std::unique_ptr { - float deltaAngle = normalizeAngle(context.directionToTarget - context.direction); - context.direction = turnDrone( - deltaAngle, context.config->angularSpeed, context.config->simTimeStep, context.direction - ); - context.timeToStop = fabs(normalizeAngle(context.prevDirectionToTarget - context.direction)) / context.config->angularSpeed; - - if (std::fabs(deltaAngle) <= context.config->turnThreshold) { - return std::make_unique(); - } +auto StateTurning::execute(const DroneStateInput& input) -> std::unique_ptr +{ + if (std::fabs(input.deltaAngle) <= input.config.turnThreshold) { + return std::make_unique(); + } - return nullptr; -} + return nullptr; +} -auto StateTurning::name() const -> std::string { - return NAME; -} +auto StateTurning::name() const -> DroneStates +{ + return DroneStates::Turning; +} diff --git a/drone_simulation/src/main.cpp b/drone_simulation/src/main.cpp index a2e6d72..04c4dcd 100644 --- a/drone_simulation/src/main.cpp +++ b/drone_simulation/src/main.cpp @@ -5,13 +5,13 @@ #include "simulation.hpp" #include "mission_processor.hpp" #include "config_loaders/factory.hpp" -#include "drone_states/state_stopped.hpp" +#include "providers/thread_safe_target_provider.hpp" using json = nlohmann::json; -#define ENABLE_LOG 1 -#define ENABLE_DEBUG 0 - +#define ENABLE_LOG 0 +#define ENABLE_DEBUG 0 + #if ENABLE_LOG #define LOG(msg) std::cout << "[LOG] " << msg << std::endl #else @@ -24,55 +24,63 @@ using json = nlohmann::json; #define DEBUG(msg) #endif +auto main(int argc, char* argv[]) -> int +{ + try { + const std::string configPath = (argc > 1) ? argv[1] : "config.json"; + const std::string ammoPath = (argc > 2) ? argv[2] : "ammo.json"; + const std::string targetsPath = (argc > 3) ? argv[3] : "targets.json"; + const std::string solverArg = (argc > 4) ? argv[4] : "analytical"; + const std::string ballisticTablePath = (argc > 5) ? argv[5] : "ballistic_table.txt"; + + auto loader = createLoader(LoaderType::FILE, configPath, ammoPath); + + loader->load(); + DroneConfig droneConfig = loader->getConfig(); + auto physics = std::make_unique(droneConfig); + auto targets = std::make_unique(targetsPath, droneConfig.arrayTimeStep, droneConfig.timeScale); + auto* targetsPtr = targets.get(); + auto* physicsPtr = physics.get(); + + SolverType solverType = (solverArg == "analytical") ? SolverType::ANALYTICAL : SolverType::TABLE; + auto ballisticSolver = createSolver(solverType, droneConfig, ballisticTablePath); - auto main(int argc, char* argv[]) -> int { - try { - const std::string configPath = (argc > 1) ? argv[1] : "config.json"; - const std::string ammoPath = (argc > 2) ? argv[2] : "ammo.json"; - const std::string targetsPath = (argc > 3) ? argv[3] : "targets.json"; - const std::string solverArg = (argc > 4) ? argv[4] : "analytical"; - const std::string ballisticTablePath = (argc > 5) ? argv[5] : "ballistic_table.txt"; - - auto loader = createLoader(LoaderType::FILE, configPath, ammoPath); - auto targets = createProvider(ProviderType::JSON, targetsPath); - - loader->load(); - DroneConfig droneConfig = loader->getConfig(); - - SolverType solverType = (solverArg == "analytical") ? SolverType::ANALYTICAL : SolverType::TABLE; - auto ballisticSolver = createSolver(solverType, droneConfig, ballisticTablePath); - std::vector simSteps; - - MissionProcessor missionProcessor(std::move(targets), std::move(ballisticSolver)); - missionProcessor.init(std::move(loader)); - - //write initial sim data - simSteps.push_back({ - .hit = false, - .target = -1, - .droneDirection = droneConfig.initialDir, - .dropPoint = {0, 0}, - .aimPoint = {0, 0}, - .predictedTarget = {0, 0}, - .dronePosition = droneConfig.startPos, - .droneState = StateStopped::NAME - }); - - // simulation loop - while (missionProcessor.hasNext()) { - SimulationStep simulationStep = missionProcessor.step(); - if (simulationStep.target >= 0) { - simSteps.push_back(simulationStep); - } - } - - writeSimulationJSONFile(simSteps); - - return 0; - } catch (const std::exception& error) { - LOG("Error: " << error.what()); - - return 1; + if (ballisticSolver == nullptr) { + LOG("Error: " << "Unknown ballistic solver"); + + return 1; } -} + MissionProcessor missionProcessor(std::move(targets), std::move(ballisticSolver), physicsPtr); + missionProcessor.init(std::move(loader)); + + std::thread providerThread(&ThreadSafeTargetProvider::run, targetsPtr); + std::thread physicsThread(&DronePhysics::run, physicsPtr); + std::thread missionThread(&MissionProcessor::run, &missionProcessor); + + while (!targetsPtr->isThreadReady() || !physicsPtr->isThreadReady() || !missionProcessor.isThreadReady()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + targetsPtr->start(); + physicsPtr->start(); + missionProcessor.start(); + + missionThread.join(); + + physicsPtr->stop(); + targetsPtr->stop(); + + physicsThread.join(); + providerThread.join(); + + writeSimulationJSONFile(missionProcessor.getSteps()); + + return 0; + } + catch (const std::exception& error) { + LOG("Error: " << error.what()); + + return 1; + } +} diff --git a/drone_simulation/src/mission_processor.cpp b/drone_simulation/src/mission_processor.cpp index 982f60a..9250968 100644 --- a/drone_simulation/src/mission_processor.cpp +++ b/drone_simulation/src/mission_processor.cpp @@ -3,10 +3,14 @@ #include "mission_processor.hpp" #include "drone_states/state_stopped.hpp" -MissionProcessor::MissionProcessor(std::unique_ptr targetProvider, std::unique_ptr solver) +MissionProcessor::MissionProcessor(std::unique_ptr targetProvider, + std::unique_ptr solver, + DronePhysics* physics) : targets(std::move(targetProvider)) , ballisticSolver(std::move(solver)) + , dronePhysics(physics) { + this->droneState = std::make_unique(); } auto MissionProcessor::init(std::unique_ptr configLoader) -> void @@ -15,23 +19,10 @@ auto MissionProcessor::init(std::unique_ptr configLoader) -> void this->droneConfig = configLoader->getConfig(); this->ammo = configLoader->getAmmoParams(); - this->ammoFlightTime = getAmmoFlightTime(this->droneConfig, this->ammo); - this->ammoHorizontalFlightDistance = getAmmoHorizontalFlightDistance(this->droneConfig.attackSpeed, this->ammoFlightTime, this->ammo); - this->droneContext = { - .directionToTarget = 0.0f, - .prevDirectionToTarget = 0.0f, - .speed = 0.0f, - .direction = this->droneConfig.initialDir, - .acceleration = this->droneConfig.attackSpeed * this->droneConfig.attackSpeed / (2.0f * this->droneConfig.accelPath), - .timeToStop = 0.0f, - .position = this->droneConfig.startPos, - .config = &this->droneConfig}; - this->state = std::make_unique(); -} - -auto MissionProcessor::hasNext() -> bool -{ - return this->currentStep <= this->MAX_STEPS && !this->hit; + BallisticResult ballisticResult = this->ballisticSolver->solve(this->ammo); + this->ammoFlightTime = ballisticResult.flightTime; + this->ammoHorizontalFlightDistance = ballisticResult.hDist; + this->acceleration = this->droneConfig.attackSpeed * this->droneConfig.attackSpeed / (2.0f * this->droneConfig.accelPath); } auto MissionProcessor::getCurrentStep() -> int @@ -44,10 +35,11 @@ auto MissionProcessor::step() -> SimulationStep int bestTargetIndex = -1; float bestTotalTime = INFINITY; Coord bestFireCoord = {.x = 0, .y = 0}; + DroneTelemetry droneTelemetry = this->dronePhysics->getTelemetry(); for (int i = 0; i < this->targets->getTargetCount(); ++i) { Coord fireCoord; - float totalTime = this->evaluateTarget(i, this->currentTime, fireCoord); + float totalTime = this->evaluateTarget(i, droneTelemetry, fireCoord); if (totalTime < bestTotalTime) { bestTotalTime = totalTime; @@ -57,38 +49,51 @@ auto MissionProcessor::step() -> SimulationStep } if (bestTargetIndex < 0) { - std::cout << "No valid target at this step, skipping" << std::endl; ++this->currentStep; this->currentTime += this->droneConfig.simTimeStep; SimulationStep emptyStep = {.hit = false, .target = -1, - .droneSpeed = this->droneContext.speed, - .droneDirection = this->droneContext.direction, + .droneSpeed = droneTelemetry.speed, + .droneDirection = droneTelemetry.direction, + .timeSecSinceStart = droneTelemetry.timeSecSinceStart, .dropPoint = {0, 0}, .aimPoint = {0, 0}, .predictedTarget = {0, 0}, - .dronePosition = this->droneContext.position, - .droneState = StateStopped::NAME}; + .dronePosition = droneTelemetry.pos, + .droneState = DroneStates::Stopped}; return emptyStep; } - Coord deltaToFire = bestFireCoord - this->droneContext.position; + Coord deltaToFire = bestFireCoord - droneTelemetry.pos; Coord dirVec = normalize(deltaToFire); - this->droneContext.prevDirectionToTarget = this->droneContext.directionToTarget; - this->droneContext.directionToTarget = atan2(dirVec.y, dirVec.x); + float newDirection = atan2(dirVec.y, dirVec.x); - auto nextState = this->state->execute(this->droneContext); - if (nextState) - this->state = std::move(nextState); + float deltaAngle = normalizeAngle(newDirection - droneTelemetry.direction); + + DroneStateInput stateInput = {.deltaAngle = deltaAngle, .speed = droneTelemetry.speed, .config = this->droneConfig}; + auto nextState = this->droneState->execute(stateInput); + if (nextState) { + this->droneState = std::move(nextState); + } + + SimulationStep simulationStep = this->getSimulationStep(bestTargetIndex, bestFireCoord, droneTelemetry); + simulationStep.timeSecSinceStart = droneTelemetry.timeSecSinceStart; + simulationStep.droneState = this->droneState->name(); - SimulationStep simulationStep = this->getSimulationStep(bestTargetIndex, bestFireCoord); this->hit = simulationStep.hit; + float sign = (deltaAngle > 0) ? 1.0f : -1.0f; + float angleSpeed = sign * std::min(std::fabs(deltaAngle) / this->droneConfig.simTimeStep, this->droneConfig.angularSpeed); + + DroneCommand command = {.state = this->droneState->name(), .angleSpeed = angleSpeed}; + this->dronePhysics->sendCommand(command); + this->prevTargetIndex = bestTargetIndex; this->currentStep++; this->currentTime += this->droneConfig.simTimeStep; + this->prevDirectionToTarget = newDirection; return simulationStep; } @@ -99,17 +104,6 @@ auto MissionProcessor::reset() -> void this->prevTargetIndex = -1; this->currentStep = 1; this->currentTime = 0.0f; - - this->droneContext = {.directionToTarget = 0.0f, - .prevDirectionToTarget = this->droneConfig.initialDir, - .speed = 0.0f, - .direction = this->droneConfig.initialDir, - .acceleration = this->droneContext.acceleration, - .timeToStop = 0.0f, - .position = this->droneConfig.startPos, - .config = &this->droneConfig}; - - this->state = std::make_unique(); } auto MissionProcessor::changeSolver(std::unique_ptr ballisticSolver) -> void @@ -117,34 +111,73 @@ auto MissionProcessor::changeSolver(std::unique_ptr ballisticS this->ballisticSolver = std::move(ballisticSolver); } -auto MissionProcessor::evaluateTarget(int targetIndex, float currentTime, Coord& outFireCoord) -> float +auto MissionProcessor::start() -> void +{ + this->running.store(true); +} + +auto MissionProcessor::isThreadReady() const -> bool { - Coord target = getInterpolatedCoords(currentTime, this->droneConfig.arrayTimeStep, this->targets->getTarget(targetIndex)); + return this->ready.load(); +} + +auto MissionProcessor::getSteps() const -> const std::vector& +{ + return this->simulationSteps; +} - float distanceToTarget = getDistanceToTarget(target, this->droneContext.position); +void MissionProcessor::run() +{ + this->ready.store(true); + + while (!this->running.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + this->simulationSteps.push_back({.hit = false, + .target = -1, + .droneDirection = this->droneConfig.initialDir, + .dropPoint = {0, 0}, + .aimPoint = {0, 0}, + .predictedTarget = {0, 0}, + .dronePosition = this->droneConfig.startPos, + .droneState = DroneStates::Stopped}); + + while (this->hasNext()) { + SimulationStep simulationStep = this->step(); + if (simulationStep.target >= 0) { + this->simulationSteps.push_back(simulationStep); + } + std::this_thread::sleep_for(std::chrono::duration(this->droneConfig.simTimeStep / this->droneConfig.timeScale)); + } +} + +auto MissionProcessor::evaluateTarget(int targetIndex, const DroneTelemetry& telemetry, Coord& outFireCoord) -> float +{ + Target t = this->targets->getTarget(targetIndex); + Coord target = t.pos; + + float distanceToTarget = getDistanceToTarget(target, telemetry.pos); if (distanceToTarget < 1e-3f) { - std::cout << "Distance to the target " << targetIndex << " is near or less then 0\n" << std::endl; return INFINITY; } float ratio = getRatio(distanceToTarget, this->ammoHorizontalFlightDistance); - Coord fireCoord = getFireCoords(target, this->droneContext.position, ratio); - float distanceToFire = getDistanceToTarget(fireCoord, this->droneContext.position); + Coord fireCoord = getFireCoords(target, telemetry.pos, ratio); + float distanceToFire = getDistanceToTarget(fireCoord, telemetry.pos); float droneToFireTime = distanceToFire / this->droneConfig.attackSpeed; float totalTimeRough = droneToFireTime + this->ammoFlightTime; - Coord predictedTarget = - getPredictedTargetCoords(currentTime, this->droneConfig.arrayTimeStep, this->targets->getTarget(targetIndex), target, totalTimeRough); + Coord predictedTarget = t.pos + t.velocity * totalTimeRough; - float newDistanceToTarget = getDistanceToTarget(predictedTarget, this->droneContext.position); + float newDistanceToTarget = getDistanceToTarget(predictedTarget, telemetry.pos); if (newDistanceToTarget < 1e-3f) { - std::cout << "Predicted distance to the target " << targetIndex << " is near or less then 0\n" << std::endl; return INFINITY; } float newRatio = getRatio(newDistanceToTarget, this->ammoHorizontalFlightDistance); - outFireCoord = getFireCoords(predictedTarget, this->droneContext.position, newRatio); - float newDistanceToFire = getDistanceToTarget(outFireCoord, this->droneContext.position); + outFireCoord = getFireCoords(predictedTarget, telemetry.pos, newRatio); + float newDistanceToFire = getDistanceToTarget(outFireCoord, telemetry.pos); float newDroneToFireTime = newDistanceToFire / this->droneConfig.attackSpeed; float totalTime = newDroneToFireTime + this->ammoFlightTime; @@ -152,41 +185,42 @@ auto MissionProcessor::evaluateTarget(int targetIndex, float currentTime, Coord& totalTime += getManeuveringTime(newDistanceToTarget, this->ammoHorizontalFlightDistance, this->droneConfig.attackSpeed, - this->droneContext.acceleration, + this->acceleration, this->droneConfig.angularSpeed); } if (this->prevTargetIndex != targetIndex && this->prevTargetIndex != -1) { - totalTime += this->droneContext.timeToStop; + totalTime += telemetry.speed / this->acceleration; } return totalTime; } -auto MissionProcessor::getSimulationStep(int targetIndex, const Coord& fireCoord) -> SimulationStep +auto MissionProcessor::getSimulationStep(int targetIndex, const Coord& fireCoord, const DroneTelemetry& telemetry) -> SimulationStep { - Coord bombLandCoord = getBombLandCoord(this->droneContext, this->ammoHorizontalFlightDistance); - Coord targetAtImpact = - getInterpolatedCoords(this->currentTime + this->ammoFlightTime, this->droneConfig.arrayTimeStep, this->targets->getTarget(targetIndex)); + Coord bombLandCoord = getBombLandCoord(telemetry, this->ammoHorizontalFlightDistance); + + Target target = this->targets->getTarget(targetIndex); + Coord targetAtImpact = target.pos + target.velocity * this->ammoFlightTime; float bombMissDistance = getDistanceToTarget(bombLandCoord, targetAtImpact); - Coord dir = {.x = std::cos(this->droneContext.direction), .y = std::sin(this->droneContext.direction)}; - Coord aimPoint = this->droneContext.position + dir * this->ammoHorizontalFlightDistance; + Coord dir = {.x = std::cos(telemetry.direction), .y = std::sin(telemetry.direction)}; + Coord aimPoint = telemetry.pos + dir * this->ammoHorizontalFlightDistance; bool isHit = bombMissDistance < this->droneConfig.hitRadius || bombLandCoord == targetAtImpact; - if (isHit) { - std::cout << (bombLandCoord == targetAtImpact ? "Direct hit" : "Drone hit the target. Bomb miss: ") - << (bombLandCoord == targetAtImpact ? "" : std::to_string(bombMissDistance)) << std::endl; - } - return {.hit = isHit, .target = targetIndex, - .droneSpeed = this->droneContext.speed, - .droneDirection = this->droneContext.direction, + .droneSpeed = telemetry.speed, + .droneDirection = telemetry.direction, .dropPoint = fireCoord, .aimPoint = aimPoint, .predictedTarget = targetAtImpact, - .dronePosition = this->droneContext.position, - .droneState = this->state->name()}; + .dronePosition = telemetry.pos, + .droneState = DroneStates::Stopped}; +} + +auto MissionProcessor::hasNext() -> bool +{ + return this->currentStep <= this->MAX_STEPS && !this->hit; } \ No newline at end of file diff --git a/drone_simulation/src/providers/json_target_provider.cpp b/drone_simulation/src/providers/json_target_provider.cpp index fc7a6bc..70492bf 100644 --- a/drone_simulation/src/providers/json_target_provider.cpp +++ b/drone_simulation/src/providers/json_target_provider.cpp @@ -1,18 +1,20 @@ #include "providers/json_target_provider.hpp" -JsonTargetProvider::JsonTargetProvider(const std::string path) { - json jsonFile = parseJSONfile(path); - count = jsonFile["targetCount"]; - targets = fillTargets(jsonFile); +JsonTargetProvider::JsonTargetProvider(const std::string path) +{ + json jsonFile = parseJSONfile(path); + count = jsonFile["targetCount"]; + targets = fillTargets(jsonFile); } -JsonTargetProvider::~JsonTargetProvider() { -} +JsonTargetProvider::~JsonTargetProvider() {} -auto JsonTargetProvider::getTargetCount() -> int { - return count; +auto JsonTargetProvider::getTargetCount() -> int +{ + return count; } -auto JsonTargetProvider::getTarget(int i) -> std::vector& { - return targets[i]; +auto JsonTargetProvider::getTarget(int i) -> Target +{ + return {.pos = {static_cast(i), static_cast(i)}, .velocity = {1, 1}}; } diff --git a/drone_simulation/src/providers/thread_safe_target_provider.cpp b/drone_simulation/src/providers/thread_safe_target_provider.cpp new file mode 100644 index 0000000..22b29bc --- /dev/null +++ b/drone_simulation/src/providers/thread_safe_target_provider.cpp @@ -0,0 +1,86 @@ +#include +#include "providers/thread_safe_target_provider.hpp" + +ThreadSafeTargetProvider::ThreadSafeTargetProvider(const std::string path, float arrayTimeStep, float timeScale) + : arrayTimeStep(arrayTimeStep) + , timeScale(timeScale) +{ + json jsonFile = parseJSONfile(path); + this->count = jsonFile["targetCount"]; + this->trajectories = fillTargets(jsonFile); + this->currentIndex.resize(this->trajectories.size(), 0); + this->currentTargets.resize(this->trajectories.size()); + + for (size_t i = 0; i < this->trajectories.size(); i++) { + this->currentTargets[i].pos = this->trajectories[i][0]; + + // Швидкість — куди і як швидко ціль рухається + int next = 1 % this->trajectories[i].size(); // наступний вузол (перестраховка якщо всього 1 вузол в массиві позицій цілі) + this->currentTargets[i].velocity = (this->trajectories[i][next] - this->trajectories[i][0]) / arrayTimeStep; + } +} + +ThreadSafeTargetProvider::~ThreadSafeTargetProvider() {} + +auto ThreadSafeTargetProvider::getTargetCount() -> int +{ + std::lock_guard lock(mtx); + + return this->count; +} + +auto ThreadSafeTargetProvider::getTarget(int i) -> Target +{ + std::lock_guard lock(mtx); + + return this->currentTargets[i]; +} + +auto ThreadSafeTargetProvider::run() -> void +{ + this->ready.store(true); + + while (!this->running.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + while (!this->stopFlag.load()) { + { + std::lock_guard lock(mtx); + this->updateTargets(); + } + + std::this_thread::sleep_for(std::chrono::duration(this->arrayTimeStep / this->timeScale)); + } +} + +auto ThreadSafeTargetProvider::start() -> void +{ + this->running.store(true); +} + +auto ThreadSafeTargetProvider::stop() -> void +{ + this->stopFlag.store(true); +} + +auto ThreadSafeTargetProvider::isThreadReady() const -> bool +{ + return this->ready.load(); +} + +auto ThreadSafeTargetProvider::updateTargets() -> void +{ + for (size_t i = 0; i < this->trajectories.size(); i++) { + int size = this->trajectories[i].size(); + + // +1 index, із зацикленням + this->currentIndex[i] = (this->currentIndex[i] + 1) % size; + + int curr = this->currentIndex[i]; + int next = (curr + 1) % size; + + this->currentTargets[i].pos = this->trajectories[i][curr]; + this->currentTargets[i].velocity = (this->trajectories[i][next] - this->trajectories[i][curr]) / this->arrayTimeStep; + } +} \ No newline at end of file diff --git a/drone_simulation/src/simulation.cpp b/drone_simulation/src/simulation.cpp index 812c1fd..28a6406 100644 --- a/drone_simulation/src/simulation.cpp +++ b/drone_simulation/src/simulation.cpp @@ -2,14 +2,14 @@ #include #include #include -#include "../include/json.hpp" -#include "../include/simulation.hpp" +#include "json.hpp" +#include "simulation.hpp" using json = nlohmann::json; -#define ENABLE_LOG 1 -#define ENABLE_DEBUG 0 - +#define ENABLE_LOG 0 +#define ENABLE_DEBUG 0 + #if ENABLE_LOG #define LOG(msg) std::cout << "[LOG] " << msg << std::endl #else @@ -22,294 +22,260 @@ using json = nlohmann::json; #define DEBUG(msg) #endif - - -float getDistanceToTarget( - const Coord& target, const Coord& posiiton -) { - return sqrt(pow(target.x - posiiton.x, 2) + pow(target.y - posiiton.y, 2)); +float getDistanceToTarget(const Coord& target, const Coord& posiiton) +{ + return sqrt(pow(target.x - posiiton.x, 2) + pow(target.y - posiiton.y, 2)); } -Coord getFireCoords( - const Coord& targetCoord, const Coord& dronePos, const float& ratio -) { - return dronePos + (targetCoord - dronePos) * ratio; +Coord getFireCoords(const Coord& targetCoord, const Coord& dronePos, const float& ratio) +{ + return dronePos + (targetCoord - dronePos) * ratio; } -Coord getInterpolatedCoords( - const float& currentTime, - const float& arrayTimeStep, - const std::vector& coordInTime -) { - int timeSteps = coordInTime.size(); - int idx = (int)floor(currentTime / arrayTimeStep) % timeSteps; - int next = (idx + 1) % timeSteps; - float frac = (currentTime - idx * arrayTimeStep) / arrayTimeStep; +Coord getInterpolatedCoords(const float& currentTime, const float& arrayTimeStep, const std::vector& coordInTime) +{ + int timeSteps = coordInTime.size(); + int idx = (int)floor(currentTime / arrayTimeStep) % timeSteps; + int next = (idx + 1) % timeSteps; + float frac = (currentTime - idx * arrayTimeStep) / arrayTimeStep; - Coord interpolatedCoord = coordInTime[idx] + (coordInTime[next] - coordInTime[idx]) * frac; + Coord interpolatedCoord = coordInTime[idx] + (coordInTime[next] - coordInTime[idx]) * frac; - return interpolatedCoord; + return interpolatedCoord; } -Coord getPredictedTargetCoords ( - float& currentTime, - float& timePeriod, - const std::vector& coordInTime, - const Coord& target, - float& totalTime - ) { - Coord nextTargetCoord = getInterpolatedCoords(currentTime + timePeriod, timePeriod, coordInTime); - Coord targetVelocity = (nextTargetCoord - target) / timePeriod; - Coord predictedTarget = target + targetVelocity * totalTime; - - return predictedTarget; - } - -float normalizeAngle(float angle) { - while (angle > M_PI) { - angle -= 2 * M_PI; - } - while (angle < -M_PI) { - angle += 2 * M_PI; - } +Coord getPredictedTargetCoords( + float& currentTime, float& timePeriod, const std::vector& coordInTime, const Coord& target, float& totalTime) +{ + Coord nextTargetCoord = getInterpolatedCoords(currentTime + timePeriod, timePeriod, coordInTime); + Coord targetVelocity = (nextTargetCoord - target) / timePeriod; + Coord predictedTarget = target + targetVelocity * totalTime; - return angle; + return predictedTarget; } -float turnDrone( - const float& deltaAngle, - const float& angularSpeed, - const float& simTimeStep, - float droneDir -) { - const float sign = (deltaAngle > 0) ? 1.0f : -1.0f; - float step = angularSpeed * simTimeStep; - if (step > fabs(deltaAngle)) { - step = fabs(deltaAngle); - } - droneDir += sign * step; - droneDir = normalizeAngle(droneDir); +float normalizeAngle(float angle) +{ + while (angle > M_PI) { + angle -= 2 * M_PI; + } + while (angle < -M_PI) { + angle += 2 * M_PI; + } + + return angle; +} - return droneDir; +float turnDrone(const float& deltaAngle, const float& angularSpeed, const float& simTimeStep, float droneDir) +{ + const float sign = (deltaAngle > 0) ? 1.0f : -1.0f; + float step = angularSpeed * simTimeStep; + if (step > fabs(deltaAngle)) { + step = fabs(deltaAngle); + } + droneDir += sign * step; + droneDir = normalizeAngle(droneDir); + + return droneDir; } -float getRatio(const float& distanceToTarget, const float& ammoHorizontalFlightDistance) { - return (distanceToTarget - ammoHorizontalFlightDistance) / distanceToTarget; +float getRatio(const float& distanceToTarget, const float& ammoHorizontalFlightDistance) +{ + return (distanceToTarget - ammoHorizontalFlightDistance) / distanceToTarget; } auto updateDronePosition(const DroneContext& context) -> Coord { - Coord position = { - context.position.x, - context.position.y - }; + Coord position = {context.position.x, context.position.y}; - position.x += context.speed * cos(context.direction) * context.config->simTimeStep; - position.y += context.speed * sin(context.direction) * context.config->simTimeStep; + position.x += context.speed * cos(context.direction) * context.config->physicsTimeStep; + position.y += context.speed * sin(context.direction) * context.config->physicsTimeStep; - return position; + return position; } -float getManeuveringTime( - const float& distanceToTarget, - const float& ammoHorizontalFlightDistance, - const float& attackSpeed, - const float& acceleration, - const float& angularSpeed -) { - float overshootDistance = ammoHorizontalFlightDistance - distanceToTarget; - float flyAwayTime = overshootDistance / attackSpeed; - float decelerateTime = attackSpeed / acceleration; - float turnAroundTime = M_PI / angularSpeed; - float accelerateTime = attackSpeed / acceleration; - float flyBackTime = overshootDistance / attackSpeed; - - return flyAwayTime + decelerateTime + turnAroundTime + accelerateTime + flyBackTime; +float getManeuveringTime(const float& distanceToTarget, + const float& ammoHorizontalFlightDistance, + const float& attackSpeed, + const float& acceleration, + const float& angularSpeed) +{ + float overshootDistance = ammoHorizontalFlightDistance - distanceToTarget; + float flyAwayTime = overshootDistance / attackSpeed; + float decelerateTime = attackSpeed / acceleration; + float turnAroundTime = M_PI / angularSpeed; + float accelerateTime = attackSpeed / acceleration; + float flyBackTime = overshootDistance / attackSpeed; + + return flyAwayTime + decelerateTime + turnAroundTime + accelerateTime + flyBackTime; } -Coord getBombLandCoord( - const DroneContext& droneContext, - const float& ammoHorizontalFlightDistance -) { - const float bombLandX = droneContext.position.x + ammoHorizontalFlightDistance * cos(droneContext.direction); - const float bombLandY = droneContext.position.y + ammoHorizontalFlightDistance * sin(droneContext.direction); - Coord bombLandCoord = { - .x = bombLandX, - .y = bombLandY - }; - - return bombLandCoord; -} +Coord getBombLandCoord(const DroneTelemetry& telemetry, const float& ammoHorizontalFlightDistance) +{ + const float bombLandX = telemetry.pos.x + ammoHorizontalFlightDistance * cos(telemetry.direction); + const float bombLandY = telemetry.pos.y + ammoHorizontalFlightDistance * sin(telemetry.direction); + Coord bombLandCoord = {.x = bombLandX, .y = bombLandY}; -void writeSimulationJSONFile( - const std::vector& simSteps -) { - json out; - out["totalSteps"] = simSteps.size(); - out["steps"] = json::array(); - - for (const SimulationStep& step : simSteps) { - json stepJson; - stepJson["position"] = { - {"x", step.dronePosition.x}, - {"y", step.dronePosition.y} - }; - stepJson["direction"] = step.droneDirection; - stepJson["state"] = step.droneState; - stepJson["targetIndex"] = step.target; - stepJson["dropPoint"] = { - {"x", step.dropPoint.x}, - {"y", step.dropPoint.y} - }; - stepJson["aimPoint"] = { - {"x", step.aimPoint.x}, - {"y", step.aimPoint.y} - }; - stepJson["predictedTarget"] = { - {"x", step.predictedTarget.x}, - {"y", step.predictedTarget.y} - }; - out["steps"].push_back(stepJson); - } - std::ofstream fout("simulation.json"); - fout << out.dump(2); + return bombLandCoord; } -float getAmmoHorizontalFlightDistance( - const float& attackSpeed, - const float& ammoFlightTime, - const AmmoParams& ammo -) { - const float g = 9.81f; - float ammoHorizontalFlightDistance = attackSpeed * ammoFlightTime - - pow(ammoFlightTime, 2) * ammo.drag * attackSpeed / (2 * ammo.mass) - + pow(ammoFlightTime, 3) * (6 * ammo.drag * g * ammo.lift * ammo.mass - 6 * pow(ammo.drag, 2) * (pow(ammo.lift, 2) - 1) * attackSpeed) / (36 * pow(ammo.mass, 2)) - + pow(ammoFlightTime, 4) * (-6 * pow(ammo.drag, 2) * g * ammo.lift * (1 + pow(ammo.lift, 2) + pow(ammo.lift, 4)) * ammo.mass + 3 * pow(ammo.drag, 3) * pow(ammo.lift, 2) * (1 + pow(ammo.lift, 2)) * attackSpeed + 6 * pow(ammo.drag, 3) * pow(ammo.lift, 4) * (1 + pow(ammo.lift, 2)) * attackSpeed) / (36 * pow(1 + pow(ammo.lift, 2), 2) * pow(ammo.mass, 3)) - + pow(ammoFlightTime, 5) * (3 * pow(ammo.drag, 3) * g * pow(ammo.lift, 3) * ammo.mass - 3 * pow(ammo.drag, 4) * pow(ammo.lift, 2) * (1 + pow(ammo.lift, 2)) * attackSpeed) / (36 * (1 + pow(ammo.lift, 2)) * pow(ammo.mass, 4)); - - if (ammoHorizontalFlightDistance < 1e-6f) { - throw std::runtime_error("Horizontal distance to the target should be more than 0\n"); - } +void writeSimulationJSONFile(const std::vector& simSteps) +{ + json out; + out["totalSteps"] = simSteps.size(); + out["steps"] = json::array(); + + for (const SimulationStep& step : simSteps) { + json stepJson; + stepJson["position"] = {{"x", step.dronePosition.x}, {"y", step.dronePosition.y}}; + stepJson["direction"] = step.droneDirection; + stepJson["state"] = static_cast(step.droneState); + stepJson["targetIndex"] = step.target; + stepJson["dropPoint"] = {{"x", step.dropPoint.x}, {"y", step.dropPoint.y}}; + stepJson["aimPoint"] = {{"x", step.aimPoint.x}, {"y", step.aimPoint.y}}; + stepJson["predictedTarget"] = {{"x", step.predictedTarget.x}, {"y", step.predictedTarget.y}}; + stepJson["timeSecSinceStart"] = step.timeSecSinceStart; + out["steps"].push_back(stepJson); + } + std::ofstream fout("simulation.json"); + fout << out.dump(2); +} - return ammoHorizontalFlightDistance; +float getAmmoHorizontalFlightDistance(const float& attackSpeed, const float& ammoFlightTime, const AmmoParams& ammo) +{ + const float g = 9.81f; + float ammoHorizontalFlightDistance = + attackSpeed * ammoFlightTime - pow(ammoFlightTime, 2) * ammo.drag * attackSpeed / (2 * ammo.mass) + + pow(ammoFlightTime, 3) * (6 * ammo.drag * g * ammo.lift * ammo.mass - 6 * pow(ammo.drag, 2) * (pow(ammo.lift, 2) - 1) * attackSpeed) / + (36 * pow(ammo.mass, 2)) + + pow(ammoFlightTime, 4) * + (-6 * pow(ammo.drag, 2) * g * ammo.lift * (1 + pow(ammo.lift, 2) + pow(ammo.lift, 4)) * ammo.mass + + 3 * pow(ammo.drag, 3) * pow(ammo.lift, 2) * (1 + pow(ammo.lift, 2)) * attackSpeed + + 6 * pow(ammo.drag, 3) * pow(ammo.lift, 4) * (1 + pow(ammo.lift, 2)) * attackSpeed) / + (36 * pow(1 + pow(ammo.lift, 2), 2) * pow(ammo.mass, 3)) + + pow(ammoFlightTime, 5) * + (3 * pow(ammo.drag, 3) * g * pow(ammo.lift, 3) * ammo.mass - + 3 * pow(ammo.drag, 4) * pow(ammo.lift, 2) * (1 + pow(ammo.lift, 2)) * attackSpeed) / + (36 * (1 + pow(ammo.lift, 2)) * pow(ammo.mass, 4)); + + if (ammoHorizontalFlightDistance < 1e-6f) { + throw std::runtime_error("Horizontal distance to the target should be more than 0\n"); + } + + return ammoHorizontalFlightDistance; } -json parseJSONfile(const std::string& path) { - std::ifstream configFile(path); - if (!configFile) { - throw std::runtime_error("Could not open " + path + " file\n "); - } +json parseJSONfile(const std::string& path) +{ + std::ifstream configFile(path); + if (!configFile) { + throw std::runtime_error("Could not open " + path + " file\n "); + } - json configJSON; - configFile >> configJSON; - configFile.close(); + json configJSON; + configFile >> configJSON; + configFile.close(); - return configJSON; + return configJSON; } -DroneConfig readDroneConfig(const std::string& path) { - json configJSON = parseJSONfile(path); - - DroneConfig droneConfig = { - .startPos = { - .x = configJSON["drone"]["position"]["x"], - .y = configJSON["drone"]["position"]["y"] - }, - .altitude = configJSON["drone"]["altitude"], - .initialDir = configJSON["drone"]["initialDirection"], - .attackSpeed = configJSON["drone"]["attackSpeed"], - .accelPath = configJSON["drone"]["accelerationPath"], - .ammoName = "", - .arrayTimeStep = configJSON["targetArrayTimeStep"], - .simTimeStep = configJSON["simulation"]["timeStep"], - .hitRadius = configJSON["simulation"]["hitRadius"], - .angularSpeed = configJSON["drone"]["angularSpeed"], - .turnThreshold = configJSON["drone"]["turnThreshold"], - }; - - droneConfig.ammoName = configJSON["ammo"].get(); - - DEBUG("ammo config: " << droneConfig.ammoName); - - return droneConfig; +DroneConfig readDroneConfig(const std::string& path) +{ + json configJSON = parseJSONfile(path); + + DroneConfig droneConfig = { + .startPos = {.x = configJSON["drone"]["position"]["x"], .y = configJSON["drone"]["position"]["y"]}, + .altitude = configJSON["drone"]["altitude"], + .initialDir = configJSON["drone"]["initialDirection"], + .attackSpeed = configJSON["drone"]["attackSpeed"], + .accelPath = configJSON["drone"]["accelerationPath"], + .ammoName = "", + .arrayTimeStep = configJSON["targetArrayTimeStep"], + .simTimeStep = configJSON["simulation"]["timeStep"], + .hitRadius = configJSON["simulation"]["hitRadius"], + .angularSpeed = configJSON["drone"]["angularSpeed"], + .turnThreshold = configJSON["drone"]["turnThreshold"], + .physicsTimeStep = configJSON["simulation"].value("physicsTimeStep", 0.01f), + .timeScale = configJSON["simulation"].value("timeScale", 10.0f), + }; + + droneConfig.ammoName = configJSON["ammo"].get(); + + DEBUG("ammo config: " << droneConfig.ammoName); + + return droneConfig; } -std::unordered_map getAmmoMap(json& ammoJSON) { - int ammoCount = ammoJSON.size(); - std::unordered_map ammoMap; - - for (int i = 0; i < ammoCount; i++) { - const std::string ammoName = ammoJSON[i]["name"].get(); - AmmoParams ammo = { - .name = ammoName, - .mass = ammoJSON[i]["mass"], - .drag = ammoJSON[i]["drag"], - .lift = ammoJSON[i]["lift"] - }; - ammoMap[ammoName] = ammo; - } +std::unordered_map getAmmoMap(json& ammoJSON) +{ + int ammoCount = ammoJSON.size(); + std::unordered_map ammoMap; - return ammoMap; -} + for (int i = 0; i < ammoCount; i++) { + const std::string ammoName = ammoJSON[i]["name"].get(); + AmmoParams ammo = {.name = ammoName, .mass = ammoJSON[i]["mass"], .drag = ammoJSON[i]["drag"], .lift = ammoJSON[i]["lift"]}; + ammoMap[ammoName] = ammo; + } -AmmoParams findAmmo(const std::unordered_map& ammoMap, const std::string ammoName) { - auto it = ammoMap.find(ammoName); - if (it == ammoMap.end()) { - throw std::runtime_error("Unknown ammo\n"); - } - return it->second; + return ammoMap; } -std::vector> fillTargets(json& targetsJSON) { - int targetsCount = targetsJSON["targetCount"]; - int timeSteps = targetsJSON["timeSteps"]; - - std::vector> targets; - for (int i = 0; i < targetsCount; i++) { - std::vector track; - for (int j = 0; j < timeSteps; j++) { - track.push_back({ - targetsJSON["targets"][i]["positions"][j]["x"], - targetsJSON["targets"][i]["positions"][j]["y"] - }); - } - targets.push_back(track); - } - - return targets; +AmmoParams findAmmo(const std::unordered_map& ammoMap, const std::string ammoName) +{ + auto it = ammoMap.find(ammoName); + if (it == ammoMap.end()) { + throw std::runtime_error("Unknown ammo\n"); + } + return it->second; } - -float getAmmoFlightTime( - const DroneConfig& droneConfig, - const AmmoParams& ammo -) { - const float g = 9.81f; - float a = ammo.drag * g * ammo.mass - 2 * pow(ammo.drag, 2) * ammo.lift * droneConfig.attackSpeed; - float b = -3 * g * pow(ammo.mass, 2) + 3 * ammo.drag * ammo.lift * ammo.mass * droneConfig.attackSpeed; - float c = 6 * pow(ammo.mass, 2) * droneConfig.altitude; - float p = -pow(b, 2) / (3 * pow(a, 2)); - float q = 2 * pow(b, 3) / (27 * pow(a, 3)) + c / a; - float acosArg = 3 * q / (2 * p) * sqrt(-3 / p); - - if (acosArg > 1 || acosArg < -1) { - throw std::runtime_error("Drone position is too high\n"); +std::vector> fillTargets(json& targetsJSON) +{ + int targetsCount = targetsJSON["targetCount"]; + int timeSteps = targetsJSON["timeSteps"]; + + std::vector> targets; + for (int i = 0; i < targetsCount; i++) { + std::vector track; + for (int j = 0; j < timeSteps; j++) { + track.push_back({targetsJSON["targets"][i]["positions"][j]["x"], targetsJSON["targets"][i]["positions"][j]["y"]}); } + targets.push_back(track); + } - float phi = acos(acosArg); - float ammoFlightTime = 2 * sqrt(-p / 3 ) * cos((phi + 4 * M_PI) / 3) - b / (3 * a); - - if (ammoFlightTime <= 0) { - throw std::runtime_error("Ammo flight time should be more than 0\n"); - } + return targets; +} - return ammoFlightTime; +float getAmmoFlightTime(const DroneConfig& droneConfig, const AmmoParams& ammo) +{ + const float g = 9.81f; + float a = ammo.drag * g * ammo.mass - 2 * pow(ammo.drag, 2) * ammo.lift * droneConfig.attackSpeed; + float b = -3 * g * pow(ammo.mass, 2) + 3 * ammo.drag * ammo.lift * ammo.mass * droneConfig.attackSpeed; + float c = 6 * pow(ammo.mass, 2) * droneConfig.altitude; + float p = -pow(b, 2) / (3 * pow(a, 2)); + float q = 2 * pow(b, 3) / (27 * pow(a, 3)) + c / a; + float acosArg = 3 * q / (2 * p) * sqrt(-3 / p); + + if (acosArg > 1 || acosArg < -1) { + throw std::runtime_error("Drone position is too high\n"); + } + + float phi = acos(acosArg); + float ammoFlightTime = 2 * sqrt(-p / 3) * cos((phi + 4 * M_PI) / 3) - b / (3 * a); + + if (ammoFlightTime <= 0) { + throw std::runtime_error("Ammo flight time should be more than 0\n"); + } + + return ammoFlightTime; } -float length(const Coord& coord) { - return std::hypot(coord.x, coord.y); +float length(const Coord& coord) +{ + return std::hypot(coord.x, coord.y); } -Coord normalize(const Coord& coord) { - return coord / length(coord); +Coord normalize(const Coord& coord) +{ + return coord / length(coord); } \ No newline at end of file diff --git a/drone_simulation/src/solvers/analytical_solver.cpp b/drone_simulation/src/solvers/analytical_solver.cpp index 43db77e..89497c7 100644 --- a/drone_simulation/src/solvers/analytical_solver.cpp +++ b/drone_simulation/src/solvers/analytical_solver.cpp @@ -5,12 +5,10 @@ AnalyticalSolver::AnalyticalSolver(const DroneConfig& config) { } -auto AnalyticalSolver::solve(const Coord& dronePosition, const Coord& targetPosition, const AmmoParams& ammo) -> Coord +auto AnalyticalSolver::solve(const AmmoParams& ammo) -> BallisticResult { float flightTime = getAmmoFlightTime(droneConfig, ammo); - float ammoHorizontalFlightDistance = getAmmoHorizontalFlightDistance(droneConfig.attackSpeed, flightTime, ammo); - float distanceToTarget = getDistanceToTarget(targetPosition, dronePosition); - float ratio = getRatio(distanceToTarget, ammoHorizontalFlightDistance); + float hDist = getAmmoHorizontalFlightDistance(droneConfig.attackSpeed, flightTime, ammo); - return getFireCoords(targetPosition, dronePosition, std::max(ratio, 0.01f)); + return {.flightTime = flightTime, .hDist = hDist}; } \ No newline at end of file diff --git a/drone_simulation/src/solvers/table_solver.cpp b/drone_simulation/src/solvers/table_solver.cpp index cf35c99..8ccae90 100644 --- a/drone_simulation/src/solvers/table_solver.cpp +++ b/drone_simulation/src/solvers/table_solver.cpp @@ -1,18 +1,14 @@ #include "solvers/table_solver.hpp" - -TableSolver::TableSolver(const DroneConfig& config, std::string path): droneConfig(config) { - this->table.load(path); +TableSolver::TableSolver(const DroneConfig& config, std::string path) + : droneConfig(config) +{ + this->table.load(path); } -auto TableSolver::solve( - const Coord& dronePosition, - const Coord& targetPosition, - const AmmoParams& ammo -) -> Coord { - Result result = this->table.lookup(this->droneConfig.altitude, this->droneConfig.attackSpeed, ammo.mass, ammo.drag, ammo.lift); - float distanceToTarget = getDistanceToTarget(targetPosition, dronePosition); - float ratio = getRatio(distanceToTarget, result.hDist); - - return getFireCoords(targetPosition, dronePosition, ratio); +auto TableSolver::solve(const AmmoParams& ammo) -> BallisticResult +{ + Result result = this->table.lookup(this->droneConfig.altitude, this->droneConfig.attackSpeed, ammo.mass, ammo.drag, ammo.lift); + + return {.flightTime = result.t, .hDist = result.hDist}; } \ No newline at end of file diff --git a/simulation.json b/simulation.json deleted file mode 100644 index 6a9cc91..0000000 --- a/simulation.json +++ /dev/null @@ -1,5066 +0,0 @@ -{ - "steps": [ - { - "aimPoint": { - "x": 0.0, - "y": 0.0 - }, - "direction": 0.0, - "dropPoint": { - "x": 0.0, - "y": 0.0 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 0.0, - "y": 0.0 - }, - "state": "STOPPED", - "targetIndex": -1 - }, - { - "aimPoint": { - "x": 66.57669830322266, - "y": 0.0 - }, - "direction": 0.0, - "dropPoint": { - "x": 260.41705322265625, - "y": 181.8731689453125 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.97802734375, - "y": 203.54396057128906 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 66.49349212646484, - "y": 3.3274481296539307 - }, - "direction": 0.05000000074505806, - "dropPoint": { - "x": 259.95257568359375, - "y": 181.90521240234375 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.9380187988281, - "y": 203.62396240234375 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 66.24409484863281, - "y": 6.646579742431641 - }, - "direction": 0.10000000149011612, - "dropPoint": { - "x": 259.48736572265625, - "y": 181.93711853027344 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.89801025390625, - "y": 203.70394897460938 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 65.82911682128906, - "y": 9.949098587036133 - }, - "direction": 0.15000000596046448, - "dropPoint": { - "x": 259.02288818359375, - "y": 181.96900939941406 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.8580017089844, - "y": 203.78395080566406 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 65.24959564208984, - "y": 13.2267484664917 - }, - "direction": 0.20000000298023224, - "dropPoint": { - "x": 258.5577087402344, - "y": 182.00076293945313 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.8180236816406, - "y": 203.86395263671875 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 64.50698852539063, - "y": 16.471338272094727 - }, - "direction": 0.25, - "dropPoint": { - "x": 258.09326171875, - "y": 182.03282165527344 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.77252197265625, - "y": 203.93846130371094 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 63.6031494140625, - "y": 19.674760818481445 - }, - "direction": 0.30000001192092896, - "dropPoint": { - "x": 257.6287841796875, - "y": 182.06417846679688 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.7225036621094, - "y": 204.00845336914063 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 62.54033279418945, - "y": 22.82900619506836 - }, - "direction": 0.3500000238418579, - "dropPoint": { - "x": 257.1636047363281, - "y": 182.095703125 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.6725158691406, - "y": 204.07846069335938 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 61.321197509765625, - "y": 25.926189422607422 - }, - "direction": 0.40000003576278687, - "dropPoint": { - "x": 256.69915771484375, - "y": 182.12725830078125 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.6225280761719, - "y": 204.14846801757813 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 59.94879150390625, - "y": 28.958572387695313 - }, - "direction": 0.4500000476837158, - "dropPoint": { - "x": 256.2340393066406, - "y": 182.1586456298828 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.572509765625, - "y": 204.2184600830078 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 58.42654800415039, - "y": 31.91857147216797 - }, - "direction": 0.5000000596046448, - "dropPoint": { - "x": 255.7695770263672, - "y": 182.19003295898438 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.52252197265625, - "y": 204.28846740722656 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 56.758262634277344, - "y": 34.798797607421875 - }, - "direction": 0.5500000715255737, - "dropPoint": { - "x": 255.75718688964844, - "y": 182.26031494140625 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.4725036621094, - "y": 204.35845947265625 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.948116302490234, - "y": 37.592037200927734 - }, - "direction": 0.6000000834465027, - "dropPoint": { - "x": 255.7447509765625, - "y": 182.33058166503906 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.4225158691406, - "y": 204.428466796875 - }, - "state": "TURNING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.2022819519043, - "y": 38.65966033935547 - }, - "direction": 0.6195619106292725, - "dropPoint": { - "x": 255.7322998046875, - "y": 182.40052795410156 - }, - "position": { - "x": 0.0, - "y": 0.0 - }, - "predictedTarget": { - "x": 313.3725280761719, - "y": 204.49847412109375 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.22060012817383, - "y": 38.672725677490234 - }, - "direction": 0.6195619106292725, - "dropPoint": { - "x": 255.7205810546875, - "y": 182.4708709716797 - }, - "position": { - "x": 0.018317991867661476, - "y": 0.013065268285572529 - }, - "predictedTarget": { - "x": 313.322509765625, - "y": 204.56846618652344 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.25723648071289, - "y": 38.698856353759766 - }, - "direction": 0.6195619106292725, - "dropPoint": { - "x": 255.70762634277344, - "y": 182.54005432128906 - }, - "position": { - "x": 0.05495397374033928, - "y": 0.03919580578804016 - }, - "predictedTarget": { - "x": 313.26702880859375, - "y": 204.6384735107422 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.312191009521484, - "y": 38.73805236816406 - }, - "direction": 0.6195619106292725, - "dropPoint": { - "x": 255.6956024169922, - "y": 182.60861206054688 - }, - "position": { - "x": 0.10990794748067856, - "y": 0.07839161157608032 - }, - "predictedTarget": { - "x": 313.20703125, - "y": 204.70846557617188 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.38546371459961, - "y": 38.790313720703125 - }, - "direction": 0.6195619106292725, - "dropPoint": { - "x": 255.68447875976563, - "y": 182.67584228515625 - }, - "position": { - "x": 0.18317991495132446, - "y": 0.13065268099308014 - }, - "predictedTarget": { - "x": 313.14703369140625, - "y": 204.77847290039063 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.47705078125, - "y": 38.85564041137695 - }, - "direction": 0.6195619106292725, - "dropPoint": { - "x": 255.67279052734375, - "y": 182.74163818359375 - }, - "position": { - "x": 0.2747698724269867, - "y": 0.1959790140390396 - }, - "predictedTarget": { - "x": 313.0870361328125, - "y": 204.8484649658203 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.5410270690918, - "y": 38.998355865478516 - }, - "direction": 0.6207467317581177, - "dropPoint": { - "x": 255.66127014160156, - "y": 182.8067626953125 - }, - "position": { - "x": 0.38458487391471863, - "y": 0.2745007872581482 - }, - "predictedTarget": { - "x": 313.02703857421875, - "y": 204.91847229003906 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.66914367675781, - "y": 39.089962005615234 - }, - "direction": 0.6207467317581177, - "dropPoint": { - "x": 255.64996337890625, - "y": 182.87083435058594 - }, - "position": { - "x": 0.5127023458480835, - "y": 0.3661095201969147 - }, - "predictedTarget": { - "x": 312.967041015625, - "y": 204.98846435546875 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.8155632019043, - "y": 39.19466018676758 - }, - "direction": 0.6207467317581177, - "dropPoint": { - "x": 255.1474151611328, - "y": 182.67149353027344 - }, - "position": { - "x": 0.6591223478317261, - "y": 0.47080522775650024 - }, - "predictedTarget": { - "x": 312.90704345703125, - "y": 205.0584716796875 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 54.940860748291016, - "y": 39.36751937866211 - }, - "direction": 0.6217609643936157, - "dropPoint": { - "x": 254.64686584472656, - "y": 182.4710235595703 - }, - "position": { - "x": 0.8237252831459045, - "y": 0.5887548923492432 - }, - "predictedTarget": { - "x": 312.8470458984375, - "y": 205.1284637451172 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 55.12375259399414, - "y": 39.498573303222656 - }, - "direction": 0.6217609643936157, - "dropPoint": { - "x": 254.1457061767578, - "y": 182.27029418945313 - }, - "position": { - "x": 1.0066174268722534, - "y": 0.7198100686073303 - }, - "predictedTarget": { - "x": 312.78704833984375, - "y": 205.19847106933594 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 55.32493209838867, - "y": 39.64273452758789 - }, - "direction": 0.6217609643936157, - "dropPoint": { - "x": 253.64532470703125, - "y": 182.06808471679688 - }, - "position": { - "x": 1.2077988386154175, - "y": 0.8639707565307617 - }, - "predictedTarget": { - "x": 312.72705078125, - "y": 205.26846313476563 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 55.496463775634766, - "y": 39.86681365966797 - }, - "direction": 0.6229912042617798, - "dropPoint": { - "x": 253.14662170410156, - "y": 181.86550903320313 - }, - "position": { - "x": 1.427075743675232, - "y": 1.021506905555725 - }, - "predictedTarget": { - "x": 312.66156005859375, - "y": 205.33847045898438 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 55.73401641845703, - "y": 40.0374755859375 - }, - "direction": 0.6229912042617798, - "dropPoint": { - "x": 252.64886474609375, - "y": 181.6622772216797 - }, - "position": { - "x": 1.6646257638931274, - "y": 1.1921709775924683 - }, - "predictedTarget": { - "x": 312.591552734375, - "y": 205.40846252441406 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 55.989837646484375, - "y": 40.22126770019531 - }, - "direction": 0.6229912042617798, - "dropPoint": { - "x": 252.15147399902344, - "y": 181.45794677734375 - }, - "position": { - "x": 1.9204487800598145, - "y": 1.3759630918502808 - }, - "predictedTarget": { - "x": 312.52154541015625, - "y": 205.4784698486328 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 56.2165412902832, - "y": 40.48406982421875 - }, - "direction": 0.6242039799690247, - "dropPoint": { - "x": 251.65533447265625, - "y": 181.25367736816406 - }, - "position": { - "x": 2.194305896759033, - "y": 1.5732154846191406 - }, - "predictedTarget": { - "x": 312.4515380859375, - "y": 205.5484619140625 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 56.5086555480957, - "y": 40.69447326660156 - }, - "direction": 0.6242039799690247, - "dropPoint": { - "x": 251.16177368164063, - "y": 181.04835510253906 - }, - "position": { - "x": 2.486420154571533, - "y": 1.7836179733276367 - }, - "predictedTarget": { - "x": 312.38153076171875, - "y": 205.61846923828125 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 56.819026947021484, - "y": 40.918025970458984 - }, - "direction": 0.6242039799690247, - "dropPoint": { - "x": 250.66822814941406, - "y": 180.8426971435547 - }, - "position": { - "x": 2.7967915534973145, - "y": 2.0071706771850586 - }, - "predictedTarget": { - "x": 312.3115539550781, - "y": 205.68846130371094 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 57.088966369628906, - "y": 41.236087799072266 - }, - "direction": 0.6257016658782959, - "dropPoint": { - "x": 250.66566467285156, - "y": 181.10528564453125 - }, - "position": { - "x": 3.125065326690674, - "y": 2.2443654537200928 - }, - "predictedTarget": { - "x": 312.2415466308594, - "y": 205.7584686279297 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 57.43547821044922, - "y": 41.48645782470703 - }, - "direction": 0.6257016658782959, - "dropPoint": { - "x": 250.66287231445313, - "y": 181.36685180664063 - }, - "position": { - "x": 3.471576452255249, - "y": 2.4947376251220703 - }, - "predictedTarget": { - "x": 312.1715393066406, - "y": 205.82846069335938 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 57.745567321777344, - "y": 41.825538635253906 - }, - "direction": 0.6270926594734192, - "dropPoint": { - "x": 250.66189575195313, - "y": 181.62646484375 - }, - "position": { - "x": 3.8359580039978027, - "y": 2.7587945461273193 - }, - "predictedTarget": { - "x": 312.1015319824219, - "y": 205.89846801757813 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 58.128170013427734, - "y": 42.10279846191406 - }, - "direction": 0.6270926594734192, - "dropPoint": { - "x": 250.6606903076172, - "y": 181.88528442382813 - }, - "position": { - "x": 4.2185587882995605, - "y": 3.0360541343688965 - }, - "predictedTarget": { - "x": 312.0315246582031, - "y": 205.9684600830078 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 58.47478485107422, - "y": 42.467952728271484 - }, - "direction": 0.6284686326980591, - "dropPoint": { - "x": 250.6592559814453, - "y": 182.14222717285156 - }, - "position": { - "x": 4.618978500366211, - "y": 3.3270678520202637 - }, - "predictedTarget": { - "x": 311.9615478515625, - "y": 206.03297424316406 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 58.893402099609375, - "y": 42.772193908691406 - }, - "direction": 0.6284686326980591, - "dropPoint": { - "x": 250.65896606445313, - "y": 182.3977813720703 - }, - "position": { - "x": 5.037599086761475, - "y": 3.6313095092773438 - }, - "predictedTarget": { - "x": 311.89154052734375, - "y": 206.0929718017578 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 59.27662658691406, - "y": 43.163307189941406 - }, - "direction": 0.6298257112503052, - "dropPoint": { - "x": 250.65914916992188, - "y": 182.65203857421875 - }, - "position": { - "x": 5.473989486694336, - "y": 3.949371576309204 - }, - "predictedTarget": { - "x": 311.821533203125, - "y": 206.15296936035156 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 59.731201171875, - "y": 43.49462127685547 - }, - "direction": 0.6298257112503052, - "dropPoint": { - "x": 250.660400390625, - "y": 182.90420532226563 - }, - "position": { - "x": 5.928562641143799, - "y": 4.280686378479004 - }, - "predictedTarget": { - "x": 311.75152587890625, - "y": 206.2129669189453 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 60.150978088378906, - "y": 43.91177749633789 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 250.6608428955078, - "y": 183.1551971435547 - }, - "position": { - "x": 6.400856971740723, - "y": 4.625885963439941 - }, - "predictedTarget": { - "x": 311.6815185546875, - "y": 206.27296447753906 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 60.64143753051758, - "y": 44.27025604248047 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 250.66229248046875, - "y": 183.40431213378906 - }, - "position": { - "x": 6.891316890716553, - "y": 4.9843621253967285 - }, - "predictedTarget": { - "x": 311.6115417480469, - "y": 206.33297729492188 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 61.150062561035156, - "y": 44.642005920410156 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 250.393798828125, - "y": 183.2076416015625 - }, - "position": { - "x": 7.399941921234131, - "y": 5.356115341186523 - }, - "predictedTarget": { - "x": 311.5415344238281, - "y": 206.39297485351563 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 61.67685317993164, - "y": 45.02703857421875 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 250.1259002685547, - "y": 183.0103759765625 - }, - "position": { - "x": 7.926732063293457, - "y": 5.741145610809326 - }, - "predictedTarget": { - "x": 311.4715270996094, - "y": 206.45297241210938 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 62.22180938720703, - "y": 45.42534637451172 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 249.85943603515625, - "y": 182.8135986328125 - }, - "position": { - "x": 8.471687316894531, - "y": 6.1394524574279785 - }, - "predictedTarget": { - "x": 311.4015197753906, - "y": 206.51296997070313 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 62.78493118286133, - "y": 45.83692932128906 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 249.59426879882813, - "y": 182.6160430908203 - }, - "position": { - "x": 9.034808158874512, - "y": 6.551036357879639 - }, - "predictedTarget": { - "x": 311.3315124511719, - "y": 206.57296752929688 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 63.366214752197266, - "y": 46.26179122924805 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 249.33126831054688, - "y": 182.4188232421875 - }, - "position": { - "x": 9.616093635559082, - "y": 6.975897312164307 - }, - "predictedTarget": { - "x": 311.25604248046875, - "y": 206.63296508789063 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 63.965667724609375, - "y": 46.699928283691406 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 249.06976318359375, - "y": 182.2215576171875 - }, - "position": { - "x": 10.215544700622559, - "y": 7.414035320281982 - }, - "predictedTarget": { - "x": 311.176025390625, - "y": 206.69297790527344 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 64.58328247070313, - "y": 47.151344299316406 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 248.80906677246094, - "y": 182.0242462158203 - }, - "position": { - "x": 10.833160400390625, - "y": 7.865449905395508 - }, - "predictedTarget": { - "x": 311.0960388183594, - "y": 206.7529754638672 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 65.21906280517578, - "y": 47.61603546142578 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 248.5500030517578, - "y": 181.82699584960938 - }, - "position": { - "x": 11.468941688537598, - "y": 8.330141067504883 - }, - "predictedTarget": { - "x": 311.0160217285156, - "y": 206.81297302246094 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 65.87300872802734, - "y": 48.09400177001953 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 248.29249572753906, - "y": 181.62954711914063 - }, - "position": { - "x": 12.12288761138916, - "y": 8.808109283447266 - }, - "predictedTarget": { - "x": 310.93603515625, - "y": 206.8729705810547 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 66.54512023925781, - "y": 48.58524703979492 - }, - "direction": 0.6311637759208679, - "dropPoint": { - "x": 248.03733825683594, - "y": 181.43260192871094 - }, - "position": { - "x": 12.794999122619629, - "y": 9.299354553222656 - }, - "predictedTarget": { - "x": 310.8560485839844, - "y": 206.93296813964844 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 67.1938705444336, - "y": 49.146522521972656 - }, - "direction": 0.6322066187858582, - "dropPoint": { - "x": 247.79469299316406, - "y": 181.43658447265625 - }, - "position": { - "x": 13.484749794006348, - "y": 9.804596900939941 - }, - "predictedTarget": { - "x": 310.7760314941406, - "y": 206.9929656982422 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 67.90177154541016, - "y": 49.665061950683594 - }, - "direction": 0.6322066187858582, - "dropPoint": { - "x": 247.55441284179688, - "y": 181.43978881835938 - }, - "position": { - "x": 14.192651748657227, - "y": 10.323134422302246 - }, - "predictedTarget": { - "x": 310.696044921875, - "y": 207.05296325683594 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 68.62782287597656, - "y": 50.196895599365234 - }, - "direction": 0.6322066187858582, - "dropPoint": { - "x": 247.31524658203125, - "y": 181.44174194335938 - }, - "position": { - "x": 14.918704986572266, - "y": 10.854968070983887 - }, - "predictedTarget": { - "x": 310.61602783203125, - "y": 207.1129608154297 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 69.3126220703125, - "y": 50.822998046875 - }, - "direction": 0.6336945295333862, - "dropPoint": { - "x": 247.07870483398438, - "y": 181.44349670410156 - }, - "position": { - "x": 15.662096977233887, - "y": 11.401204109191895 - }, - "predictedTarget": { - "x": 310.5360412597656, - "y": 207.17295837402344 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 70.07414245605469, - "y": 51.3825569152832 - }, - "direction": 0.6336945295333862, - "dropPoint": { - "x": 246.8432159423828, - "y": 181.44384765625 - }, - "position": { - "x": 16.423620223999023, - "y": 11.960762977600098 - }, - "predictedTarget": { - "x": 310.4505615234375, - "y": 207.2274627685547 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 70.85379791259766, - "y": 51.955440521240234 - }, - "direction": 0.6336945295333862, - "dropPoint": { - "x": 246.6097869873047, - "y": 181.44395446777344 - }, - "position": { - "x": 17.203275680541992, - "y": 12.533644676208496 - }, - "predictedTarget": { - "x": 310.3605651855469, - "y": 207.2774658203125 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 71.59341430664063, - "y": 52.620697021484375 - }, - "direction": 0.6351472735404968, - "dropPoint": { - "x": 246.3789825439453, - "y": 181.44259643554688 - }, - "position": { - "x": 18.00020980834961, - "y": 13.121007919311523 - }, - "predictedTarget": { - "x": 310.2705383300781, - "y": 207.3274688720703 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 72.40845489501953, - "y": 53.221412658691406 - }, - "direction": 0.6351472735404968, - "dropPoint": { - "x": 246.14944458007813, - "y": 181.4408416748047 - }, - "position": { - "x": 18.815256118774414, - "y": 13.721720695495605 - }, - "predictedTarget": { - "x": 310.1805419921875, - "y": 207.37747192382813 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 73.24161529541016, - "y": 53.835472106933594 - }, - "direction": 0.6351472735404968, - "dropPoint": { - "x": 245.92266845703125, - "y": 181.43824768066406 - }, - "position": { - "x": 19.648414611816406, - "y": 14.335782051086426 - }, - "predictedTarget": { - "x": 310.0905456542969, - "y": 207.42747497558594 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 74.0360107421875, - "y": 54.53994369506836 - }, - "direction": 0.6365633606910706, - "dropPoint": { - "x": 245.69735717773438, - "y": 181.43472290039063 - }, - "position": { - "x": 20.498796463012695, - "y": 14.964397430419922 - }, - "predictedTarget": { - "x": 310.00054931640625, - "y": 207.4774627685547 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 74.90447998046875, - "y": 55.18193435668945 - }, - "direction": 0.6365633606910706, - "dropPoint": { - "x": 245.4650421142578, - "y": 181.42930603027344 - }, - "position": { - "x": 21.367271423339844, - "y": 15.606388092041016 - }, - "predictedTarget": { - "x": 309.9105529785156, - "y": 207.5274658203125 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 75.79105377197266, - "y": 55.83729934692383 - }, - "direction": 0.6365633606910706, - "dropPoint": { - "x": 245.23495483398438, - "y": 181.4230194091797 - }, - "position": { - "x": 22.25383949279785, - "y": 16.26175308227539 - }, - "predictedTarget": { - "x": 309.820556640625, - "y": 207.5774688720703 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 76.63836669921875, - "y": 56.58350372314453 - }, - "direction": 0.637986958026886, - "dropPoint": { - "x": 245.00782775878906, - "y": 181.41590881347656 - }, - "position": { - "x": 23.157548904418945, - "y": 16.931779861450195 - }, - "predictedTarget": { - "x": 309.7305603027344, - "y": 207.62747192382813 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 77.56015014648438, - "y": 57.266929626464844 - }, - "direction": 0.637986958026886, - "dropPoint": { - "x": 244.7821807861328, - "y": 181.40811157226563 - }, - "position": { - "x": 24.07933235168457, - "y": 17.61520767211914 - }, - "predictedTarget": { - "x": 309.64056396484375, - "y": 207.67747497558594 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 78.50000762939453, - "y": 57.96376037597656 - }, - "direction": 0.637986958026886, - "dropPoint": { - "x": 244.55892944335938, - "y": 181.39944458007813 - }, - "position": { - "x": 25.019189834594727, - "y": 18.312036514282227 - }, - "predictedTarget": { - "x": 309.5450744628906, - "y": 207.7274627685547 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 79.40205383300781, - "y": 58.749244689941406 - }, - "direction": 0.6393700838088989, - "dropPoint": { - "x": 244.3380889892578, - "y": 181.3899383544922 - }, - "position": { - "x": 25.976137161254883, - "y": 19.023590087890625 - }, - "predictedTarget": { - "x": 309.445068359375, - "y": 207.7774658203125 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 80.37705993652344, - "y": 59.47422409057617 - }, - "direction": 0.6393700838088989, - "dropPoint": { - "x": 244.11952209472656, - "y": 181.3798065185547 - }, - "position": { - "x": 26.951141357421875, - "y": 19.74856948852539 - }, - "predictedTarget": { - "x": 309.3450622558594, - "y": 207.8274688720703 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 81.3701171875, - "y": 60.21262741088867 - }, - "direction": 0.6393700838088989, - "dropPoint": { - "x": 243.90408325195313, - "y": 181.36888122558594 - }, - "position": { - "x": 27.94420051574707, - "y": 20.48697280883789 - }, - "predictedTarget": { - "x": 309.24505615234375, - "y": 207.87747192382813 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 82.32704162597656, - "y": 61.0372428894043 - }, - "direction": 0.640707790851593, - "dropPoint": { - "x": 243.69052124023438, - "y": 181.35704040527344 - }, - "position": { - "x": 28.954309463500977, - "y": 21.240154266357422 - }, - "predictedTarget": { - "x": 309.1450500488281, - "y": 207.92747497558594 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 83.35519409179688, - "y": 61.80387496948242 - }, - "direction": 0.640707790851593, - "dropPoint": { - "x": 243.4793701171875, - "y": 181.34461975097656 - }, - "position": { - "x": 29.98245620727539, - "y": 22.006786346435547 - }, - "predictedTarget": { - "x": 309.0450744628906, - "y": 207.9774627685547 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 84.40137481689453, - "y": 62.583953857421875 - }, - "direction": 0.640707790851593, - "dropPoint": { - "x": 243.4500274658203, - "y": 181.15509033203125 - }, - "position": { - "x": 31.02863883972168, - "y": 22.786867141723633 - }, - "predictedTarget": { - "x": 308.945068359375, - "y": 208.0274658203125 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 85.46559143066406, - "y": 63.37748718261719 - }, - "direction": 0.640707790851593, - "dropPoint": { - "x": 243.42161560058594, - "y": 180.96624755859375 - }, - "position": { - "x": 32.09286117553711, - "y": 23.580398559570313 - }, - "predictedTarget": { - "x": 308.8450622558594, - "y": 208.0774688720703 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 86.59197998046875, - "y": 64.1252212524414 - }, - "direction": 0.6396202445030212, - "dropPoint": { - "x": 243.39402770996094, - "y": 180.7786865234375 - }, - "position": { - "x": 33.17599868774414, - "y": 24.386201858520508 - }, - "predictedTarget": { - "x": 308.74505615234375, - "y": 208.12747192382813 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 87.69317626953125, - "y": 64.94445037841797 - }, - "direction": 0.6396202445030212, - "dropPoint": { - "x": 243.36753845214844, - "y": 180.59197998046875 - }, - "position": { - "x": 34.27718734741211, - "y": 25.205434799194336 - }, - "predictedTarget": { - "x": 308.6450500488281, - "y": 208.17747497558594 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 88.85408020019531, - "y": 65.72103881835938 - }, - "direction": 0.6385923624038696, - "dropPoint": { - "x": 243.3419189453125, - "y": 180.40634155273438 - }, - "position": { - "x": 35.397281646728516, - "y": 26.03694725036621 - }, - "predictedTarget": { - "x": 308.5450744628906, - "y": 208.2219696044922 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 89.99224853515625, - "y": 66.56596374511719 - }, - "direction": 0.6385923624038696, - "dropPoint": { - "x": 243.3173370361328, - "y": 180.22134399414063 - }, - "position": { - "x": 36.53544235229492, - "y": 26.88187026977539 - }, - "predictedTarget": { - "x": 308.445068359375, - "y": 208.26197814941406 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 91.19053649902344, - "y": 67.3675765991211 - }, - "direction": 0.6375541090965271, - "dropPoint": { - "x": 243.293701171875, - "y": 180.03782653808594 - }, - "position": { - "x": 37.692562103271484, - "y": 27.739004135131836 - }, - "predictedTarget": { - "x": 308.3450622558594, - "y": 208.30197143554688 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 92.36573791503906, - "y": 68.23809814453125 - }, - "direction": 0.6375541090965271, - "dropPoint": { - "x": 243.27102661132813, - "y": 179.85501098632813 - }, - "position": { - "x": 38.867759704589844, - "y": 28.60953140258789 - }, - "predictedTarget": { - "x": 308.24505615234375, - "y": 208.34197998046875 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 93.60137176513672, - "y": 69.06477355957031 - }, - "direction": 0.6365078091621399, - "dropPoint": { - "x": 243.24935913085938, - "y": 179.67405700683594 - }, - "position": { - "x": 40.06196212768555, - "y": 29.492202758789063 - }, - "predictedTarget": { - "x": 308.1450500488281, - "y": 208.38197326660156 - }, - "state": "ACCELERATING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 94.80763244628906, - "y": 69.95635986328125 - }, - "direction": 0.6365078091621399, - "dropPoint": { - "x": 243.2286834716797, - "y": 179.4936065673828 - }, - "position": { - "x": 41.268226623535156, - "y": 30.3837890625 - }, - "predictedTarget": { - "x": 308.0450744628906, - "y": 208.42198181152344 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 96.01390075683594, - "y": 70.84794616699219 - }, - "direction": 0.6365078091621399, - "dropPoint": { - "x": 243.02963256835938, - "y": 179.47142028808594 - }, - "position": { - "x": 42.474491119384766, - "y": 31.275375366210938 - }, - "predictedTarget": { - "x": 307.945068359375, - "y": 208.46197509765625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 97.22016906738281, - "y": 71.73953247070313 - }, - "direction": 0.6365078091621399, - "dropPoint": { - "x": 242.8314208984375, - "y": 179.44908142089844 - }, - "position": { - "x": 43.680755615234375, - "y": 32.166961669921875 - }, - "predictedTarget": { - "x": 307.8450622558594, - "y": 208.50198364257813 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 98.42642974853516, - "y": 72.63111877441406 - }, - "direction": 0.6365078091621399, - "dropPoint": { - "x": 242.63648986816406, - "y": 179.42684936523438 - }, - "position": { - "x": 44.887020111083984, - "y": 33.05854797363281 - }, - "predictedTarget": { - "x": 307.74505615234375, - "y": 208.54197692871094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 99.58953857421875, - "y": 73.58103942871094 - }, - "direction": 0.6375737190246582, - "dropPoint": { - "x": 242.44357299804688, - "y": 179.40435791015625 - }, - "position": { - "x": 46.09233474731445, - "y": 33.951419830322266 - }, - "predictedTarget": { - "x": 307.6450500488281, - "y": 208.5819854736328 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 100.79484558105469, - "y": 74.47390747070313 - }, - "direction": 0.6375737190246582, - "dropPoint": { - "x": 242.25192260742188, - "y": 179.38230895996094 - }, - "position": { - "x": 47.29764938354492, - "y": 34.84429168701172 - }, - "predictedTarget": { - "x": 307.5395812988281, - "y": 208.62197875976563 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 102.00016784667969, - "y": 75.36677551269531 - }, - "direction": 0.6375737190246582, - "dropPoint": { - "x": 242.0629119873047, - "y": 179.36032104492188 - }, - "position": { - "x": 48.50296401977539, - "y": 35.73716354370117 - }, - "predictedTarget": { - "x": 307.4295654296875, - "y": 208.6619873046875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 103.15744018554688, - "y": 76.32441711425781 - }, - "direction": 0.6387582421302795, - "dropPoint": { - "x": 241.8754119873047, - "y": 179.3385467529297 - }, - "position": { - "x": 49.707218170166016, - "y": 36.63146209716797 - }, - "predictedTarget": { - "x": 307.319580078125, - "y": 208.7019805908203 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 104.3616943359375, - "y": 77.21871948242188 - }, - "direction": 0.6387582421302795, - "dropPoint": { - "x": 241.68971252441406, - "y": 179.3167724609375 - }, - "position": { - "x": 50.91147232055664, - "y": 37.525760650634766 - }, - "predictedTarget": { - "x": 307.2095642089844, - "y": 208.7419891357422 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 105.56594848632813, - "y": 78.11302185058594 - }, - "direction": 0.6387582421302795, - "dropPoint": { - "x": 241.50599670410156, - "y": 179.2947540283203 - }, - "position": { - "x": 52.115726470947266, - "y": 38.42005920410156 - }, - "predictedTarget": { - "x": 307.0995788574219, - "y": 208.781982421875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 106.72274017333984, - "y": 79.0711441040039 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 241.32496643066406, - "y": 179.27305603027344 - }, - "position": { - "x": 53.31893539428711, - "y": 39.315765380859375 - }, - "predictedTarget": { - "x": 306.98956298828125, - "y": 208.8219757080078 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 107.92594909667969, - "y": 79.96685028076172 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 241.11451721191406, - "y": 179.0736083984375 - }, - "position": { - "x": 54.52214431762695, - "y": 40.21147155761719 - }, - "predictedTarget": { - "x": 306.8795471191406, - "y": 208.8619842529297 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 109.12915802001953, - "y": 80.86255645751953 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 240.90663146972656, - "y": 178.8762969970703 - }, - "position": { - "x": 55.7253532409668, - "y": 41.107177734375 - }, - "predictedTarget": { - "x": 306.7695617675781, - "y": 208.9019775390625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 110.33236694335938, - "y": 81.75826263427734 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 240.70018005371094, - "y": 178.6807098388672 - }, - "position": { - "x": 56.92856216430664, - "y": 42.00288391113281 - }, - "predictedTarget": { - "x": 306.6595458984375, - "y": 208.94198608398438 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 111.53557586669922, - "y": 82.65396881103516 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 240.496337890625, - "y": 178.48751831054688 - }, - "position": { - "x": 58.131771087646484, - "y": 42.898590087890625 - }, - "predictedTarget": { - "x": 306.549560546875, - "y": 208.9819793701172 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 112.73878479003906, - "y": 83.54967498779297 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 240.29344177246094, - "y": 178.29624938964844 - }, - "position": { - "x": 59.33498001098633, - "y": 43.79429626464844 - }, - "predictedTarget": { - "x": 306.4395446777344, - "y": 209.0164794921875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 113.9419937133789, - "y": 84.44538116455078 - }, - "direction": 0.6399266123771667, - "dropPoint": { - "x": 240.09307861328125, - "y": 178.10687255859375 - }, - "position": { - "x": 60.53818893432617, - "y": 44.69000244140625 - }, - "predictedTarget": { - "x": 306.3295593261719, - "y": 209.04647827148438 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 115.18734741210938, - "y": 85.284423828125 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 239.89532470703125, - "y": 177.91986083984375 - }, - "position": { - "x": 61.74232864379883, - "y": 45.58445739746094 - }, - "predictedTarget": { - "x": 306.21954345703125, - "y": 209.0764923095703 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 116.3914794921875, - "y": 86.17887878417969 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 239.69924926757813, - "y": 177.73431396484375 - }, - "position": { - "x": 62.946468353271484, - "y": 46.478912353515625 - }, - "predictedTarget": { - "x": 306.10955810546875, - "y": 209.1064910888672 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 117.59561920166016, - "y": 87.07333374023438 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 239.50576782226563, - "y": 177.55113220214844 - }, - "position": { - "x": 64.15060424804688, - "y": 47.37336730957031 - }, - "predictedTarget": { - "x": 305.9995422363281, - "y": 209.13648986816406 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 118.79975891113281, - "y": 87.96778869628906 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 239.3137969970703, - "y": 177.3699493408203 - }, - "position": { - "x": 65.35474395751953, - "y": 48.267822265625 - }, - "predictedTarget": { - "x": 305.8895568847656, - "y": 209.16648864746094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 120.00389862060547, - "y": 88.86224365234375 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 239.1363525390625, - "y": 177.34703063964844 - }, - "position": { - "x": 66.55888366699219, - "y": 49.16227722167969 - }, - "predictedTarget": { - "x": 305.779541015625, - "y": 209.1964874267578 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 121.20803833007813, - "y": 89.75669860839844 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 238.96095275878906, - "y": 177.32411193847656 - }, - "position": { - "x": 67.76302337646484, - "y": 50.056732177734375 - }, - "predictedTarget": { - "x": 305.6695556640625, - "y": 209.2264862060547 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 122.41217803955078, - "y": 90.65115356445313 - }, - "direction": 0.6388893127441406, - "dropPoint": { - "x": 238.78759765625, - "y": 177.3011474609375 - }, - "position": { - "x": 68.9671630859375, - "y": 50.95118713378906 - }, - "predictedTarget": { - "x": 305.5595397949219, - "y": 209.25648498535156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 123.5688705444336, - "y": 91.60940551757813 - }, - "direction": 0.6400571465492249, - "dropPoint": { - "x": 238.617431640625, - "y": 177.2783203125 - }, - "position": { - "x": 70.17025756835938, - "y": 51.847049713134766 - }, - "predictedTarget": { - "x": 305.4495544433594, - "y": 209.28648376464844 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 124.77196502685547, - "y": 92.50526428222656 - }, - "direction": 0.6400571465492249, - "dropPoint": { - "x": 238.44805908203125, - "y": 177.25570678710938 - }, - "position": { - "x": 71.37335205078125, - "y": 52.74291229248047 - }, - "predictedTarget": { - "x": 305.33953857421875, - "y": 209.3164825439453 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 125.97505950927734, - "y": 93.401123046875 - }, - "direction": 0.6400571465492249, - "dropPoint": { - "x": 238.2807159423828, - "y": 177.23306274414063 - }, - "position": { - "x": 72.57644653320313, - "y": 53.63877487182617 - }, - "predictedTarget": { - "x": 305.22955322265625, - "y": 209.3464813232422 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 127.12982940673828, - "y": 94.36180114746094 - }, - "direction": 0.6412447690963745, - "dropPoint": { - "x": 238.115478515625, - "y": 177.21041870117188 - }, - "position": { - "x": 73.77847290039063, - "y": 54.53606414794922 - }, - "predictedTarget": { - "x": 305.1195373535156, - "y": 209.37649536132813 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 128.33184814453125, - "y": 95.25909423828125 - }, - "direction": 0.6412447690963745, - "dropPoint": { - "x": 237.95205688476563, - "y": 177.18812561035156 - }, - "position": { - "x": 74.98049926757813, - "y": 55.433353424072266 - }, - "predictedTarget": { - "x": 305.0095520019531, - "y": 209.406494140625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 129.53387451171875, - "y": 96.15638732910156 - }, - "direction": 0.6412447690963745, - "dropPoint": { - "x": 237.7912139892578, - "y": 177.16587829589844 - }, - "position": { - "x": 76.18252563476563, - "y": 56.33064270019531 - }, - "predictedTarget": { - "x": 304.8995361328125, - "y": 209.43649291992188 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 130.6882781982422, - "y": 97.1174087524414 - }, - "direction": 0.6424135565757751, - "dropPoint": { - "x": 237.63194274902344, - "y": 177.14358520507813 - }, - "position": { - "x": 77.38350677490234, - "y": 57.22933578491211 - }, - "predictedTarget": { - "x": 304.78955078125, - "y": 209.46649169921875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 131.88925170898438, - "y": 98.01609802246094 - }, - "direction": 0.6424135565757751, - "dropPoint": { - "x": 237.61273193359375, - "y": 176.98670959472656 - }, - "position": { - "x": 78.58448791503906, - "y": 58.128028869628906 - }, - "predictedTarget": { - "x": 304.6795349121094, - "y": 209.49649047851563 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 133.13677978515625, - "y": 98.85252380371094 - }, - "direction": 0.6412714719772339, - "dropPoint": { - "x": 237.59349060058594, - "y": 176.83175659179688 - }, - "position": { - "x": 79.78649139404297, - "y": 59.025352478027344 - }, - "predictedTarget": { - "x": 304.5695495605469, - "y": 209.5264892578125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 134.3387908935547, - "y": 99.74984741210938 - }, - "direction": 0.6412714719772339, - "dropPoint": { - "x": 237.57455444335938, - "y": 176.67877197265625 - }, - "position": { - "x": 80.98849487304688, - "y": 59.92267608642578 - }, - "predictedTarget": { - "x": 304.45953369140625, - "y": 209.55648803710938 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 135.58682250976563, - "y": 100.5854263305664 - }, - "direction": 0.6401402354240417, - "dropPoint": { - "x": 237.5556182861328, - "y": 176.52789306640625 - }, - "position": { - "x": 82.19151306152344, - "y": 60.81863784790039 - }, - "predictedTarget": { - "x": 304.34954833984375, - "y": 209.58648681640625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 136.7898406982422, - "y": 101.48138427734375 - }, - "direction": 0.6401402354240417, - "dropPoint": { - "x": 237.5369415283203, - "y": 176.37881469726563 - }, - "position": { - "x": 83.39453125, - "y": 61.714599609375 - }, - "predictedTarget": { - "x": 304.2340393066406, - "y": 209.61099243164063 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 138.03842163085938, - "y": 102.31610870361328 - }, - "direction": 0.6390190124511719, - "dropPoint": { - "x": 237.51821899414063, - "y": 176.23159790039063 - }, - "position": { - "x": 84.59855651855469, - "y": 62.60921096801758 - }, - "predictedTarget": { - "x": 304.1140441894531, - "y": 209.63099670410156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 139.24244689941406, - "y": 103.21072387695313 - }, - "direction": 0.6390190124511719, - "dropPoint": { - "x": 237.49977111816406, - "y": 176.0863800048828 - }, - "position": { - "x": 85.80258178710938, - "y": 63.503822326660156 - }, - "predictedTarget": { - "x": 303.9940490722656, - "y": 209.6510009765625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 140.49151611328125, - "y": 104.04464721679688 - }, - "direction": 0.637908935546875, - "dropPoint": { - "x": 237.48129272460938, - "y": 175.9430389404297 - }, - "position": { - "x": 87.00759887695313, - "y": 64.3970947265625 - }, - "predictedTarget": { - "x": 303.8740539550781, - "y": 209.67098999023438 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 141.696533203125, - "y": 104.93791198730469 - }, - "direction": 0.637908935546875, - "dropPoint": { - "x": 237.4631805419922, - "y": 175.8018798828125 - }, - "position": { - "x": 88.21261596679688, - "y": 65.29036712646484 - }, - "predictedTarget": { - "x": 303.7540588378906, - "y": 209.6909942626953 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 142.94601440429688, - "y": 105.7711181640625 - }, - "direction": 0.636810839176178, - "dropPoint": { - "x": 237.4449920654297, - "y": 175.66241455078125 - }, - "position": { - "x": 89.41860961914063, - "y": 66.18231964111328 - }, - "predictedTarget": { - "x": 303.6340637207031, - "y": 209.71099853515625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 144.15200805664063, - "y": 106.66307067871094 - }, - "direction": 0.636810839176178, - "dropPoint": { - "x": 237.28944396972656, - "y": 175.63934326171875 - }, - "position": { - "x": 90.62460327148438, - "y": 67.07427215576172 - }, - "predictedTarget": { - "x": 303.5140380859375, - "y": 209.7310028076172 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 145.35800170898438, - "y": 107.55502319335938 - }, - "direction": 0.636810839176178, - "dropPoint": { - "x": 237.136474609375, - "y": 175.61630249023438 - }, - "position": { - "x": 91.83059692382813, - "y": 67.96622467041016 - }, - "predictedTarget": { - "x": 303.39404296875, - "y": 209.75099182128906 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 146.5152130126953, - "y": 108.51285552978516 - }, - "direction": 0.6380151510238647, - "dropPoint": { - "x": 236.98509216308594, - "y": 175.59295654296875 - }, - "position": { - "x": 93.03551483154297, - "y": 68.85962677001953 - }, - "predictedTarget": { - "x": 303.2740478515625, - "y": 209.77099609375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 147.72012329101563, - "y": 109.40625762939453 - }, - "direction": 0.6380151510238647, - "dropPoint": { - "x": 236.8360595703125, - "y": 175.57052612304688 - }, - "position": { - "x": 94.24043273925781, - "y": 69.7530288696289 - }, - "predictedTarget": { - "x": 303.154052734375, - "y": 209.79100036621094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 148.925048828125, - "y": 110.2996597290039 - }, - "direction": 0.6380151510238647, - "dropPoint": { - "x": 236.68812561035156, - "y": 175.54751586914063 - }, - "position": { - "x": 95.44535064697266, - "y": 70.64643096923828 - }, - "predictedTarget": { - "x": 303.03955078125, - "y": 209.8054962158203 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 150.08181762695313, - "y": 111.25792694091797 - }, - "direction": 0.639201819896698, - "dropPoint": { - "x": 236.54327392578125, - "y": 175.5250701904297 - }, - "position": { - "x": 96.64920806884766, - "y": 71.541259765625 - }, - "predictedTarget": { - "x": 302.9295654296875, - "y": 209.81549072265625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 151.28567504882813, - "y": 112.15275573730469 - }, - "direction": 0.639201819896698, - "dropPoint": { - "x": 236.39968872070313, - "y": 175.50250244140625 - }, - "position": { - "x": 97.85306549072266, - "y": 72.43608856201172 - }, - "predictedTarget": { - "x": 302.8195495605469, - "y": 209.82550048828125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 152.48953247070313, - "y": 113.0475845336914 - }, - "direction": 0.639201819896698, - "dropPoint": { - "x": 236.25872802734375, - "y": 175.48045349121094 - }, - "position": { - "x": 99.05692291259766, - "y": 73.33091735839844 - }, - "predictedTarget": { - "x": 302.70953369140625, - "y": 209.8354949951172 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 153.64605712890625, - "y": 114.00599670410156 - }, - "direction": 0.6403660774230957, - "dropPoint": { - "x": 236.11932373046875, - "y": 175.45790100097656 - }, - "position": { - "x": 100.25973510742188, - "y": 74.2271499633789 - }, - "predictedTarget": { - "x": 302.59954833984375, - "y": 209.84548950195313 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 154.848876953125, - "y": 114.90223693847656 - }, - "direction": 0.6403660774230957, - "dropPoint": { - "x": 235.9817657470703, - "y": 175.43597412109375 - }, - "position": { - "x": 101.4625473022461, - "y": 75.12338256835938 - }, - "predictedTarget": { - "x": 302.4895324707031, - "y": 209.85549926757813 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 156.05169677734375, - "y": 115.7984619140625 - }, - "direction": 0.6403660774230957, - "dropPoint": { - "x": 235.96359252929688, - "y": 175.30018615722656 - }, - "position": { - "x": 102.66535949707031, - "y": 76.01961517333984 - }, - "predictedTarget": { - "x": 302.3795471191406, - "y": 209.86549377441406 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 157.25450134277344, - "y": 116.6947021484375 - }, - "direction": 0.6403660774230957, - "dropPoint": { - "x": 235.94541931152344, - "y": 175.16671752929688 - }, - "position": { - "x": 103.86817169189453, - "y": 76.91584777832031 - }, - "predictedTarget": { - "x": 302.26953125, - "y": 209.87548828125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 158.5126495361328, - "y": 117.51655578613281 - }, - "direction": 0.6390044093132019, - "dropPoint": { - "x": 235.92730712890625, - "y": 175.03546142578125 - }, - "position": { - "x": 105.07220458984375, - "y": 77.81044006347656 - }, - "predictedTarget": { - "x": 302.1595458984375, - "y": 209.885498046875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 159.7166748046875, - "y": 118.41114807128906 - }, - "direction": 0.6390044093132019, - "dropPoint": { - "x": 235.90943908691406, - "y": 174.9058074951172 - }, - "position": { - "x": 106.27623748779297, - "y": 78.70503234863281 - }, - "predictedTarget": { - "x": 302.0495300292969, - "y": 209.89549255371094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 160.96726989746094, - "y": 119.24301147460938 - }, - "direction": 0.6378570199012756, - "dropPoint": { - "x": 235.89163208007813, - "y": 174.7783203125 - }, - "position": { - "x": 107.4813003540039, - "y": 79.5982437133789 - }, - "predictedTarget": { - "x": 301.9340515136719, - "y": 209.90548706054688 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 162.17233276367188, - "y": 120.13623046875 - }, - "direction": 0.6378570199012756, - "dropPoint": { - "x": 235.87408447265625, - "y": 174.65237426757813 - }, - "position": { - "x": 108.68636322021484, - "y": 80.491455078125 - }, - "predictedTarget": { - "x": 301.8140563964844, - "y": 209.91549682617188 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 163.42340087890625, - "y": 120.9673080444336 - }, - "direction": 0.6367213726043701, - "dropPoint": { - "x": 235.8565673828125, - "y": 174.52874755859375 - }, - "position": { - "x": 109.89244079589844, - "y": 81.38330078125 - }, - "predictedTarget": { - "x": 301.69403076171875, - "y": 209.9254913330078 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 164.6294708251953, - "y": 121.8591537475586 - }, - "direction": 0.6367213726043701, - "dropPoint": { - "x": 235.83929443359375, - "y": 174.40664672851563 - }, - "position": { - "x": 111.09851837158203, - "y": 82.275146484375 - }, - "predictedTarget": { - "x": 301.57403564453125, - "y": 209.93548583984375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 165.88095092773438, - "y": 122.68951416015625 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.82211303710938, - "y": 174.286865234375 - }, - "position": { - "x": 112.30559539794922, - "y": 83.16563415527344 - }, - "predictedTarget": { - "x": 301.45404052734375, - "y": 209.94549560546875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 167.08804321289063, - "y": 123.58000183105469 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.80517578125, - "y": 174.16879272460938 - }, - "position": { - "x": 113.5126724243164, - "y": 84.05612182617188 - }, - "predictedTarget": { - "x": 301.33404541015625, - "y": 209.9554901123047 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 168.29510498046875, - "y": 124.47048950195313 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.80963134765625, - "y": 174.16860961914063 - }, - "position": { - "x": 114.7197494506836, - "y": 84.94660949707031 - }, - "predictedTarget": { - "x": 301.2140197753906, - "y": 209.9654998779297 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 169.502197265625, - "y": 125.36097717285156 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.8140411376953, - "y": 174.16842651367188 - }, - "position": { - "x": 115.92682647705078, - "y": 85.83709716796875 - }, - "predictedTarget": { - "x": 301.0940246582031, - "y": 209.97549438476563 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 170.70925903320313, - "y": 126.25146484375 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.81845092773438, - "y": 174.16827392578125 - }, - "position": { - "x": 117.13390350341797, - "y": 86.72758483886719 - }, - "predictedTarget": { - "x": 300.9740295410156, - "y": 209.98548889160156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 171.91635131835938, - "y": 127.14195251464844 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.82284545898438, - "y": 174.1681365966797 - }, - "position": { - "x": 118.34098052978516, - "y": 87.61807250976563 - }, - "predictedTarget": { - "x": 300.8540344238281, - "y": 209.99549865722656 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 173.1234130859375, - "y": 128.03244018554688 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.82723999023438, - "y": 174.16802978515625 - }, - "position": { - "x": 119.54805755615234, - "y": 88.50856018066406 - }, - "predictedTarget": { - "x": 300.7340393066406, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 174.33050537109375, - "y": 128.9229278564453 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.83157348632813, - "y": 174.16790771484375 - }, - "position": { - "x": 120.75513458251953, - "y": 89.3990478515625 - }, - "predictedTarget": { - "x": 300.6140441894531, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 175.53756713867188, - "y": 129.81341552734375 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.83595275878906, - "y": 174.1678466796875 - }, - "position": { - "x": 121.96221160888672, - "y": 90.28953552246094 - }, - "predictedTarget": { - "x": 300.4940185546875, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 176.74465942382813, - "y": 130.7039031982422 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.84027099609375, - "y": 174.16778564453125 - }, - "position": { - "x": 123.1692886352539, - "y": 91.18002319335938 - }, - "predictedTarget": { - "x": 300.3740234375, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 177.95172119140625, - "y": 131.59439086914063 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.8446044921875, - "y": 174.16773986816406 - }, - "position": { - "x": 124.3763656616211, - "y": 92.07051086425781 - }, - "predictedTarget": { - "x": 300.2540283203125, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 179.1588134765625, - "y": 132.48487854003906 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.848876953125, - "y": 174.16770935058594 - }, - "position": { - "x": 125.58344268798828, - "y": 92.96099853515625 - }, - "predictedTarget": { - "x": 300.134033203125, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 180.36587524414063, - "y": 133.3753662109375 - }, - "direction": 0.6355987191200256, - "dropPoint": { - "x": 235.70321655273438, - "y": 174.03175354003906 - }, - "position": { - "x": 126.79051971435547, - "y": 93.85148620605469 - }, - "predictedTarget": { - "x": 300.0140380859375, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 181.6199493408203, - "y": 134.20208740234375 - }, - "direction": 0.6344349980354309, - "dropPoint": { - "x": 235.55999755859375, - "y": 173.8981475830078 - }, - "position": { - "x": 127.99862670898438, - "y": 94.74057006835938 - }, - "predictedTarget": { - "x": 299.89404296875, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 182.82806396484375, - "y": 135.09115600585938 - }, - "direction": 0.6344349980354309, - "dropPoint": { - "x": 235.41885375976563, - "y": 173.76602172851563 - }, - "position": { - "x": 129.2067413330078, - "y": 95.62965393066406 - }, - "predictedTarget": { - "x": 299.7740478515625, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 184.0361785888672, - "y": 135.98025512695313 - }, - "direction": 0.6344349980354309, - "dropPoint": { - "x": 235.28012084960938, - "y": 173.63623046875 - }, - "position": { - "x": 130.41485595703125, - "y": 96.51873779296875 - }, - "predictedTarget": { - "x": 299.654052734375, - "y": 210.0 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 185.24429321289063, - "y": 136.86932373046875 - }, - "direction": 0.6344349980354309, - "dropPoint": { - "x": 235.1431121826172, - "y": 173.50848388671875 - }, - "position": { - "x": 131.6229705810547, - "y": 97.40782165527344 - }, - "predictedTarget": { - "x": 299.5340270996094, - "y": 209.9945068359375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 186.45240783691406, - "y": 137.7584228515625 - }, - "direction": 0.6344349980354309, - "dropPoint": { - "x": 235.0081787109375, - "y": 173.38290405273438 - }, - "position": { - "x": 132.83108520507813, - "y": 98.29690551757813 - }, - "predictedTarget": { - "x": 299.4140319824219, - "y": 209.9844970703125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 187.6605224609375, - "y": 138.64749145507813 - }, - "direction": 0.6344349980354309, - "dropPoint": { - "x": 234.87530517578125, - "y": 173.25930786132813 - }, - "position": { - "x": 134.03919982910156, - "y": 99.18598937988281 - }, - "predictedTarget": { - "x": 299.2940368652344, - "y": 209.97450256347656 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 188.9102783203125, - "y": 139.4799346923828 - }, - "direction": 0.6334022879600525, - "dropPoint": { - "x": 234.744873046875, - "y": 173.1380615234375 - }, - "position": { - "x": 135.24822998046875, - "y": 100.07382202148438 - }, - "predictedTarget": { - "x": 299.1740417480469, - "y": 209.96450805664063 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 190.1193084716797, - "y": 140.36776733398438 - }, - "direction": 0.6334022879600525, - "dropPoint": { - "x": 234.61648559570313, - "y": 173.0184326171875 - }, - "position": { - "x": 136.45726013183594, - "y": 100.96165466308594 - }, - "predictedTarget": { - "x": 299.0540466308594, - "y": 209.95449829101563 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 191.32833862304688, - "y": 141.25559997558594 - }, - "direction": 0.6334022879600525, - "dropPoint": { - "x": 234.49050903320313, - "y": 172.90084838867188 - }, - "position": { - "x": 137.66629028320313, - "y": 101.8494873046875 - }, - "predictedTarget": { - "x": 298.93402099609375, - "y": 209.9445037841797 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 192.59225463867188, - "y": 142.06857299804688 - }, - "direction": 0.6320387125015259, - "dropPoint": { - "x": 234.59048461914063, - "y": 172.82144165039063 - }, - "position": { - "x": 138.87652587890625, - "y": 102.73567199707031 - }, - "predictedTarget": { - "x": 298.81402587890625, - "y": 209.9344940185547 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 193.84335327148438, - "y": 142.89889526367188 - }, - "direction": 0.631022036075592, - "dropPoint": { - "x": 234.68853759765625, - "y": 172.74371337890625 - }, - "position": { - "x": 140.08766174316406, - "y": 103.6206283569336 - }, - "predictedTarget": { - "x": 298.69403076171875, - "y": 209.92449951171875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 195.0950164794922, - "y": 143.72833251953125 - }, - "direction": 0.6300122141838074, - "dropPoint": { - "x": 234.78518676757813, - "y": 172.66775512695313 - }, - "position": { - "x": 141.29969787597656, - "y": 104.50436401367188 - }, - "predictedTarget": { - "x": 298.57403564453125, - "y": 209.9145050048828 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 196.34718322753906, - "y": 144.55697631835938 - }, - "direction": 0.6290110349655151, - "dropPoint": { - "x": 234.87973022460938, - "y": 172.59329223632813 - }, - "position": { - "x": 142.5126190185547, - "y": 105.38687896728516 - }, - "predictedTarget": { - "x": 298.45404052734375, - "y": 209.9044952392578 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 197.5601043701172, - "y": 145.43948364257813 - }, - "direction": 0.6290110349655151, - "dropPoint": { - "x": 234.97325134277344, - "y": 172.5208282470703 - }, - "position": { - "x": 143.7255401611328, - "y": 106.26939392089844 - }, - "predictedTarget": { - "x": 298.3340148925781, - "y": 209.89450073242188 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 198.85247802734375, - "y": 146.21255493164063 - }, - "direction": 0.6270242929458618, - "dropPoint": { - "x": 235.06399536132813, - "y": 172.45022583007813 - }, - "position": { - "x": 144.94020080566406, - "y": 107.14949798583984 - }, - "predictedTarget": { - "x": 298.2140197753906, - "y": 209.88449096679688 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 200.067138671875, - "y": 147.0926513671875 - }, - "direction": 0.6270242929458618, - "dropPoint": { - "x": 235.1537322998047, - "y": 172.38104248046875 - }, - "position": { - "x": 146.1548614501953, - "y": 108.02960205078125 - }, - "predictedTarget": { - "x": 298.0940246582031, - "y": 209.87449645996094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 201.359619140625, - "y": 147.86517333984375 - }, - "direction": 0.6250739693641663, - "dropPoint": { - "x": 235.24110412597656, - "y": 172.31396484375 - }, - "position": { - "x": 147.37124633789063, - "y": 108.90734100341797 - }, - "predictedTarget": { - "x": 297.9740295410156, - "y": 209.864501953125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 202.57598876953125, - "y": 148.742919921875 - }, - "direction": 0.6250739693641663, - "dropPoint": { - "x": 235.32705688476563, - "y": 172.24794006347656 - }, - "position": { - "x": 148.58763122558594, - "y": 109.78507995605469 - }, - "predictedTarget": { - "x": 297.8540344238281, - "y": 209.85450744628906 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 203.8685760498047, - "y": 149.5148468017578 - }, - "direction": 0.6231585741043091, - "dropPoint": { - "x": 235.41131591796875, - "y": 172.18423461914063 - }, - "position": { - "x": 149.80569458007813, - "y": 110.66048431396484 - }, - "predictedTarget": { - "x": 297.7340393066406, - "y": 209.84449768066406 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 205.08663940429688, - "y": 150.39024353027344 - }, - "direction": 0.6231585741043091, - "dropPoint": { - "x": 235.31027221679688, - "y": 172.16259765625 - }, - "position": { - "x": 151.0237579345703, - "y": 111.535888671875 - }, - "predictedTarget": { - "x": 297.6140441894531, - "y": 209.83450317382813 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 206.30470275878906, - "y": 151.26565551757813 - }, - "direction": 0.6231585741043091, - "dropPoint": { - "x": 235.21066284179688, - "y": 172.14056396484375 - }, - "position": { - "x": 152.2418212890625, - "y": 112.41129302978516 - }, - "predictedTarget": { - "x": 297.4940490722656, - "y": 209.8245086669922 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 207.47586059570313, - "y": 152.20623779296875 - }, - "direction": 0.6243380904197693, - "dropPoint": { - "x": 235.11383056640625, - "y": 172.11878967285156 - }, - "position": { - "x": 153.45884704589844, - "y": 113.28813171386719 - }, - "predictedTarget": { - "x": 297.3740234375, - "y": 209.8144989013672 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 208.69288635253906, - "y": 153.08306884765625 - }, - "direction": 0.6243380904197693, - "dropPoint": { - "x": 235.0184326171875, - "y": 172.09765625 - }, - "position": { - "x": 154.67587280273438, - "y": 114.16497039794922 - }, - "predictedTarget": { - "x": 297.2540283203125, - "y": 209.80450439453125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 209.909912109375, - "y": 153.95989990234375 - }, - "direction": 0.6243380904197693, - "dropPoint": { - "x": 234.92539978027344, - "y": 172.0763397216797 - }, - "position": { - "x": 155.8928985595703, - "y": 115.04180908203125 - }, - "predictedTarget": { - "x": 297.134033203125, - "y": 209.78900146484375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 211.08119201660156, - "y": 154.90017700195313 - }, - "direction": 0.6254869699478149, - "dropPoint": { - "x": 234.83424377441406, - "y": 172.05523681640625 - }, - "position": { - "x": 157.10891723632813, - "y": 115.9200439453125 - }, - "predictedTarget": { - "x": 297.0140380859375, - "y": 209.76901245117188 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 212.29721069335938, - "y": 155.77841186523438 - }, - "direction": 0.6254869699478149, - "dropPoint": { - "x": 234.74472045898438, - "y": 172.03456115722656 - }, - "position": { - "x": 158.32493591308594, - "y": 116.79827880859375 - }, - "predictedTarget": { - "x": 296.89404296875, - "y": 209.74900817871094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 213.5132293701172, - "y": 156.65664672851563 - }, - "direction": 0.6254869699478149, - "dropPoint": { - "x": 234.65731811523438, - "y": 172.01382446289063 - }, - "position": { - "x": 159.54095458984375, - "y": 117.676513671875 - }, - "predictedTarget": { - "x": 296.7740478515625, - "y": 209.72900390625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 214.68479919433594, - "y": 157.5963592529297 - }, - "direction": 0.6266013383865356, - "dropPoint": { - "x": 234.57235717773438, - "y": 171.993408203125 - }, - "position": { - "x": 160.75599670410156, - "y": 118.55610656738281 - }, - "predictedTarget": { - "x": 296.6540222167969, - "y": 209.70901489257813 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 215.89984130859375, - "y": 158.4759521484375 - }, - "direction": 0.6266013383865356, - "dropPoint": { - "x": 234.48873901367188, - "y": 171.97340393066406 - }, - "position": { - "x": 161.97103881835938, - "y": 119.43569946289063 - }, - "predictedTarget": { - "x": 296.5340270996094, - "y": 209.6890106201172 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 217.11488342285156, - "y": 159.3555450439453 - }, - "direction": 0.6266013383865356, - "dropPoint": { - "x": 234.47238159179688, - "y": 171.89337158203125 - }, - "position": { - "x": 163.1860809326172, - "y": 120.31529235839844 - }, - "predictedTarget": { - "x": 296.4140319824219, - "y": 209.66900634765625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 218.32992553710938, - "y": 160.23513793945313 - }, - "direction": 0.6266013383865356, - "dropPoint": { - "x": 234.45599365234375, - "y": 171.81565856933594 - }, - "position": { - "x": 164.401123046875, - "y": 121.19488525390625 - }, - "predictedTarget": { - "x": 296.2940368652344, - "y": 209.6490020751953 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 219.60372924804688, - "y": 161.03341674804688 - }, - "direction": 0.6251277327537537, - "dropPoint": { - "x": 234.4395751953125, - "y": 171.74014282226563 - }, - "position": { - "x": 165.61746215820313, - "y": 122.07268524169922 - }, - "predictedTarget": { - "x": 296.17401123046875, - "y": 209.62901306152344 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 220.820068359375, - "y": 161.91122436523438 - }, - "direction": 0.6251277327537537, - "dropPoint": { - "x": 234.42369079589844, - "y": 171.6659393310547 - }, - "position": { - "x": 166.83380126953125, - "y": 122.95048522949219 - }, - "predictedTarget": { - "x": 296.05401611328125, - "y": 209.6090087890625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 222.08416748046875, - "y": 162.7227783203125 - }, - "direction": 0.6239281296730042, - "dropPoint": { - "x": 234.40782165527344, - "y": 171.5940399169922 - }, - "position": { - "x": 168.0511932373047, - "y": 123.82682800292969 - }, - "predictedTarget": { - "x": 295.93951416015625, - "y": 209.58900451660156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 223.30154418945313, - "y": 163.59912109375 - }, - "direction": 0.6239281296730042, - "dropPoint": { - "x": 234.39263916015625, - "y": 171.5236358642578 - }, - "position": { - "x": 169.26858520507813, - "y": 124.70317077636719 - }, - "predictedTarget": { - "x": 295.82952880859375, - "y": 209.5690155029297 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 224.5658416748047, - "y": 164.41021728515625 - }, - "direction": 0.6227478981018066, - "dropPoint": { - "x": 234.37722778320313, - "y": 171.4554443359375 - }, - "position": { - "x": 170.48699951171875, - "y": 125.57807159423828 - }, - "predictedTarget": { - "x": 295.7195129394531, - "y": 209.54901123046875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 225.7842559814453, - "y": 165.28512573242188 - }, - "direction": 0.6227478981018066, - "dropPoint": { - "x": 234.36227416992188, - "y": 171.38876342773438 - }, - "position": { - "x": 171.70541381835938, - "y": 126.45297241210938 - }, - "predictedTarget": { - "x": 295.6095275878906, - "y": 209.5290069580078 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 227.048583984375, - "y": 166.09600830078125 - }, - "direction": 0.6215905547142029, - "dropPoint": { - "x": 234.3472900390625, - "y": 171.32415771484375 - }, - "position": { - "x": 172.9248504638672, - "y": 127.32646942138672 - }, - "predictedTarget": { - "x": 295.49951171875, - "y": 209.50900268554688 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 228.26803588867188, - "y": 166.96949768066406 - }, - "direction": 0.6215905547142029, - "dropPoint": { - "x": 234.33282470703125, - "y": 171.26113891601563 - }, - "position": { - "x": 174.144287109375, - "y": 128.19996643066406 - }, - "predictedTarget": { - "x": 295.3895263671875, - "y": 209.48899841308594 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 229.53477478027344, - "y": 167.77687072753906 - }, - "direction": 0.6203962564468384, - "dropPoint": { - "x": 234.31642150878906, - "y": 171.1934051513672 - }, - "position": { - "x": 175.36476135253906, - "y": 129.07200622558594 - }, - "predictedTarget": { - "x": 295.2795104980469, - "y": 209.468994140625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 230.7552490234375, - "y": 168.64891052246094 - }, - "direction": 0.6203962564468384, - "dropPoint": { - "x": 234.30056762695313, - "y": 171.1273193359375 - }, - "position": { - "x": 176.58523559570313, - "y": 129.9440460205078 - }, - "predictedTarget": { - "x": 295.1695251464844, - "y": 209.44900512695313 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 232.0244140625, - "y": 169.45269775390625 - }, - "direction": 0.6191645860671997, - "dropPoint": { - "x": 234.28466796875, - "y": 171.06346130371094 - }, - "position": { - "x": 177.80677795410156, - "y": 130.8145751953125 - }, - "predictedTarget": { - "x": 295.05950927734375, - "y": 209.4290008544922 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 233.24595642089844, - "y": 170.32322692871094 - }, - "direction": 0.6191645860671997, - "dropPoint": { - "x": 234.2692413330078, - "y": 171.00094604492188 - }, - "position": { - "x": 179.0283203125, - "y": 131.6851043701172 - }, - "predictedTarget": { - "x": 294.94952392578125, - "y": 209.40899658203125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 234.51522827148438, - "y": 171.12669372558594 - }, - "direction": 0.6179553866386414, - "dropPoint": { - "x": 234.25357055664063, - "y": 170.94068908691406 - }, - "position": { - "x": 180.25091552734375, - "y": 132.5541534423828 - }, - "predictedTarget": { - "x": 294.8395080566406, - "y": 209.3834991455078 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 235.73782348632813, - "y": 171.99574279785156 - }, - "direction": 0.6179553866386414, - "dropPoint": { - "x": 234.2386474609375, - "y": 170.88186645507813 - }, - "position": { - "x": 181.4735107421875, - "y": 133.42320251464844 - }, - "predictedTarget": { - "x": 294.7295227050781, - "y": 209.35350036621094 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 237.0072021484375, - "y": 172.7989044189453 - }, - "direction": 0.6167684197425842, - "dropPoint": { - "x": 234.22372436523438, - "y": 170.82530212402344 - }, - "position": { - "x": 182.6971435546875, - "y": 134.29080200195313 - }, - "predictedTarget": { - "x": 294.6195068359375, - "y": 209.32350158691406 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 238.2308349609375, - "y": 173.66650390625 - }, - "direction": 0.6167684197425842, - "dropPoint": { - "x": 234.2093505859375, - "y": 170.77020263671875 - }, - "position": { - "x": 183.9207763671875, - "y": 135.1584014892578 - }, - "predictedTarget": { - "x": 294.509521484375, - "y": 209.2935028076172 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 239.50018310546875, - "y": 174.46954345703125 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.1949462890625, - "y": 170.7171630859375 - }, - "position": { - "x": 185.14541625976563, - "y": 136.0245819091797 - }, - "predictedTarget": { - "x": 294.3995056152344, - "y": 209.2635040283203 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 240.72482299804688, - "y": 175.33572387695313 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.1810760498047, - "y": 170.66563415527344 - }, - "position": { - "x": 186.37005615234375, - "y": 136.89076232910156 - }, - "predictedTarget": { - "x": 294.28948974609375, - "y": 209.23350524902344 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 241.949462890625, - "y": 176.201904296875 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.18789672851563, - "y": 170.66854858398438 - }, - "position": { - "x": 187.59469604492188, - "y": 137.75694274902344 - }, - "predictedTarget": { - "x": 294.17950439453125, - "y": 209.20350646972656 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 243.17410278320313, - "y": 177.06808471679688 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.19467163085938, - "y": 170.67153930664063 - }, - "position": { - "x": 188.8193359375, - "y": 138.6231231689453 - }, - "predictedTarget": { - "x": 294.0694885253906, - "y": 209.1735076904297 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 244.39874267578125, - "y": 177.93426513671875 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.201416015625, - "y": 170.67457580566406 - }, - "position": { - "x": 190.04397583007813, - "y": 139.4893035888672 - }, - "predictedTarget": { - "x": 293.9595031738281, - "y": 209.1435089111328 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 245.62338256835938, - "y": 178.80044555664063 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.20819091796875, - "y": 170.67774963378906 - }, - "position": { - "x": 191.26861572265625, - "y": 140.35548400878906 - }, - "predictedTarget": { - "x": 293.8494873046875, - "y": 209.11351013183594 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 246.8480224609375, - "y": 179.6666259765625 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.21499633789063, - "y": 170.68087768554688 - }, - "position": { - "x": 192.49325561523438, - "y": 141.22166442871094 - }, - "predictedTarget": { - "x": 293.739501953125, - "y": 209.0780029296875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 248.07266235351563, - "y": 180.53280639648438 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.22164916992188, - "y": 170.68417358398438 - }, - "position": { - "x": 193.7178955078125, - "y": 142.0878448486328 - }, - "predictedTarget": { - "x": 293.6295166015625, - "y": 209.0380096435547 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 249.29730224609375, - "y": 181.39898681640625 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.2282257080078, - "y": 170.6874542236328 - }, - "position": { - "x": 194.94253540039063, - "y": 142.9540252685547 - }, - "predictedTarget": { - "x": 293.5195007324219, - "y": 208.9980010986328 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 250.52194213867188, - "y": 182.26516723632813 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.23471069335938, - "y": 170.69073486328125 - }, - "position": { - "x": 196.16717529296875, - "y": 143.82020568847656 - }, - "predictedTarget": { - "x": 293.4095153808594, - "y": 208.9580078125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 251.74658203125, - "y": 183.13134765625 - }, - "direction": 0.615606427192688, - "dropPoint": { - "x": 234.2412109375, - "y": 170.6942138671875 - }, - "position": { - "x": 197.39181518554688, - "y": 144.68638610839844 - }, - "predictedTarget": { - "x": 293.29949951171875, - "y": 208.91799926757813 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 253.0115509033203, - "y": 183.94046020507813 - }, - "direction": 0.6145797371864319, - "dropPoint": { - "x": 234.24758911132813, - "y": 170.6976318359375 - }, - "position": { - "x": 198.61734008789063, - "y": 145.5513153076172 - }, - "predictedTarget": { - "x": 293.18951416015625, - "y": 208.8780059814453 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 254.23707580566406, - "y": 184.80538940429688 - }, - "direction": 0.6145797371864319, - "dropPoint": { - "x": 234.23170471191406, - "y": 170.6526641845703 - }, - "position": { - "x": 199.84286499023438, - "y": 146.41624450683594 - }, - "predictedTarget": { - "x": 293.0794982910156, - "y": 208.83799743652344 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 255.5124053955078, - "y": 185.5996551513672 - }, - "direction": 0.6133100390434265, - "dropPoint": { - "x": 234.21572875976563, - "y": 170.60983276367188 - }, - "position": { - "x": 201.06948852539063, - "y": 147.2796173095703 - }, - "predictedTarget": { - "x": 292.9695129394531, - "y": 208.79800415039063 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 256.739013671875, - "y": 186.46302795410156 - }, - "direction": 0.6133100390434265, - "dropPoint": { - "x": 234.20034790039063, - "y": 170.5682830810547 - }, - "position": { - "x": 202.29611206054688, - "y": 148.1429901123047 - }, - "predictedTarget": { - "x": 292.8595275878906, - "y": 208.75799560546875 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 258.01422119140625, - "y": 187.25732421875 - }, - "direction": 0.6120696663856506, - "dropPoint": { - "x": 234.18479919433594, - "y": 170.5289764404297 - }, - "position": { - "x": 203.5238037109375, - "y": 149.0048370361328 - }, - "predictedTarget": { - "x": 292.74951171875, - "y": 208.71800231933594 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 259.2419128417969, - "y": 188.11917114257813 - }, - "direction": 0.6120696663856506, - "dropPoint": { - "x": 234.17005920410156, - "y": 170.49098205566406 - }, - "position": { - "x": 204.75149536132813, - "y": 149.86668395996094 - }, - "predictedTarget": { - "x": 292.64501953125, - "y": 208.67799377441406 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 260.5169372558594, - "y": 188.91346740722656 - }, - "direction": 0.6108580827713013, - "dropPoint": { - "x": 234.15530395507813, - "y": 170.45513916015625 - }, - "position": { - "x": 205.980224609375, - "y": 150.72703552246094 - }, - "predictedTarget": { - "x": 292.5450134277344, - "y": 208.63800048828125 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 261.74566650390625, - "y": 189.77381896972656 - }, - "direction": 0.6108580827713013, - "dropPoint": { - "x": 234.14137268066406, - "y": 170.42059326171875 - }, - "position": { - "x": 207.20895385742188, - "y": 151.58738708496094 - }, - "predictedTarget": { - "x": 292.44500732421875, - "y": 208.59800720214844 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 263.0204162597656, - "y": 190.56837463378906 - }, - "direction": 0.6096786856651306, - "dropPoint": { - "x": 234.12734985351563, - "y": 170.38829040527344 - }, - "position": { - "x": 208.43870544433594, - "y": 152.4462890625 - }, - "predictedTarget": { - "x": 292.34503173828125, - "y": 208.55799865722656 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 264.25018310546875, - "y": 191.42727661132813 - }, - "direction": 0.6096786856651306, - "dropPoint": { - "x": 234.11378479003906, - "y": 170.35699462890625 - }, - "position": { - "x": 209.66845703125, - "y": 153.30519104003906 - }, - "predictedTarget": { - "x": 292.2450256347656, - "y": 208.51800537109375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 265.5244140625, - "y": 192.22238159179688 - }, - "direction": 0.6085359454154968, - "dropPoint": { - "x": 234.10043334960938, - "y": 170.32797241210938 - }, - "position": { - "x": 210.89918518066406, - "y": 154.16268920898438 - }, - "predictedTarget": { - "x": 292.14501953125, - "y": 208.47799682617188 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 266.7551574707031, - "y": 193.0798797607422 - }, - "direction": 0.6085359454154968, - "dropPoint": { - "x": 234.1614227294922, - "y": 170.3545684814453 - }, - "position": { - "x": 212.12991333007813, - "y": 155.0201873779297 - }, - "predictedTarget": { - "x": 292.0450134277344, - "y": 208.43800354003906 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 267.9858703613281, - "y": 193.9373779296875 - }, - "direction": 0.6085359454154968, - "dropPoint": { - "x": 234.22006225585938, - "y": 170.38116455078125 - }, - "position": { - "x": 213.3606414794922, - "y": 155.877685546875 - }, - "predictedTarget": { - "x": 291.94500732421875, - "y": 208.39801025390625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 269.2703552246094, - "y": 194.71762084960938 - }, - "direction": 0.6071534752845764, - "dropPoint": { - "x": 234.2768096923828, - "y": 170.40794372558594 - }, - "position": { - "x": 214.59255981445313, - "y": 156.73348999023438 - }, - "predictedTarget": { - "x": 291.84503173828125, - "y": 208.35800170898438 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 270.5022888183594, - "y": 195.57342529296875 - }, - "direction": 0.6071534752845764, - "dropPoint": { - "x": 234.3319549560547, - "y": 170.43338012695313 - }, - "position": { - "x": 215.82447814941406, - "y": 157.58929443359375 - }, - "predictedTarget": { - "x": 291.7450256347656, - "y": 208.31800842285156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 271.73419189453125, - "y": 196.42922973632813 - }, - "direction": 0.6071534752845764, - "dropPoint": { - "x": 234.38511657714844, - "y": 170.45896911621094 - }, - "position": { - "x": 217.056396484375, - "y": 158.44509887695313 - }, - "predictedTarget": { - "x": 291.64501953125, - "y": 208.2725067138672 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 273.017333984375, - "y": 197.211181640625 - }, - "direction": 0.6058332920074463, - "dropPoint": { - "x": 234.43600463867188, - "y": 170.48455810546875 - }, - "position": { - "x": 218.28944396972656, - "y": 159.2992706298828 - }, - "predictedTarget": { - "x": 291.5450134277344, - "y": 208.22250366210938 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 274.2503967285156, - "y": 198.06536865234375 - }, - "direction": 0.6058332920074463, - "dropPoint": { - "x": 234.48573303222656, - "y": 170.50897216796875 - }, - "position": { - "x": 219.52249145507813, - "y": 160.1534423828125 - }, - "predictedTarget": { - "x": 291.44500732421875, - "y": 208.17250061035156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 275.4834289550781, - "y": 198.91952514648438 - }, - "direction": 0.6058332920074463, - "dropPoint": { - "x": 234.53326416015625, - "y": 170.5333251953125 - }, - "position": { - "x": 220.7555389404297, - "y": 161.0076141357422 - }, - "predictedTarget": { - "x": 291.3450012207031, - "y": 208.12249755859375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 276.76513671875, - "y": 199.70335388183594 - }, - "direction": 0.6045769453048706, - "dropPoint": { - "x": 234.57870483398438, - "y": 170.55772399902344 - }, - "position": { - "x": 221.98965454101563, - "y": 161.8602294921875 - }, - "predictedTarget": { - "x": 291.2450256347656, - "y": 208.072509765625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 277.999267578125, - "y": 200.55596923828125 - }, - "direction": 0.6045769453048706, - "dropPoint": { - "x": 234.62277221679688, - "y": 170.58079528808594 - }, - "position": { - "x": 223.22377014160156, - "y": 162.7128448486328 - }, - "predictedTarget": { - "x": 291.14501953125, - "y": 208.0225067138672 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 279.27374267578125, - "y": 201.35009765625 - }, - "direction": 0.6035329103469849, - "dropPoint": { - "x": 234.60577392578125, - "y": 170.55886840820313 - }, - "position": { - "x": 224.45877075195313, - "y": 163.56417846679688 - }, - "predictedTarget": { - "x": 291.0450134277344, - "y": 207.97250366210938 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 280.50872802734375, - "y": 202.20143127441406 - }, - "direction": 0.6035329103469849, - "dropPoint": { - "x": 234.58944702148438, - "y": 170.53819274902344 - }, - "position": { - "x": 225.6937713623047, - "y": 164.41551208496094 - }, - "predictedTarget": { - "x": 290.94500732421875, - "y": 207.92250061035156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 281.7925109863281, - "y": 202.98193359375 - }, - "direction": 0.6022695899009705, - "dropPoint": { - "x": 234.57305908203125, - "y": 170.51979064941406 - }, - "position": { - "x": 226.9298553466797, - "y": 165.26528930664063 - }, - "predictedTarget": { - "x": 290.8450012207031, - "y": 207.87249755859375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 283.0285949707031, - "y": 203.8317108154297 - }, - "direction": 0.6022695899009705, - "dropPoint": { - "x": 234.55758666992188, - "y": 170.50228881835938 - }, - "position": { - "x": 228.1659393310547, - "y": 166.1150665283203 - }, - "predictedTarget": { - "x": 290.7450256347656, - "y": 207.822509765625 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 284.3117980957031, - "y": 204.61285400390625 - }, - "direction": 0.601046621799469, - "dropPoint": { - "x": 234.5420684814453, - "y": 170.4870147705078 - }, - "position": { - "x": 229.40306091308594, - "y": 166.9633331298828 - }, - "predictedTarget": { - "x": 290.6505126953125, - "y": 207.7725067138672 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 285.5489196777344, - "y": 205.46112060546875 - }, - "direction": 0.601046621799469, - "dropPoint": { - "x": 234.52745056152344, - "y": 170.47256469726563 - }, - "position": { - "x": 230.6401824951172, - "y": 167.8115997314453 - }, - "predictedTarget": { - "x": 290.5605163574219, - "y": 207.72250366210938 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 286.831298828125, - "y": 206.24327087402344 - }, - "direction": 0.5998696684837341, - "dropPoint": { - "x": 234.51283264160156, - "y": 170.4602813720703 - }, - "position": { - "x": 231.8782958984375, - "y": 168.6584014892578 - }, - "predictedTarget": { - "x": 290.47052001953125, - "y": 207.67250061035156 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 288.0694274902344, - "y": 207.09007263183594 - }, - "direction": 0.5998696684837341, - "dropPoint": { - "x": 234.49920654296875, - "y": 170.44879150390625 - }, - "position": { - "x": 233.1164093017578, - "y": 169.5052032470703 - }, - "predictedTarget": { - "x": 290.3804931640625, - "y": 207.62249755859375 - }, - "state": "MOVING", - "targetIndex": 0 - }, - { - "aimPoint": { - "x": 289.35064697265625, - "y": 207.87374877929688 - }, - "direction": 0.598746657371521, - "dropPoint": { - "x": 234.48561096191406, - "y": 170.4394073486328 - }, - "position": { - "x": 234.35546875, - "y": 170.35061645507813 - }, - "predictedTarget": { - "x": 290.2904968261719, - "y": 207.572509765625 - }, - "state": "MOVING", - "targetIndex": 0 - } - ], - "totalSteps": 241 -} \ No newline at end of file