Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow borrowing plot points by adding PlotPoints::Borrowed #35

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions demo/src/plot_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ impl LineDemo {
});
}

fn circle(&self) -> Line {
fn circle<'a>(&self) -> Line<'a> {
let n = 512;
let circle_points: PlotPoints = (0..=n)
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;
Expand All @@ -227,7 +227,7 @@ impl LineDemo {
.name("circle")
}

fn sin(&self) -> Line {
fn sin<'a>(&self) -> Line<'a> {
let time = self.time;
Line::new(PlotPoints::from_explicit_callback(
move |x| 0.5 * (2.0 * x).sin() * time.sin(),
Expand All @@ -239,7 +239,7 @@ impl LineDemo {
.name("wave")
}

fn thingy(&self) -> Line {
fn thingy<'a>(&self) -> Line<'a> {
let time = self.time;
Line::new(PlotPoints::from_parametric_callback(
move |t| ((2.0 * t + time).sin(), (3.0 * t).sin()),
Expand Down Expand Up @@ -304,7 +304,7 @@ impl Default for MarkerDemo {
}

impl MarkerDemo {
fn markers(&self) -> Vec<Points> {
fn markers<'a>(&self) -> Vec<Points<'a>> {
MarkerShape::all()
.enumerate()
.map(|(i, marker)| {
Expand Down Expand Up @@ -367,23 +367,23 @@ struct LegendDemo {
}

impl LegendDemo {
fn line_with_slope(slope: f64) -> Line {
fn line_with_slope<'a>(slope: f64) -> Line<'a> {
Line::new(PlotPoints::from_explicit_callback(
move |x| slope * x,
..,
100,
))
}

fn sin() -> Line {
fn sin<'a>() -> Line<'a> {
Line::new(PlotPoints::from_explicit_callback(
move |x| x.sin(),
..,
100,
))
}

fn cos() -> Line {
fn cos<'a>() -> Line<'a> {
Line::new(PlotPoints::from_explicit_callback(
move |x| x.cos(),
..,
Expand Down Expand Up @@ -444,7 +444,7 @@ impl CustomAxesDemo {
const MINS_PER_DAY: f64 = 24.0 * 60.0;
const MINS_PER_H: f64 = 60.0;

fn logistic_fn() -> Line {
fn logistic_fn<'a>() -> Line<'a> {
fn days(min: f64) -> f64 {
CustomAxesDemo::MINS_PER_DAY * min
}
Expand Down Expand Up @@ -598,31 +598,31 @@ impl Default for LinkedAxesDemo {
}

impl LinkedAxesDemo {
fn line_with_slope(slope: f64) -> Line {
fn line_with_slope<'a>(slope: f64) -> Line<'a> {
Line::new(PlotPoints::from_explicit_callback(
move |x| slope * x,
..,
100,
))
}

fn sin() -> Line {
fn sin<'a>() -> Line<'a> {
Line::new(PlotPoints::from_explicit_callback(
move |x| x.sin(),
..,
100,
))
}

fn cos() -> Line {
fn cos<'a>() -> Line<'a> {
Line::new(PlotPoints::from_explicit_callback(
move |x| x.cos(),
..,
100,
))
}

fn configure_plot(plot_ui: &mut egui_plot::PlotUi) {
fn configure_plot(plot_ui: &mut egui_plot::PlotUi<'_>) {
plot_ui.line(Self::line_with_slope(0.5));
plot_ui.line(Self::line_with_slope(1.0));
plot_ui.line(Self::line_with_slope(2.0));
Expand Down
42 changes: 21 additions & 21 deletions egui_plot/src/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,8 @@ impl PlotItem for VLine {
}

/// A series of values forming a path.
pub struct Line {
pub(super) series: PlotPoints,
pub struct Line<'a> {
pub(super) series: PlotPoints<'a>,
pub(super) stroke: Stroke,
pub(super) name: String,
pub(super) highlight: bool,
Expand All @@ -431,8 +431,8 @@ pub struct Line {
id: Option<Id>,
}

impl Line {
pub fn new(series: impl Into<PlotPoints>) -> Self {
impl<'a> Line<'a> {
pub fn new(series: impl Into<PlotPoints<'a>>) -> Self {
Self {
series: series.into(),
stroke: Stroke::new(1.5, Color32::TRANSPARENT), // Note: a stroke of 1.0 (or less) can look bad on low-dpi-screens
Expand Down Expand Up @@ -522,7 +522,7 @@ fn y_intersection(p1: &Pos2, p2: &Pos2, y: f32) -> Option<f32> {
.then_some(((y * (p1.x - p2.x)) - (p1.x * p2.y - p1.y * p2.x)) / (p1.y - p2.y))
}

impl PlotItem for Line {
impl<'a> PlotItem for Line<'a> {
fn shapes(&self, _ui: &Ui, transform: &PlotTransform, shapes: &mut Vec<Shape>) {
let Self {
series,
Expand Down Expand Up @@ -620,8 +620,8 @@ impl PlotItem for Line {
}

/// A convex polygon.
pub struct Polygon {
pub(super) series: PlotPoints,
pub struct Polygon<'a> {
pub(super) series: PlotPoints<'a>,
pub(super) stroke: Stroke,
pub(super) name: String,
pub(super) highlight: bool,
Expand All @@ -631,8 +631,8 @@ pub struct Polygon {
id: Option<Id>,
}

impl Polygon {
pub fn new(series: impl Into<PlotPoints>) -> Self {
impl<'a> Polygon<'a> {
pub fn new(series: impl Into<PlotPoints<'a>>) -> Self {
Self {
series: series.into(),
stroke: Stroke::new(1.0, Color32::TRANSPARENT),
Expand Down Expand Up @@ -709,7 +709,7 @@ impl Polygon {
}
}

impl PlotItem for Polygon {
impl<'a> PlotItem for Polygon<'a> {
fn shapes(&self, _ui: &Ui, transform: &PlotTransform, shapes: &mut Vec<Shape>) {
let Self {
series,
Expand Down Expand Up @@ -918,8 +918,8 @@ impl PlotItem for Text {
}

/// A set of points.
pub struct Points {
pub(super) series: PlotPoints,
pub struct Points<'a> {
pub(super) series: PlotPoints<'a>,

pub(super) shape: MarkerShape,

Expand All @@ -942,8 +942,8 @@ pub struct Points {
id: Option<Id>,
}

impl Points {
pub fn new(series: impl Into<PlotPoints>) -> Self {
impl<'a> Points<'a> {
pub fn new(series: impl Into<PlotPoints<'a>>) -> Self {
Self {
series: series.into(),
shape: MarkerShape::Circle,
Expand Down Expand Up @@ -1028,7 +1028,7 @@ impl Points {
}
}

impl PlotItem for Points {
impl<'a> PlotItem for Points<'a> {
#[allow(clippy::too_many_lines)] // TODO(emilk): shorten this function
fn shapes(&self, _ui: &Ui, transform: &PlotTransform, shapes: &mut Vec<Shape>) {
let sqrt_3 = 3_f32.sqrt();
Expand Down Expand Up @@ -1196,9 +1196,9 @@ impl PlotItem for Points {
}

/// A set of arrows.
pub struct Arrows {
pub(super) origins: PlotPoints,
pub(super) tips: PlotPoints,
pub struct Arrows<'a> {
pub(super) origins: PlotPoints<'a>,
pub(super) tips: PlotPoints<'a>,
pub(super) tip_length: Option<f32>,
pub(super) color: Color32,
pub(super) name: String,
Expand All @@ -1207,8 +1207,8 @@ pub struct Arrows {
id: Option<Id>,
}

impl Arrows {
pub fn new(origins: impl Into<PlotPoints>, tips: impl Into<PlotPoints>) -> Self {
impl<'a> Arrows<'a> {
pub fn new(origins: impl Into<PlotPoints<'a>>, tips: impl Into<PlotPoints<'a>>) -> Self {
Self {
origins: origins.into(),
tips: tips.into(),
Expand Down Expand Up @@ -1270,7 +1270,7 @@ impl Arrows {
}
}

impl PlotItem for Arrows {
impl<'a> PlotItem for Arrows<'a> {
fn shapes(&self, _ui: &Ui, transform: &PlotTransform, shapes: &mut Vec<Shape>) {
let Self {
origins,
Expand Down
41 changes: 29 additions & 12 deletions egui_plot/src/items/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,37 +154,45 @@ impl Default for Orientation {
/// Represents many [`PlotPoint`]s.
///
/// These can be an owned `Vec` or generated with a function.
pub enum PlotPoints {
pub enum PlotPoints<'a> {
Owned(Vec<PlotPoint>),
Generator(ExplicitGenerator),
// Borrowed(&[PlotPoint]), // TODO(EmbersArc): Lifetimes are tricky in this case.
Generator(ExplicitGenerator<'a>),
Borrowed(&'a [PlotPoint]),
Comment on lines 155 to +160
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update this docstring

}

impl Default for PlotPoints {
impl<'a> Default for PlotPoints<'a> {
fn default() -> Self {
Self::Owned(Vec::new())
}
}

impl From<[f64; 2]> for PlotPoints {
impl<'a> From<[f64; 2]> for PlotPoints<'a> {
fn from(coordinate: [f64; 2]) -> Self {
Self::new(vec![coordinate])
}
}

impl From<Vec<[f64; 2]>> for PlotPoints {
impl<'a> From<Vec<[f64; 2]>> for PlotPoints<'a> {
#[inline]
fn from(coordinates: Vec<[f64; 2]>) -> Self {
Self::new(coordinates)
}
}

impl FromIterator<[f64; 2]> for PlotPoints {
impl<'a> From<&'a [PlotPoint]> for PlotPoints<'a> {
#[inline]
fn from(points: &'a [PlotPoint]) -> Self {
Self::Borrowed(points)
}
}

impl<'a> FromIterator<[f64; 2]> for PlotPoints<'a> {
fn from_iter<T: IntoIterator<Item = [f64; 2]>>(iter: T) -> Self {
Self::Owned(iter.into_iter().map(|point| point.into()).collect())
}
}

impl PlotPoints {
impl<'a> PlotPoints<'a> {
pub fn new(points: Vec<[f64; 2]>) -> Self {
Self::from_iter(points)
}
Expand All @@ -193,12 +201,13 @@ impl PlotPoints {
match self {
Self::Owned(points) => points.as_slice(),
Self::Generator(_) => &[],
Self::Borrowed(points) => points,
}
}

/// Draw a line based on a function `y=f(x)`, a range (which can be infinite) for x and the number of points.
pub fn from_explicit_callback(
function: impl Fn(f64) -> f64 + 'static,
function: impl Fn(f64) -> f64 + 'a,
x_range: impl RangeBounds<f64>,
points: usize,
) -> Self {
Expand Down Expand Up @@ -271,6 +280,7 @@ impl PlotPoints {
match self {
Self::Owned(points) => points.is_empty(),
Self::Generator(_) => false,
Self::Borrowed(points) => points.is_empty(),
}
}

Expand Down Expand Up @@ -314,6 +324,13 @@ impl PlotPoints {
bounds
}
Self::Generator(generator) => generator.estimate_bounds(),
Self::Borrowed(points) => {
let mut bounds = PlotBounds::NOTHING;
for point in *points {
bounds.extend_with(point);
}
bounds
}
}
}
}
Expand Down Expand Up @@ -374,13 +391,13 @@ pub enum PlotGeometry<'a> {
// ----------------------------------------------------------------------------

/// Describes a function y = f(x) with an optional range for x and a number of points.
pub struct ExplicitGenerator {
function: Box<dyn Fn(f64) -> f64>,
pub struct ExplicitGenerator<'a> {
function: Box<dyn Fn(f64) -> f64 + 'a>,
x_range: RangeInclusive<f64>,
points: usize,
}

impl ExplicitGenerator {
impl ExplicitGenerator<'_> {
fn estimate_bounds(&self) -> PlotBounds {
let mut bounds = PlotBounds::NOTHING;

Expand Down
4 changes: 2 additions & 2 deletions egui_plot/src/legend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ pub(super) struct LegendWidget {
impl LegendWidget {
/// Create a new legend from items, the names of items that are hidden and the style of the
/// text. Returns `None` if the legend has no entries.
pub(super) fn try_new(
pub(super) fn try_new<'a>(
rect: Rect,
config: Legend,
items: &[Box<dyn PlotItem>],
items: &[Box<dyn PlotItem + 'a>],
hidden_items: &ahash::HashSet<String>, // Existing hidden items in the plot memory.
) -> Option<Self> {
// If `config.hidden_items` is not `None`, it is used.
Expand Down
10 changes: 5 additions & 5 deletions egui_plot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,20 +728,20 @@ impl<'a> Plot<'a> {
}

/// Interact with and add items to the plot and finally draw it.
pub fn show<R>(
pub fn show<'b, R>(
self,
ui: &mut Ui,
build_fn: impl FnOnce(&mut PlotUi) -> R + 'a,
build_fn: impl FnOnce(&mut PlotUi<'b>) -> R + 'a,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a tricky one.

The following example helped me with figuring out the correct lifetime annotation:

trait PlotItem: std::fmt::Debug {}

#[derive(Debug)]
struct Line<'a>(&'a [f64]);

impl<'a> PlotItem for Line<'a> {}

#[derive(Debug)]
struct PlotUi<'a>(Vec<Box<dyn PlotItem + 'a>>);

impl<'a> PlotUi<'a> {
    fn line(&mut self, line: Line<'a>) {
        self.0.push(Box::new(line));
    }
}

fn show<'a>(f: impl FnOnce(&mut PlotUi<'a>)) {
    let mut foo = PlotUi(Vec::new());
    f(&mut foo);
}

fn main() {
    let v = vec![1.0, 2.0];
    show(|plot_ui| {
        plot_ui.line(Line(&v));
        println!("{plot_ui:?}");
    });
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like we should add that code as an example or test then 🤔

) -> PlotResponse<R> {
self.show_dyn(ui, Box::new(build_fn))
}

#[allow(clippy::too_many_lines)] // TODO(emilk): shorten this function
#[allow(clippy::type_complexity)] // build_fn
fn show_dyn<R>(
fn show_dyn<'b, R>(
self,
ui: &mut Ui,
build_fn: Box<dyn FnOnce(&mut PlotUi) -> R + 'a>,
build_fn: Box<dyn FnOnce(&mut PlotUi<'b>) -> R + 'a>,
) -> PlotResponse<R> {
let Self {
id_source,
Expand Down Expand Up @@ -1467,7 +1467,7 @@ pub fn uniform_grid_spacer<'a>(spacer: impl Fn(GridInput) -> [f64; 3] + 'a) -> G
// ----------------------------------------------------------------------------

struct PreparedPlot<'a> {
items: Vec<Box<dyn PlotItem>>,
items: Vec<Box<dyn PlotItem + 'a>>,
show_x: bool,
show_y: bool,
label_formatter: LabelFormatter<'a>,
Expand Down
Loading
Loading