Skip to content

Commit

Permalink
Use a reference to the element to draw the hover shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
niladic committed Jun 26, 2021
1 parent e429ac5 commit 24d6547
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 112 deletions.
255 changes: 147 additions & 108 deletions egui/src/widgets/plot/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,20 @@ pub(super) trait PlotItem {
fn get_shapes(&self, transform: &ScreenTransform, shapes: &mut Vec<Shape>);
fn series_mut(&mut self) -> &mut Values;
fn get_bounds(&self) -> Bounds;
fn closest<'a>(
&'a self,
ui: &'a Ui,
fn find_closest_element(
&self,
pointer: Pos2,
transform: &'a ScreenTransform,
transform: &ScreenTransform,
) -> Option<ElementDistance>;
fn hover_shapes(
&self,
ui: &Ui,
transform: &ScreenTransform,
show_x: bool,
show_y: bool,
) -> Option<HoverElement<'a>>;
element: ElementRef,
shapes: &mut Vec<Shape>,
);
fn name(&self) -> &str;
fn color(&self) -> Color32;
fn highlight(&mut self);
Expand Down Expand Up @@ -326,52 +332,57 @@ impl PlotItem for Line {
self.series.get_bounds()
}

fn closest<'a>(
&'a self,
ui: &'a Ui,
fn find_closest_element(
&self,
pointer: Pos2,
transform: &'a ScreenTransform,
show_x: bool,
show_y: bool,
) -> Option<HoverElement<'a>> {
transform: &ScreenTransform,
) -> Option<ElementDistance> {
let mut closest_value = None;
let mut closest_item = None;
let mut closest_dist_sq = f32::MAX;
for value in &self.series.values {
let pos = transform.position_from_value(value);
let dist_sq = pointer.distance_sq(pos);
if dist_sq < closest_dist_sq {
closest_dist_sq = dist_sq;
closest_value = Some(value);
closest_item = Some(self.name());
}
}
closest_value
.zip(closest_item)
.map(move |(value, name)| HoverElement {
distance_square: closest_dist_sq,
hover_shapes: Box::new(move |mut shapes| {
let line_color = if ui.visuals().dark_mode {
Color32::from_gray(100).additive()
} else {
Color32::from_black_alpha(180)
};

let position = transform.position_from_value(value);
shapes.push(Shape::circle_filled(position, 3.0, line_color));

rulers_at_value(
ui,
position,
transform,
show_x,
show_y,
*value,
name,
&mut shapes,
);
}),
})
closest_value.map(|value| ElementDistance {
distance_square: closest_dist_sq,
element: ElementRef::XY(*value),
})
}

fn hover_shapes(
&self,
ui: &Ui,
transform: &ScreenTransform,
show_x: bool,
show_y: bool,
element: ElementRef,
shapes: &mut Vec<Shape>,
) {
if let ElementRef::XY(value) = element {
let line_color = if ui.visuals().dark_mode {
Color32::from_gray(100).additive()
} else {
Color32::from_black_alpha(180)
};

let position = transform.position_from_value(&value);
shapes.push(Shape::circle_filled(position, 3.0, line_color));

rulers_at_value(
ui,
position,
transform,
show_x,
show_y,
value,
self.name(),
shapes,
);
}
}

fn name(&self) -> &str {
Expand Down Expand Up @@ -608,52 +619,57 @@ impl PlotItem for Points {
self.series.get_bounds()
}

fn closest<'a>(
&'a self,
ui: &'a Ui,
fn find_closest_element(
&self,
pointer: Pos2,
transform: &'a ScreenTransform,
show_x: bool,
show_y: bool,
) -> Option<HoverElement<'a>> {
transform: &ScreenTransform,
) -> Option<ElementDistance> {
let mut closest_value = None;
let mut closest_item = None;
let mut closest_dist_sq = f32::MAX;
for value in &self.series.values {
let pos = transform.position_from_value(value);
let dist_sq = pointer.distance_sq(pos);
if dist_sq < closest_dist_sq {
closest_dist_sq = dist_sq;
closest_value = Some(value);
closest_item = Some(self.name());
}
}
closest_value
.zip(closest_item)
.map(move |(value, name)| HoverElement {
distance_square: closest_dist_sq,
hover_shapes: Box::new(move |mut shapes| {
let line_color = if ui.visuals().dark_mode {
Color32::from_gray(100).additive()
} else {
Color32::from_black_alpha(180)
};

let position = transform.position_from_value(value);
shapes.push(Shape::circle_filled(position, 3.0, line_color));

rulers_at_value(
ui,
position,
transform,
show_x,
show_y,
*value,
name,
&mut shapes,
);
}),
})
closest_value.map(move |value| ElementDistance {
distance_square: closest_dist_sq,
element: ElementRef::XY(*value),
})
}

fn hover_shapes(
&self,
ui: &Ui,
transform: &ScreenTransform,
show_x: bool,
show_y: bool,
element: ElementRef,
shapes: &mut Vec<Shape>,
) {
if let ElementRef::XY(value) = element {
let line_color = if ui.visuals().dark_mode {
Color32::from_gray(100).additive()
} else {
Color32::from_black_alpha(180)
};

let position = transform.position_from_value(&value);
shapes.push(Shape::circle_filled(position, 3.0, line_color));

rulers_at_value(
ui,
position,
transform,
show_x,
show_y,
value,
self.name(),
shapes,
);
}
}

