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:
parent
6da6fd8326
commit
45ea316bef
19
config.toml
19
config.toml
|
@ -4,10 +4,6 @@ output_dir = "images"
|
||||||
width = 1600
|
width = 1600
|
||||||
height = 900
|
height = 900
|
||||||
|
|
||||||
max_time = 45
|
|
||||||
max_weight = 35
|
|
||||||
max_flow = 5
|
|
||||||
|
|
||||||
|
|
||||||
[shots]
|
[shots]
|
||||||
|
|
||||||
|
@ -123,22 +119,37 @@ title = "Laura 19.07.2022 #1"
|
||||||
[charts.0]
|
[charts.0]
|
||||||
title = "Elisabeth Shots"
|
title = "Elisabeth Shots"
|
||||||
shots = [0, 1, 10, 14, 15]
|
shots = [0, 1, 10, 14, 15]
|
||||||
|
max_time = 45
|
||||||
|
max_weight = 35
|
||||||
|
max_flow = 5
|
||||||
|
|
||||||
[charts.1]
|
[charts.1]
|
||||||
title = "Laura Shots"
|
title = "Laura Shots"
|
||||||
shots = [2, 3, 4, 5, 6, 7, 8, 9, 12, 16, 17, 18, 19, 20, 21, 22, 23]
|
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]
|
[charts.2]
|
||||||
title = "Josephine Shots"
|
title = "Josephine Shots"
|
||||||
shots = [11, 13]
|
shots = [11, 13]
|
||||||
|
max_time = 45
|
||||||
|
max_weight = 35
|
||||||
|
max_flow = 5
|
||||||
|
|
||||||
[charts.3]
|
[charts.3]
|
||||||
title = "First Shots"
|
title = "First Shots"
|
||||||
shots = [0, 1, 2, 4, 5, 7, 9, 10, 12, 13, 14, 16, 18, 20, 23]
|
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]
|
[charts.4]
|
||||||
title = "Second Shots"
|
title = "Second Shots"
|
||||||
shots = [3, 6, 8, 15, 17, 19, 21]
|
shots = [3, 6, 8, 15, 17, 19, 21]
|
||||||
|
max_time = 45
|
||||||
|
max_weight = 35
|
||||||
|
max_flow = 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
204
src/main.rs
204
src/main.rs
|
@ -24,10 +24,14 @@ struct Shot {
|
||||||
disable: Option<bool>,
|
disable: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
struct Chart {
|
struct Chart {
|
||||||
title: String,
|
title: String,
|
||||||
shots: Vec<u64>,
|
shots: Vec<u64>,
|
||||||
|
|
||||||
|
max_time: u64,
|
||||||
|
max_weight: u64,
|
||||||
|
max_flow: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -40,10 +44,6 @@ struct Config {
|
||||||
|
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
|
||||||
max_time: u64,
|
|
||||||
max_weight: u64,
|
|
||||||
max_flow: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const WGHT_SHEET: usize = 0;
|
const WGHT_SHEET: usize = 0;
|
||||||
|
@ -144,158 +144,43 @@ fn load_data(path: &str, cutoff: Option<f64>) -> Option<Data> {
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct PlottingState {
|
struct PlottingState {
|
||||||
mean_x: f64,
|
|
||||||
mean_y: f64,
|
|
||||||
std_x: f64,
|
|
||||||
std_y: f64,
|
|
||||||
pitch: f64,
|
|
||||||
roll: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlottingState {
|
impl PlottingState {
|
||||||
fn guassian_pdf(&self, x: f64, y: f64) -> f64 {
|
fn plot<'a, DB: DrawingBackend + 'a>(
|
||||||
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>(
|
|
||||||
&self,
|
&self,
|
||||||
backend: DB,
|
backend: DB,
|
||||||
|
chart: Rc<RefCell<Chart>>,
|
||||||
|
config: Rc<RefCell<Config>>,
|
||||||
) -> Result<(), Box<dyn Error + 'a>> {
|
) -> Result<(), Box<dyn Error + 'a>> {
|
||||||
let root = backend.into_drawing_area();
|
let root_area = backend.into_drawing_area();
|
||||||
|
|
||||||
root.fill(&WHITE)?;
|
root_area.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: >k::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: >k::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();
|
|
||||||
|
|
||||||
let mut ctx = ChartBuilder::on(&root_area)
|
let mut ctx = ChartBuilder::on(&root_area)
|
||||||
.set_label_area_size(LabelAreaPosition::Left, 40)
|
.set_label_area_size(LabelAreaPosition::Left, 40)
|
||||||
.set_label_area_size(LabelAreaPosition::Bottom, 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(
|
.build_cartesian_2d(
|
||||||
0f64..(config.max_time as f64),
|
0f64..(chart.borrow().max_time as f64),
|
||||||
0f64..(config.max_weight as f64),
|
0f64..(chart.borrow().max_weight as f64),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
ctx.configure_mesh()
|
ctx.configure_mesh()
|
||||||
.label_style(("Fira Code", 12))
|
.label_style(("Fira Code", 12))
|
||||||
.draw()
|
.draw()?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
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 palette = ColorPalette::new(shot_count as u32, PaletteType::Random, false);
|
||||||
let mut palette_iter = palette.colors.iter();
|
let mut palette_iter = palette.colors.iter();
|
||||||
|
|
||||||
for shot_nr in chart.1.shots {
|
for shot_nr in &chart.borrow().shots {
|
||||||
if let Some(shot) = config.shots.get(&shot_nr.to_string()) {
|
if let Some(shot) = config.borrow().shots.get(&shot_nr.to_string()) {
|
||||||
// println!("\tShot: {}n", shot.title);
|
// println!("\tShot: {}n", shot.title);
|
||||||
|
|
||||||
if let Some(data) = load_data(
|
if let Some(data) = load_data(
|
||||||
&format!("{}/{}", config.shot_dir, shot.filename),
|
&format!("{}/{}", config.borrow().shot_dir, shot.filename),
|
||||||
shot.cutoff,
|
shot.cutoff,
|
||||||
) {
|
) {
|
||||||
if let Some(disable) = shot.disable {
|
if let Some(disable) = shot.disable {
|
||||||
|
@ -346,13 +231,58 @@ fn main() {
|
||||||
.label_font(("Fira Code", 12))
|
.label_font(("Fira Code", 12))
|
||||||
.draw()
|
.draw()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
|
root_area.present()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_ui(app: >k::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 =
|
let application =
|
||||||
gtk::Application::new(Some("de.dustvoice.beanconqueror-gtk"), Default::default());
|
gtk::Application::new(Some("de.dustvoice.beanconqueror-gtk"), Default::default());
|
||||||
|
|
||||||
application.connect_activate(|app| {
|
application.connect_activate(move |app| {
|
||||||
build_ui(app);
|
build_ui(&app)
|
||||||
});
|
});
|
||||||
|
|
||||||
application.run();
|
application.run();
|
||||||
|
|
Loading…
Reference in New Issue