diff --git a/plate-tool-eframe/src/plate.rs b/plate-tool-eframe/src/plate.rs index b08cc89..786d467 100644 --- a/plate-tool-eframe/src/plate.rs +++ b/plate-tool-eframe/src/plate.rs @@ -35,16 +35,16 @@ impl Default for PlateUiState { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] struct WellInfo { volume: f32, - color: [f64; 3], + color: Vec<[f64; 3]>, highlight: bool, fill: bool, } impl WellInfo { - fn new(volume: f32, color: [f64; 3]) -> Self { + fn new(volume: f32, color: Vec<[f64; 3]>) -> Self { WellInfo { volume, color, @@ -61,6 +61,7 @@ pub struct PlateDisplayOptions { pub show_well_volumes: bool, pub show_volume_heatmap: bool, pub show_coordinates: bool, + pub show_pie_piece_shading: bool, } impl Default for PlateDisplayOptions { fn default() -> Self { @@ -69,6 +70,7 @@ impl Default for PlateDisplayOptions { show_well_volumes: true, show_volume_heatmap: false, show_coordinates: true, + show_pie_piece_shading: true, } } } @@ -202,10 +204,10 @@ fn calculate_shading_for_wells( // Well info already existed. x.volume += volume; - x.color = if display_options.show_volume_heatmap { - PALETTE.get_linear(x.volume.into(), max_volume.into()) + if display_options.show_volume_heatmap { + x.color = vec![PALETTE.get_linear(x.volume.into(), max_volume.into())]; } else { - PALETTE.get_ordered(transfer.id, ordered_ids) + x.color.push(PALETTE.get_ordered(transfer.id, ordered_ids)); }; } else { // Well info does not already exist, we need to make it. @@ -216,9 +218,9 @@ fn calculate_shading_for_wells( *wi = Some(WellInfo::new( volume, if display_options.show_volume_heatmap { - PALETTE.get_linear(volume.into(), max_volume.into()) + vec![PALETTE.get_linear(volume.into(), max_volume.into())] } else { - PALETTE.get_ordered(transfer.id, ordered_ids) + vec![PALETTE.get_ordered(transfer.id, ordered_ids)] }, )); } @@ -253,6 +255,45 @@ fn draw_cross(painter: &egui::Painter, center: egui::Pos2, radius: f32, stroke: painter.line_segment([pts[1], pts[3]], stroke); } +fn draw_multicolor_circle( + painter: &egui::Painter, + center: egui::Pos2, + radius: f32, + colors: &[[f64; 3]], +) { + if colors.is_empty() { + return; + } + + let adjusted_radius = radius * 0.80f32; + let angle_per_slice_rad = std::f32::consts::TAU / colors.len() as f32; + + for (index, color) in colors.iter().enumerate() { + let start_angle = index as f32 * angle_per_slice_rad; + let stop_angle = (index + 1) as f32 * angle_per_slice_rad; + + let mut points = vec![center]; + + const NUM_POINTS: u8 = 32; + + for j in 0..=NUM_POINTS { + let t = j as f32 / NUM_POINTS as f32; + let angle = start_angle + t * angle_per_slice_rad; + let x = center.x + adjusted_radius * angle.cos(); + let y = center.y + adjusted_radius * angle.sin(); + points.push(egui::pos2(x, y)); + } + + points.push(center); + + painter.add(egui::Shape::convex_polygon( + points, + f64_to_color32(*color), + egui::Stroke::NONE, + )); + } +} + fn add_plate_sub( size: egui::Vec2, rows: u8, @@ -371,9 +412,11 @@ fn add_plate_sub( if let Some(mut well_info) = well_infos.get_mut((w.0 - 1) * columns as usize + (w.1 - 1)) { - let volume = well_info.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(); + let (volume, color, fill) = if let Some(ref info) = well_info { + (info.volume, info.color.clone(), info.fill) + } else { + (0.0, vec![[255.0, 255.0, 255.0]], false) + }; *well_info = Some(WellInfo { color, @@ -414,10 +457,20 @@ fn add_plate_sub( // 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)); + if display_options.show_pie_piece_shading && !display_options.show_volume_heatmap { + draw_multicolor_circle(&painter, center, radius, &well_info.color); + } else { + painter.circle_filled( + center, + radius * 0.80, + f64_to_color32( + *well_info.color.first().unwrap_or(&[255.0, 255.0, 255.0]), + ), + ); + } } if well_info.highlight && display_options.show_transfer_hashes { draw_cross(&painter, center, radius * 0.80, *STROKE_CURRENT); @@ -428,7 +481,7 @@ fn add_plate_sub( if display_options.show_well_volumes { 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] { painter.text( center, diff --git a/plate-tool-eframe/src/upper_menu.rs b/plate-tool-eframe/src/upper_menu.rs index e526213..3799c9f 100644 --- a/plate-tool-eframe/src/upper_menu.rs +++ b/plate-tool-eframe/src/upper_menu.rs @@ -206,6 +206,12 @@ fn render_options_menu( &mut main_window_state.plate_display_options.show_well_volumes, "Show volumes in wells", ); + ui.toggle_value( + &mut main_window_state + .plate_display_options + .show_pie_piece_shading, + "Show well shading as pie pieces", + ); ui.toggle_value( &mut main_window_state.plate_display_options.show_volume_heatmap, "Toggle volume heatmap",