Skip to content

Commit c657a06

Browse files
committed
improve point dragging ui
1 parent d2caf4c commit c657a06

4 files changed

Lines changed: 61 additions & 31 deletions

File tree

src/app_ui.rs

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ use eframe::egui::{
55
self, Align, Area, Button, CollapsingHeader, DragValue, Grid, Id, RichText, ScrollArea, SidePanel, Slider, Stroke, TextEdit, TextStyle, Window
66
};
77
use eframe::epaint::Color32;
8-
use egui_plot::{HLine, Legend, Plot, PlotBounds, PlotImage, PlotPoint, PlotTransform, PlotUi, VLine};
8+
use egui_plot::{
9+
HLine, Legend, Plot, PlotBounds, PlotImage, PlotItem, PlotPoint, PlotTransform, PlotUi, VLine
10+
};
911
use evalexpr::{EvalexprFloat, Stack, istr};
1012
use serde::{Deserialize, Serialize};
1113

1214
use crate::builtins::{init_builtins, show_builtin_information};
1315
use crate::draw_buffer::{DrawMeshType, PointInteraction, PointInteractionType, process_draw_buffers};
1416
use crate::drawing::{duplicate_entry_btn, popup_label, remove_entry_btn};
1517
use crate::entry::{
16-
self, Entry, EntrySymbol, EntryType, point_dragging, prepare_entries, schedule_create_plot_elements
18+
self, Entry, EntrySymbol, EntryType, point_dragging, point_radius_outer, prepare_entries, schedule_create_plot_elements
1719
};
1820
use crate::{AppConfig, IdGenerator, State, UiState, persistence, scope};
1921

