use crate::bevy::diagnostic::FrameTimeDiagnosticsPlugin;
use bevy::render::RenderPlugin;
use crate::engine::{schedule::Schedule, state::State};
use crate::visualization::{
asset_handle_factory::AssetHandleFactory,
simulation_descriptor::SimulationDescriptor,
systems::{
camera_system::camera_system, init_system::init_system, renderer_system::renderer_system,
simulation_system::simulation_system, ui_system::ui_system,
},
visualization_state::VisualizationState,
wrappers::{ActiveSchedule, ActiveState, Initializer},
};
use bevy::{prelude::*, DefaultPlugins};
use bevy_egui::{EguiPlugin, EguiPrimaryContextPass};
use bevy_prototype_lyon::prelude::ShapePlugin;
use std::sync::{Arc, Mutex};
pub struct Visualization {
width: f32,
height: f32,
sim_width: f32,
sim_height: f32,
window_name: &'static str,
background_color: Color,
}
impl Visualization {
pub fn with_window_dimensions(mut self, width: f32, height: f32) -> Visualization {
self.width = width;
self.height = height;
self
}
pub fn with_simulation_dimensions(mut self, width: f32, height: f32) -> Visualization {
self.sim_width = width;
self.sim_height = height;
self
}
pub fn with_name(mut self, name: &'static str) -> Visualization {
self.window_name = name;
self
}
pub fn with_background_color(mut self, color: Color) -> Visualization {
self.background_color = color;
self
}
pub fn start<I: VisualizationState<S> + 'static + bevy::prelude::Resource + Clone, S: State>(
self,
init_call: I,
state: S,
) {
let mut app_builder = self.setup(init_call, state);
app_builder.run();
}
pub fn setup<I: VisualizationState<S> + Clone + 'static + bevy::prelude::Resource, S: State>(
&self,
init_call: I,
mut state: S,
) -> App {
let mut app = App::new();
let mut schedule = Schedule::new();
state.init(&mut schedule);
let cloned_init_call = init_call.clone();
app.add_plugins(DefaultPlugins.set(RenderPlugin {
..default()
}))
.add_plugins(EguiPlugin::default());
app.add_plugins(ShapePlugin);
app.insert_resource(SimulationDescriptor {
title: self
.window_name
.parse()
.expect("Error: can't parse window name"),
width: self.sim_width,
height: self.sim_height,
center_x: (self.width * 0.5) - (self.width - self.sim_width as f32) / 2.,
center_y: (self.height * 0.5) - (self.height - self.sim_height as f32) / 2.,
paused: true,
ui_width: 300.,
})
.insert_resource(ClearColor(self.background_color))
.insert_resource(AssetHandleFactory::new())
.insert_resource(init_call)
.insert_resource(ActiveState(Arc::new(Mutex::new(state))))
.insert_resource(ActiveSchedule(Arc::new(Mutex::new(schedule))))
.insert_resource(Initializer(cloned_init_call, Default::default()))
.add_systems(FixedPreUpdate, simulation_system::<S>)
.add_systems(EguiPrimaryContextPass, ui_system::<I, S>)
.add_systems(FixedPostUpdate, renderer_system::<I, S>)
.insert_resource(Time::<Fixed>::default())
.add_systems(Startup, init_system::<I, S>)
.add_plugins(FrameTimeDiagnosticsPlugin::default())
.add_systems(Update, camera_system);
app
}
}
impl Default for Visualization {
fn default() -> Self {
Visualization {
width: 600.,
height: 300.,
sim_width: 300.,
sim_height: 300.,
window_name: env!("CARGO_PKG_NAME"),
background_color: Color::srgb(1., 1., 1.),
}
}
}