honeycomb_render/
gui.rs

1use bevy::input::common_conditions::input_just_pressed;
2use bevy::prelude::*;
3use bevy::utils::HashSet;
4use bevy_egui::egui::Window;
5use bevy_egui::{EguiContexts, EguiPlugin};
6use bevy_mod_picking::picking_core::PickingPluginsSettings;
7use bevy_mod_picking::selection::SelectionPluginSettings;
8use egui_dock::egui;
9
10use crate::components::{Beta, Dart, DartId, Edge, EdgeId, Face, FaceId, Vertex, VertexId, Volume};
11use crate::resources::{
12    DartHeadMul, DartRenderColor, DartShrink, DartWidth, EdgeRenderColor, EdgeWidth,
13    FaceRenderColor, FaceShrink, VertexRenderColor, VertexWidth, VolumeRenderColor, VolumeShrink,
14};
15
16// --- plugin
17
18/// Plugin used to build the interface.
19pub struct GuiPlugin;
20
21impl Plugin for GuiPlugin {
22    fn build(&self, app: &mut App) {
23        app.add_plugins(EguiPlugin)
24            .insert_resource(UiState::default())
25            .insert_resource(WindowVisible(false))
26            .add_systems(
27                Update,
28                toggle_window_system.run_if(input_just_pressed(KeyCode::Tab)),
29            )
30            .add_systems(Update, ui_system);
31    }
32}
33
34// --- system
35
36/// Configuration window status resource.
37#[derive(Resource)]
38pub struct WindowVisible(pub bool);
39
40fn toggle_window_system(mut window_visible: ResMut<WindowVisible>) {
41    window_visible.0 = !window_visible.0;
42}
43
44#[allow(clippy::too_many_arguments)]
45fn ui_system(
46    mut contexts: EguiContexts,
47    window_visible: Res<WindowVisible>,
48    drc: ResMut<DartRenderColor>,
49    ds: ResMut<DartShrink>,
50    dw: ResMut<DartWidth>,
51    dhm: ResMut<DartHeadMul>,
52    verc: ResMut<VertexRenderColor>,
53    v_width: ResMut<VertexWidth>,
54    edrc: ResMut<EdgeRenderColor>,
55    edw: ResMut<EdgeWidth>,
56    farc: ResMut<FaceRenderColor>,
57    fas: ResMut<FaceShrink>,
58    vorc: ResMut<VolumeRenderColor>,
59    vos: ResMut<VolumeShrink>,
60) {
61    if window_visible.0 {
62        Window::new("Rendering Options")
63            .collapsible(false)
64            .show(contexts.ctx_mut(), |ui| {
65                draw_options(
66                    ui, drc, ds, dw, dhm, verc, v_width, edrc, edw, farc, fas, vorc, vos,
67                );
68            });
69    }
70}
71
72// --- structs
73
74#[derive(Resource)]
75pub struct UiState {
76    pub(crate) selected_entities: HashSet<Entity>,
77}
78
79impl Default for UiState {
80    fn default() -> Self {
81        Self {
82            selected_entities: HashSet::new(),
83        }
84    }
85}
86
87// --- map options
88
89/// rendering options drawing function.
90#[allow(clippy::too_many_arguments)]
91pub fn draw_options(
92    ui: &mut egui::Ui,
93    mut drc: ResMut<DartRenderColor>,
94    mut ds: ResMut<DartShrink>,
95    mut dw: ResMut<DartWidth>,
96    mut dhm: ResMut<DartHeadMul>,
97    mut verc: ResMut<VertexRenderColor>,
98    mut v_width: ResMut<VertexWidth>,
99    mut edrc: ResMut<EdgeRenderColor>,
100    mut edw: ResMut<EdgeWidth>,
101    mut farc: ResMut<FaceRenderColor>,
102    mut fas: ResMut<FaceShrink>,
103    mut vorc: ResMut<VolumeRenderColor>,
104    mut vos: ResMut<VolumeShrink>,
105) {
106    ui.label(egui::RichText::new("Map Rendering").size(15.));
107    ui.separator(); // ---
108
109    egui::Grid::new("map_opt_grid")
110        .num_columns(6)
111        .show(ui, |ui| {
112            // column names
113            ui.label(egui::RichText::new("Object"));
114            ui.label(egui::RichText::new("Render"));
115            ui.label(egui::RichText::new("Color"));
116            ui.label(egui::RichText::new("Shrink"));
117            ui.label(egui::RichText::new("Width"));
118            ui.label(egui::RichText::new("Head Factor"));
119            ui.end_row();
120            // darts
121            ui.label("Darts");
122            ui.checkbox(&mut drc.0, "");
123            ui.add_enabled_ui(drc.0, |ui| draw_color_picker(ui, &mut drc.1));
124            ui.add(egui::DragValue::new(&mut ds.0).speed(0.01));
125            ui.add(egui::DragValue::new(&mut dw.0).speed(0.01));
126            ui.add(egui::DragValue::new(&mut dhm.0).speed(0.01));
127            ui.end_row();
128
129            // vertices
130            ui.label("Vertices");
131            ui.checkbox(&mut verc.0, "");
132            ui.add_enabled_ui(verc.0, |ui| draw_color_picker(ui, &mut verc.1));
133            ui.label("");
134            ui.add(egui::DragValue::new(&mut v_width.0).speed(0.01));
135            ui.end_row();
136
137            // edges
138            ui.label("Edges");
139            ui.checkbox(&mut edrc.0, "");
140            ui.add_enabled_ui(edrc.0, |ui| draw_color_picker(ui, &mut edrc.1));
141            ui.label("");
142            ui.add(egui::DragValue::new(&mut edw.0).speed(0.01));
143            ui.end_row();
144
145            // faces
146            ui.label("Faces");
147            ui.checkbox(&mut farc.0, "");
148            ui.add_enabled_ui(farc.0, |ui| draw_color_picker(ui, &mut farc.1));
149            ui.add(egui::DragValue::new(&mut fas.0).speed(0.01));
150            ui.end_row();
151
152            // faces
153            ui.label("Volumes");
154            ui.checkbox(&mut vorc.0, "");
155            ui.add_enabled_ui(vorc.0, |ui| draw_color_picker(ui, &mut vorc.1));
156            ui.label("");
157            ui.add(egui::DragValue::new(&mut vos.0).speed(0.01));
158            ui.end_row();
159        });
160}
161
162fn draw_color_picker(ui: &mut egui::Ui, color: &mut egui::Color32) {
163    // create intermediate variable
164    let mut egui_color =
165        egui::Color32::from_rgba_premultiplied(color.r(), color.g(), color.b(), color.a());
166    // insert the color picker
167    let color_picker = egui::color_picker::color_edit_button_srgba(
168        ui,
169        &mut egui_color,
170        egui::color_picker::Alpha::Opaque,
171    );
172    // update the original color variable
173    if color_picker.changed() {
174        *color = egui::Color32::from_rgba_premultiplied(
175            egui_color.r(),
176            egui_color.g(),
177            egui_color.b(),
178            egui_color.a(),
179        );
180    }
181}
182
183// --- picking
184
185#[allow(unused)]
186fn draw_picking_options(ui: &mut egui::Ui, world: &mut World) {
187    ui.label(egui::RichText::new("Picking").size(15.));
188    ui.separator(); // ---
189
190    egui::Grid::new("pick_opt_grid")
191        .num_columns(2)
192        .show(ui, |ui| {
193            ui.label("Enable");
194            {
195                let mut picking_params = world.resource_mut::<PickingPluginsSettings>();
196                ui.checkbox(&mut picking_params.is_enabled, "");
197            }
198            ui.end_row();
199            ui.label("Click nothing deselect all");
200            {
201                let mut picking_params = world.resource_mut::<SelectionPluginSettings>();
202                ui.checkbox(&mut picking_params.click_nothing_deselect_all, "");
203            }
204        });
205}
206
207/// Inspection panel drawing function.
208pub fn draw_inspected_data(
209    ui: &mut egui::Ui,
210    world: &mut World,
211    selected_entities: &HashSet<Entity>,
212) {
213    ui.label(egui::RichText::new("Selected Components").size(15.));
214
215    if selected_entities.is_empty() {
216        ui.separator();
217    }
218
219    for entity in selected_entities {
220        ui.separator();
221        if world.get::<Dart>(*entity).is_some() {
222            let Some(id) = world.get::<DartId>(*entity) else {
223                unreachable!()
224            };
225            ui.label(format!("Dart #{}", id.0));
226            let Some(vid) = world.get::<VertexId>(*entity) else {
227                unreachable!();
228            };
229            let Some(eid) = world.get::<EdgeId>(*entity) else {
230                unreachable!();
231            };
232            let Some(fid) = world.get::<FaceId>(*entity) else {
233                unreachable!();
234            };
235            egui::Grid::new(format!("dart #{}", id.0)) // need a unique id
236                .num_columns(4)
237                .show(ui, |ui| {
238                    ui.label("i-cells");
239                    ui.label("Vertex");
240                    ui.label("Edge");
241                    ui.label("Face");
242                    ui.end_row();
243                    ui.label("IDs");
244                    ui.label(format!("{}", vid.0));
245                    ui.label(format!("{}", eid.0));
246                    ui.label(format!("{}", fid.0));
247                });
248        } else if world.get::<Beta>(*entity).is_some() {
249            ui.label("Beta");
250        } else if world.get::<Vertex>(*entity).is_some() {
251            let Some(id) = world.get::<VertexId>(*entity) else {
252                unreachable!()
253            };
254            ui.label(format!("Vertex #{}", id.0));
255        } else if world.get::<Edge>(*entity).is_some() {
256            let Some(id) = world.get::<EdgeId>(*entity) else {
257                unreachable!()
258            };
259            ui.label(format!("Edge #{}", id.0));
260        } else if world.get::<Face>(*entity).is_some() {
261            ui.label("Face");
262        } else if world.get::<Volume>(*entity).is_some() {
263            ui.label("Volume");
264        }
265    }
266}