Improve plot item UX (#1816)
* initial work * changelog entry * fix CI * Update egui/src/widgets/plot/items/values.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * Update egui/src/widgets/plot/items/values.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * derive 'FromIterator' * remove `bytemuck` dependency again and remove borrowing plot points for now * update doctest * update documentation * remove unnecessary numeric cast * cargo fmt * Update egui/src/widgets/plot/items/values.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
cf591da1a0
commit
0bf9fc9428
14 changed files with 302 additions and 234 deletions
|
@ -22,6 +22,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui-w
|
|||
* `PaintCallback` shapes now require the whole callback to be put in an `Arc<dyn Any>` with the value being a backend-specific callback type ([#1684](https://github.com/emilk/egui/pull/1684)).
|
||||
* Replaced `needs_repaint` in `FullOutput` with `repaint_after`. Used to force repaint after the set duration in reactive mode ([#1694](https://github.com/emilk/egui/pull/1694)).
|
||||
* `Layout::left_to_right` and `Layout::right_to_left` now takes the vertical align as an argument. Previous default was `Align::Center`.
|
||||
* Improved ergonomics of adding plot items. All plot items that take a series of 2D coordinates can now be created directly from `Vec<[f64; 2]>`. The `Value` and `Values` types were removed in favor of `PlotPoint` and `PlotPoints` respectively.
|
||||
|
||||
### Fixed 🐛
|
||||
* Fixed `Response::changed` for `ui.toggle_value` ([#1573](https://github.com/emilk/egui/pull/1573)).
|
||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3871,7 +3871,7 @@ version = "1.6.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-if 1.0.0",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
|
|
|
@ -374,7 +374,7 @@ impl Theme {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// `WebGl` Context options
|
||||
/// WebGL Context options
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum WebGlContextOption {
|
||||
|
|
|
@ -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, ScreenTransform, Value};
|
||||
use crate::plot::{BarChart, PlotPoint, ScreenTransform};
|
||||
|
||||
/// One bar in a [`BarChart`]. Potentially floating, allowing stacked bar charts.
|
||||
/// Width can be changed to allow variable-width histograms.
|
||||
|
@ -157,15 +157,15 @@ impl RectElement for Bar {
|
|||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn bounds_min(&self) -> Value {
|
||||
fn bounds_min(&self) -> PlotPoint {
|
||||
self.point_at(self.argument - self.bar_width / 2.0, self.lower())
|
||||
}
|
||||
|
||||
fn bounds_max(&self) -> Value {
|
||||
fn bounds_max(&self) -> PlotPoint {
|
||||
self.point_at(self.argument + self.bar_width / 2.0, self.upper())
|
||||
}
|
||||
|
||||
fn values_with_ruler(&self) -> Vec<Value> {
|
||||
fn values_with_ruler(&self) -> Vec<PlotPoint> {
|
||||
let base = self.base_offset.unwrap_or(0.0);
|
||||
let value_center = self.point_at(self.argument, base + self.value);
|
||||
|
||||
|
|
|
@ -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, ScreenTransform, Value};
|
||||
use crate::plot::{BoxPlot, PlotPoint, ScreenTransform};
|
||||
|
||||
/// Contains the values of a single box in a box plot.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -161,8 +161,8 @@ impl BoxElem {
|
|||
let line_between = |v1, v2| {
|
||||
Shape::line_segment(
|
||||
[
|
||||
transform.position_from_value(&v1),
|
||||
transform.position_from_value(&v2),
|
||||
transform.position_from_point(&v1),
|
||||
transform.position_from_point(&v2),
|
||||
],
|
||||
stroke,
|
||||
)
|
||||
|
@ -236,19 +236,19 @@ impl RectElement for BoxElem {
|
|||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn bounds_min(&self) -> Value {
|
||||
fn bounds_min(&self) -> PlotPoint {
|
||||
let argument = self.argument - self.box_width.max(self.whisker_width) / 2.0;
|
||||
let value = self.spread.lower_whisker;
|
||||
self.point_at(argument, value)
|
||||
}
|
||||
|
||||
fn bounds_max(&self) -> Value {
|
||||
fn bounds_max(&self) -> PlotPoint {
|
||||
let argument = self.argument + self.box_width.max(self.whisker_width) / 2.0;
|
||||
let value = self.spread.upper_whisker;
|
||||
self.point_at(argument, value)
|
||||
}
|
||||
|
||||
fn values_with_ruler(&self) -> Vec<Value> {
|
||||
fn values_with_ruler(&self) -> Vec<PlotPoint> {
|
||||
let median = self.point_at(self.argument, self.spread.median);
|
||||
let q1 = self.point_at(self.argument, self.spread.quartile1);
|
||||
let q3 = self.point_at(self.argument, self.spread.quartile3);
|
||||
|
@ -262,7 +262,7 @@ impl RectElement for BoxElem {
|
|||
self.orientation
|
||||
}
|
||||
|
||||
fn corner_value(&self) -> Value {
|
||||
fn corner_value(&self) -> PlotPoint {
|
||||
self.point_at(self.argument, self.spread.upper_whisker)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use values::{ClosestElem, PlotGeometry};
|
|||
|
||||
pub use bar::Bar;
|
||||
pub use box_elem::{BoxElem, BoxSpread};
|
||||
pub use values::{LineStyle, MarkerShape, Orientation, Value, Values};
|
||||
pub use values::{LineStyle, MarkerShape, Orientation, PlotPoint, PlotPoints};
|
||||
|
||||
mod bar;
|
||||
mod box_elem;
|
||||
|
@ -49,7 +49,7 @@ pub(super) trait PlotItem {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, value)| {
|
||||
let pos = transform.position_from_value(value);
|
||||
let pos = transform.position_from_point(value);
|
||||
let dist_sq = point.distance_sq(pos);
|
||||
ClosestElem { index, dist_sq }
|
||||
})
|
||||
|
@ -86,7 +86,7 @@ pub(super) trait PlotItem {
|
|||
|
||||
// this method is only called, if the value is in the result set of find_closest()
|
||||
let value = points[elem.index];
|
||||
let pointer = plot.transform.position_from_value(&value);
|
||||
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);
|
||||
|
@ -169,8 +169,8 @@ impl PlotItem for HLine {
|
|||
..
|
||||
} = self;
|
||||
let points = vec![
|
||||
transform.position_from_value(&Value::new(transform.bounds().min[0], *y)),
|
||||
transform.position_from_value(&Value::new(transform.bounds().max[0], *y)),
|
||||
transform.position_from_point(&PlotPoint::new(transform.bounds().min[0], *y)),
|
||||
transform.position_from_point(&PlotPoint::new(transform.bounds().max[0], *y)),
|
||||
];
|
||||
style.style_line(points, *stroke, *highlight, shapes);
|
||||
}
|
||||
|
@ -279,8 +279,8 @@ impl PlotItem for VLine {
|
|||
..
|
||||
} = self;
|
||||
let points = vec![
|
||||
transform.position_from_value(&Value::new(*x, transform.bounds().min[1])),
|
||||
transform.position_from_value(&Value::new(*x, transform.bounds().max[1])),
|
||||
transform.position_from_point(&PlotPoint::new(*x, transform.bounds().min[1])),
|
||||
transform.position_from_point(&PlotPoint::new(*x, transform.bounds().max[1])),
|
||||
];
|
||||
style.style_line(points, *stroke, *highlight, shapes);
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ impl PlotItem for VLine {
|
|||
|
||||
/// A series of values forming a path.
|
||||
pub struct Line {
|
||||
pub(super) series: Values,
|
||||
pub(super) series: PlotPoints,
|
||||
pub(super) stroke: Stroke,
|
||||
pub(super) name: String,
|
||||
pub(super) highlight: bool,
|
||||
|
@ -326,9 +326,9 @@ pub struct Line {
|
|||
}
|
||||
|
||||
impl Line {
|
||||
pub fn new(series: Values) -> Self {
|
||||
pub fn new(series: impl Into<PlotPoints>) -> Self {
|
||||
Self {
|
||||
series,
|
||||
series: series.into(),
|
||||
stroke: Stroke::new(1.0, Color32::TRANSPARENT),
|
||||
name: Default::default(),
|
||||
highlight: false,
|
||||
|
@ -405,9 +405,9 @@ impl PlotItem for Line {
|
|||
} = self;
|
||||
|
||||
let values_tf: Vec<_> = series
|
||||
.values
|
||||
.points()
|
||||
.iter()
|
||||
.map(|v| transform.position_from_value(v))
|
||||
.map(|v| transform.position_from_point(v))
|
||||
.collect();
|
||||
let n_values = values_tf.len();
|
||||
|
||||
|
@ -421,7 +421,7 @@ impl PlotItem for Line {
|
|||
fill_alpha = (2.0 * fill_alpha).at_most(1.0);
|
||||
}
|
||||
let y = transform
|
||||
.position_from_value(&Value::new(0.0, y_reference))
|
||||
.position_from_point(&PlotPoint::new(0.0, y_reference))
|
||||
.y;
|
||||
let fill_color = Rgba::from(stroke.color)
|
||||
.to_opaque()
|
||||
|
@ -474,7 +474,7 @@ impl PlotItem for Line {
|
|||
}
|
||||
|
||||
fn geometry(&self) -> PlotGeometry<'_> {
|
||||
PlotGeometry::Points(&self.series.values)
|
||||
PlotGeometry::Points(self.series.points())
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> PlotBounds {
|
||||
|
@ -484,7 +484,7 @@ impl PlotItem for Line {
|
|||
|
||||
/// A convex polygon.
|
||||
pub struct Polygon {
|
||||
pub(super) series: Values,
|
||||
pub(super) series: PlotPoints,
|
||||
pub(super) stroke: Stroke,
|
||||
pub(super) name: String,
|
||||
pub(super) highlight: bool,
|
||||
|
@ -493,9 +493,9 @@ pub struct Polygon {
|
|||
}
|
||||
|
||||
impl Polygon {
|
||||
pub fn new(series: Values) -> Self {
|
||||
pub fn new(series: impl Into<PlotPoints>) -> Self {
|
||||
Self {
|
||||
series,
|
||||
series: series.into(),
|
||||
stroke: Stroke::new(1.0, Color32::TRANSPARENT),
|
||||
name: Default::default(),
|
||||
highlight: false,
|
||||
|
@ -570,9 +570,9 @@ impl PlotItem for Polygon {
|
|||
}
|
||||
|
||||
let mut values_tf: Vec<_> = series
|
||||
.values
|
||||
.points()
|
||||
.iter()
|
||||
.map(|v| transform.position_from_value(v))
|
||||
.map(|v| transform.position_from_point(v))
|
||||
.collect();
|
||||
|
||||
let fill = Rgba::from(stroke.color).to_opaque().multiply(fill_alpha);
|
||||
|
@ -604,7 +604,7 @@ impl PlotItem for Polygon {
|
|||
}
|
||||
|
||||
fn geometry(&self) -> PlotGeometry<'_> {
|
||||
PlotGeometry::Points(&self.series.values)
|
||||
PlotGeometry::Points(self.series.points())
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> PlotBounds {
|
||||
|
@ -616,7 +616,7 @@ impl PlotItem for Polygon {
|
|||
#[derive(Clone)]
|
||||
pub struct Text {
|
||||
pub(super) text: WidgetText,
|
||||
pub(super) position: Value,
|
||||
pub(super) position: PlotPoint,
|
||||
pub(super) name: String,
|
||||
pub(super) highlight: bool,
|
||||
pub(super) color: Color32,
|
||||
|
@ -624,7 +624,7 @@ pub struct Text {
|
|||
}
|
||||
|
||||
impl Text {
|
||||
pub fn new(position: Value, text: impl Into<WidgetText>) -> Self {
|
||||
pub fn new(position: PlotPoint, text: impl Into<WidgetText>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
position,
|
||||
|
@ -679,7 +679,7 @@ impl PlotItem for Text {
|
|||
.clone()
|
||||
.into_galley(ui, Some(false), f32::INFINITY, TextStyle::Small);
|
||||
|
||||
let pos = transform.position_from_value(&self.position);
|
||||
let pos = transform.position_from_point(&self.position);
|
||||
let rect = self
|
||||
.anchor
|
||||
.anchor_rect(Rect::from_min_size(pos, galley.size()));
|
||||
|
@ -730,7 +730,7 @@ impl PlotItem for Text {
|
|||
|
||||
/// A set of points.
|
||||
pub struct Points {
|
||||
pub(super) series: Values,
|
||||
pub(super) series: PlotPoints,
|
||||
pub(super) shape: MarkerShape,
|
||||
/// Color of the marker. `Color32::TRANSPARENT` means that it will be picked automatically.
|
||||
pub(super) color: Color32,
|
||||
|
@ -744,9 +744,9 @@ pub struct Points {
|
|||
}
|
||||
|
||||
impl Points {
|
||||
pub fn new(series: Values) -> Self {
|
||||
pub fn new(series: impl Into<PlotPoints>) -> Self {
|
||||
Self {
|
||||
series,
|
||||
series: series.into(),
|
||||
shape: MarkerShape::Circle,
|
||||
color: Color32::TRANSPARENT,
|
||||
filled: true,
|
||||
|
@ -837,12 +837,12 @@ impl PlotItem for Points {
|
|||
stem_stroke.width *= 2.0;
|
||||
}
|
||||
|
||||
let y_reference = stems.map(|y| transform.position_from_value(&Value::new(0.0, y)).y);
|
||||
let y_reference = stems.map(|y| transform.position_from_point(&PlotPoint::new(0.0, y)).y);
|
||||
|
||||
series
|
||||
.values
|
||||
.points()
|
||||
.iter()
|
||||
.map(|value| transform.position_from_value(value))
|
||||
.map(|value| transform.position_from_point(value))
|
||||
.for_each(|center| {
|
||||
let tf = |dx: f32, dy: f32| -> Pos2 { center + radius * vec2(dx, dy) };
|
||||
|
||||
|
@ -955,7 +955,7 @@ impl PlotItem for Points {
|
|||
}
|
||||
|
||||
fn geometry(&self) -> PlotGeometry<'_> {
|
||||
PlotGeometry::Points(&self.series.values)
|
||||
PlotGeometry::Points(self.series.points())
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> PlotBounds {
|
||||
|
@ -965,18 +965,18 @@ impl PlotItem for Points {
|
|||
|
||||
/// A set of arrows.
|
||||
pub struct Arrows {
|
||||
pub(super) origins: Values,
|
||||
pub(super) tips: Values,
|
||||
pub(super) origins: PlotPoints,
|
||||
pub(super) tips: PlotPoints,
|
||||
pub(super) color: Color32,
|
||||
pub(super) name: String,
|
||||
pub(super) highlight: bool,
|
||||
}
|
||||
|
||||
impl Arrows {
|
||||
pub fn new(origins: Values, tips: Values) -> Self {
|
||||
pub fn new(origins: impl Into<PlotPoints>, tips: impl Into<PlotPoints>) -> Self {
|
||||
Self {
|
||||
origins,
|
||||
tips,
|
||||
origins: origins.into(),
|
||||
tips: tips.into(),
|
||||
color: Color32::TRANSPARENT,
|
||||
name: Default::default(),
|
||||
highlight: false,
|
||||
|
@ -1020,13 +1020,13 @@ impl PlotItem for Arrows {
|
|||
} = self;
|
||||
let stroke = Stroke::new(if *highlight { 2.0 } else { 1.0 }, *color);
|
||||
origins
|
||||
.values
|
||||
.points()
|
||||
.iter()
|
||||
.zip(tips.values.iter())
|
||||
.zip(tips.points().iter())
|
||||
.map(|(origin, tip)| {
|
||||
(
|
||||
transform.position_from_value(origin),
|
||||
transform.position_from_value(tip),
|
||||
transform.position_from_point(origin),
|
||||
transform.position_from_point(tip),
|
||||
)
|
||||
})
|
||||
.for_each(|(origin, tip)| {
|
||||
|
@ -1070,7 +1070,7 @@ impl PlotItem for Arrows {
|
|||
}
|
||||
|
||||
fn geometry(&self) -> PlotGeometry<'_> {
|
||||
PlotGeometry::Points(&self.origins.values)
|
||||
PlotGeometry::Points(self.origins.points())
|
||||
}
|
||||
|
||||
fn get_bounds(&self) -> PlotBounds {
|
||||
|
@ -1081,7 +1081,7 @@ impl PlotItem for Arrows {
|
|||
/// An image in the plot.
|
||||
#[derive(Clone)]
|
||||
pub struct PlotImage {
|
||||
pub(super) position: Value,
|
||||
pub(super) position: PlotPoint,
|
||||
pub(super) texture_id: TextureId,
|
||||
pub(super) uv: Rect,
|
||||
pub(super) size: Vec2,
|
||||
|
@ -1095,7 +1095,7 @@ impl PlotImage {
|
|||
/// Create a new image with position and size in plot coordinates.
|
||||
pub fn new(
|
||||
texture_id: impl Into<TextureId>,
|
||||
center_position: Value,
|
||||
center_position: PlotPoint,
|
||||
size: impl Into<Vec2>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -1160,16 +1160,16 @@ impl PlotItem for PlotImage {
|
|||
..
|
||||
} = self;
|
||||
let rect = {
|
||||
let left_top = Value::new(
|
||||
let left_top = PlotPoint::new(
|
||||
position.x as f32 - size.x / 2.0,
|
||||
position.y as f32 - size.y / 2.0,
|
||||
);
|
||||
let right_bottom = Value::new(
|
||||
let right_bottom = PlotPoint::new(
|
||||
position.x as f32 + size.x / 2.0,
|
||||
position.y as f32 + size.y / 2.0,
|
||||
);
|
||||
let left_top_tf = transform.position_from_value(&left_top);
|
||||
let right_bottom_tf = transform.position_from_value(&right_bottom);
|
||||
let left_top_tf = transform.position_from_point(&left_top);
|
||||
let right_bottom_tf = transform.position_from_point(&right_bottom);
|
||||
Rect::from_two_pos(left_top_tf, right_bottom_tf)
|
||||
};
|
||||
Image::new(*texture_id, *size)
|
||||
|
@ -1210,11 +1210,11 @@ impl PlotItem for PlotImage {
|
|||
|
||||
fn get_bounds(&self) -> PlotBounds {
|
||||
let mut bounds = PlotBounds::NOTHING;
|
||||
let left_top = Value::new(
|
||||
let left_top = PlotPoint::new(
|
||||
self.position.x as f32 - self.size.x / 2.0,
|
||||
self.position.y as f32 - self.size.y / 2.0,
|
||||
);
|
||||
let right_bottom = Value::new(
|
||||
let right_bottom = PlotPoint::new(
|
||||
self.position.x as f32 + self.size.x / 2.0,
|
||||
self.position.y as f32 + self.size.y / 2.0,
|
||||
);
|
||||
|
@ -1586,8 +1586,8 @@ fn add_rulers_and_text(
|
|||
|
||||
// Rulers for argument (usually vertical)
|
||||
if show_argument {
|
||||
let push_argument_ruler = |argument: Value, shapes: &mut Vec<Shape>| {
|
||||
let position = plot.transform.position_from_value(&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),
|
||||
|
@ -1602,8 +1602,8 @@ fn add_rulers_and_text(
|
|||
|
||||
// Rulers for values (usually horizontal)
|
||||
if show_values {
|
||||
let push_value_ruler = |value: Value, shapes: &mut Vec<Shape>| {
|
||||
let position = plot.transform.position_from_value(&value);
|
||||
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),
|
||||
|
@ -1632,7 +1632,7 @@ fn add_rulers_and_text(
|
|||
let corner_value = elem.corner_value();
|
||||
shapes.push(Shape::text(
|
||||
&*plot.ui.fonts(),
|
||||
plot.transform.position_from_value(&corner_value) + vec2(3.0, -2.0),
|
||||
plot.transform.position_from_point(&corner_value) + vec2(3.0, -2.0),
|
||||
Align2::LEFT_BOTTOM,
|
||||
text,
|
||||
font_id,
|
||||
|
@ -1645,7 +1645,7 @@ fn add_rulers_and_text(
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn rulers_at_value(
|
||||
pointer: Pos2,
|
||||
value: Value,
|
||||
value: PlotPoint,
|
||||
name: &str,
|
||||
plot: &PlotConfig<'_>,
|
||||
shapes: &mut Vec<Shape>,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{Orientation, Value};
|
||||
use super::{Orientation, PlotPoint};
|
||||
use crate::plot::transform::{PlotBounds, ScreenTransform};
|
||||
use epaint::emath::NumExt;
|
||||
use epaint::{Color32, Rgba, Stroke};
|
||||
|
@ -6,8 +6,8 @@ use epaint::{Color32, Rgba, Stroke};
|
|||
/// Trait that abstracts from rectangular 'Value'-like elements, such as bars or boxes
|
||||
pub(super) trait RectElement {
|
||||
fn name(&self) -> &str;
|
||||
fn bounds_min(&self) -> Value;
|
||||
fn bounds_max(&self) -> Value;
|
||||
fn bounds_min(&self) -> PlotPoint;
|
||||
fn bounds_max(&self) -> PlotPoint;
|
||||
|
||||
fn bounds(&self) -> PlotBounds {
|
||||
let mut bounds = PlotBounds::NOTHING;
|
||||
|
@ -17,29 +17,29 @@ pub(super) trait RectElement {
|
|||
}
|
||||
|
||||
/// At which argument (input; usually X) there is a ruler (usually vertical)
|
||||
fn arguments_with_ruler(&self) -> Vec<Value> {
|
||||
fn arguments_with_ruler(&self) -> Vec<PlotPoint> {
|
||||
// Default: one at center
|
||||
vec![self.bounds().center()]
|
||||
}
|
||||
|
||||
/// At which value (output; usually Y) there is a ruler (usually horizontal)
|
||||
fn values_with_ruler(&self) -> Vec<Value>;
|
||||
fn values_with_ruler(&self) -> Vec<PlotPoint>;
|
||||
|
||||
/// The diagram's orientation (vertical/horizontal)
|
||||
fn orientation(&self) -> Orientation;
|
||||
|
||||
/// Get X/Y-value for (argument, value) pair, taking into account orientation
|
||||
fn point_at(&self, argument: f64, value: f64) -> Value {
|
||||
fn point_at(&self, argument: f64, value: f64) -> PlotPoint {
|
||||
match self.orientation() {
|
||||
Orientation::Horizontal => Value::new(value, argument),
|
||||
Orientation::Vertical => Value::new(argument, value),
|
||||
Orientation::Horizontal => PlotPoint::new(value, argument),
|
||||
Orientation::Vertical => PlotPoint::new(argument, value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Right top of the rectangle (position of text)
|
||||
fn corner_value(&self) -> Value {
|
||||
fn corner_value(&self) -> PlotPoint {
|
||||
//self.point_at(self.position + self.width / 2.0, value)
|
||||
Value {
|
||||
PlotPoint {
|
||||
x: self.bounds_max().x,
|
||||
y: self.bounds_max().y,
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ use std::ops::{Bound, RangeBounds, RangeInclusive};
|
|||
|
||||
use crate::plot::transform::PlotBounds;
|
||||
|
||||
/// A value in the value-space of the plot.
|
||||
/// A point coordinate in the plot.
|
||||
///
|
||||
/// Uses f64 for improved accuracy to enable plotting
|
||||
/// large values (e.g. unix time on x axis).
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Value {
|
||||
pub struct PlotPoint {
|
||||
/// This is often something monotonically increasing, such as time, but doesn't have to be.
|
||||
/// Goes from left to right.
|
||||
pub x: f64,
|
||||
|
@ -16,7 +16,14 @@ pub struct Value {
|
|||
pub y: f64,
|
||||
}
|
||||
|
||||
impl Value {
|
||||
impl From<[f64; 2]> for PlotPoint {
|
||||
#[inline]
|
||||
fn from([x, y]: [f64; 2]) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlotPoint {
|
||||
#[inline(always)]
|
||||
pub fn new(x: impl Into<f64>, y: impl Into<f64>) -> Self {
|
||||
Self {
|
||||
|
@ -140,22 +147,49 @@ impl Default for Orientation {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Values {
|
||||
pub(super) values: Vec<Value>,
|
||||
generator: Option<ExplicitGenerator>,
|
||||
/// Represents many [`PlotPoint`]s.
|
||||
///
|
||||
/// These can be an owned `Vec` or generated with a function.
|
||||
pub enum PlotPoints {
|
||||
Owned(Vec<PlotPoint>),
|
||||
Generator(ExplicitGenerator),
|
||||
// Borrowed(&[PlotPoint]), // TODO: Lifetimes are tricky in this case.
|
||||
}
|
||||
|
||||
impl Values {
|
||||
pub fn from_values(values: Vec<Value>) -> Self {
|
||||
Self {
|
||||
values,
|
||||
generator: None,
|
||||
impl Default for PlotPoints {
|
||||
fn default() -> Self {
|
||||
Self::Owned(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_values_iter(iter: impl Iterator<Item = Value>) -> Self {
|
||||
Self::from_values(iter.collect())
|
||||
impl From<[f64; 2]> for PlotPoints {
|
||||
fn from(coordinate: [f64; 2]) -> Self {
|
||||
Self::new(vec![coordinate])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<[f64; 2]>> for PlotPoints {
|
||||
fn from(coordinates: Vec<[f64; 2]>) -> Self {
|
||||
Self::new(coordinates)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<[f64; 2]> for PlotPoints {
|
||||
fn from_iter<T: IntoIterator<Item = [f64; 2]>>(iter: T) -> Self {
|
||||
Self::Owned(iter.into_iter().map(|point| point.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl PlotPoints {
|
||||
pub fn new(points: Vec<[f64; 2]>) -> Self {
|
||||
Self::from_iter(points)
|
||||
}
|
||||
|
||||
pub fn points(&self) -> &[PlotPoint] {
|
||||
match self {
|
||||
PlotPoints::Owned(points) => points.as_slice(),
|
||||
PlotPoints::Generator(_) => &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a line based on a function `y=f(x)`, a range (which can be infinite) for x and the number of points.
|
||||
|
@ -180,10 +214,7 @@ impl Values {
|
|||
points,
|
||||
};
|
||||
|
||||
Self {
|
||||
values: Vec::new(),
|
||||
generator: Some(generator),
|
||||
}
|
||||
Self::Generator(generator)
|
||||
}
|
||||
|
||||
/// Draw a line based on a function `(x,y)=f(t)`, a range for t and the number of points.
|
||||
|
@ -208,48 +239,55 @@ impl Values {
|
|||
} else {
|
||||
(end - start) / points as f64
|
||||
};
|
||||
let values = (0..points).map(|i| {
|
||||
(0..points)
|
||||
.map(|i| {
|
||||
let t = start + i as f64 * increment;
|
||||
let (x, y) = function(t);
|
||||
Value { x, y }
|
||||
});
|
||||
Self::from_values_iter(values)
|
||||
[x, y]
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// From a series of y-values.
|
||||
/// The x-values will be the indices of these values
|
||||
pub fn from_ys_f32(ys: &[f32]) -> Self {
|
||||
let values: Vec<Value> = ys
|
||||
.iter()
|
||||
ys.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &y)| Value {
|
||||
x: i as f64,
|
||||
y: y as f64,
|
||||
})
|
||||
.collect();
|
||||
Self::from_values(values)
|
||||
.map(|(i, &y)| [i as f64, y as f64])
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// From a series of y-values.
|
||||
/// The x-values will be the indices of these values
|
||||
pub fn from_ys_f64(ys: &[f64]) -> Self {
|
||||
ys.iter().enumerate().map(|(i, &y)| [i as f64, y]).collect()
|
||||
}
|
||||
|
||||
/// Returns true if there are no data points available and there is no function to generate any.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.generator.is_none() && self.values.is_empty()
|
||||
match self {
|
||||
PlotPoints::Owned(points) => points.is_empty(),
|
||||
PlotPoints::Generator(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If initialized with a generator function, this will generate `n` evenly spaced points in the
|
||||
/// given range.
|
||||
pub(super) fn generate_points(&mut self, x_range: RangeInclusive<f64>) {
|
||||
if let Some(generator) = self.generator.take() {
|
||||
if let Some(intersection) = Self::range_intersection(&x_range, &generator.x_range) {
|
||||
if let Self::Generator(generator) = self {
|
||||
*self = Self::range_intersection(&x_range, &generator.x_range)
|
||||
.map(|intersection| {
|
||||
let increment =
|
||||
(intersection.end() - intersection.start()) / (generator.points - 1) as f64;
|
||||
self.values = (0..generator.points)
|
||||
(0..generator.points)
|
||||
.map(|i| {
|
||||
let x = intersection.start() + i as f64 * increment;
|
||||
let y = (generator.function)(x);
|
||||
Value { x, y }
|
||||
[x, y]
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,19 +302,16 @@ impl Values {
|
|||
}
|
||||
|
||||
pub(super) fn get_bounds(&self) -> PlotBounds {
|
||||
if self.values.is_empty() {
|
||||
if let Some(generator) = &self.generator {
|
||||
generator.estimate_bounds()
|
||||
} else {
|
||||
PlotBounds::NOTHING
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
PlotPoints::Owned(points) => {
|
||||
let mut bounds = PlotBounds::NOTHING;
|
||||
for value in &self.values {
|
||||
bounds.extend_with(value);
|
||||
for point in points {
|
||||
bounds.extend_with(point);
|
||||
}
|
||||
bounds
|
||||
}
|
||||
PlotPoints::Generator(generator) => generator.estimate_bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,13 +353,13 @@ impl MarkerShape {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Query the values of the plot, for geometric relations like closest checks
|
||||
/// Query the points of the plot, for geometric relations like closest checks
|
||||
pub(crate) enum PlotGeometry<'a> {
|
||||
/// No geometry based on single elements (examples: text, image, horizontal/vertical line)
|
||||
None,
|
||||
|
||||
/// Point values (X-Y graphs)
|
||||
Points(&'a [Value]),
|
||||
Points(&'a [PlotPoint]),
|
||||
|
||||
/// Rectangles (examples: boxes or bars)
|
||||
// Has currently no data, as it would require copying rects or iterating a list of pointers.
|
||||
|
@ -335,7 +370,7 @@ pub(crate) enum PlotGeometry<'a> {
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Describes a function y = f(x) with an optional range for x and a number of points.
|
||||
struct ExplicitGenerator {
|
||||
pub struct ExplicitGenerator {
|
||||
function: Box<dyn Fn(f64) -> f64>,
|
||||
x_range: RangeInclusive<f64>,
|
||||
points: usize,
|
||||
|
|
|
@ -13,7 +13,7 @@ use transform::ScreenTransform;
|
|||
|
||||
pub use items::{
|
||||
Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, HLine, Line, LineStyle, MarkerShape,
|
||||
Orientation, PlotImage, Points, Polygon, Text, VLine, Value, Values,
|
||||
Orientation, PlotImage, PlotPoint, PlotPoints, Points, Polygon, Text, VLine,
|
||||
};
|
||||
pub use legend::{Corner, Legend};
|
||||
pub use transform::PlotBounds;
|
||||
|
@ -22,7 +22,7 @@ mod items;
|
|||
mod legend;
|
||||
mod transform;
|
||||
|
||||
type LabelFormatterFn = dyn Fn(&str, &Value) -> String;
|
||||
type LabelFormatterFn = dyn Fn(&str, &PlotPoint) -> String;
|
||||
type LabelFormatter = Option<Box<LabelFormatterFn>>;
|
||||
type AxisFormatterFn = dyn Fn(f64, &RangeInclusive<f64>) -> String;
|
||||
type AxisFormatter = Option<Box<AxisFormatterFn>>;
|
||||
|
@ -32,12 +32,12 @@ type GridSpacer = Box<GridSpacerFn>;
|
|||
|
||||
/// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`].
|
||||
pub struct CoordinatesFormatter {
|
||||
function: Box<dyn Fn(&Value, &PlotBounds) -> String>,
|
||||
function: Box<dyn Fn(&PlotPoint, &PlotBounds) -> String>,
|
||||
}
|
||||
|
||||
impl CoordinatesFormatter {
|
||||
/// Create a new formatter based on the pointer coordinate and the plot bounds.
|
||||
pub fn new(function: impl Fn(&Value, &PlotBounds) -> String + 'static) -> Self {
|
||||
pub fn new(function: impl Fn(&PlotPoint, &PlotBounds) -> String + 'static) -> Self {
|
||||
Self {
|
||||
function: Box::new(function),
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ impl CoordinatesFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
fn format(&self, value: &Value, bounds: &PlotBounds) -> String {
|
||||
fn format(&self, value: &PlotPoint, bounds: &PlotBounds) -> String {
|
||||
(self.function)(value, bounds)
|
||||
}
|
||||
}
|
||||
|
@ -178,12 +178,12 @@ impl LinkedAxisGroup {
|
|||
///
|
||||
/// ```
|
||||
/// # egui::__run_test_ui(|ui| {
|
||||
/// use egui::plot::{Line, Plot, Value, Values};
|
||||
/// let sin = (0..1000).map(|i| {
|
||||
/// use egui::plot::{Line, Plot, PlotPoints};
|
||||
/// let sin: PlotPoints = (0..1000).map(|i| {
|
||||
/// let x = i as f64 * 0.01;
|
||||
/// Value::new(x, x.sin())
|
||||
/// });
|
||||
/// let line = Line::new(Values::from_values_iter(sin));
|
||||
/// [x, x.sin()]
|
||||
/// }).collect();
|
||||
/// let line = Line::new(sin);
|
||||
/// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line));
|
||||
/// # });
|
||||
/// ```
|
||||
|
@ -359,12 +359,12 @@ impl Plot {
|
|||
///
|
||||
/// ```
|
||||
/// # egui::__run_test_ui(|ui| {
|
||||
/// use egui::plot::{Line, Plot, Value, Values};
|
||||
/// let sin = (0..1000).map(|i| {
|
||||
/// use egui::plot::{Line, Plot, PlotPoints};
|
||||
/// let sin: PlotPoints = (0..1000).map(|i| {
|
||||
/// let x = i as f64 * 0.01;
|
||||
/// Value::new(x, x.sin())
|
||||
/// });
|
||||
/// let line = Line::new(Values::from_values_iter(sin));
|
||||
/// [x, x.sin()]
|
||||
/// }).collect();
|
||||
/// let line = Line::new(sin);
|
||||
/// Plot::new("my_plot").view_aspect(2.0)
|
||||
/// .label_formatter(|name, value| {
|
||||
/// if !name.is_empty() {
|
||||
|
@ -378,7 +378,7 @@ impl Plot {
|
|||
/// ```
|
||||
pub fn label_formatter(
|
||||
mut self,
|
||||
label_formatter: impl Fn(&str, &Value) -> String + 'static,
|
||||
label_formatter: impl Fn(&str, &PlotPoint) -> String + 'static,
|
||||
) -> Self {
|
||||
self.label_formatter = Some(Box::new(label_formatter));
|
||||
self
|
||||
|
@ -892,7 +892,7 @@ impl PlotUi {
|
|||
}
|
||||
|
||||
/// The pointer position in plot coordinates. Independent of whether the pointer is in the plot area.
|
||||
pub fn pointer_coordinate(&self) -> Option<Value> {
|
||||
pub fn pointer_coordinate(&self) -> Option<PlotPoint> {
|
||||
// We need to subtract the drag delta to keep in sync with the frame-delayed screen transform:
|
||||
let last_pos = self.ctx().input().pointer.latest_pos()? - self.response.drag_delta();
|
||||
let value = self.plot_from_screen(last_pos);
|
||||
|
@ -907,12 +907,12 @@ impl PlotUi {
|
|||
}
|
||||
|
||||
/// Transform the plot coordinates to screen coordinates.
|
||||
pub fn screen_from_plot(&self, position: Value) -> Pos2 {
|
||||
self.last_screen_transform.position_from_value(&position)
|
||||
pub fn screen_from_plot(&self, position: PlotPoint) -> Pos2 {
|
||||
self.last_screen_transform.position_from_point(&position)
|
||||
}
|
||||
|
||||
/// Transform the screen coordinates to plot coordinates.
|
||||
pub fn plot_from_screen(&self, position: Pos2) -> Value {
|
||||
pub fn plot_from_screen(&self, position: Pos2) -> PlotPoint {
|
||||
self.last_screen_transform.value_from_position(position)
|
||||
}
|
||||
|
||||
|
@ -1188,12 +1188,12 @@ impl PreparedPlot {
|
|||
let value_main = step.value;
|
||||
|
||||
let value = if axis == 0 {
|
||||
Value::new(value_main, value_cross)
|
||||
PlotPoint::new(value_main, value_cross)
|
||||
} else {
|
||||
Value::new(value_cross, value_main)
|
||||
PlotPoint::new(value_cross, value_main)
|
||||
};
|
||||
|
||||
let pos_in_gui = transform.position_from_value(&value);
|
||||
let pos_in_gui = transform.position_from_point(&value);
|
||||
let spacing_in_points = (transform.dpos_dvalue()[axis] * step.step_size).abs() as f32;
|
||||
|
||||
let line_alpha = remap_clamp(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::RangeInclusive;
|
||||
|
||||
use super::items::Value;
|
||||
use super::PlotPoint;
|
||||
use crate::*;
|
||||
|
||||
/// 2D bounding box of f64 precision.
|
||||
|
@ -52,15 +52,16 @@ impl PlotBounds {
|
|||
self.max[1] - self.min[1]
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Value {
|
||||
Value {
|
||||
x: (self.min[0] + self.max[0]) / 2.0,
|
||||
y: (self.min[1] + self.max[1]) / 2.0,
|
||||
}
|
||||
pub fn center(&self) -> PlotPoint {
|
||||
[
|
||||
(self.min[0] + self.max[0]) / 2.0,
|
||||
(self.min[1] + self.max[1]) / 2.0,
|
||||
]
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Expand to include the given (x,y) value
|
||||
pub(crate) fn extend_with(&mut self, value: &Value) {
|
||||
pub(crate) fn extend_with(&mut self, value: &PlotPoint) {
|
||||
self.extend_with_x(value.x);
|
||||
self.extend_with_y(value.y);
|
||||
}
|
||||
|
@ -236,7 +237,7 @@ impl ScreenTransform {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn position_from_value(&self, value: &Value) -> Pos2 {
|
||||
pub fn position_from_point(&self, value: &PlotPoint) -> Pos2 {
|
||||
let x = remap(
|
||||
value.x,
|
||||
self.bounds.min[0]..=self.bounds.max[0],
|
||||
|
@ -250,7 +251,7 @@ impl ScreenTransform {
|
|||
pos2(x as f32, y as f32)
|
||||
}
|
||||
|
||||
pub fn value_from_position(&self, pos: Pos2) -> Value {
|
||||
pub fn value_from_position(&self, pos: Pos2) -> PlotPoint {
|
||||
let x = remap(
|
||||
pos.x as f64,
|
||||
(self.frame.left() as f64)..=(self.frame.right() as f64),
|
||||
|
@ -261,16 +262,16 @@ impl ScreenTransform {
|
|||
(self.frame.bottom() as f64)..=(self.frame.top() as f64), // negated y axis!
|
||||
self.bounds.min[1]..=self.bounds.max[1],
|
||||
);
|
||||
Value::new(x, y)
|
||||
PlotPoint::new(x, y)
|
||||
}
|
||||
|
||||
/// Transform a rectangle of plot values to a screen-coordinate rectangle.
|
||||
///
|
||||
/// This typically means that the rect is mirrored vertically (top becomes bottom and vice versa),
|
||||
/// since the plot's coordinate system has +Y up, while egui has +Y down.
|
||||
pub fn rect_from_values(&self, value1: &Value, value2: &Value) -> Rect {
|
||||
let pos1 = self.position_from_value(value1);
|
||||
let pos2 = self.position_from_value(value2);
|
||||
pub fn rect_from_values(&self, value1: &PlotPoint, value2: &PlotPoint) -> Rect {
|
||||
let pos1 = self.position_from_point(value1);
|
||||
let pos2 = self.position_from_point(value2);
|
||||
|
||||
let mut rect = Rect::NOTHING;
|
||||
rect.extend_with(pos1);
|
||||
|
|
|
@ -45,7 +45,7 @@ egui_demo_lib = { version = "0.18.0", path = "../egui_demo_lib", features = ["ch
|
|||
|
||||
# Optional dependencies:
|
||||
|
||||
bytemuck = { version = "1.9.1", optional = true }
|
||||
bytemuck = { version = "1.7.1", optional = true }
|
||||
egui_extras = { version = "0.18.0", optional = true, path = "../egui_extras" }
|
||||
wgpu = { version = "0.13", optional = true, features = ["webgl"] }
|
||||
|
||||
|
|
|
@ -116,17 +116,21 @@ impl super::View for ContextMenus {
|
|||
|
||||
impl ContextMenus {
|
||||
fn example_plot(&self, ui: &mut egui::Ui) -> egui::Response {
|
||||
use egui::plot::{Line, Value, Values};
|
||||
use egui::plot::{Line, PlotPoints};
|
||||
let n = 128;
|
||||
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
|
||||
let line = Line::new(
|
||||
(0..=n)
|
||||
.map(|i| {
|
||||
use std::f64::consts::TAU;
|
||||
let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU);
|
||||
match self.plot {
|
||||
Plot::Sin => Value::new(x, x.sin()),
|
||||
Plot::Bell => Value::new(x, 10.0 * gaussian(x)),
|
||||
Plot::Sigmoid => Value::new(x, sigmoid(x)),
|
||||
Plot::Sin => [x, x.sin()],
|
||||
Plot::Bell => [x, 10.0 * gaussian(x)],
|
||||
Plot::Sigmoid => [x, sigmoid(x)],
|
||||
}
|
||||
})));
|
||||
})
|
||||
.collect::<PlotPoints>(),
|
||||
);
|
||||
egui::plot::Plot::new("example_plot")
|
||||
.show_axes(self.show_axes)
|
||||
.allow_drag(self.allow_drag)
|
||||
|
|
|
@ -5,8 +5,8 @@ use egui::plot::{GridInput, GridMark};
|
|||
use egui::*;
|
||||
use plot::{
|
||||
Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner, HLine,
|
||||
Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, Points, Polygon, Text, VLine, Value,
|
||||
Values,
|
||||
Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, PlotPoint, PlotPoints, Points, Polygon,
|
||||
Text, VLine,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -108,15 +108,17 @@ impl LineDemo {
|
|||
|
||||
fn circle(&self) -> Line {
|
||||
let n = 512;
|
||||
let circle = (0..=n).map(|i| {
|
||||
let circle_points: PlotPoints = (0..=n)
|
||||
.map(|i| {
|
||||
let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU);
|
||||
let r = self.circle_radius;
|
||||
Value::new(
|
||||
[
|
||||
r * t.cos() + self.circle_center.x as f64,
|
||||
r * t.sin() + self.circle_center.y as f64,
|
||||
)
|
||||
});
|
||||
Line::new(Values::from_values_iter(circle))
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
Line::new(circle_points)
|
||||
.color(Color32::from_rgb(100, 200, 100))
|
||||
.style(self.line_style)
|
||||
.name("circle")
|
||||
|
@ -124,7 +126,7 @@ impl LineDemo {
|
|||
|
||||
fn sin(&self) -> Line {
|
||||
let time = self.time;
|
||||
Line::new(Values::from_explicit_callback(
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| 0.5 * (2.0 * x).sin() * time.sin(),
|
||||
..,
|
||||
512,
|
||||
|
@ -136,7 +138,7 @@ impl LineDemo {
|
|||
|
||||
fn thingy(&self) -> Line {
|
||||
let time = self.time;
|
||||
Line::new(Values::from_parametric_callback(
|
||||
Line::new(PlotPoints::from_parametric_callback(
|
||||
move |t| ((2.0 * t + time).sin(), (3.0 * t).sin()),
|
||||
0.0..=TAU,
|
||||
256,
|
||||
|
@ -199,15 +201,15 @@ impl MarkerDemo {
|
|||
MarkerShape::all()
|
||||
.enumerate()
|
||||
.map(|(i, marker)| {
|
||||
let y_offset = i as f32 * 0.5 + 1.0;
|
||||
let mut points = Points::new(Values::from_values(vec![
|
||||
Value::new(1.0, 0.0 + y_offset),
|
||||
Value::new(2.0, 0.5 + y_offset),
|
||||
Value::new(3.0, 0.0 + y_offset),
|
||||
Value::new(4.0, 0.5 + y_offset),
|
||||
Value::new(5.0, 0.0 + y_offset),
|
||||
Value::new(6.0, 0.5 + y_offset),
|
||||
]))
|
||||
let y_offset = i as f64 * 0.5 + 1.0;
|
||||
let mut points = Points::new(vec![
|
||||
[1.0, 0.0 + y_offset],
|
||||
[2.0, 0.5 + y_offset],
|
||||
[3.0, 0.0 + y_offset],
|
||||
[4.0, 0.5 + y_offset],
|
||||
[5.0, 0.0 + y_offset],
|
||||
[6.0, 0.5 + y_offset],
|
||||
])
|
||||
.name(format!("{:?}", marker))
|
||||
.filled(self.fill_markers)
|
||||
.radius(self.marker_radius)
|
||||
|
@ -259,13 +261,25 @@ struct LegendDemo {
|
|||
|
||||
impl LegendDemo {
|
||||
fn line_with_slope(slope: f64) -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| slope * x, .., 100))
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| slope * x,
|
||||
..,
|
||||
100,
|
||||
))
|
||||
}
|
||||
fn sin() -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| x.sin(), .., 100))
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| x.sin(),
|
||||
..,
|
||||
100,
|
||||
))
|
||||
}
|
||||
fn cos() -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| x.cos(), .., 100))
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| x.cos(),
|
||||
..,
|
||||
100,
|
||||
))
|
||||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut Ui) -> Response {
|
||||
|
@ -327,7 +341,7 @@ impl CustomAxisDemo {
|
|||
CustomAxisDemo::MINS_PER_DAY * min
|
||||
}
|
||||
|
||||
let values = Values::from_explicit_callback(
|
||||
let values = PlotPoints::from_explicit_callback(
|
||||
move |x| 1.0 / (1.0 + (-2.5 * (x / CustomAxisDemo::MINS_PER_DAY - 2.0)).exp()),
|
||||
days(0.0)..days(5.0),
|
||||
100,
|
||||
|
@ -410,7 +424,7 @@ impl CustomAxisDemo {
|
|||
}
|
||||
};
|
||||
|
||||
let label_fmt = |_s: &str, val: &Value| {
|
||||
let label_fmt = |_s: &str, val: &PlotPoint| {
|
||||
format!(
|
||||
"Day {d}, {h}:{m:02}\n{p:.2}%",
|
||||
d = get_day(val.x),
|
||||
|
@ -458,13 +472,25 @@ impl Default for LinkedAxisDemo {
|
|||
|
||||
impl LinkedAxisDemo {
|
||||
fn line_with_slope(slope: f64) -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| slope * x, .., 100))
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| slope * x,
|
||||
..,
|
||||
100,
|
||||
))
|
||||
}
|
||||
fn sin() -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| x.sin(), .., 100))
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| x.sin(),
|
||||
..,
|
||||
100,
|
||||
))
|
||||
}
|
||||
fn cos() -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| x.cos(), .., 100))
|
||||
Line::new(PlotPoints::from_explicit_callback(
|
||||
move |x| x.cos(),
|
||||
..,
|
||||
100,
|
||||
))
|
||||
}
|
||||
|
||||
fn configure_plot(plot_ui: &mut plot::PlotUi) {
|
||||
|
@ -519,28 +545,26 @@ impl ItemsDemo {
|
|||
let n = 100;
|
||||
let mut sin_values: Vec<_> = (0..=n)
|
||||
.map(|i| remap(i as f64, 0.0..=n as f64, -TAU..=TAU))
|
||||
.map(|i| Value::new(i, i.sin()))
|
||||
.map(|i| [i, i.sin()])
|
||||
.collect();
|
||||
|
||||
let line = Line::new(Values::from_values(sin_values.split_off(n / 2))).fill(-1.5);
|
||||
let polygon = Polygon::new(Values::from_parametric_callback(
|
||||
let line = Line::new(sin_values.split_off(n / 2)).fill(-1.5);
|
||||
let polygon = Polygon::new(PlotPoints::from_parametric_callback(
|
||||
|t| (4.0 * t.sin() + 2.0 * t.cos(), 4.0 * t.cos() + 2.0 * t.sin()),
|
||||
0.0..TAU,
|
||||
100,
|
||||
));
|
||||
let points = Points::new(Values::from_values(sin_values))
|
||||
.stems(-1.5)
|
||||
.radius(1.0);
|
||||
let points = Points::new(sin_values).stems(-1.5).radius(1.0);
|
||||
|
||||
let arrows = {
|
||||
let pos_radius = 8.0;
|
||||
let tip_radius = 7.0;
|
||||
let arrow_origins = Values::from_parametric_callback(
|
||||
let arrow_origins = PlotPoints::from_parametric_callback(
|
||||
|t| (pos_radius * t.sin(), pos_radius * t.cos()),
|
||||
0.0..TAU,
|
||||
36,
|
||||
);
|
||||
let arrow_tips = Values::from_parametric_callback(
|
||||
let arrow_tips = PlotPoints::from_parametric_callback(
|
||||
|t| (tip_radius * t.sin(), tip_radius * t.cos()),
|
||||
0.0..TAU,
|
||||
36,
|
||||
|
@ -557,7 +581,7 @@ impl ItemsDemo {
|
|||
});
|
||||
let image = PlotImage::new(
|
||||
texture,
|
||||
Value::new(0.0, 10.0),
|
||||
PlotPoint::new(0.0, 10.0),
|
||||
5.0 * vec2(texture.aspect_ratio(), 1.0),
|
||||
);
|
||||
|
||||
|
@ -574,10 +598,10 @@ impl ItemsDemo {
|
|||
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.text(Text::new(PlotPoint::new(-3.0, -3.0), "wow").name("Text"));
|
||||
plot_ui.text(Text::new(PlotPoint::new(-2.0, 2.5), "so graph").name("Text"));
|
||||
plot_ui.text(Text::new(PlotPoint::new(3.0, 3.0), "much color").name("Text"));
|
||||
plot_ui.text(Text::new(PlotPoint::new(2.5, -2.0), "such plot").name("Text"));
|
||||
plot_ui.image(image.name("Image"));
|
||||
plot_ui.arrows(arrows.name("Arrows"));
|
||||
})
|
||||
|
@ -600,7 +624,7 @@ impl InteractionDemo {
|
|||
inner: (screen_pos, pointer_coordinate, pointer_coordinate_drag_delta, bounds, hovered),
|
||||
} = plot.show(ui, |plot_ui| {
|
||||
(
|
||||
plot_ui.screen_from_plot(Value::new(0.0, 0.0)),
|
||||
plot_ui.screen_from_plot(PlotPoint::new(0.0, 0.0)),
|
||||
plot_ui.pointer_coordinate(),
|
||||
plot_ui.pointer_coordinate_drag_delta(),
|
||||
plot_ui.plot_bounds(),
|
||||
|
|
|
@ -260,13 +260,16 @@ impl WidgetGallery {
|
|||
}
|
||||
|
||||
fn example_plot(ui: &mut egui::Ui) -> egui::Response {
|
||||
use egui::plot::{Line, Value, Values};
|
||||
use egui::plot::{Line, PlotPoints};
|
||||
let n = 128;
|
||||
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
|
||||
let line_points: PlotPoints = (0..=n)
|
||||
.map(|i| {
|
||||
use std::f64::consts::TAU;
|
||||
let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU);
|
||||
Value::new(x, x.sin())
|
||||
})));
|
||||
[x, x.sin()]
|
||||
})
|
||||
.collect();
|
||||
let line = Line::new(line_points);
|
||||
egui::plot::Plot::new("example_plot")
|
||||
.height(32.0)
|
||||
.data_aspect(1.0)
|
||||
|
|
Loading…
Reference in a new issue