parent
e4e1638fc0
commit
99af63fad2
5 changed files with 80 additions and 12 deletions
|
@ -8,8 +8,11 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
|
||||||
### Added ⭐
|
### Added ⭐
|
||||||
* `Event::Key` now has a `repeat` field that is set to `true` if the event was the result of a key-repeat ([#2435](https://github.com/emilk/egui/pull/2435)).
|
* `Event::Key` now has a `repeat` field that is set to `true` if the event was the result of a key-repeat ([#2435](https://github.com/emilk/egui/pull/2435)).
|
||||||
* Add `Slider::drag_value_speed`, which lets you ask for finer precision when dragging the slider value rather than the actual slider.
|
* Add `Slider::drag_value_speed`, which lets you ask for finer precision when dragging the slider value rather than the actual slider.
|
||||||
* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).
|
|
||||||
* Add `Memory::any_popup_open`, which returns true if any popup is currently open ([#2464](https://github.com/emilk/egui/pull/2464)).
|
* Add `Memory::any_popup_open`, which returns true if any popup is currently open ([#2464](https://github.com/emilk/egui/pull/2464)).
|
||||||
|
* Add `Plot::clamp_grid` to only show grid where there is data ([#2480](https://github.com/emilk/egui/pull/2480)).
|
||||||
|
|
||||||
|
### Changed 🔧
|
||||||
|
* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)).
|
* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)).
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub(super) struct PlotConfig<'a> {
|
||||||
pub(super) trait PlotItem {
|
pub(super) trait PlotItem {
|
||||||
fn shapes(&self, ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec<Shape>);
|
fn shapes(&self, ui: &mut Ui, transform: &ScreenTransform, shapes: &mut Vec<Shape>);
|
||||||
|
|
||||||
|
/// For plot-items which are generated based on x values (plotting functions).
|
||||||
fn initialize(&mut self, x_range: RangeInclusive<f64>);
|
fn initialize(&mut self, x_range: RangeInclusive<f64>);
|
||||||
|
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
|
|
|
@ -291,8 +291,10 @@ pub struct Plot {
|
||||||
legend_config: Option<Legend>,
|
legend_config: Option<Legend>,
|
||||||
show_background: bool,
|
show_background: bool,
|
||||||
show_axes: [bool; 2],
|
show_axes: [bool; 2],
|
||||||
|
|
||||||
grid_spacers: [GridSpacer; 2],
|
grid_spacers: [GridSpacer; 2],
|
||||||
sharp_grid_lines: bool,
|
sharp_grid_lines: bool,
|
||||||
|
clamp_grid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plot {
|
impl Plot {
|
||||||
|
@ -331,8 +333,10 @@ impl Plot {
|
||||||
legend_config: None,
|
legend_config: None,
|
||||||
show_background: true,
|
show_background: true,
|
||||||
show_axes: [true; 2],
|
show_axes: [true; 2],
|
||||||
|
|
||||||
grid_spacers: [log_grid_spacer(10), log_grid_spacer(10)],
|
grid_spacers: [log_grid_spacer(10), log_grid_spacer(10)],
|
||||||
sharp_grid_lines: true,
|
sharp_grid_lines: true,
|
||||||
|
clamp_grid: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +561,14 @@ impl Plot {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clamp the grid to only be visible at the range of data where we have values.
|
||||||
|
///
|
||||||
|
/// Default: `false`.
|
||||||
|
pub fn clamp_grid(mut self, clamp_grid: bool) -> Self {
|
||||||
|
self.clamp_grid = clamp_grid;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand bounds to include the given x value.
|
/// Expand bounds to include the given x value.
|
||||||
/// For instance, to always show the y axis, call `plot.include_x(0.0)`.
|
/// For instance, to always show the y axis, call `plot.include_x(0.0)`.
|
||||||
pub fn include_x(mut self, x: impl Into<f64>) -> Self {
|
pub fn include_x(mut self, x: impl Into<f64>) -> Self {
|
||||||
|
@ -671,6 +683,8 @@ impl Plot {
|
||||||
show_axes,
|
show_axes,
|
||||||
linked_axes,
|
linked_axes,
|
||||||
linked_cursors,
|
linked_cursors,
|
||||||
|
|
||||||
|
clamp_grid,
|
||||||
grid_spacers,
|
grid_spacers,
|
||||||
sharp_grid_lines,
|
sharp_grid_lines,
|
||||||
} = self;
|
} = self;
|
||||||
|
@ -971,11 +985,12 @@ impl Plot {
|
||||||
axis_formatters,
|
axis_formatters,
|
||||||
show_axes,
|
show_axes,
|
||||||
transform: transform.clone(),
|
transform: transform.clone(),
|
||||||
grid_spacers,
|
|
||||||
draw_cursor_x: linked_cursors.as_ref().map_or(false, |group| group.link_x),
|
draw_cursor_x: linked_cursors.as_ref().map_or(false, |group| group.link_x),
|
||||||
draw_cursor_y: linked_cursors.as_ref().map_or(false, |group| group.link_y),
|
draw_cursor_y: linked_cursors.as_ref().map_or(false, |group| group.link_y),
|
||||||
draw_cursors,
|
draw_cursors,
|
||||||
|
grid_spacers,
|
||||||
sharp_grid_lines,
|
sharp_grid_lines,
|
||||||
|
clamp_grid,
|
||||||
};
|
};
|
||||||
let plot_cursors = prepared.ui(ui, &response);
|
let plot_cursors = prepared.ui(ui, &response);
|
||||||
|
|
||||||
|
@ -1297,11 +1312,13 @@ struct PreparedPlot {
|
||||||
axis_formatters: [AxisFormatter; 2],
|
axis_formatters: [AxisFormatter; 2],
|
||||||
show_axes: [bool; 2],
|
show_axes: [bool; 2],
|
||||||
transform: ScreenTransform,
|
transform: ScreenTransform,
|
||||||
grid_spacers: [GridSpacer; 2],
|
|
||||||
draw_cursor_x: bool,
|
draw_cursor_x: bool,
|
||||||
draw_cursor_y: bool,
|
draw_cursor_y: bool,
|
||||||
draw_cursors: Vec<Cursor>,
|
draw_cursors: Vec<Cursor>,
|
||||||
|
|
||||||
|
grid_spacers: [GridSpacer; 2],
|
||||||
sharp_grid_lines: bool,
|
sharp_grid_lines: bool,
|
||||||
|
clamp_grid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PreparedPlot {
|
impl PreparedPlot {
|
||||||
|
@ -1400,10 +1417,13 @@ impl PreparedPlot {
|
||||||
shapes: &mut Vec<(Shape, f32)>,
|
shapes: &mut Vec<(Shape, f32)>,
|
||||||
sharp_grid_lines: bool,
|
sharp_grid_lines: bool,
|
||||||
) {
|
) {
|
||||||
|
#![allow(clippy::collapsible_else_if)]
|
||||||
|
|
||||||
let Self {
|
let Self {
|
||||||
transform,
|
transform,
|
||||||
axis_formatters,
|
axis_formatters,
|
||||||
grid_spacers,
|
grid_spacers,
|
||||||
|
clamp_grid,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
@ -1417,7 +1437,6 @@ impl PreparedPlot {
|
||||||
let font_id = TextStyle::Body.resolve(ui.style());
|
let font_id = TextStyle::Body.resolve(ui.style());
|
||||||
|
|
||||||
// Where on the cross-dimension to show the label values
|
// Where on the cross-dimension to show the label values
|
||||||
let bounds = transform.bounds();
|
|
||||||
let value_cross = 0.0_f64.clamp(bounds.min[1 - axis], bounds.max[1 - axis]);
|
let value_cross = 0.0_f64.clamp(bounds.min[1 - axis], bounds.max[1 - axis]);
|
||||||
|
|
||||||
let input = GridInput {
|
let input = GridInput {
|
||||||
|
@ -1426,9 +1445,31 @@ impl PreparedPlot {
|
||||||
};
|
};
|
||||||
let steps = (grid_spacers[axis])(input);
|
let steps = (grid_spacers[axis])(input);
|
||||||
|
|
||||||
|
let clamp_range = clamp_grid.then(|| {
|
||||||
|
let mut tight_bounds = PlotBounds::NOTHING;
|
||||||
|
for item in &self.items {
|
||||||
|
let item_bounds = item.bounds();
|
||||||
|
tight_bounds.merge_x(&item_bounds);
|
||||||
|
tight_bounds.merge_y(&item_bounds);
|
||||||
|
}
|
||||||
|
tight_bounds
|
||||||
|
});
|
||||||
|
|
||||||
for step in steps {
|
for step in steps {
|
||||||
let value_main = step.value;
|
let value_main = step.value;
|
||||||
|
|
||||||
|
if let Some(clamp_range) = clamp_range {
|
||||||
|
if axis == 0 {
|
||||||
|
if !clamp_range.range_x().contains(&value_main) {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if !clamp_range.range_y().contains(&value_main) {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let value = if axis == 0 {
|
let value = if axis == 0 {
|
||||||
PlotPoint::new(value_main, value_cross)
|
PlotPoint::new(value_main, value_cross)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1451,11 +1492,23 @@ impl PreparedPlot {
|
||||||
let mut p1 = pos_in_gui;
|
let mut p1 = pos_in_gui;
|
||||||
p0[1 - axis] = transform.frame().min[1 - axis];
|
p0[1 - axis] = transform.frame().min[1 - axis];
|
||||||
p1[1 - axis] = transform.frame().max[1 - axis];
|
p1[1 - axis] = transform.frame().max[1 - axis];
|
||||||
|
|
||||||
|
if let Some(clamp_range) = clamp_range {
|
||||||
|
if axis == 0 {
|
||||||
|
p0.y = transform.position_from_point_y(clamp_range.min[1]);
|
||||||
|
p1.y = transform.position_from_point_y(clamp_range.max[1]);
|
||||||
|
} else {
|
||||||
|
p0.x = transform.position_from_point_x(clamp_range.min[0]);
|
||||||
|
p1.x = transform.position_from_point_x(clamp_range.max[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if sharp_grid_lines {
|
if sharp_grid_lines {
|
||||||
// Round to avoid aliasing
|
// Round to avoid aliasing
|
||||||
p0 = ui.ctx().round_pos_to_pixels(p0);
|
p0 = ui.ctx().round_pos_to_pixels(p0);
|
||||||
p1 = ui.ctx().round_pos_to_pixels(p1);
|
p1 = ui.ctx().round_pos_to_pixels(p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
shapes.push((
|
shapes.push((
|
||||||
Shape::line_segment([p0, p1], Stroke::new(1.0, line_color)),
|
Shape::line_segment([p0, p1], Stroke::new(1.0, line_color)),
|
||||||
line_strength,
|
line_strength,
|
||||||
|
|
|
@ -224,6 +224,7 @@ impl ScreenTransform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ui-space rectangle.
|
||||||
pub fn frame(&self) -> &Rect {
|
pub fn frame(&self) -> &Rect {
|
||||||
&self.frame
|
&self.frame
|
||||||
}
|
}
|
||||||
|
@ -263,18 +264,27 @@ impl ScreenTransform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position_from_point(&self, value: &PlotPoint) -> Pos2 {
|
pub fn position_from_point_x(&self, value: f64) -> f32 {
|
||||||
let x = remap(
|
remap(
|
||||||
value.x,
|
value,
|
||||||
self.bounds.min[0]..=self.bounds.max[0],
|
self.bounds.min[0]..=self.bounds.max[0],
|
||||||
(self.frame.left() as f64)..=(self.frame.right() as f64),
|
(self.frame.left() as f64)..=(self.frame.right() as f64),
|
||||||
);
|
) as f32
|
||||||
let y = remap(
|
}
|
||||||
value.y,
|
|
||||||
|
pub fn position_from_point_y(&self, value: f64) -> f32 {
|
||||||
|
remap(
|
||||||
|
value,
|
||||||
self.bounds.min[1]..=self.bounds.max[1],
|
self.bounds.min[1]..=self.bounds.max[1],
|
||||||
(self.frame.bottom() as f64)..=(self.frame.top() as f64), // negated y axis!
|
(self.frame.bottom() as f64)..=(self.frame.top() as f64), // negated y axis!
|
||||||
);
|
) as f32
|
||||||
pos2(x as f32, y as f32)
|
}
|
||||||
|
|
||||||
|
pub fn position_from_point(&self, value: &PlotPoint) -> Pos2 {
|
||||||
|
pos2(
|
||||||
|
self.position_from_point_x(value.x),
|
||||||
|
self.position_from_point_y(value.y),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_from_position(&self, pos: Pos2) -> PlotPoint {
|
pub fn value_from_position(&self, pos: Pos2) -> PlotPoint {
|
||||||
|
|
|
@ -755,6 +755,7 @@ impl ChartsDemo {
|
||||||
Plot::new("Normal Distribution Demo")
|
Plot::new("Normal Distribution Demo")
|
||||||
.legend(Legend::default())
|
.legend(Legend::default())
|
||||||
.data_aspect(1.0)
|
.data_aspect(1.0)
|
||||||
|
.clamp_grid(true)
|
||||||
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
|
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
|
||||||
.response
|
.response
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue