Skip to content

Commit

Permalink
Allow cursors to be linked for plots (#1722)
Browse files Browse the repository at this point in the history
* Allow cursors to be linked

* Link cursors in demo

* Refactor cursor memory to deal with removal of plots

* Refactor PlotItem::on_hover to produce a list of cursors

* Use a separate `LinkedCursorsGroup` type to link cursors.

* Refactor `Cursor`.

* Inline `push_argument_ruler` and `push_value_ruler`.

* Update documentation

* Update doc reference
  • Loading branch information
Zoxc authored Aug 28, 2022
1 parent b978b06 commit 9be060f
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 46 deletions.
5 changes: 3 additions & 2 deletions crates/egui/src/widgets/plot/items/bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::emath::NumExt;
use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke};

use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
use crate::plot::{BarChart, PlotPoint, ScreenTransform};
use crate::plot::{BarChart, Cursor, PlotPoint, ScreenTransform};

/// One bar in a [`BarChart`]. Potentially floating, allowing stacked bar charts.
/// Width can be changed to allow variable-width histograms.
Expand Down Expand Up @@ -142,13 +142,14 @@ impl Bar {
parent: &BarChart,
plot: &PlotConfig<'_>,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
) {
let text: Option<String> = parent
.element_formatter
.as_ref()
.map(|fmt| fmt(self, parent));

add_rulers_and_text(self, plot, text, shapes);
add_rulers_and_text(self, plot, text, shapes, cursors);
}
}

Expand Down
5 changes: 3 additions & 2 deletions crates/egui/src/widgets/plot/items/box_elem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::emath::NumExt;
use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke};

use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
use crate::plot::{BoxPlot, PlotPoint, ScreenTransform};
use crate::plot::{BoxPlot, Cursor, PlotPoint, ScreenTransform};

/// Contains the values of a single box in a box plot.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -221,13 +221,14 @@ impl BoxElem {
parent: &BoxPlot,
plot: &PlotConfig<'_>,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
) {
let text: Option<String> = parent
.element_formatter
.as_ref()
.map(|fmt| fmt(self, parent));

add_rulers_and_text(self, plot, text, shapes);
add_rulers_and_text(self, plot, text, shapes, cursors);
}
}

Expand Down
70 changes: 38 additions & 32 deletions crates/egui/src/widgets/plot/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use epaint::Mesh;

use crate::*;

use super::{LabelFormatter, PlotBounds, ScreenTransform};
use super::{Cursor, LabelFormatter, PlotBounds, ScreenTransform};
use rect_elem::*;
use values::{ClosestElem, PlotGeometry};

Expand Down Expand Up @@ -72,6 +72,7 @@ pub(super) trait PlotItem {
&self,
elem: ClosestElem,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
label_formatter: &LabelFormatter,
) {
Expand All @@ -96,7 +97,15 @@ pub(super) trait PlotItem {
let pointer = plot.transform.position_from_point(&value);
shapes.push(Shape::circle_filled(pointer, 3.0, line_color));

rulers_at_value(pointer, value, self.name(), plot, shapes, label_formatter);
rulers_at_value(
pointer,
value,
self.name(),
plot,
shapes,
cursors,
label_formatter,
);
}
}

Expand Down Expand Up @@ -1392,13 +1401,14 @@ impl PlotItem for BarChart {
&self,
elem: ClosestElem,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
_: &LabelFormatter,
) {
let bar = &self.bars[elem.index];

bar.add_shapes(plot.transform, true, shapes);
bar.add_rulers_and_text(self, plot, shapes);
bar.add_rulers_and_text(self, plot, shapes, cursors);
}
}

Expand Down Expand Up @@ -1534,28 +1544,33 @@ impl PlotItem for BoxPlot {
&self,
elem: ClosestElem,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
_: &LabelFormatter,
) {
let box_plot = &self.boxes[elem.index];

box_plot.add_shapes(plot.transform, true, shapes);
box_plot.add_rulers_and_text(self, plot, shapes);
box_plot.add_rulers_and_text(self, plot, shapes, cursors);
}
}

// ----------------------------------------------------------------------------
// Helper functions

fn rulers_color(ui: &Ui) -> Color32 {
pub(crate) fn rulers_color(ui: &Ui) -> Color32 {
if ui.visuals().dark_mode {
Color32::from_gray(100).additive()
} else {
Color32::from_black_alpha(180)
}
}

fn vertical_line(pointer: Pos2, transform: &ScreenTransform, line_color: Color32) -> Shape {
pub(crate) fn vertical_line(
pointer: Pos2,
transform: &ScreenTransform,
line_color: Color32,
) -> Shape {
let frame = transform.frame();
Shape::line_segment(
[
Expand All @@ -1566,7 +1581,11 @@ fn vertical_line(pointer: Pos2, transform: &ScreenTransform, line_color: Color32
)
}

fn horizontal_line(pointer: Pos2, transform: &ScreenTransform, line_color: Color32) -> Shape {
pub(crate) fn horizontal_line(
pointer: Pos2,
transform: &ScreenTransform,
line_color: Color32,
) -> Shape {
let frame = transform.frame();
Shape::line_segment(
[
Expand All @@ -1582,44 +1601,31 @@ fn add_rulers_and_text(
plot: &PlotConfig<'_>,
text: Option<String>,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
) {
let orientation = elem.orientation();
let show_argument = plot.show_x && orientation == Orientation::Vertical
|| plot.show_y && orientation == Orientation::Horizontal;
let show_values = plot.show_y && orientation == Orientation::Vertical
|| plot.show_x && orientation == Orientation::Horizontal;

let line_color = rulers_color(plot.ui);

// Rulers for argument (usually vertical)
if show_argument {
let push_argument_ruler = |argument: PlotPoint, shapes: &mut Vec<Shape>| {
let position = plot.transform.position_from_point(&argument);
let line = match orientation {
Orientation::Horizontal => horizontal_line(position, plot.transform, line_color),
Orientation::Vertical => vertical_line(position, plot.transform, line_color),
};
shapes.push(line);
};

for pos in elem.arguments_with_ruler() {
push_argument_ruler(pos, shapes);
cursors.push(match orientation {
Orientation::Horizontal => Cursor::Horizontal { y: pos.y },
Orientation::Vertical => Cursor::Vertical { x: pos.x },
});
}
}

// Rulers for values (usually horizontal)
if show_values {
let push_value_ruler = |value: PlotPoint, shapes: &mut Vec<Shape>| {
let position = plot.transform.position_from_point(&value);
let line = match orientation {
Orientation::Horizontal => vertical_line(position, plot.transform, line_color),
Orientation::Vertical => horizontal_line(position, plot.transform, line_color),
};
shapes.push(line);
};

for pos in elem.values_with_ruler() {
push_value_ruler(pos, shapes);
cursors.push(match orientation {
Orientation::Horizontal => Cursor::Vertical { x: pos.x },
Orientation::Vertical => Cursor::Horizontal { y: pos.y },
});
}
}

Expand Down Expand Up @@ -1656,14 +1662,14 @@ pub(super) fn rulers_at_value(
name: &str,
plot: &PlotConfig<'_>,
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
label_formatter: &LabelFormatter,
) {
let line_color = rulers_color(plot.ui);
if plot.show_x {
shapes.push(vertical_line(pointer, plot.transform, line_color));
cursors.push(Cursor::Vertical { x: value.x });
}
if plot.show_y {
shapes.push(horizontal_line(pointer, plot.transform, line_color));
cursors.push(Cursor::Horizontal { y: value.y });
}

let mut prefix = String::new();
Expand Down
Loading

0 comments on commit 9be060f

Please sign in to comment.