diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/plotters/src/chart/context.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/plotters/src/chart/context.rs')
-rw-r--r-- | vendor/plotters/src/chart/context.rs | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/vendor/plotters/src/chart/context.rs b/vendor/plotters/src/chart/context.rs new file mode 100644 index 000000000..ef91af195 --- /dev/null +++ b/vendor/plotters/src/chart/context.rs @@ -0,0 +1,221 @@ +use std::borrow::Borrow; + +use plotters_backend::{BackendCoord, DrawingBackend}; + +use crate::chart::{SeriesAnno, SeriesLabelStyle}; +use crate::coord::{CoordTranslate, ReverseCoordTranslate, Shift}; +use crate::drawing::{DrawingArea, DrawingAreaErrorKind}; +use crate::element::{CoordMapper, Drawable, PointCollection}; + +pub(super) mod cartesian2d; +pub(super) mod cartesian3d; + +pub(super) use cartesian3d::Coord3D; + +/** +The context of the chart. This is the core object of Plotters. + +Any plot/chart is abstracted as this type, and any data series can be placed to the chart context. + +- To draw a series on a chart context, use [`ChartContext::draw_series()`]. +- To draw a single element on the chart, you may want to use [`ChartContext::plotting_area()`]. + +See [`crate::series::LineSeries`] and [`ChartContext::configure_series_labels()`] for more information and examples +*/ +pub struct ChartContext<'a, DB: DrawingBackend, CT: CoordTranslate> { + pub(crate) x_label_area: [Option<DrawingArea<DB, Shift>>; 2], + pub(crate) y_label_area: [Option<DrawingArea<DB, Shift>>; 2], + pub(crate) drawing_area: DrawingArea<DB, CT>, + pub(crate) series_anno: Vec<SeriesAnno<'a, DB>>, + pub(crate) drawing_area_pos: (i32, i32), +} + +impl<'a, DB: DrawingBackend, CT: ReverseCoordTranslate> ChartContext<'a, DB, CT> { + /// Convert the chart context into an closure that can be used for coordinate translation + pub fn into_coord_trans(self) -> impl Fn(BackendCoord) -> Option<CT::From> { + let coord_spec = self.drawing_area.into_coord_spec(); + move |coord| coord_spec.reverse_translate(coord) + } +} + +impl<'a, DB: DrawingBackend, CT: CoordTranslate> ChartContext<'a, DB, CT> { + /** + Configure the styles for drawing series labels in the chart + + # Example + + ``` + use plotters::prelude::*; + let data = [(1.0, 3.3), (2., 2.1), (3., 1.5), (4., 1.9), (5., 1.0)]; + let drawing_area = SVGBackend::new("configure_series_labels.svg", (300, 200)).into_drawing_area(); + drawing_area.fill(&WHITE).unwrap(); + let mut chart_builder = ChartBuilder::on(&drawing_area); + chart_builder.margin(7).set_left_and_bottom_label_area_size(20); + let mut chart_context = chart_builder.build_cartesian_2d(0.0..5.5, 0.0..5.5).unwrap(); + chart_context.configure_mesh().draw().unwrap(); + chart_context.draw_series(LineSeries::new(data, BLACK)).unwrap().label("Series 1") + .legend(|(x,y)| Rectangle::new([(x - 15, y + 1), (x, y)], BLACK)); + chart_context.configure_series_labels().position(SeriesLabelPosition::UpperRight).margin(20) + .legend_area_size(5).border_style(BLUE).background_style(BLUE.mix(0.1)).label_font(("Calibri", 20)).draw().unwrap(); + ``` + + The result is a chart with one data series labeled "Series 1" in a blue legend box: + + ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@8e0fe60/apidoc/configure_series_labels.svg) + + # See also + + See [`crate::series::LineSeries`] for more information and examples + */ + pub fn configure_series_labels<'b>(&'b mut self) -> SeriesLabelStyle<'a, 'b, DB, CT> + where + DB: 'a, + { + SeriesLabelStyle::new(self) + } + + /// Get a reference of underlying plotting area + pub fn plotting_area(&self) -> &DrawingArea<DB, CT> { + &self.drawing_area + } + + /// Cast the reference to a chart context to a reference to underlying coordinate specification. + pub fn as_coord_spec(&self) -> &CT { + self.drawing_area.as_coord_spec() + } + + // TODO: All draw_series_impl is overly strict about lifetime, because we don't have stable HKT, + // what we can ensure is for all lifetime 'b the element reference &'b E is a iterator + // of points reference with the same lifetime. + // However, this doesn't work if the coordinate doesn't live longer than the backend, + // this is unnecessarily strict + pub(crate) fn draw_series_impl<B, E, R, S>( + &mut self, + series: S, + ) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> + where + B: CoordMapper, + for<'b> &'b E: PointCollection<'b, CT::From, B>, + E: Drawable<DB, B>, + R: Borrow<E>, + S: IntoIterator<Item = R>, + { + for element in series { + self.drawing_area.draw(element.borrow())?; + } + Ok(()) + } + + pub(crate) fn alloc_series_anno(&mut self) -> &mut SeriesAnno<'a, DB> { + let idx = self.series_anno.len(); + self.series_anno.push(SeriesAnno::new()); + &mut self.series_anno[idx] + } + + /** + Draws a data series. A data series in Plotters is abstracted as an iterator of elements. + + See [`crate::series::LineSeries`] and [`ChartContext::configure_series_labels()`] for more information and examples. + */ + pub fn draw_series<B, E, R, S>( + &mut self, + series: S, + ) -> Result<&mut SeriesAnno<'a, DB>, DrawingAreaErrorKind<DB::ErrorType>> + where + B: CoordMapper, + for<'b> &'b E: PointCollection<'b, CT::From, B>, + E: Drawable<DB, B>, + R: Borrow<E>, + S: IntoIterator<Item = R>, + { + self.draw_series_impl(series)?; + Ok(self.alloc_series_anno()) + } +} + +#[cfg(test)] +mod test { + use crate::prelude::*; + + #[test] + fn test_chart_context() { + let drawing_area = create_mocked_drawing_area(200, 200, |_| {}); + + drawing_area.fill(&WHITE).expect("Fill"); + + let mut chart = ChartBuilder::on(&drawing_area) + .caption("Test Title", ("serif", 10)) + .x_label_area_size(20) + .y_label_area_size(20) + .set_label_area_size(LabelAreaPosition::Top, 20) + .set_label_area_size(LabelAreaPosition::Right, 20) + .build_cartesian_2d(0..10, 0..10) + .expect("Create chart") + .set_secondary_coord(0.0..1.0, 0.0..1.0); + + chart + .configure_mesh() + .x_desc("X") + .y_desc("Y") + .draw() + .expect("Draw mesh"); + chart + .configure_secondary_axes() + .x_desc("X") + .y_desc("Y") + .draw() + .expect("Draw Secondary axes"); + + // test that chart states work correctly with dual coord charts + let cs = chart.into_chart_state(); + let mut chart = cs.clone().restore(&drawing_area); + + chart + .draw_series(std::iter::once(Circle::new((5, 5), 5, &RED))) + .expect("Drawing error"); + chart + .draw_secondary_series(std::iter::once(Circle::new((0.3, 0.8), 5, &GREEN))) + .expect("Drawing error") + .label("Test label") + .legend(|(x, y)| Rectangle::new([(x - 10, y - 5), (x, y + 5)], &GREEN)); + + chart + .configure_series_labels() + .position(SeriesLabelPosition::UpperMiddle) + .draw() + .expect("Drawing error"); + } + + #[test] + fn test_chart_context_3d() { + let drawing_area = create_mocked_drawing_area(200, 200, |_| {}); + + drawing_area.fill(&WHITE).expect("Fill"); + + let mut chart = ChartBuilder::on(&drawing_area) + .caption("Test Title", ("serif", 10)) + .x_label_area_size(20) + .y_label_area_size(20) + .set_label_area_size(LabelAreaPosition::Top, 20) + .set_label_area_size(LabelAreaPosition::Right, 20) + .build_cartesian_3d(0..10, 0..10, 0..10) + .expect("Create chart"); + + chart.with_projection(|mut pb| { + pb.yaw = 0.5; + pb.pitch = 0.5; + pb.scale = 0.5; + pb.into_matrix() + }); + + chart.configure_axes().draw().expect("Drawing axes"); + + // test that chart states work correctly with 3d coordinates + let cs = chart.into_chart_state(); + let mut chart = cs.clone().restore(&drawing_area); + + chart + .draw_series(std::iter::once(Circle::new((5, 5, 5), 5, &RED))) + .expect("Drawing error"); + } +} |