1use eframe::{
7 egui::{self, Widget},
8 epi,
9};
10use libcaliph::routines;
11
12#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
13#[cfg_attr(feature = "persistence", serde(default))]
14pub struct TemplateApp {
15 ph4: f64,
16 ph10: f64,
17 temperature: f64,
18 slope: f64,
19 offset: f64,
20 ph_measured: f64,
21 calibrated_ph: f64,
22}
23
24impl Default for TemplateApp {
25 fn default() -> Self {
26 Self {
27 ph4: 4.01,
29 ph10: 10.01,
30 temperature: 25.0,
31 slope: 1.0,
32 offset: 0.0,
33 ph_measured: 7.0,
34 calibrated_ph: 7.0,
35 }
36 }
37}
38
39impl epi::App for TemplateApp {
40 fn name(&self) -> &str {
41 "Caliphui"
42 }
43
44 fn setup(
46 &mut self,
47 _ctx: &egui::CtxRef,
48 _frame: &mut epi::Frame<'_>,
49 _storage: Option<&dyn epi::Storage>,
50 ) {
51 let _spacing_mut = egui::style::Spacing {
57 item_spacing: egui::Vec2::new(8.0, 12.0),
58 window_padding: egui::Vec2::splat(6.0),
59 button_padding: egui::Vec2::new(4.0, 1.0),
60 indent: 18.0, interact_size: egui::Vec2::new(40.0, 18.0),
62 slider_width: 100.0,
63 text_edit_width: 280.0,
64 icon_width: 14.0,
65 icon_spacing: 0.0,
66 tooltip_width: 600.0,
67 combo_height: 200.0,
68 scroll_bar_width: 8.0,
69 indent_ends_with_horizontal_line: false,
70 };
71
72 let mut fonts = egui::FontDefinitions::default();
73
74 fonts.family_and_size.insert(
75 egui::TextStyle::Heading,
76 (egui::FontFamily::Proportional, 26.0),
77 );
78 fonts.family_and_size.insert(
79 egui::TextStyle::Body,
80 (egui::FontFamily::Proportional, 22.0),
81 );
82
83 fonts.family_and_size.insert(
84 egui::TextStyle::Button,
85 (egui::FontFamily::Proportional, 22.0),
86 );
87 fonts.family_and_size.insert(
88 egui::TextStyle::Monospace,
89 (egui::FontFamily::Monospace, 22.0),
90 );
91
92 _ctx.set_fonts(fonts);
93 #[cfg(feature = "persistence")]
96 if let Some(storage) = _storage {
97 *self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
98 }
99
100 self.calibrated_ph =
101 update_conversion(&self.ph_measured, &mut self.slope, &mut self.offset);
102 }
103
104 #[cfg(feature = "persistence")]
106 fn save(&mut self, storage: &mut dyn epi::Storage) {
107 epi::set_value(storage, epi::APP_KEY, self);
108 }
109
110 fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
112 let Self {
113 ph4,
114 ph10,
115 temperature,
116 slope,
117 offset,
118 ph_measured,
119 calibrated_ph,
120 } = self;
121
122 egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
123 egui::widgets::global_dark_light_mode_switch(ui);
124 });
125
126 egui::CentralPanel::default().show(ctx, |ui| {
127 ui.heading("Calibrate");
128 let ph4_slider = egui::Slider::new(ph4, 0.0..=14.0)
129 .text("pH 4")
130 .fixed_decimals(2);
131 let ph4_response = ph4_slider.ui(ui);
132 if ph4_response.changed() {
133 update_calibration(ph4, ph10, temperature, slope, offset);
134 *calibrated_ph = update_conversion(ph_measured, slope, offset);
135 }
136
137 let ph10_slider = egui::Slider::new(ph10, 0.0..=14.0)
138 .text("pH 10")
139 .fixed_decimals(2);
140 let ph10_response = ph10_slider.ui(ui);
141 if ph10_response.changed() {
142 update_calibration(ph4, ph10, temperature, slope, offset);
143 *calibrated_ph = update_conversion(ph_measured, slope, offset);
144 }
145
146 let temperature_slider = egui::Slider::new(temperature, 0.0..=100.0)
147 .suffix(" ˚C")
148 .text("T")
149 .fixed_decimals(1);
150 let temperature_response = temperature_slider.ui(ui);
151 if temperature_response.changed() {
152 update_calibration(ph4, ph10, temperature, slope, offset);
153 *calibrated_ph = update_conversion(ph_measured, slope, offset);
154 }
155
156 ui.add_space(5.0);
157
158 ui.heading("Convert");
160 let ph_measured_slider = egui::Slider::new(ph_measured, 0.0..=14.0)
161 .text("Input pH")
162 .fixed_decimals(2);
163 let ph_measured_response = ph_measured_slider.ui(ui);
164 if ph_measured_response.changed() {
165 *calibrated_ph = update_conversion(ph_measured, slope, offset);
166 }
167
168 ui.add_space(10.0);
169 ui.heading("Calibrated pH:");
170 ui.add(egui::DragValue::new(calibrated_ph).speed(1.0));
171
172 egui::warn_if_debug_build(ui);
173 });
174
175 if false {
176 egui::Window::new("Window").show(ctx, |ui| {
177 ui.label("Windows can be moved by dragging them.");
178 ui.label("They are automatically sized based on contents.");
179 ui.label("You can turn on resizing and scrolling if you like.");
180 ui.label("You would normally chose either panels OR windows.");
181 });
182 }
183 }
184}
185
186pub fn update_calibration(
187 ph4: &f64,
188 ph10: &f64,
189 temperature: &f64,
190 slope: &mut f64,
191 offset: &mut f64,
192) {
193 let calibration = routines::ph_calibration(&[*ph4, *ph10], temperature);
194 *slope = calibration.slope;
195 *offset = calibration.offset;
196}
197
198pub fn update_conversion(ph_measured: &f64, slope: &mut f64, offset: &mut f64) -> f64 {
199 routines::ph_convert(ph_measured, &[*slope, *offset])
200}