summaryrefslogtreecommitdiffstats
path: root/vendor/plotters/src/element/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/plotters/src/element/mod.rs')
-rw-r--r--vendor/plotters/src/element/mod.rs290
1 files changed, 290 insertions, 0 deletions
diff --git a/vendor/plotters/src/element/mod.rs b/vendor/plotters/src/element/mod.rs
new file mode 100644
index 000000000..e2790051f
--- /dev/null
+++ b/vendor/plotters/src/element/mod.rs
@@ -0,0 +1,290 @@
+/*!
+ Defines the drawing elements, the high-level drawing unit in Plotters drawing system
+
+ ## Introduction
+ An element is the drawing unit for Plotter's high-level drawing API.
+ Different from low-level drawing API, an element is a logic unit of component in the image.
+ There are few built-in elements, including `Circle`, `Pixel`, `Rectangle`, `Path`, `Text`, etc.
+
+ All element can be drawn onto the drawing area using API `DrawingArea::draw(...)`.
+ Plotters use "iterator of elements" as the abstraction of any type of plot.
+
+ ## Implementing your own element
+ You can also define your own element, `CandleStick` is a good sample of implementing complex
+ element. There are two trait required for an element:
+
+ - `PointCollection` - the struct should be able to return an iterator of key-points under guest coordinate
+ - `Drawable` - the struct is a pending drawing operation on a drawing backend with pixel-based coordinate
+
+ An example of element that draws a red "X" in a red rectangle onto the backend:
+
+ ```rust
+ use std::iter::{Once, once};
+ use plotters::element::{PointCollection, Drawable};
+ use plotters_backend::{BackendCoord, DrawingErrorKind, BackendStyle};
+ use plotters::style::IntoTextStyle;
+ use plotters::prelude::*;
+
+ // Any example drawing a red X
+ struct RedBoxedX((i32, i32));
+
+ // For any reference to RedX, we can convert it into an iterator of points
+ impl <'a> PointCollection<'a, (i32, i32)> for &'a RedBoxedX {
+ type Point = &'a (i32, i32);
+ type IntoIter = Once<&'a (i32, i32)>;
+ fn point_iter(self) -> Self::IntoIter {
+ once(&self.0)
+ }
+ }
+
+ // How to actually draw this element
+ impl <DB:DrawingBackend> Drawable<DB> for RedBoxedX {
+ fn draw<I:Iterator<Item = BackendCoord>>(
+ &self,
+ mut pos: I,
+ backend: &mut DB,
+ _: (u32, u32),
+ ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
+ let pos = pos.next().unwrap();
+ backend.draw_rect(pos, (pos.0 + 10, pos.1 + 12), &RED, false)?;
+ let text_style = &("sans-serif", 20).into_text_style(&backend.get_size()).color(&RED);
+ backend.draw_text("X", text_style, pos)
+ }
+ }
+
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let root = BitMapBackend::new(
+ "plotters-doc-data/element-0.png",
+ (640, 480)
+ ).into_drawing_area();
+ root.draw(&RedBoxedX((200, 200)))?;
+ Ok(())
+ }
+ ```
+ ![](https://plotters-rs.github.io/plotters-doc-data/element-0.png)
+
+ ## Composable Elements
+ You also have an convenient way to build an element that isn't built into the Plotters library by
+ combining existing elements into a logic group. To build an composable element, you need to use an
+ logic empty element that draws nothing to the backend but denotes the relative zero point of the logical
+ group. Any element defined with pixel based offset coordinate can be added into the group later using
+ the `+` operator.
+
+ For example, the red boxed X element can be implemented with Composable element in the following way:
+ ```rust
+ use plotters::prelude::*;
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let root = BitMapBackend::new(
+ "plotters-doc-data/element-1.png",
+ (640, 480)
+ ).into_drawing_area();
+ let font:FontDesc = ("sans-serif", 20).into();
+ root.draw(&(EmptyElement::at((200, 200))
+ + Text::new("X", (0, 0), &"sans-serif".into_font().resize(20.0).color(&RED))
+ + Rectangle::new([(0,0), (10, 12)], &RED)
+ ))?;
+ Ok(())
+ }
+ ```
+ ![](https://plotters-rs.github.io/plotters-doc-data/element-1.png)
+
+ ## Dynamic Elements
+ By default, Plotters uses static dispatch for all the elements and series. For example,
+ the `ChartContext::draw_series` method accepts an iterator of `T` where type `T` implements
+ all the traits a element should implement. Although, we can use the series of composable element
+ for complex series drawing. But sometimes, we still want to make the series heterogynous, which means
+ the iterator should be able to holds elements in different type.
+ For example, a point series with cross and circle. This requires the dynamically dispatched elements.
+ In plotters, all the elements can be converted into `DynElement`, the dynamic dispatch container for
+ all elements (include external implemented ones).
+ Plotters automatically implements `IntoDynElement` for all elements, by doing so, any dynamic element should have
+ `into_dyn` function which would wrap the element into a dynamic element wrapper.
+
+ For example, the following code counts the number of factors of integer and mark all prime numbers in cross.
+ ```rust
+ use plotters::prelude::*;
+ fn num_of_factor(n: i32) -> i32 {
+ let mut ret = 2;
+ for i in 2..n {
+ if i * i > n {
+ break;
+ }
+
+ if n % i == 0 {
+ if i * i != n {
+ ret += 2;
+ } else {
+ ret += 1;
+ }
+ }
+ }
+ return ret;
+ }
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let root =
+ BitMapBackend::new("plotters-doc-data/element-3.png", (640, 480))
+ .into_drawing_area();
+ root.fill(&WHITE)?;
+ let mut chart = ChartBuilder::on(&root)
+ .x_label_area_size(40)
+ .y_label_area_size(40)
+ .margin(5)
+ .build_cartesian_2d(0..50, 0..10)?;
+
+ chart
+ .configure_mesh()
+ .disable_x_mesh()
+ .disable_y_mesh()
+ .draw()?;
+
+ chart.draw_series((0..50).map(|x| {
+ let center = (x, num_of_factor(x));
+ // Although the arms of if statement has different types,
+ // but they can be placed into a dynamic element wrapper,
+ // by doing so, the type is unified.
+ if center.1 == 2 {
+ Cross::new(center, 4, Into::<ShapeStyle>::into(&RED).filled()).into_dyn()
+ } else {
+ Circle::new(center, 4, Into::<ShapeStyle>::into(&GREEN).filled()).into_dyn()
+ }
+ }))?;
+
+ Ok(())
+ }
+ ```
+ ![](https://plotters-rs.github.io/plotters-doc-data/element-3.png)
+*/
+use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
+use std::borrow::Borrow;
+
+mod basic_shapes;
+pub use basic_shapes::*;
+
+mod basic_shapes_3d;
+pub use basic_shapes_3d::*;
+
+mod text;
+pub use text::*;
+
+mod points;
+pub use points::*;
+
+mod composable;
+pub use composable::{ComposedElement, EmptyElement};
+
+#[cfg(feature = "candlestick")]
+mod candlestick;
+#[cfg(feature = "candlestick")]
+pub use candlestick::CandleStick;
+
+#[cfg(feature = "errorbar")]
+mod errorbar;
+#[cfg(feature = "errorbar")]
+pub use errorbar::{ErrorBar, ErrorBarOrientH, ErrorBarOrientV};
+
+#[cfg(feature = "boxplot")]
+mod boxplot;
+#[cfg(feature = "boxplot")]
+pub use boxplot::Boxplot;
+
+#[cfg(feature = "bitmap_backend")]
+mod image;
+#[cfg(feature = "bitmap_backend")]
+pub use self::image::BitMapElement;
+
+mod dynelem;
+pub use dynelem::{DynElement, IntoDynElement};
+
+mod pie;
+pub use pie::Pie;
+
+use crate::coord::CoordTranslate;
+use crate::drawing::Rect;
+
+/// A type which is logically a collection of points, under any given coordinate system.
+/// Note: Ideally, a point collection trait should be any type of which coordinate elements can be
+/// iterated. This is similar to `iter` method of many collection types in std.
+///
+/// ```ignore
+/// trait PointCollection<Coord> {
+/// type PointIter<'a> : Iterator<Item = &'a Coord>;
+/// fn iter(&self) -> PointIter<'a>;
+/// }
+/// ```
+///
+/// However,
+/// [Generic Associated Types](https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md)
+/// is far away from stablize.
+/// So currently we have the following workaround:
+///
+/// Instead of implement the PointCollection trait on the element type itself, it implements on the
+/// reference to the element. By doing so, we now have a well-defined lifetime for the iterator.
+///
+/// In addition, for some element, the coordinate is computed on the fly, thus we can't hard-code
+/// the iterator's return type is `&'a Coord`.
+/// `Borrow` trait seems to strict in this case, since we don't need the order and hash
+/// preservation properties at this point. However, `AsRef` doesn't work with `Coord`
+///
+/// This workaround also leads overly strict lifetime bound on `ChartContext::draw_series`.
+///
+/// TODO: Once GAT is ready on stable Rust, we should simplify the design.
+///
+pub trait PointCollection<'a, Coord, CM = BackendCoordOnly> {
+ /// The item in point iterator
+ type Point: Borrow<Coord> + 'a;
+
+ /// The point iterator
+ type IntoIter: IntoIterator<Item = Self::Point>;
+
+ /// framework to do the coordinate mapping
+ fn point_iter(self) -> Self::IntoIter;
+}
+/// The trait indicates we are able to draw it on a drawing area
+pub trait Drawable<DB: DrawingBackend, CM: CoordMapper = BackendCoordOnly> {
+ /// Actually draws the element. The key points is already translated into the
+ /// image coordinate and can be used by DC directly
+ fn draw<I: Iterator<Item = CM::Output>>(
+ &self,
+ pos: I,
+ backend: &mut DB,
+ parent_dim: (u32, u32),
+ ) -> Result<(), DrawingErrorKind<DB::ErrorType>>;
+}
+
+/// Useful to translate from guest coordinates to backend coordinates
+pub trait CoordMapper {
+ /// Specifies the output data from the translation
+ type Output;
+ /// Performs the translation from guest coordinates to backend coordinates
+ fn map<CT: CoordTranslate>(coord_trans: &CT, from: &CT::From, rect: &Rect) -> Self::Output;
+}
+
+/// Used for 2d coordinate transformations.
+pub struct BackendCoordOnly;
+
+impl CoordMapper for BackendCoordOnly {
+ type Output = BackendCoord;
+ fn map<CT: CoordTranslate>(coord_trans: &CT, from: &CT::From, rect: &Rect) -> BackendCoord {
+ rect.truncate(coord_trans.translate(from))
+ }
+}
+
+/**
+Used for 3d coordinate transformations.
+
+See [`Cubiod`] for more information and an example.
+*/
+pub struct BackendCoordAndZ;
+
+impl CoordMapper for BackendCoordAndZ {
+ type Output = (BackendCoord, i32);
+ fn map<CT: CoordTranslate>(
+ coord_trans: &CT,
+ from: &CT::From,
+ rect: &Rect,
+ ) -> (BackendCoord, i32) {
+ let coord = rect.truncate(coord_trans.translate(from));
+ let z = coord_trans.depth(from);
+ (coord, z)
+ }
+}