Make some changes to gtk beanconqueror GUI part, but prepare for switching to dioxus or flutter with the html5 canvas backend for plotters

This commit is contained in:
David Holland 2022-11-02 12:40:10 +01:00
parent 6da6fd8326
commit 45ea316bef
Signed by: DustVoice
GPG Key ID: 47068995A14EDCA9
2 changed files with 82 additions and 141 deletions

View File

@ -4,10 +4,6 @@ output_dir = "images"
width = 1600
height = 900
max_time = 45
max_weight = 35
max_flow = 5
[shots]
@ -123,22 +119,37 @@ title = "Laura 19.07.2022 #1"
[charts.0]
title = "Elisabeth Shots"
shots = [0, 1, 10, 14, 15]
max_time = 45
max_weight = 35
max_flow = 5
[charts.1]
title = "Laura Shots"
shots = [2, 3, 4, 5, 6, 7, 8, 9, 12, 16, 17, 18, 19, 20, 21, 22, 23]
max_time = 45
max_weight = 35
max_flow = 5
[charts.2]
title = "Josephine Shots"
shots = [11, 13]
max_time = 45
max_weight = 35
max_flow = 5
[charts.3]
title = "First Shots"
shots = [0, 1, 2, 4, 5, 7, 9, 10, 12, 13, 14, 16, 18, 20, 23]
max_time = 45
max_weight = 35
max_flow = 5
[charts.4]
title = "Second Shots"
shots = [3, 6, 8, 15, 17, 19, 21]
max_time = 45
max_weight = 35
max_flow = 5

View File