fn name(&self) -> &str {
Expand Down Expand Up @@ -1051,33 +1067,43 @@ impl PlotItem for BarChart {
bounds
}

fn closest<'a>(
&'a self,
ui: &'a Ui,
fn find_closest_element(
&self,
pointer: Pos2,
transform: &'a ScreenTransform,
show_x: bool,
show_y: bool,
) -> Option<HoverElement<'a>> {
transform: &ScreenTransform,
) -> Option<ElementDistance> {
let mut closest = None;
let mut closest_dist_sq = f32::MAX;
for bar in &self.bars {
for (index, bar) in self.bars.iter().enumerate() {
let box_rect: Rect = transform.rect_from_values(&bar.bounds_min(), &bar.bounds_max());
let dist_sq = pointer.distance_from_rect_sq(box_rect);
if dist_sq < closest_dist_sq {
closest_dist_sq = dist_sq;
closest = Some(bar);
closest = Some(index);
}
}
closest.map(move |bar| HoverElement {
closest.map(move |index| ElementDistance {
distance_square: closest_dist_sq,
hover_shapes: Box::new(move |mut shapes| {
bar.shapes(transform, true, &mut shapes);
bar.rulers(self, ui, transform, show_x, show_y, &mut shapes);
}),
element: ElementRef::Index(index),
})
}

fn hover_shapes(
&self,
ui: &Ui,
transform: &ScreenTransform,
show_x: bool,
show_y: bool,
element: ElementRef,
shapes: &mut Vec<Shape>,
) {
if let ElementRef::Index(index) = element {
let element = &self.bars[index];
element.shapes(transform, true, shapes);
element.rulers(self, ui, transform, show_x, show_y, shapes);
}
}

fn name(&self) -> &str {
self.name.as_str()
}
Expand Down Expand Up @@ -1473,34 +1499,44 @@ impl PlotItem for BoxplotSeries {
bounds
}

fn closest<'a>(
&'a self,
ui: &'a Ui,
fn find_closest_element(
&self,
pointer: Pos2,
transform: &'a ScreenTransform,
show_x: bool,
show_y: bool,
) -> Option<HoverElement<'a>> {
transform: &ScreenTransform,
) -> Option<ElementDistance> {
let mut closest = None;
let mut closest_dist_sq = f32::MAX;
for boxplot in &self.plots {
for (index, boxplot) in self.plots.iter().enumerate() {
let box_rect: Rect =
transform.rect_from_values(&boxplot.bounds_min(), &boxplot.bounds_max());
let dist_sq = pointer.distance_from_rect_sq(box_rect);
if dist_sq < closest_dist_sq {
closest_dist_sq = dist_sq;
closest = Some(boxplot);
closest = Some(index);
}
}
closest.map(move |boxplot| HoverElement {
closest.map(move |index| ElementDistance {
distance_square: closest_dist_sq,
hover_shapes: Box::new(move |mut shapes| {
boxplot.shapes(transform, true, &mut shapes);
boxplot.rulers(self, ui, transform, show_x, show_y, &mut shapes);
}),
element: ElementRef::Index(index),
})
}

fn hover_shapes(
&self,
ui: &Ui,
transform: &ScreenTransform,
show_x: bool,
show_y: bool,
element: ElementRef,
shapes: &mut Vec<Shape>,
) {
if let ElementRef::Index(index) = element {
let element = &self.plots[index];
element.shapes(transform, true, shapes);
element.rulers(self, ui, transform, show_x, show_y, shapes);
}
}

fn name(&self) -> &str {
self.name.as_str()
}
Expand All @@ -1518,12 +1554,15 @@ impl PlotItem for BoxplotSeries {
}
}

pub(crate) struct HoverElement<'a> {
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum ElementRef {
XY(Value),
Index(usize),
}

pub(crate) struct ElementDistance {
pub(crate) distance_square: f32,
// Note: the Box<dyn Fn> here is a compromise between an owned Vec<Shape>
// (overhead of precalculating the shapes) and an impl Fn
// (typing all the way up to PlotItem with trait object safety workarounds)
pub(crate) hover_shapes: Box<dyn Fn(&mut Vec<Shape>) + 'a>,
pub(crate) element: ElementRef,
}

fn highlighted_color(mut stroke: Stroke, fill: Color32) -> (Stroke, Color32) {
Expand Down
8 changes: 4 additions & 4 deletions egui/src/widgets/plot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,16 +661,16 @@ impl Prepared {
let mut closest_item = None;
let mut closest_dist_sq = interact_radius.powi(2);
for item in items {
if let Some(closest) = item.closest(ui, pointer, transform, *show_x, *show_y) {
if let Some(closest) = item.find_closest_element(pointer, transform) {
if closest.distance_square < closest_dist_sq {
closest_dist_sq = closest.distance_square;
closest_item = Some(closest);
closest_item = Some((item, closest));
}
}
}

if let Some(ref mut closest) = closest_item {
(closest.hover_shapes)(shapes);
if let Some((item, element)) = closest_item {
item.hover_shapes(ui, transform, *show_x, *show_y, element.element, shapes);
} else {
let value = transform.value_from_position(pointer);
items::rulers_at_value(ui, pointer, transform, *show_x, *show_y, value, "", shapes);
Expand Down

0 comments on commit 24d6547

Please sign in to comment.