Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Plot::clamp_grid #2480

Merged
merged 2 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
### 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)).
* 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 `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 🐛
* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)).
Expand Down
1 change: 1 addition & 0 deletions crates/egui/src/widgets/plot/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub(super) struct PlotConfig<'a> {
pub(super) trait PlotItem {
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 name(&self) -> &str;
Expand Down
59 changes: 56 additions & 3 deletions crates/egui/src/widgets/plot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,10 @@ pub struct Plot {
legend_config: Option<Legend>,
show_background: bool,
show_axes: [bool; 2],

grid_spacers: [GridSpacer; 2],
sharp_grid_lines: bool,
clamp_grid: bool,
}

impl Plot {
Expand Down Expand Up @@ -331,8 +333,10 @@ impl Plot {
legend_config: None,
show_background: true,
show_axes: [true; 2],

grid_spacers: [log_grid_spacer(10), log_grid_spacer(10)],
sharp_grid_lines: true,
clamp_grid: false,
}
}

Expand Down Expand Up @@ -557,6 +561,14 @@ impl Plot {
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.
/// 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 {
Expand Down Expand Up @@ -671,6 +683,8 @@ impl Plot {
show_axes,
linked_axes,
linked_cursors,

clamp_grid,
grid_spacers,
sharp_grid_lines,
} = self;
Expand Down Expand Up @@ -971,11 +985,12 @@ impl Plot {
axis_formatters,
show_axes,
transform: transform.clone(),
grid_spacers,
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_cursors,
grid_spacers,
sharp_grid_lines,
clamp_grid,
};
let plot_cursors = prepared.ui(ui, &response);

Expand Down Expand Up @@ -1297,11 +1312,13 @@ struct PreparedPlot {
axis_formatters: [AxisFormatter; 2],
show_axes: [bool; 2],
transform: ScreenTransform,
grid_spacers: [GridSpacer; 2],
draw_cursor_x: bool,
draw_cursor_y: bool,
draw_cursors: Vec<Cursor>,

grid_spacers: [GridSpacer; 2],
sharp_grid_lines: bool,
clamp_grid: bool,
}

impl PreparedPlot {
Expand Down Expand Up @@ -1400,10 +1417,13 @@ impl PreparedPlot {
shapes: &mut Vec<(Shape, f32)>,
sharp_grid_lines: bool,
) {
#![allow(clippy::collapsible_else_if)]

let Self {
transform,
axis_formatters,
grid_spacers,
clamp_grid,
..
} = self;

Expand All @@ -1417,7 +1437,6 @@ impl PreparedPlot {
let font_id = TextStyle::Body.resolve(ui.style());

// 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 input = GridInput {
Expand All @@ -1426,9 +1445,31 @@ impl PreparedPlot {
};
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 {
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 {
PlotPoint::new(value_main, value_cross)
} else {
Expand All @@ -1451,11 +1492,23 @@ impl PreparedPlot {
let mut p1 = pos_in_gui;
p0[1 - axis] = transform.frame().min[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 {
// Round to avoid aliasing
p0 = ui.ctx().round_pos_to_pixels(p0);
p1 = ui.ctx().round_pos_to_pixels(p1);
}

shapes.push((
Shape::line_segment([p0, p1], Stroke::new(1.0, line_color)),
line_strength,
Expand Down
26 changes: 18 additions & 8 deletions crates/egui/src/widgets/plot/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ impl ScreenTransform {
}
}

/// ui-space rectangle.
pub fn frame(&self) -> &Rect {
&self.frame
}
Expand Down Expand Up @@ -263,18 +264,27 @@ impl ScreenTransform {
}
}

pub fn position_from_point(&self, value: &PlotPoint) -> Pos2 {
let x = remap(
value.x,
pub fn position_from_point_x(&self, value: f64) -> f32 {
remap(
value,
self.bounds.min[0]..=self.bounds.max[0],
(self.frame.left() as f64)..=(self.frame.right() as f64),
);
let y = remap(
value.y,
) as f32
}

pub fn position_from_point_y(&self, value: f64) -> f32 {
remap(
value,
self.bounds.min[1]..=self.bounds.max[1],
(self.frame.bottom() as f64)..=(self.frame.top() as f64), // negated y axis!
);
pos2(x as f32, y as f32)
) 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 {
Expand Down
1 change: 1 addition & 0 deletions crates/egui_demo_lib/src/demo/plot_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ impl ChartsDemo {
Plot::new("Normal Distribution Demo")
.legend(Legend::default())
.data_aspect(1.0)
.clamp_grid(true)
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
.response
}
Expand Down