parent
0e0d72ec9c
commit
dc3ef4830a
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["plate-tool-web", "plate-tool-lib"]
|
members = ["plate-tool-web", "plate-tool-lib", "plate-tool-eframe"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[build]
|
||||||
|
target = "x86_64-unknown-linux-gnu"
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "plate-tool-eframe"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
plate-tool-lib = { path = "../plate-tool-lib" }
|
||||||
|
eframe = { version = "0.30", default-features = false, features = [
|
||||||
|
"default_fonts",
|
||||||
|
"glow",
|
||||||
|
"wayland",
|
||||||
|
]}
|
|
@ -0,0 +1,54 @@
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use eframe::egui::{self};
|
||||||
|
|
||||||
|
use plate_tool_lib::plate::PlateFormat;
|
||||||
|
|
||||||
|
use crate::plate::{add_grid, PlateUiState};
|
||||||
|
|
||||||
|
pub struct PlateToolEframe {
|
||||||
|
format: PlateFormat,
|
||||||
|
source_plate_state: Mutex<PlateUiState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlateToolEframe {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
format: PlateFormat::W96,
|
||||||
|
source_plate_state: Mutex::new(PlateUiState::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlateToolEframe {
|
||||||
|
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
|
// Would load state here
|
||||||
|
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for PlateToolEframe {
|
||||||
|
// State storage
|
||||||
|
/*
|
||||||
|
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||||
|
unimplemented!()
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
|
ui.label("shrimp");
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("lobster");
|
||||||
|
add_grid(
|
||||||
|
PlateFormat::W1536,
|
||||||
|
ui,
|
||||||
|
self.source_plate_state.lock().unwrap().deref_mut(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
mod app;
|
||||||
|
mod plate;
|
||||||
|
pub use app::PlateToolEframe;
|
|
@ -0,0 +1,15 @@
|
||||||
|
use eframe::*;
|
||||||
|
use eframe::egui;
|
||||||
|
|
||||||
|
fn main() -> eframe::Result{
|
||||||
|
let native_options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
.with_title("Shrimp"),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
eframe::run_native(
|
||||||
|
"PlateToolEframe",
|
||||||
|
native_options,
|
||||||
|
Box::new(|cc| Ok(Box::new(plate_tool_eframe::PlateToolEframe::new(cc))))
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
use eframe::egui::{self, pos2, Color32, Rounding};
|
||||||
|
use plate_tool_lib::plate::PlateFormat;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct PlateUiState {
|
||||||
|
pub drag_start_position: Option<egui::Pos2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlateUiState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
drag_start_position: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_grid(pf: PlateFormat, ui: &mut egui::Ui, state: &mut PlateUiState) {
|
||||||
|
fn calculate_grid_params(
|
||||||
|
ul_origin: (f32, f32),
|
||||||
|
tw: f32,
|
||||||
|
th: f32,
|
||||||
|
rows: u8,
|
||||||
|
columns: u8,
|
||||||
|
) -> (f32, f32, f32) {
|
||||||
|
// (Start X, Start Y, Radius)
|
||||||
|
const PADDING: f32 = 20.0;
|
||||||
|
|
||||||
|
let usable_width = tw - 2.0 * PADDING;
|
||||||
|
let usable_height = th - 2.0 * PADDING;
|
||||||
|
|
||||||
|
let shared_width = f32::ceil(usable_width / columns as f32);
|
||||||
|
let shared_height = f32::ceil(usable_height / rows as f32);
|
||||||
|
let radius = f32::min(shared_width, shared_height) / 2.0;
|
||||||
|
(ul_origin.0 + PADDING, ul_origin.1 + PADDING, radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hover_well(
|
||||||
|
response: &egui::Response,
|
||||||
|
start_x: f32,
|
||||||
|
start_y: f32,
|
||||||
|
radius: f32,
|
||||||
|
) -> Option<(u8, u8)> {
|
||||||
|
get_well_from_pos(response.hover_pos(), start_x, start_y, radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_well_from_pos(
|
||||||
|
position: Option<egui::Pos2>,
|
||||||
|
start_x: f32,
|
||||||
|
start_y: f32,
|
||||||
|
radius: f32,
|
||||||
|
) -> Option<(u8, u8)> {
|
||||||
|
// Some((row, column))
|
||||||
|
position
|
||||||
|
.map(|p| Into::<(f32, f32)>::into(p))
|
||||||
|
.and_then(|(x, y)| {
|
||||||
|
// Check bounds
|
||||||
|
if x < start_x || y < start_y {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// CHECK Bottom Right BOUND
|
||||||
|
|
||||||
|
let solved_column: u8 = (x - start_x).div_euclid(radius * 2.0) as u8;
|
||||||
|
let solved_row: u8 = (y - start_y).div_euclid(radius * 2.0) as u8;
|
||||||
|
|
||||||
|
Some((solved_row, solved_column))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_grid_sub(rows: u8, columns: u8, ui: &mut egui::Ui, state: &mut PlateUiState) {
|
||||||
|
let available_size = ui.available_size();
|
||||||
|
let (response, painter) =
|
||||||
|
ui.allocate_painter(available_size, egui::Sense::click_and_drag());
|
||||||
|
|
||||||
|
let rect = response.rect;
|
||||||
|
|
||||||
|
let ul_origin = rect.left_top();
|
||||||
|
let total_width = rect.width();
|
||||||
|
let total_height = rect.height();
|
||||||
|
|
||||||
|
let stroke = egui::Stroke::new(2.0, egui::Color32::from_gray(128));
|
||||||
|
let (start_x, start_y, radius) =
|
||||||
|
calculate_grid_params(ul_origin.into(), total_width, total_height, rows, columns);
|
||||||
|
|
||||||
|
// Manage clicks and drags
|
||||||
|
if response.drag_started() {
|
||||||
|
state.drag_start_position = Some(response.hover_pos().unwrap());
|
||||||
|
}
|
||||||
|
if response.drag_stopped() {
|
||||||
|
state.drag_start_position = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let drag_start_well =
|
||||||
|
get_well_from_pos(state.drag_start_position, start_x, start_y, radius);
|
||||||
|
let hovered_well = get_hover_well(&response, start_x, start_y, radius);
|
||||||
|
|
||||||
|
// Plate Frame
|
||||||
|
painter.rect_stroke(
|
||||||
|
egui::Rect {
|
||||||
|
min: pos2(start_x, start_y),
|
||||||
|
max: pos2(
|
||||||
|
start_x + 2.0 * radius * columns as f32,
|
||||||
|
start_y + 2.0 * radius * rows as f32,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Rounding::default(),
|
||||||
|
stroke,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw wells
|
||||||
|
for c_row in 0..rows {
|
||||||
|
for c_column in 0..columns {
|
||||||
|
let center = egui::pos2(
|
||||||
|
start_x + radius + 2.0 * radius * c_column as f32,
|
||||||
|
start_y + radius + 2.0 * radius * c_row as f32,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw fill first
|
||||||
|
if response.dragged() {
|
||||||
|
if let (Some(hw), Some(dw)) = (hovered_well, drag_start_well) {
|
||||||
|
if c_column <= u8::max(hw.1, dw.1)
|
||||||
|
&& c_column >= u8::min(hw.1, dw.1)
|
||||||
|
&& c_row <= u8::max(hw.0, dw.0)
|
||||||
|
&& c_row >= u8::min(hw.0, dw.0)
|
||||||
|
{
|
||||||
|
painter.circle_filled(center, radius, Color32::DARK_BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if Some((c_row, c_column)) == hovered_well {
|
||||||
|
painter.circle_filled(center, radius, Color32::RED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw stroke on top
|
||||||
|
painter.circle_stroke(center, radius - 2.0, stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match pf {
|
||||||
|
PlateFormat::W96 => add_grid_sub(8, 12, ui, state),
|
||||||
|
PlateFormat::W384 => add_grid_sub(16, 24, ui, state),
|
||||||
|
PlateFormat::W1536 => add_grid_sub(32, 48, ui, state),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue