Display options for plate
This commit is contained in:
parent
e6d80ab8a3
commit
418bf4a79e
|
@ -5,7 +5,7 @@ use eframe::egui::{self};
|
||||||
|
|
||||||
use crate::main_state::{construct_fake_mainstate, MainState};
|
use crate::main_state::{construct_fake_mainstate, MainState};
|
||||||
use crate::modals::{self, ModalState};
|
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::transfer_menu::{transfer_menu, CurrentTransferState, TransferMenuState};
|
||||||
use crate::tree::tree;
|
use crate::tree::tree;
|
||||||
|
|
||||||
|
@ -13,12 +13,14 @@ use crate::tree::tree;
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
struct MainWindowState {
|
struct MainWindowState {
|
||||||
show_side_panel: bool,
|
show_side_panel: bool,
|
||||||
|
plate_display_options: PlateDisplayOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MainWindowState {
|
impl Default for MainWindowState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
show_side_panel: true,
|
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("Options", |ui| {
|
||||||
ui.menu_button("Styles", |ui| {
|
ui.menu_button("Styles", |ui| {
|
||||||
if ui.button("Toggle transfer hashes").clicked() {}
|
if ui.button("Toggle transfer hashes").clicked() {
|
||||||
if ui.button("Toggle volume heatmap").clicked() {}
|
self.main_window_state.plate_display_options.show_transfer_hashes ^= true;
|
||||||
if ui.button("Toggle current coordinates view").clicked() {}
|
}
|
||||||
|
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| {
|
ui.menu_button("Exports", |ui| {
|
||||||
if ui.button("Change CSV export type").clicked() {}
|
if ui.button("Change CSV export type").clicked() {}
|
||||||
|
@ -169,6 +177,7 @@ impl eframe::App for PlateToolEframe {
|
||||||
Some(&self.current_transfer_state),
|
Some(&self.current_transfer_state),
|
||||||
ui,
|
ui,
|
||||||
self.source_plate_state.lock().unwrap().deref_mut(),
|
self.source_plate_state.lock().unwrap().deref_mut(),
|
||||||
|
self.main_window_state.plate_display_options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(destination_pi) =
|
if let Some(destination_pi) =
|
||||||
|
@ -184,6 +193,7 @@ impl eframe::App for PlateToolEframe {
|
||||||
Some(&self.current_transfer_state),
|
Some(&self.current_transfer_state),
|
||||||
ui,
|
ui,
|
||||||
self.destination_plate_state.lock().unwrap().deref_mut(),
|
self.destination_plate_state.lock().unwrap().deref_mut(),
|
||||||
|
self.main_window_state.plate_display_options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use eframe::egui::{self, pos2, Color32, Rounding};
|
use eframe::egui::{self, pos2, Color32, Rounding};
|
||||||
|
use eframe::glow::OFFSET;
|
||||||
use plate_tool_lib::plate::PlateFormat;
|
use plate_tool_lib::plate::PlateFormat;
|
||||||
use plate_tool_lib::transfer_region::Region;
|
use plate_tool_lib::transfer_region::Region;
|
||||||
use plate_tool_lib::uuid::Uuid;
|
use plate_tool_lib::uuid::Uuid;
|
||||||
|
@ -37,11 +38,34 @@ impl Default for PlateUiState {
|
||||||
struct WellInfo {
|
struct WellInfo {
|
||||||
volume: f32,
|
volume: f32,
|
||||||
color: [f64; 3],
|
color: [f64; 3],
|
||||||
|
highlight: bool,
|
||||||
|
fill: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WellInfo {
|
impl WellInfo {
|
||||||
fn new(volume: f32, color: [f64; 3]) -> Self {
|
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 {
|
if let Some(wells) = cache_result {
|
||||||
for well in wells.iter().filter(|x| x.row <= rows && x.col <= columns) {
|
for well in wells.iter().filter(|x| x.row <= rows && x.col <= columns) {
|
||||||
if let Some(Some(mut x)) = well_infos
|
if let Some(Some(mut x)) = well_infos.get_mut(
|
||||||
.get_mut((well.row - 1) as usize * columns as usize + (well.col - 1) as usize)
|
(well.row - 1) as usize * columns as usize + (well.col - 1) as usize,
|
||||||
{
|
) {
|
||||||
x.volume += 5.0;
|
x.volume += 5.0;
|
||||||
x.color = PALETTE.get_ordered(transfer.id, ordered_ids);
|
x.color = PALETTE.get_ordered(transfer.id, ordered_ids);
|
||||||
} else {
|
} else {
|
||||||
|
@ -173,6 +197,20 @@ fn f64_to_color32(x: [f64; 3]) -> Color32 {
|
||||||
Color32::from_rgb(r, g, b)
|
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(
|
fn add_plate_sub(
|
||||||
size: egui::Vec2,
|
size: egui::Vec2,
|
||||||
rows: u8,
|
rows: u8,
|
||||||
|
@ -184,6 +222,7 @@ fn add_plate_sub(
|
||||||
current_transfer_state: Option<&CurrentTransferState>,
|
current_transfer_state: Option<&CurrentTransferState>,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
state: &mut PlateUiState,
|
state: &mut PlateUiState,
|
||||||
|
display_options: PlateDisplayOptions,
|
||||||
) {
|
) {
|
||||||
let (response, painter) = ui.allocate_painter(size, egui::Sense::click_and_drag());
|
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())
|
.and_then(|x| x.lock().ok())
|
||||||
.map(|x| x.volume)
|
.map(|x| x.volume)
|
||||||
.unwrap_or(0.0);
|
.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 {
|
*well_info = Some(WellInfo {
|
||||||
color: [255.0, 255.0, 255.0],
|
color,
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
|
fill,
|
||||||
|
highlight: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,8 +370,13 @@ fn add_plate_sub(
|
||||||
if let Some(well_info) =
|
if let Some(well_info) =
|
||||||
well_infos[(c_row - 1) as usize * columns as usize + (c_column - 1) as usize]
|
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));
|
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
|
// Draw stroke on top
|
||||||
|
@ -366,7 +414,19 @@ fn add_plate_sub(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw row/column labels
|
// 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 {
|
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(
|
painter.text(
|
||||||
egui::pos2(
|
egui::pos2(
|
||||||
start_x - 10.0,
|
start_x - 10.0,
|
||||||
|
@ -374,11 +434,18 @@ fn add_plate_sub(
|
||||||
),
|
),
|
||||||
egui::Align2::CENTER_CENTER,
|
egui::Align2::CENTER_CENTER,
|
||||||
(c_row + 1).to_string(),
|
(c_row + 1).to_string(),
|
||||||
egui::FontId::monospace(f32::min(radius, 14.0)),
|
default_font.clone(),
|
||||||
egui::Color32::from_gray(128),
|
text_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for c_column in 0..columns {
|
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(
|
painter.text(
|
||||||
egui::pos2(
|
egui::pos2(
|
||||||
start_x + radius + 2.0 * radius * c_column as f32,
|
start_x + radius + 2.0 * radius * c_column as f32,
|
||||||
|
@ -386,8 +453,8 @@ fn add_plate_sub(
|
||||||
),
|
),
|
||||||
egui::Align2::CENTER_CENTER,
|
egui::Align2::CENTER_CENTER,
|
||||||
plate_tool_lib::util::num_to_letters(c_column + 1).unwrap(),
|
plate_tool_lib::util::num_to_letters(c_column + 1).unwrap(),
|
||||||
egui::FontId::monospace(f32::min(radius, 14.0)),
|
default_font.clone(),
|
||||||
egui::Color32::from_gray(128),
|
text_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,6 +469,7 @@ pub fn add_plate(
|
||||||
current_transfer_state: Option<&CurrentTransferState>,
|
current_transfer_state: Option<&CurrentTransferState>,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
state: &mut PlateUiState,
|
state: &mut PlateUiState,
|
||||||
|
display_options: PlateDisplayOptions,
|
||||||
) {
|
) {
|
||||||
add_plate_sub(
|
add_plate_sub(
|
||||||
size,
|
size,
|
||||||
|
@ -414,5 +482,6 @@ pub fn add_plate(
|
||||||
current_transfer_state,
|
current_transfer_state,
|
||||||
ui,
|
ui,
|
||||||
state,
|
state,
|
||||||
|
display_options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue