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