#pragma once #include "FluidSim/Scenario.hpp" #include "FluidSim/ScenarioCatalog.hpp" #include "FluidSim/TwoStrokeScenario.hpp" #include #include namespace fluidsim::port { // Shared desktop controls and snapshots. Each simulation owns its state lock. class DesktopScenario { public: explicit DesktopScenario(const ScenarioExample& e) : DesktopScenario(e.kind,e.two,e.exhaust,e.junctions,e.engine,e.title) {} explicit DesktopScenario(ScenarioKind kind, TwoStrokeConfig config = {}, ExhaustConfig exhaust = {}, JunctionConfig junctions = {}, EngineConfig engine = {}, std::string name = {}) : name_(std::move(name)) { if (kind == ScenarioKind::TwoStroke) two_ = std::make_unique(44100, config); else { if(!engine.cylinders)engine=preset_engine_definition(kind==ScenarioKind::SaabB205? FourStrokePreset::SaabB205:kind==ScenarioKind::AmericanV8?FourStrokePreset::AmericanV8:FourStrokePreset::Twin).engine; four_ = std::make_unique(44100,std::move(engine),exhaust,junctions); } } AudioFrame process() { if (four_) return four_->process(); const std::lock_guard lock(mutex_); return two_->process(); } void set_controls(float pedal, float load, float clutch) { visit([&](auto& scenario) { scenario.set_controls(pedal, load, clutch); }); } void set_starter(bool engaged) { visit([&](auto& scenario) { scenario.set_starter(engaged); }); } void shift_up() { visit([](auto& scenario) { scenario.shift_up(); }); } void shift_down() { visit([](auto& scenario) { scenario.shift_down(); }); } void set_neutral() { visit([](auto& scenario) { scenario.set_neutral(); }); } void set_pipe_solver(PipeSolver solver) { visit([&](auto& scenario) { scenario.set_pipe_solver(solver); }); } void set_audio_scene(OutdoorScene scene) { visit([&](auto& scenario) { scenario.set_audio_scene(scene); }); } void set_listener(AudioListener listener) { visit([&](auto& scenario) { scenario.set_listener(listener); }); } ScenarioSnapshot snapshot() const { if (four_) { auto s=four_->snapshot();s.example_name=name_;return s; } const std::lock_guard lock(mutex_); const auto source = two_->snapshot(); ScenarioSnapshot s; s.example_name=name_; s.pipe_solver=two_->pipes.front().solver; s.two_stroke = true; s.cylinder_angles[0]=two_->crankshaft.angle; s.cylinder_count = 1; s.racing_exhaust = two_->configuration.exhaust == TwoStrokeExhaust::Racing; s.rpm = source.rpm; s.speed_kmh = two_->vehicle.speed * 3.6f; s.kinetic_energy = two_->vehicle.kinetic_energy(); s.gear = two_->vehicle.current_gear; s.clutch = two_->clutch; s.average_power = two_->crankshaft.average_power; s.average_torque = two_->crankshaft.average_torque; s.cylinder_pressure1 = source.cylinder_pressure; s.cylinder_temperature1 = two_->cylinder.temperature(); s.cylinder_piston_fraction1 = (two_->cylinder.volume - two_->cylinder.clearance_volume()) / two_->cylinder.swept_volume(); s.crankcase_pressure = source.crankcase_pressure; s.intake_pressure = source.intake_pressure; s.exhaust_pressure = two_->pipes[3].state(0).pressure; s.throttle = source.throttle; s.pedal = source.pedal; s.target_afr = two_->carburetor.indicated_afr(); s.valve1_intake_area = source.transfer_area; s.valve1_exhaust_area = source.exhaust_area; s.reed_open = source.reed_open; s.fuel_cut = source.fuel_cut; s.ignition_cut = source.ignition_cut; s.idle_control = two_->ecu.idle_active(); s.rev_limit1 = two_->ecu.rev_limited(); s.audio_scene = source.audio_scene; s.intake_flow = two_->intake_flow; s.exhaust_flow = two_->exhaust_flow; s.capture_pipes(two_->pipes); return s; } private: template void visit(Function&& function) { const std::lock_guard lock(mutex_); if (four_) function(*four_); else function(*two_); } std::string name_; std::unique_ptr four_; std::unique_ptr two_; mutable std::mutex mutex_; }; } // namespace fluidsim::port