Display options for plate

This commit is contained in:
Emilia Allison 2025-01-12 19:22:57 -05:00
parent e6d80ab8a3
commit 418bf4a79e
Signed by: emilia
GPG Key ID: 05D5D1107E5100A1
2 changed files with 111 additions and 32 deletions

View File

@ -5,7 +5,7 @@ use eframe::egui::{self};
use crate::main_state::{construct_fake_mainstate, MainState};
use crate::modals::{self, ModalState};
use crate::plate::{add_plate, PlateUiState};
use crate::plate::{add_plate, PlateDisplayOptions, PlateUiState};
use crate::transfer_menu::{transfer_menu, CurrentTransferState, TransferMenuState};
use crate::tree::tree;
@ -13,12 +13,14 @@ use crate::tree::tree;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct MainWindowState {
show_side_panel: bool,
plate_display_options: PlateDisplayOptions,
}
impl Default for MainWindowState {
fn default() -> Self {
Self {
show_side_panel: true,
plate_display_options: PlateDisplayOptions::default(),
}
}
}
@ -101,9 +103,15 @@ impl eframe::App for PlateToolEframe {
});
ui.menu_button("Options", |ui| {
ui.menu_button("Styles", |ui| {
if ui.button("Toggle transfer hashes").clicked() {}
if ui.button("Toggle volume heatmap").clicked() {}
if ui.button("Toggle current coordinates view").clicked() {}
if ui.button("Toggle transfer hashes").clicked() {
self.main_window_state.plate_display_options.show_transfer_hashes ^= true;
}
if ui.button("Toggle volume heatmap").clicked() {
self.main_window_state.plate_display_options.show_volume_heatmap ^= true;
}
if ui.button("Toggle current coordinates view").clicked() {
self.main_window_state.plate_display_options.show_coordinates ^= true;
}
});
ui.menu_button("Exports", |ui| {
if ui.button("Change CSV export type").clicked() {}
@ -169,6 +177,7 @@ impl eframe::App for PlateToolEframe {
Some(&self.current_transfer_state),
ui,
self.source_plate_state.lock().unwrap().deref_mut(),
self.main_window_state.plate_display_options,
);
}
if let Some(destination_pi) =
@ -184,6 +193,7 @@ impl eframe::App for PlateToolEframe {
Some(&self.current_transfer_state),
ui,
self.destination_plate_state.lock().unwrap().deref_mut(),
self.main_window_state.plate_display_options,
);
}
});

View File