@@ -489,10 +491,9 @@ pub fn graph_panel<T: EvalexprFloat>(
489491

490492
let plot_id = ui.make_persistent_id("Plot");
491493
let mut plot = Plot::new(plot_id).id(plot_id);
492-
if state.graph_state.current_graph_config.show_legend {
493-
plot = plot.legend(Legend::default().text_style(TextStyle::Body));
494-
}
495-
494+
if state.graph_state.current_graph_config.show_legend {
495+
plot = plot.legend(Legend::default().text_style(TextStyle::Body));
496+
}
496497

497498
if ui_state.reset_graph {
498499
force_create_elements = true;
@@ -540,7 +541,7 @@ pub fn graph_panel<T: EvalexprFloat>(
540541
}
541542

542543
// plot.center_y_axis
543-
let mut hovered_point: Option<(bool, PointInteraction)> = None;
544+
let mut hovered_point: Option<(bool, PointInteraction, f32)> = None;
544545

545546
if let (Some(custom_renderer), Some(prev_plot_transform)) =
546547
(&mut ui_state.fan_fill_renderer, state.graph_state.prev_plot_transform)
@@ -610,28 +611,54 @@ pub fn graph_panel<T: EvalexprFloat>(
610611
for line in ui_state.processed_shapes.lines.iter() {
611612
plot_ui.add(line.clone());
612613
}
613-
for draw_point in p_draw_buffer
614-
.draw_points
615-
.into_iter()
616-
.chain(ui_state.processed_shapes.draw_points.iter().cloned())
614+
for draw_point in
615+
p_draw_buffer.draw_points.iter().chain(ui_state.processed_shapes.draw_points.iter())
617616
{
618617
if let Some(mouse_pos) = ui_state.plot_mouese_pos {
619618
let transform = state.graph_state.prev_plot_transform.as_ref().unwrap();
620619
let sel = &draw_point.interaction;
621620
let is_draggable = matches!(sel.ty, PointInteractionType::Draggable { .. });
622621
let sel_p = transform.position_from_point(&PlotPoint::new(sel.x, sel.y));
623-
let dist_sq = (sel_p.x - mouse_pos.x).powf(2.0) + (sel_p.y - mouse_pos.y).powf(2.0);
622+
let dist_sq = (sel_p.x - mouse_pos.x).powf(2.0) + (sel_p.y - mouse_pos.y).powi(2);
624623

625-
if dist_sq < sel.radius * sel.radius {
624+
let hover_radius = if is_draggable { point_radius_outer(false) } else { sel.radius };
625+
if dist_sq < hover_radius.powi(2) {
626626
if let Some(current_hovered) = &hovered_point {
627-
if !current_hovered.0 {
628-
hovered_point = Some((is_draggable, sel.clone()));
627+
let replace_current = match (current_hovered.0, is_draggable) {
628+
(false, true) => true,
629+
(true, false) => false,
630+
(false, false) | (true, true) => current_hovered.2 > dist_sq,
631+
};
632+
if replace_current {
633+
hovered_point = Some((is_draggable, sel.clone(), dist_sq));
629634
}
630635
} else {
631-
hovered_point = Some((is_draggable, sel.clone()));
636+
hovered_point = Some((is_draggable, sel.clone(), dist_sq));
632637
}
633638
}
634639
}
640+
}
641+
let dragging_or_hovered_id =
642+
ui_state.dragging_point_i.as_ref().or(hovered_point.as_ref().map(|h| &h.1)).and_then(|dp| {
643+
if let PointInteractionType::Draggable { i, .. } = dp.ty {
644+
Some(i.0.with(i.1))
645+
} else {
646+
None
647+
}
648+
});
649+
650+
for mut draw_point in p_draw_buffer
651+
.draw_points
652+
.into_iter()
653+
.chain(ui_state.processed_shapes.draw_points.iter().cloned())
654+
{
655+
if let Some(id) = dragging_or_hovered_id {
656+
if (&draw_point.points as &dyn PlotItem).id() == id {
657+
let is_selected = Some(id) == ui_state.selected_plot_line.map(|i| i.0);
658+
let radius = point_radius_outer(is_selected);
659+
draw_point.points = draw_point.points.radius(radius);
660+
}
661+
}
635662
plot_ui.points(draw_point.points);
636663
}
637664
for draw_text in ui_state.processed_shapes.draw_texts.iter() {
@@ -660,14 +687,13 @@ pub fn graph_panel<T: EvalexprFloat>(
660687
ui_state.plot_mouese_pos = plot_res.response.hover_pos();
661688
state.graph_state.prev_plot_transform = Some(plot_res.transform);
662689

663-
// println!("new frame");
690+
// println!("new frame");
664691
if force_create_elements || changed {
665692
// println!("Scheduling element creation because force {force_create_elements} changed {changed}");
666693
if !ui_state.debug_info.pause_redraw {
667694
ui_state.debug_info.plot_bounds = Some(*plot_res.transform.bounds());
668695
scope!("schedule_entry_create_plot_elements");
669696

670-
671697
ui_state.eval_errors.clear();
672698
let plot_params = entry::PlotParams::new::<T>(ui_state, &state.graph_state);
673699
let main_context = Arc::new(state.ctx.clone());
@@ -742,7 +768,7 @@ pub fn graph_panel<T: EvalexprFloat>(
742768
}
743769

744770
if !ui_state.showing_custom_label
745-
&& let Some((_, hovered_point)) = &hovered_point
771+
&& let Some((_, hovered_point, _)) = &hovered_point
746772
{
747773
let screen_x = plot_res.transform.position_from_point_x(hovered_point.x);
748774
let screen_y = plot_res.transform.position_from_point_y(hovered_point.y);

src/entry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod entry_processing;
1313
mod entry_ui;
1414

1515
pub use drag_point::point_dragging;
16-
pub use entry_plot_elements::{PlotParams, schedule_create_plot_elements};
16+
pub use entry_plot_elements::{point_radius_outer,PlotParams, schedule_create_plot_elements};
1717
pub use entry_processing::{optimize_entries, prepare_constants, prepare_entries, preprocess_ast};
1818
pub use entry_ui::entry_ui;
1919

src/entry/drag_point.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use eframe::egui::Id;
1+
use eframe::egui::{Id, PointerButton};
22
use egui_plot::PlotResponse;
33
use evalexpr::{EvalexprFloat, FlatNode, HashMapContext, IStr, Stack};
44

@@ -14,16 +14,16 @@ pub struct DragPointResult {
1414
pub fn point_dragging<T: EvalexprFloat>(
1515
entries: &mut [Entry<T>], ctx: &mut evalexpr::HashMapContext<T>, plot_res: &PlotResponse<()>,
1616
dragging_point_i: &mut Option<draw_buffer::PointInteraction>,
17-
hovered_point: Option<&(bool, draw_buffer::PointInteraction)>, plot_params: &PlotParams,
17+
hovered_point: Option<&(bool, draw_buffer::PointInteraction, f32)>, plot_params: &PlotParams,
1818
) -> Option<DragPointResult> {
1919
let mut result = None;
2020

21-
if let Some((is_draggable, hovered_point)) = &hovered_point {
22-
if *is_draggable && plot_res.response.drag_started() {
21+
if let Some((is_draggable, hovered_point,_)) = &hovered_point {
22+
if *is_draggable && plot_res.response.is_pointer_button_down_on() && dragging_point_i.is_none() {
2323
*dragging_point_i = Some(hovered_point.clone());
2424
}
2525
}
26-
if plot_res.response.drag_stopped() {
26+
if plot_res.response.drag_stopped() || !plot_res.response.is_pointer_button_down_on() {
2727
*dragging_point_i = None;
2828
}
2929
let Some(dragging_point_i) = dragging_point_i else {
@@ -109,7 +109,7 @@ pub fn point_dragging<T: EvalexprFloat>(
109109
};
110110

111111
let mut stack = Stack::<T>::new();
112-
let new_value = minimize(value.to_f64(), (pos.x, pos.y), eps , |x| {
112+
let new_value = minimize(value.to_f64(), (pos.x, pos.y), eps, |x| {
113113
ctx.set_value(x_const, f64_to_value::<T>(x)).unwrap();
114114
Some((
115115
x_node.eval_float_with_context(&mut stack, ctx).ok()?.to_f64(),

src/entry/entry_plot_elements.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use std::marker::ConstParamTy;
55

66
use eframe::egui::{self, Align2, Color32, Id, Pos2, RichText, Stroke, pos2, remap};
77
use egui_plot::{PlotItemBase, PlotPoint, PlotTransform, Points, Polygon};
8-
use evalexpr::{EvalexprError, EvalexprFloat, ExpressionFunction, FlatNode, HashMapContext, Stack, Value, istr};
8+
use evalexpr::{
9+
EvalexprError, EvalexprFloat, ExpressionFunction, FlatNode, HashMapContext, Stack, Value, istr
10+
};
911
use thread_local::ThreadLocal;
1012

1113
use crate::draw_buffer::{
@@ -465,6 +467,8 @@ pub fn entry_create_plot_elements_async<T: EvalexprFloat>(
465467
}
466468
Ok((entry.id, draw_buffer))
467469
}
470+
471+
pub fn point_radius_outer(selected: bool) -> f32 { if selected { 12.5 } else { 7.5 } }
468472
pub fn entry_create_plot_elements_sync<T: EvalexprFloat>(
469473
id: u64, ty: &EntryType<T>, name: &str, color: Color32, sorting_idx: u32, selected_id: Option<Id>,
470474
ctx: &evalexpr::HashMapContext<T>, plot_params: &PlotParams,
@@ -528,10 +532,10 @@ pub fn entry_create_plot_elements_sync<T: EvalexprFloat>(
528532
if let Some(((x, y), drag_point)) = p {
529533
let x = x.to_f64();
530534
let y = y.to_f64();
531-
let point_id = egui_id.with(i);
535+
let point_id = egui_id.with(i as u32);
532536
let selected = selected_id == Some(egui_id);
533537
let radius = if selected { 6.5 } else { 4.5 };
534-
let radius_outer = if selected { 12.5 } else { 7.5 };
538+
let radius_outer = point_radius_outer(selected);
535539

536540
if let Some((fill_mesh, plot_trans)) = &mut fill_mesh {
537541
let screen_point = gpu_position_from_point(
@@ -551,14 +555,14 @@ pub fn entry_create_plot_elements_sync<T: EvalexprFloat>(
551555
radius,
552556
ty: PointInteractionType::Other(OtherPointType::Point),
553557
},
554-
Points::new(name.to_string(), [x, y]).color(color).radius(radius),
558+
Points::new(name.to_string(), [x, y]).id(point_id).color(color).radius(radius),
555559
));
556560
if drag_point.is_some() {
557561
let selectable_point = PointInteraction {
558-
ty: PointInteractionType::Draggable { i: (egui_id, i as u32) },
559562
x,
560563
y,
561564
radius: radius_outer,
565+
ty: PointInteractionType::Draggable { i: (egui_id, i as u32) },
562566
};
563567
draw_buffer.points.push(DrawPoint::new(
564568
sorting_idx,

0 commit comments

Comments
 (0)