@ -24,10 +24,14 @@ struct Shot {
disable: Option<bool>,
}
#[derive(Deserialize)]
#[derive(Clone, Deserialize)]
struct Chart {
title: String,
shots: Vec<u64>,
max_time: u64,
max_weight: u64,
max_flow: u64,
}
#[derive(Deserialize)]
@ -40,10 +44,6 @@ struct Config {
width: u32,
height: u32,
max_time: u64,
max_weight: u64,
max_flow: u64,
}
const WGHT_SHEET: usize = 0;
@ -144,158 +144,43 @@ fn load_data(path: &str, cutoff: Option<f64>) -> Option<Data> {
#[derive(Clone, Copy)]
struct PlottingState {
mean_x: f64,
mean_y: f64,
std_x: f64,
std_y: f64,
pitch: f64,
roll: f64,
}
impl PlottingState {
fn guassian_pdf(&self, x: f64, y: f64) -> f64 {
let x_diff = (x - self.mean_x) / self.std_x;
let y_diff = (y - self.mean_y) / self.std_y;
let exponent = -(x_diff * x_diff + y_diff * y_diff) / 2.0;
let denom = (2.0 * std::f64::consts::PI / self.std_x / self.std_y).sqrt();
let gaussian_pdf = 1.0 / denom;
gaussian_pdf * exponent.exp()
}
fn plot_pdf<'a, DB: DrawingBackend + 'a>(
fn plot<'a, DB: DrawingBackend + 'a>(
&self,
backend: DB,
chart: Rc<RefCell<Chart>>,
config: Rc<RefCell<Config>>,
) -> Result<(), Box<dyn Error + 'a>> {
let root = backend.into_drawing_area();
let root_area = backend.into_drawing_area();
root.fill(&WHITE)?;
let mut chart = ChartBuilder::on(&root).build_cartesian_3d(
-10.0f64..10.0,
0.0f64..1.2,
-10.0f64..10.0,
)?;
chart.with_projection(|mut p| {
p.pitch = self.pitch;
p.yaw = self.roll;
p.scale = 0.7;
p.into_matrix() // build the projection matrix
});
chart
.configure_axes()
.light_grid_style(BLACK.mix(0.15))
.max_light_lines(3)
.draw()?;
let self_cloned = self.clone();
chart.draw_series(
SurfaceSeries::xoz(
(-50..=50).map(|x| x as f64 / 5.0),
(-50..=50).map(|x| x as f64 / 5.0),
move |x, y| self_cloned.guassian_pdf(x, y),
)
.style_func(&|&v| (&HSLColor(240.0 / 360.0 - 240.0 / 360.0 * v, 1.0, 0.7)).into()),
)?;
root.present()?;
Ok(())
}
}
fn build_ui(app: &gtk::Application) {
let builder = gtk::Builder::from_string(UI_SOURCE);
let window: gtk::Window = builder
.object::<gtk::Window>("MainWindow")
.unwrap();
window.set_title(Some("Beanconqueror GUI (gtk4)"));
let drawing_area: gtk::DrawingArea = builder.object("MainDrawingArea").unwrap();
let pitch_scale = builder.object::<gtk::Scale>("PitchScale").unwrap();
let yaw_scale = builder.object::<gtk::Scale>("YawScale").unwrap();
let mean_x_scale = builder.object::<gtk::Scale>("MeanXScale").unwrap();
let mean_y_scale = builder.object::<gtk::Scale>("MeanYScale").unwrap();
let std_x_scale = builder.object::<gtk::Scale>("SDXScale").unwrap();
let std_y_scale = builder.object::<gtk::Scale>("SDYScale").unwrap();
let app_state = Rc::new(RefCell::new(PlottingState {
mean_x: mean_x_scale.value(),
mean_y: mean_y_scale.value(),
std_x: std_x_scale.value(),
std_y: std_y_scale.value(),
pitch: pitch_scale.value(),
roll: yaw_scale.value(),
}));
window.set_application(Some(app));
let state_cloned = app_state.clone();
drawing_area.set_draw_func(move |widget, cr, _i, _j| {
let state = state_cloned.borrow().clone();
let w = widget.allocated_width();
let h = widget.allocated_height();
let backend = CairoBackend::new(cr, (w as u32, h as u32)).unwrap();
state.plot_pdf(backend).unwrap();
});
let handle_change =
|what: &gtk::Scale, how: Box<dyn Fn(&mut PlottingState) -> &mut f64 + 'static>| {
let app_state = app_state.clone();
let drawing_area = drawing_area.clone();
what.connect_value_changed(move |target| {
let mut state = app_state.borrow_mut();
*how(&mut *state) = target.value();
drawing_area.queue_draw();
});
};
handle_change(&pitch_scale, Box::new(|s| &mut s.pitch));
handle_change(&yaw_scale, Box::new(|s| &mut s.roll));
handle_change(&mean_x_scale, Box::new(|s| &mut s.mean_x));
handle_change(&mean_y_scale, Box::new(|s| &mut s.mean_y));
handle_change(&std_x_scale, Box::new(|s| &mut s.std_x));
handle_change(&std_y_scale, Box::new(|s| &mut s.std_y));
window.show();
}
fn main() {
let config_file = fs::read_to_string("config.toml").expect("Can't read config.toml");
let config: Config = toml::from_str(&config_file).expect("Can't deserialize config.toml");
for chart in config.charts {
// println!("Chart: {}\n", chart.1.title);
let title = format!("{}/{}.svg", config.output_dir, chart.1.title);
let root_area = SVGBackend::new(&title, (config.width, config.height)).into_drawing_area();
root_area.fill(&WHITE).unwrap();
root_area.fill(&WHITE)?;
let mut ctx = ChartBuilder::on(&root_area)
.set_label_area_size(LabelAreaPosition::Left, 40)
.set_label_area_size(LabelAreaPosition::Bottom, 40)
.caption(&chart.1.title, ("Fira Code", 24))
.caption(&chart.borrow().title, ("Fira Code", 24))
.build_cartesian_2d(
0f64..(config.max_time as f64),
0f64..(config.max_weight as f64),
)
.unwrap();
0f64..(chart.borrow().max_time as f64),
0f64..(chart.borrow().max_weight as f64),
)?;
ctx.configure_mesh()
.label_style(("Fira Code", 12))
.draw()
.unwrap();
.draw()?;
let shot_count = chart.1.shots.len();
let shot_count = chart.borrow().shots.len();
let palette = ColorPalette::new(shot_count as u32, PaletteType::Random, false);
let mut palette_iter = palette.colors.iter();
for shot_nr in chart.1.shots {
if let Some(shot) = config.shots.get(&shot_nr.to_string()) {
for shot_nr in &chart.borrow().shots {
if let Some(shot) = config.borrow().shots.get(&shot_nr.to_string()) {
// println!("\tShot: {}n", shot.title);
if let Some(data) = load_data(
&format!("{}/{}", config.shot_dir, shot.filename),
&format!("{}/{}", config.borrow().shot_dir, shot.filename),
shot.cutoff,
) {
if let Some(disable) = shot.disable {
@ -346,13 +231,58 @@ fn main() {
.label_font(("Fira Code", 12))
.draw()
.unwrap();
}
root_area.present()?;
Ok(())
}
}
fn build_ui(app: &gtk::Application) {
let builder = gtk::Builder::from_string(UI_SOURCE);
let config_file = fs::read_to_string("config.toml").expect("Can't read config.toml");
let config: Rc<RefCell<Config>> = Rc::new(RefCell::new(toml::from_str(&config_file).expect("Can't deserialize config.toml")));
let chart_values = config.clone().borrow().charts;
for value in chart_values {
let config_cloned = config.clone();
let chart = Rc::new(RefCell::new(value.1));
let window: gtk::Window = builder
.object::<gtk::Window>("MainWindow")
.unwrap();
window.set_title(Some(&chart.clone().borrow().title));
let drawing_area: gtk::DrawingArea = builder.object("MainDrawingArea").unwrap();
let app_state = Rc::new(RefCell::new(PlottingState {}));
window.set_application(Some(app));
{
let state_cloned = app_state.clone();
let config_cloned = Rc::clone(&config);
let chart_cloned = Rc::clone(&chart);
drawing_area.set_draw_func(move |widget, cr, _i, _j| {
let w = widget.allocated_width();
let h = widget.allocated_height();
let backend = CairoBackend::new(cr, (w as u32, h as u32)).unwrap();
(*state_cloned).borrow_mut().plot(backend, chart_cloned, config_cloned).unwrap();
});
}
window.show();
}
}
fn main() {
let application =
gtk::Application::new(Some("de.dustvoice.beanconqueror-gtk"), Default::default());
application.connect_activate(|app| {
build_ui(app);
application.connect_activate(move |app| {
build_ui(&app)
});
application.run();