@ -1,4 +1,5 @@
use eframe::egui::{self, pos2, Color32, Rounding};
use eframe::glow::OFFSET;
use plate_tool_lib::plate::PlateFormat;
use plate_tool_lib::transfer_region::Region;
use plate_tool_lib::uuid::Uuid;
@ -37,11 +38,34 @@ impl Default for PlateUiState {
struct WellInfo {
volume: f32,
color: [f64; 3],
highlight: bool,
fill: bool,
}
impl WellInfo {
fn new(volume: f32, color: [f64; 3]) -> Self {
WellInfo { volume, color }
WellInfo {
volume,
color,
highlight: false,
fill: true,
}
}
}
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub struct PlateDisplayOptions {
pub show_transfer_hashes: bool,
pub show_volume_heatmap: bool,
pub show_coordinates: bool,
}
impl Default for PlateDisplayOptions {
fn default() -> Self {
Self {
show_transfer_hashes: true,
show_volume_heatmap: false,
show_coordinates: true,
}
}
}
@ -143,9 +167,9 @@ fn calculate_shading_for_wells(
};
if let Some(wells) = cache_result {
for well in wells.iter().filter(|x| x.row <= rows && x.col <= columns) {
if let Some(Some(mut x)) = well_infos
.get_mut((well.row - 1) as usize * columns as usize + (well.col - 1) as usize)
{
if let Some(Some(mut x)) = well_infos.get_mut(
(well.row - 1) as usize * columns as usize + (well.col - 1) as usize,
) {
x.volume += 5.0;
x.color = PALETTE.get_ordered(transfer.id, ordered_ids);
} else {
@ -173,6 +197,20 @@ fn f64_to_color32(x: [f64; 3]) -> Color32 {
Color32::from_rgb(r, g, b)
}
fn draw_cross(painter: &egui::Painter, center: egui::Pos2, radius: f32, stroke: egui::Stroke) {
// Generate points on circle
const OFFSET_ARRAY_X: [f32; 4] = [0.71, -0.71, -0.71, 0.71]; // == sin(2pi/8) == cos(2pi/8)
const OFFSET_ARRAY_Y: [f32; 4] = [0.71, 0.71, -0.71, -0.71];
let radius_adjusted_array_x: [f32; 4] = core::array::from_fn(|x| OFFSET_ARRAY_X[x] * radius);
let radius_adjusted_array_y: [f32; 4] = core::array::from_fn(|y| OFFSET_ARRAY_Y[y] * radius);
let xs: [f32; 4] = core::array::from_fn(|x| radius_adjusted_array_x[x] + center.x);
let ys: [f32; 4] = core::array::from_fn(|y| radius_adjusted_array_y[y] + center.y);
let pts: [egui::Pos2; 4] = core::array::from_fn(|i| egui::Pos2::new(xs[i], ys[i]));
painter.line_segment([pts[0], pts[2]], stroke);
painter.line_segment([pts[1], pts[3]], stroke);
}
fn add_plate_sub(
size: egui::Vec2,
rows: u8,
@ -184,6 +222,7 @@ fn add_plate_sub(
current_transfer_state: Option<&CurrentTransferState>,
ui: &mut egui::Ui,
state: &mut PlateUiState,
display_options: PlateDisplayOptions,
) {
let (response, painter) = ui.allocate_painter(size, egui::Sense::click_and_drag());
@ -288,10 +327,14 @@ fn add_plate_sub(
.and_then(|x| x.lock().ok())
.map(|x| x.volume)
.unwrap_or(0.0);
let color = well_info.map(|x| x.color).unwrap_or([255.0, 255.0, 255.0]);
let fill = well_info.map(|x| x.color).is_some();
*well_info = Some(WellInfo {
color: [255.0, 255.0, 255.0],
color,
volume: 1.0,
fill,
highlight: true,
})
}
}
@ -327,8 +370,13 @@ fn add_plate_sub(
if let Some(well_info) =
well_infos[(c_row - 1) as usize * columns as usize + (c_column - 1) as usize]
{
if well_info.fill {
painter.circle_filled(center, radius * 0.80, f64_to_color32(well_info.color));
}
if well_info.highlight && display_options.show_transfer_hashes {
draw_cross(&painter, center, radius * 0.80, *STROKE_CURRENT);
}
}
//
// Draw stroke on top
@ -366,7 +414,19 @@ fn add_plate_sub(
}
// Draw row/column labels
let default_font = egui::FontId::monospace(f32::min(radius, 14.0));
static DEFAULT_TEXT_COLOR: LazyLock<egui::Color32> =
LazyLock::new(|| egui::Color32::from_gray(128));
static HIGHLIGHT_TEXT_COLOR: LazyLock<egui::Color32> =
LazyLock::new(|| egui::Color32::from_gray(255));
for c_row in 0..rows {
let text_color = {
if display_options.show_coordinates && hovered_well.is_some_and(|x| x.0 == c_row + 1) {
*HIGHLIGHT_TEXT_COLOR
} else {
*DEFAULT_TEXT_COLOR
}
};
painter.text(
egui::pos2(
start_x - 10.0,
@ -374,11 +434,18 @@ fn add_plate_sub(
),
egui::Align2::CENTER_CENTER,
(c_row + 1).to_string(),
egui::FontId::monospace(f32::min(radius, 14.0)),
egui::Color32::from_gray(128),
default_font.clone(),
text_color,
);
}
for c_column in 0..columns {
let text_color = {
if display_options.show_coordinates && hovered_well.is_some_and(|x| x.1 == c_column + 1) {
*HIGHLIGHT_TEXT_COLOR
} else {
*DEFAULT_TEXT_COLOR
}
};
painter.text(
egui::pos2(
start_x + radius + 2.0 * radius * c_column as f32,
@ -386,8 +453,8 @@ fn add_plate_sub(
),
egui::Align2::CENTER_CENTER,
plate_tool_lib::util::num_to_letters(c_column + 1).unwrap(),
egui::FontId::monospace(f32::min(radius, 14.0)),
egui::Color32::from_gray(128),
default_font.clone(),
text_color,
);
}
}
@ -402,6 +469,7 @@ pub fn add_plate(
current_transfer_state: Option<&CurrentTransferState>,
ui: &mut egui::Ui,
state: &mut PlateUiState,
display_options: PlateDisplayOptions,
) {
add_plate_sub(
size,
@ -414,5 +482,6 @@ pub fn add_plate(
current_transfer_state,
ui,
state,
display_options,
);
}