Plot interaction methods (#766)
* move to a basic plot builder with callback * add some interaction methods * move interaction demo to its own panel
This commit is contained in:
parent
6018c0e194
commit
0bad1d0c99
|
|
@ -12,6 +12,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
|||
* You can now read and write the cursor of a `TextEdit` ([#848](https://github.com/emilk/egui/pull/848)).
|
||||
* Most widgets containing text (`Label`, `Button` etc) now supports rich text ([#855](https://github.com/emilk/egui/pull/855)).
|
||||
* When using a custom font you can now specify a font index ([#873](https://github.com/emilk/egui/pull/873)).
|
||||
* You can now read the plot coordinates of the mouse when building a `Plot` ([#766](https://github.com/emilk/egui/pull/766)).
|
||||
|
||||
### Changed 🔧
|
||||
* Unifiy the four `Memory` data buckets (`data`, `data_temp`, `id_data` and `id_data_temp`) into a single `Memory::data`, with a new interface ([#836](https://github.com/emilk/egui/pull/836)).
|
||||
|
|
@ -19,6 +20,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
|||
* Replace `CtxRef::begin_frame` and `end_frame` with `CtxRef::run` ([#872](https://github.com/emilk/egui/pull/872)).
|
||||
* Replace `Ui::__test` with `egui::__run_test_ui` ([#872](https://github.com/emilk/egui/pull/872)).
|
||||
* Replace `scroll_delta` and `zoom_delta` in `RawInput` with `Event::Scroll` and `Event::Zoom`.
|
||||
* Plots now provide a `show` method that has to be used to add items to and show the plot ([#766](https://github.com/emilk/egui/pull/766)).
|
||||
|
||||
### Fixed 🐛
|
||||
* Fix `ComboBox` and other popups getting clipped to parent window ([#885](https://github.com/emilk/egui/pull/885)).
|
||||
|
|
@ -33,6 +35,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
|||
* [sumibi-yakitori](https://github.com/sumibi-yakitori) ([#830](https://github.com/emilk/egui/pull/830))
|
||||
* [5225225](https://github.com/5225225): ([#849](https://github.com/emilk/egui/pull/849)).
|
||||
* [t18b219k](https://github.com/t18b219k): ([#868](https://github.com/emilk/egui/pull/868)).
|
||||
* [EmbersArc](https://github.com/EmbersArc): ([#766](https://github.com/emilk/egui/pull/766)).
|
||||
|
||||
|
||||
## 0.15.0 - 2021-10-24 - Syntax highlighting and hscroll
|
||||
|
|
|
|||
|
|
@ -30,6 +30,16 @@ impl Value {
|
|||
y: y.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_pos2(self) -> Pos2 {
|
||||
Pos2::new(self.x as f32, self.y as f32)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_vec2(self) -> Vec2 {
|
||||
Vec2::new(self.x as f32, self.y as f32)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct PlotMemory {
|
|||
hovered_entry: Option<String>,
|
||||
hidden_items: AHashSet<String>,
|
||||
min_auto_bounds: Bounds,
|
||||
last_screen_transform: Option<ScreenTransform>,
|
||||
}
|
||||
|
||||
impl PlotMemory {
|
||||
|
|
@ -54,16 +55,11 @@ impl PlotMemory {
|
|||
/// Value::new(x, x.sin())
|
||||
/// });
|
||||
/// let line = Line::new(Values::from_values_iter(sin));
|
||||
/// ui.add(
|
||||
/// Plot::new("my_plot").line(line).view_aspect(2.0)
|
||||
/// );
|
||||
/// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line));
|
||||
/// # });
|
||||
/// ```
|
||||
pub struct Plot {
|
||||
id_source: Id,
|
||||
next_auto_color_idx: usize,
|
||||
|
||||
items: Vec<Box<dyn PlotItem>>,
|
||||
|
||||
center_x_axis: bool,
|
||||
center_y_axis: bool,
|
||||
|
|
@ -90,9 +86,6 @@ impl Plot {
|
|||
pub fn new(id_source: impl std::hash::Hash) -> Self {
|
||||
Self {
|
||||
id_source: Id::new(id_source),
|
||||
next_auto_color_idx: 0,
|
||||
|
||||
items: Default::default(),
|
||||
|
||||
center_x_axis: false,
|
||||
center_y_axis: false,
|
||||
|
|
@ -115,108 +108,6 @@ impl Plot {
|
|||
}
|
||||
}
|
||||
|
||||
fn auto_color(&mut self) -> Color32 {
|
||||
let i = self.next_auto_color_idx;
|
||||
self.next_auto_color_idx += 1;
|
||||
let golden_ratio = (5.0_f32.sqrt() - 1.0) / 2.0; // 0.61803398875
|
||||
let h = i as f32 * golden_ratio;
|
||||
Hsva::new(h, 0.85, 0.5, 1.0).into() // TODO: OkLab or some other perspective color space
|
||||
}
|
||||
|
||||
/// Add a data lines.
|
||||
pub fn line(mut self, mut line: Line) -> Self {
|
||||
if line.series.is_empty() {
|
||||
return self;
|
||||
};
|
||||
|
||||
// Give the stroke an automatic color if no color has been assigned.
|
||||
if line.stroke.color == Color32::TRANSPARENT {
|
||||
line.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(line));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a polygon. The polygon has to be convex.
|
||||
pub fn polygon(mut self, mut polygon: Polygon) -> Self {
|
||||
if polygon.series.is_empty() {
|
||||
return self;
|
||||
};
|
||||
|
||||
// Give the stroke an automatic color if no color has been assigned.
|
||||
if polygon.stroke.color == Color32::TRANSPARENT {
|
||||
polygon.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(polygon));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a text.
|
||||
pub fn text(mut self, text: Text) -> Self {
|
||||
if text.text.is_empty() {
|
||||
return self;
|
||||
};
|
||||
|
||||
self.items.push(Box::new(text));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add data points.
|
||||
pub fn points(mut self, mut points: Points) -> Self {
|
||||
if points.series.is_empty() {
|
||||
return self;
|
||||
};
|
||||
|
||||
// Give the points an automatic color if no color has been assigned.
|
||||
if points.color == Color32::TRANSPARENT {
|
||||
points.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(points));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add arrows.
|
||||
pub fn arrows(mut self, mut arrows: Arrows) -> Self {
|
||||
if arrows.origins.is_empty() || arrows.tips.is_empty() {
|
||||
return self;
|
||||
};
|
||||
|
||||
// Give the arrows an automatic color if no color has been assigned.
|
||||
if arrows.color == Color32::TRANSPARENT {
|
||||
arrows.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(arrows));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an image.
|
||||
pub fn image(mut self, image: PlotImage) -> Self {
|
||||
self.items.push(Box::new(image));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a horizontal line.
|
||||
/// Can be useful e.g. to show min/max bounds or similar.
|
||||
/// Always fills the full width of the plot.
|
||||
pub fn hline(mut self, mut hline: HLine) -> Self {
|
||||
if hline.stroke.color == Color32::TRANSPARENT {
|
||||
hline.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(hline));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a vertical line.
|
||||
/// Can be useful e.g. to show min/max bounds or similar.
|
||||
/// Always fills the full height of the plot.
|
||||
pub fn vline(mut self, mut vline: VLine) -> Self {
|
||||
if vline.stroke.color == Color32::TRANSPARENT {
|
||||
vline.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(vline));
|
||||
self
|
||||
}
|
||||
|
||||
/// width / height ratio of the data.
|
||||
/// For instance, it can be useful to set this to `1.0` for when the two axes show the same
|
||||
/// unit.
|
||||
|
|
@ -326,14 +217,11 @@ impl Plot {
|
|||
self.show_axes = show;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Plot {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
/// Interact with and add items to the plot and finally draw it.
|
||||
pub fn show(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi)) -> Response {
|
||||
let Self {
|
||||
id_source,
|
||||
next_auto_color_idx: _,
|
||||
mut items,
|
||||
center_x_axis,
|
||||
center_y_axis,
|
||||
allow_zoom,
|
||||
|
|
@ -359,6 +247,7 @@ impl Widget for Plot {
|
|||
hovered_entry: None,
|
||||
hidden_items: Default::default(),
|
||||
min_auto_bounds,
|
||||
last_screen_transform: None,
|
||||
});
|
||||
|
||||
// If the min bounds changed, recalculate everything.
|
||||
|
|
@ -378,6 +267,7 @@ impl Widget for Plot {
|
|||
mut auto_bounds,
|
||||
mut hovered_entry,
|
||||
mut hidden_items,
|
||||
last_screen_transform,
|
||||
..
|
||||
} = memory;
|
||||
|
||||
|
|
@ -408,6 +298,20 @@ impl Widget for Plot {
|
|||
let (rect, response) = ui.allocate_exact_size(size, Sense::drag());
|
||||
let plot_painter = ui.painter().sub_region(rect);
|
||||
|
||||
// Call the plot build function.
|
||||
let mut plot_ui = PlotUi {
|
||||
items: Vec::new(),
|
||||
next_auto_color_idx: 0,
|
||||
last_screen_transform,
|
||||
response,
|
||||
};
|
||||
build_fn(&mut plot_ui);
|
||||
let PlotUi {
|
||||
mut items,
|
||||
response,
|
||||
..
|
||||
} = plot_ui;
|
||||
|
||||
// Background
|
||||
if show_background {
|
||||
plot_painter.add(epaint::RectShape {
|
||||
|
|
@ -421,7 +325,6 @@ impl Widget for Plot {
|
|||
// Legend
|
||||
let legend = legend_config
|
||||
.and_then(|config| LegendWidget::try_new(rect, config, &items, &hidden_items));
|
||||
|
||||
// Don't show hover cursor when hovering over legend.
|
||||
if hovered_entry.is_some() {
|
||||
show_x = false;
|
||||
|
|
@ -439,6 +342,7 @@ impl Widget for Plot {
|
|||
// Move highlighted items to front.
|
||||
items.sort_by_key(|item| item.highlighted());
|
||||
|
||||
// Allow double clicking to reset to automatic bounds.
|
||||
auto_bounds |= response.double_clicked_by(PointerButton::Primary);
|
||||
|
||||
// Set bounds automatically based on content.
|
||||
|
|
@ -449,18 +353,6 @@ impl Widget for Plot {
|
|||
.for_each(|item| bounds.merge(&item.get_bounds()));
|
||||
bounds.add_relative_margin(margin_fraction);
|
||||
}
|
||||
// Make sure they are not empty.
|
||||
if !bounds.is_valid() {
|
||||
bounds = Bounds::new_symmetrical(1.0);
|
||||
}
|
||||
|
||||
// Scale axes so that the origin is in the center.
|
||||
if center_x_axis {
|
||||
bounds.make_x_symmetrical();
|
||||
};
|
||||
if center_y_axis {
|
||||
bounds.make_y_symmetrical();
|
||||
};
|
||||
|
||||
let mut transform = ScreenTransform::new(rect, bounds, center_x_axis, center_y_axis);
|
||||
|
||||
|
|
@ -503,12 +395,12 @@ impl Widget for Plot {
|
|||
|
||||
let bounds = *transform.bounds();
|
||||
|
||||
let prepared = Prepared {
|
||||
let prepared = PreparedPlot {
|
||||
items,
|
||||
show_x,
|
||||
show_y,
|
||||
show_axes,
|
||||
transform,
|
||||
transform: transform.clone(),
|
||||
};
|
||||
prepared.ui(ui, &response);
|
||||
|
||||
|
|
@ -524,6 +416,7 @@ impl Widget for Plot {
|
|||
hovered_entry,
|
||||
hidden_items,
|
||||
min_auto_bounds,
|
||||
last_screen_transform: Some(transform),
|
||||
};
|
||||
memory.store(ui.ctx(), plot_id);
|
||||
|
||||
|
|
@ -535,7 +428,143 @@ impl Widget for Plot {
|
|||
}
|
||||
}
|
||||
|
||||
struct Prepared {
|
||||
/// Provides methods to interact with a plot while building it. It is the single argument of the closure
|
||||
/// provided to `Plot::show`. See [`Plot`] for an example of how to use it.
|
||||
pub struct PlotUi {
|
||||
items: Vec<Box<dyn PlotItem>>,
|
||||
next_auto_color_idx: usize,
|
||||
last_screen_transform: Option<ScreenTransform>,
|
||||
response: Response,
|
||||
}
|
||||
|
||||
impl PlotUi {
|
||||
fn auto_color(&mut self) -> Color32 {
|
||||
let i = self.next_auto_color_idx;
|
||||
self.next_auto_color_idx += 1;
|
||||
let golden_ratio = (5.0_f32.sqrt() - 1.0) / 2.0; // 0.61803398875
|
||||
let h = i as f32 * golden_ratio;
|
||||
Hsva::new(h, 0.85, 0.5, 1.0).into() // TODO: OkLab or some other perspective color space
|
||||
}
|
||||
|
||||
/// The pointer position in plot coordinates, if the pointer is inside the plot area.
|
||||
pub fn pointer_coordinate(&self) -> Option<Value> {
|
||||
let last_screen_transform = self.last_screen_transform.as_ref()?;
|
||||
// We need to subtract the drag delta to keep in sync with the frame-delayed screen transform:
|
||||
let last_pos = self.response.hover_pos()? - self.response.drag_delta();
|
||||
let value = last_screen_transform.value_from_position(last_pos);
|
||||
Some(value)
|
||||
}
|
||||
|
||||
/// The pointer drag delta in plot coordinates.
|
||||
pub fn pointer_coordinate_drag_delta(&self) -> Vec2 {
|
||||
self.last_screen_transform
|
||||
.as_ref()
|
||||
.map_or(Vec2::ZERO, |tf| {
|
||||
let delta = self.response.drag_delta();
|
||||
let dp_dv = tf.dpos_dvalue();
|
||||
Vec2::new(delta.x / dp_dv[0] as f32, delta.y / dp_dv[1] as f32)
|
||||
})
|
||||
}
|
||||
|
||||
/// Transform the screen coordinates to plot coordinates.
|
||||
pub fn plot_from_screen(&self, position: Pos2) -> Pos2 {
|
||||
self.last_screen_transform
|
||||
.as_ref()
|
||||
.map_or(Pos2::ZERO, |tf| {
|
||||
tf.position_from_value(&Value::new(position.x as f64, position.y as f64))
|
||||
})
|
||||
// We need to subtract the drag delta since the last frame.
|
||||
- self.response.drag_delta()
|
||||
}
|
||||
|
||||
/// Add a data line.
|
||||
pub fn line(&mut self, mut line: Line) {
|
||||
if line.series.is_empty() {
|
||||
return;
|
||||
};
|
||||
|
||||
// Give the stroke an automatic color if no color has been assigned.
|
||||
if line.stroke.color == Color32::TRANSPARENT {
|
||||
line.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(line));
|
||||
}
|
||||
|
||||
/// Add a polygon. The polygon has to be convex.
|
||||
pub fn polygon(&mut self, mut polygon: Polygon) {
|
||||
if polygon.series.is_empty() {
|
||||
return;
|
||||
};
|
||||
|
||||
// Give the stroke an automatic color if no color has been assigned.
|
||||
if polygon.stroke.color == Color32::TRANSPARENT {
|
||||
polygon.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(polygon));
|
||||
}
|
||||
|
||||
/// Add a text.
|
||||
pub fn text(&mut self, text: Text) {
|
||||
if text.text.is_empty() {
|
||||
return;
|
||||
};
|
||||
|
||||
self.items.push(Box::new(text));
|
||||
}
|
||||
|
||||
/// Add data points.
|
||||
pub fn points(&mut self, mut points: Points) {
|
||||
if points.series.is_empty() {
|
||||
return;
|
||||
};
|
||||
|
||||
// Give the points an automatic color if no color has been assigned.
|
||||
if points.color == Color32::TRANSPARENT {
|
||||
points.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(points));
|
||||
}
|
||||
|
||||
/// Add arrows.
|
||||
pub fn arrows(&mut self, mut arrows: Arrows) {
|
||||
if arrows.origins.is_empty() || arrows.tips.is_empty() {
|
||||
return;
|
||||
};
|
||||
|
||||
// Give the arrows an automatic color if no color has been assigned.
|
||||
if arrows.color == Color32::TRANSPARENT {
|
||||
arrows.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(arrows));
|
||||
}
|
||||
|
||||
/// Add an image.
|
||||
pub fn image(&mut self, image: PlotImage) {
|
||||
self.items.push(Box::new(image));
|
||||
}
|
||||
|
||||
/// Add a horizontal line.
|
||||
/// Can be useful e.g. to show min/max bounds or similar.
|
||||
/// Always fills the full width of the plot.
|
||||
pub fn hline(&mut self, mut hline: HLine) {
|
||||
if hline.stroke.color == Color32::TRANSPARENT {
|
||||
hline.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(hline));
|
||||
}
|
||||
|
||||
/// Add a vertical line.
|
||||
/// Can be useful e.g. to show min/max bounds or similar.
|
||||
/// Always fills the full height of the plot.
|
||||
pub fn vline(&mut self, mut vline: VLine) {
|
||||
if vline.stroke.color == Color32::TRANSPARENT {
|
||||
vline.stroke.color = self.auto_color();
|
||||
}
|
||||
self.items.push(Box::new(vline));
|
||||
}
|
||||
}
|
||||
|
||||
struct PreparedPlot {
|
||||
items: Vec<Box<dyn PlotItem>>,
|
||||
show_x: bool,
|
||||
show_y: bool,
|
||||
|
|
@ -543,7 +572,7 @@ struct Prepared {
|
|||
transform: ScreenTransform,
|
||||
}
|
||||
|
||||
impl Prepared {
|
||||
impl PreparedPlot {
|
||||
fn ui(self, ui: &mut Ui, response: &Response) {
|
||||
let mut shapes = Vec::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ impl Bounds {
|
|||
}
|
||||
|
||||
/// Contains the screen rectangle and the plot bounds and provides methods to transform them.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ScreenTransform {
|
||||
/// The screen rectangle.
|
||||
|
|
@ -131,7 +132,20 @@ pub(crate) struct ScreenTransform {
|
|||
}
|
||||
|
||||
impl ScreenTransform {
|
||||
pub fn new(frame: Rect, bounds: Bounds, x_centered: bool, y_centered: bool) -> Self {
|
||||
pub fn new(frame: Rect, mut bounds: Bounds, x_centered: bool, y_centered: bool) -> Self {
|
||||
// Make sure they are not empty.
|
||||
if !bounds.is_valid() {
|
||||
bounds = Bounds::new_symmetrical(1.0);
|
||||
}
|
||||
|
||||
// Scale axes so that the origin is in the center.
|
||||
if x_centered {
|
||||
bounds.make_x_symmetrical();
|
||||
};
|
||||
if y_centered {
|
||||
bounds.make_y_symmetrical();
|
||||
};
|
||||
|
||||
Self {
|
||||
frame,
|
||||
bounds,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl super::View for ContextMenus {
|
|||
|
||||
ui.label("Right-click plot to edit it!");
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(self.example_plot()).context_menu(|ui| {
|
||||
self.example_plot(ui).context_menu(|ui| {
|
||||
ui.menu_button("Plot", |ui| {
|
||||
if ui.radio_value(&mut self.plot, Plot::Sin, "Sin").clicked()
|
||||
|| ui
|
||||
|
|
@ -109,7 +109,7 @@ impl super::View for ContextMenus {
|
|||
}
|
||||
|
||||
impl ContextMenus {
|
||||
fn example_plot(&self) -> egui::plot::Plot {
|
||||
fn example_plot(&self, ui: &mut egui::Ui) -> egui::Response {
|
||||
use egui::plot::{Line, Value, Values};
|
||||
let n = 128;
|
||||
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
|
||||
|
|
@ -127,10 +127,10 @@ impl ContextMenus {
|
|||
.allow_zoom(self.allow_zoom)
|
||||
.center_x_axis(self.center_x_axis)
|
||||
.center_x_axis(self.center_y_axis)
|
||||
.line(line)
|
||||
.width(self.width)
|
||||
.height(self.height)
|
||||
.data_aspect(1.0)
|
||||
.show(ui, |plot_ui| plot_ui.line(line))
|
||||
}
|
||||
|
||||
fn nested_menus(ui: &mut egui::Ui) {
|
||||
|
|
|
|||
|
|
@ -143,18 +143,18 @@ impl Widget for &mut LineDemo {
|
|||
ui.ctx().request_repaint();
|
||||
self.time += ui.input().unstable_dt.at_most(1.0 / 30.0) as f64;
|
||||
};
|
||||
let mut plot = Plot::new("lines_demo")
|
||||
.line(self.circle())
|
||||
.line(self.sin())
|
||||
.line(self.thingy())
|
||||
.legend(Legend::default());
|
||||
let mut plot = Plot::new("lines_demo").legend(Legend::default());
|
||||
if self.square {
|
||||
plot = plot.view_aspect(1.0);
|
||||
}
|
||||
if self.proportional {
|
||||
plot = plot.data_aspect(1.0);
|
||||
}
|
||||
ui.add(plot)
|
||||
plot.show(ui, |plot_ui| {
|
||||
plot_ui.line(self.circle());
|
||||
plot_ui.line(self.sin());
|
||||
plot_ui.line(self.thingy());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,13 +222,14 @@ impl Widget for &mut MarkerDemo {
|
|||
}
|
||||
});
|
||||
|
||||
let mut markers_plot = Plot::new("markers_demo")
|
||||
let markers_plot = Plot::new("markers_demo")
|
||||
.data_aspect(1.0)
|
||||
.legend(Legend::default());
|
||||
for marker in self.markers() {
|
||||
markers_plot = markers_plot.points(marker);
|
||||
}
|
||||
ui.add(markers_plot)
|
||||
markers_plot.show(ui, |plot_ui| {
|
||||
for marker in self.markers() {
|
||||
plot_ui.points(marker);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,15 +288,14 @@ impl Widget for &mut LegendDemo {
|
|||
ui.end_row();
|
||||
});
|
||||
|
||||
let legend_plot = Plot::new("legend_demo")
|
||||
.line(LegendDemo::line_with_slope(0.5).name("lines"))
|
||||
.line(LegendDemo::line_with_slope(1.0).name("lines"))
|
||||
.line(LegendDemo::line_with_slope(2.0).name("lines"))
|
||||
.line(LegendDemo::sin().name("sin(x)"))
|
||||
.line(LegendDemo::cos().name("cos(x)"))
|
||||
.legend(*config)
|
||||
.data_aspect(1.0);
|
||||
ui.add(legend_plot)
|
||||
let legend_plot = Plot::new("legend_demo").legend(*config).data_aspect(1.0);
|
||||
legend_plot.show(ui, |plot_ui| {
|
||||
plot_ui.line(LegendDemo::line_with_slope(0.5).name("lines"));
|
||||
plot_ui.line(LegendDemo::line_with_slope(1.0).name("lines"));
|
||||
plot_ui.line(LegendDemo::line_with_slope(2.0).name("lines"));
|
||||
plot_ui.line(LegendDemo::sin().name("sin(x)"));
|
||||
plot_ui.line(LegendDemo::cos().name("cos(x)"));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -347,24 +347,64 @@ impl Widget for &mut ItemsDemo {
|
|||
);
|
||||
|
||||
let plot = Plot::new("items_demo")
|
||||
.hline(HLine::new(9.0).name("Lines horizontal"))
|
||||
.hline(HLine::new(-9.0).name("Lines horizontal"))
|
||||
.vline(VLine::new(9.0).name("Lines vertical"))
|
||||
.vline(VLine::new(-9.0).name("Lines vertical"))
|
||||
.line(line.name("Line with fill"))
|
||||
.polygon(polygon.name("Convex polygon"))
|
||||
.points(points.name("Points with stems"))
|
||||
.text(Text::new(Value::new(-3.0, -3.0), "wow").name("Text"))
|
||||
.text(Text::new(Value::new(-2.0, 2.5), "so graph").name("Text"))
|
||||
.text(Text::new(Value::new(3.0, 3.0), "much color").name("Text"))
|
||||
.text(Text::new(Value::new(2.5, -2.0), "such plot").name("Text"))
|
||||
.image(image.name("Image"))
|
||||
.arrows(arrows.name("Arrows"))
|
||||
.legend(Legend::default().position(Corner::RightBottom))
|
||||
.show_x(false)
|
||||
.show_y(false)
|
||||
.data_aspect(1.0);
|
||||
ui.add(plot)
|
||||
plot.show(ui, |plot_ui| {
|
||||
plot_ui.hline(HLine::new(9.0).name("Lines horizontal"));
|
||||
plot_ui.hline(HLine::new(-9.0).name("Lines horizontal"));
|
||||
plot_ui.vline(VLine::new(9.0).name("Lines vertical"));
|
||||
plot_ui.vline(VLine::new(-9.0).name("Lines vertical"));
|
||||
plot_ui.line(line.name("Line with fill"));
|
||||
plot_ui.polygon(polygon.name("Convex polygon"));
|
||||
plot_ui.points(points.name("Points with stems"));
|
||||
plot_ui.text(Text::new(Value::new(-3.0, -3.0), "wow").name("Text"));
|
||||
plot_ui.text(Text::new(Value::new(-2.0, 2.5), "so graph").name("Text"));
|
||||
plot_ui.text(Text::new(Value::new(3.0, 3.0), "much color").name("Text"));
|
||||
plot_ui.text(Text::new(Value::new(2.5, -2.0), "such plot").name("Text"));
|
||||
plot_ui.image(image.name("Image"));
|
||||
plot_ui.arrows(arrows.name("Arrows"));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct InteractionDemo {
|
||||
pointer_coordinate: Option<Value>,
|
||||
pointer_coordinate_drag_delta: Vec2,
|
||||
}
|
||||
|
||||
impl Default for InteractionDemo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pointer_coordinate: None,
|
||||
pointer_coordinate_drag_delta: Vec2::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut InteractionDemo {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
let coordinate_text = if let Some(coordinate) = self.pointer_coordinate {
|
||||
format!("x: {:.03}, y: {:.03}", coordinate.x, coordinate.y)
|
||||
} else {
|
||||
"None".to_string()
|
||||
};
|
||||
ui.label(format!("pointer coordinate: {}", coordinate_text));
|
||||
let coordinate_text = format!(
|
||||
"x: {:.03}, y: {:.03}",
|
||||
self.pointer_coordinate_drag_delta.x, self.pointer_coordinate_drag_delta.y
|
||||
);
|
||||
ui.label(format!(
|
||||
"pointer coordinate drag delta: {}",
|
||||
coordinate_text
|
||||
));
|
||||
let plot = Plot::new("interaction_demo");
|
||||
plot.show(ui, |plot_ui| {
|
||||
self.pointer_coordinate = plot_ui.pointer_coordinate();
|
||||
self.pointer_coordinate_drag_delta = plot_ui.pointer_coordinate_drag_delta();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,6 +414,7 @@ enum Panel {
|
|||
Markers,
|
||||
Legend,
|
||||
Items,
|
||||
Interaction,
|
||||
}
|
||||
|
||||
impl Default for Panel {
|
||||
|
|
@ -388,6 +429,7 @@ pub struct PlotDemo {
|
|||
marker_demo: MarkerDemo,
|
||||
legend_demo: LegendDemo,
|
||||
items_demo: ItemsDemo,
|
||||
interaction_demo: InteractionDemo,
|
||||
open_panel: Panel,
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +455,7 @@ impl super::View for PlotDemo {
|
|||
ui.collapsing("Instructions", |ui| {
|
||||
ui.label("Pan by dragging, or scroll (+ shift = horizontal).");
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
ui.label("Zoom with ctrl / ⌘ + mouse wheel, or with pinch gesture.");
|
||||
ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture.");
|
||||
} else if cfg!(target_os = "macos") {
|
||||
ui.label("Zoom with ctrl / ⌘ + scroll.");
|
||||
} else {
|
||||
|
|
@ -429,6 +471,7 @@ impl super::View for PlotDemo {
|
|||
ui.selectable_value(&mut self.open_panel, Panel::Markers, "Markers");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Legend, "Legend");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Items, "Items");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction");
|
||||
});
|
||||
ui.separator();
|
||||
|
||||
|
|
@ -445,6 +488,9 @@ impl super::View for PlotDemo {
|
|||
Panel::Items => {
|
||||
ui.add(&mut self.items_demo);
|
||||
}
|
||||
Panel::Interaction => {
|
||||
ui.add(&mut self.interaction_demo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,8 +211,7 @@ impl WidgetGallery {
|
|||
ui.end_row();
|
||||
|
||||
ui.add(doc_link_label("Plot", "plot"));
|
||||
ui.add(example_plot());
|
||||
|
||||
example_plot(ui);
|
||||
ui.end_row();
|
||||
|
||||
ui.hyperlink_to(
|
||||
|
|
@ -227,7 +226,7 @@ impl WidgetGallery {
|
|||
}
|
||||
}
|
||||
|
||||
fn example_plot() -> egui::plot::Plot {
|
||||
fn example_plot(ui: &mut egui::Ui) -> egui::Response {
|
||||
use egui::plot::{Line, Value, Values};
|
||||
let n = 128;
|
||||
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
|
||||
|
|
@ -236,9 +235,9 @@ fn example_plot() -> egui::plot::Plot {
|
|||
Value::new(x, x.sin())
|
||||
})));
|
||||
egui::plot::Plot::new("example_plot")
|
||||
.line(line)
|
||||
.height(32.0)
|
||||
.data_aspect(1.0)
|
||||
.show(ui, |plot_ui| plot_ui.line(line))
|
||||
}
|
||||
|
||||
fn doc_link_label<'a>(title: &'a str, search_term: &'a str) -> impl egui::Widget + 'a {
|
||||
|
|
|
|||
Loading…
Reference in New Issue