1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
//!
//! Plot to SVG and style with CSS
//!
//! You can find poloto on [github](https://github.com/tiby312/poloto) and [crates.io](https://crates.io/crates/poloto).
//! Documentation at [docs.rs](https://docs.rs/poloto)
//!
//! Check out the [github examples](https://github.com/tiby312/poloto/tree/master/examples).
//! The latest graph outputs of the examples can be found in the [assets](https://github.com/tiby312/poloto/tree/master/target/assets) folder.
//!
//!
//!
//! Pipeline:
//! * Collect plots using functions in [`build`] module
//! * Compute min/max by calling [`build()`](build::PlotIteratorExt::build()).
//! * Link the data with canvas options by calling [`Data::stage_with()`] or use default canvas with [`Data::stage()`]
//! * Create tick distributions. (This step can be done automatically using [`Stager::plot()`])
//! * Collect title/xname/yname using [`Stager::plot()`] or [`Stager::plot_with()`]
//! * Write everything to svg. [`Plotter::render()`] for no svg tag/css. [`simple_theme::SimpleTheme`] for basic css/svg tag.
//!
//! Poloto provides by default 3 impls of [`HasDefaultTicks`] for the following types:
//!
//! * [`i128`] - decimal/scientific notation ticks.
//! * [`f64`] - decimal/scientific notation ticks.
//! * [`UnixTime`](num::timestamp::UnixTime) - date/time
//!
//! The above types have the advantage of automatically selecting reasonable
//! tick intervals. The user can change the formatting of the ticks while still using
//! the ticks that were selected via its automatic methods using [`TickFormatExt::with_tick_fmt`].
//!
//! However, sometimes you may want more control on the ticks, or want to use a type
//! other than [`i128`]/[`f64`]/[`UnixTime`](num::timestamp::UnixTime). One way would be to write your own function that returns a [`TickInfo`].
//! Alternatively you can use the [`ticks::from_iter`] function that just takes an iterator of ticks and returns a [`TickInfo`].
//! This puts more responsibility on the user to pass a decent number of ticks. This should only really be used when the user
//! knows up front the min and max values of that axis. This is typically the case for
//! at least one of the axis, typically the x axis. [See step example](https://github.com/tiby312/poloto/blob/master/examples/steps.rs)
#[cfg(doctest)]
mod test_readme {
macro_rules! external_doc_test {
($x:expr) => {
#[doc = $x]
extern "C" {}
};
}
external_doc_test!(include_str!("../README.md"));
}
use std::fmt;
pub use tagger::upgrade_write;
pub mod build;
pub mod plotnum;
pub mod render;
pub mod ticks;
pub mod util;
use plotnum::*;
pub mod num;
pub mod simple_theme;
///
/// The poloto prelude.
///
pub mod prelude {
pub use super::build::crop::Croppable;
pub use super::build::PlotIteratorExt;
pub use super::formatm;
pub use super::plots;
pub use super::simple_theme::SimpleTheme;
pub use super::ticks::TickFormatExt;
}
use fmt::Display;
use std::marker::PhantomData;
use ticks::*;
///The width of the svg tag.
const WIDTH: f64 = 800.0;
///The height of the svg tag.
const HEIGHT: f64 = 500.0;
use render::*;
use build::PlotIterator;
use std::borrow::Borrow;
/// Shorthand for `disp_const(move |w|write!(w,...))`
/// Similar to `std::format_args!()` except has a more flexible lifetime.
#[macro_export]
macro_rules! formatm {
($($arg:tt)*) => {
$crate::disp_const(move |w| write!(w,$($arg)*))
}
}
///
/// Macro to chain multiple plots together instead of caling [`chain`](build::PlotIteratorExt::chain) repeatedly.
///
#[macro_export]
macro_rules! plots {
( $a:expr,$( $x:expr ),* ) => {
{
let mut a=$a;
$(
let a=a.chain($x);
)*
a
}
};
}
///
/// Leverage rust's display format system using [`std::cell::RefCell`] under the hood.
///
pub fn disp<F: FnOnce(&mut fmt::Formatter) -> fmt::Result>(
a: F,
) -> util::DisplayableClosureOnce<F> {
util::DisplayableClosureOnce::new(a)
}
///
/// Leverage rust's display format system using [`std::cell::RefCell`] under the hood.
///
pub fn disp_mut<F: FnMut(&mut fmt::Formatter) -> fmt::Result>(
a: F,
) -> util::DisplayableClosureMut<F> {
util::DisplayableClosureMut::new(a)
}
///
/// Convert a closure to a object that implements Display
///
pub fn disp_const<F: Fn(&mut fmt::Formatter) -> fmt::Result>(a: F) -> util::DisplayableClosure<F> {
util::DisplayableClosure::new(a)
}
///
/// Create a plot formatter that implements [`plotnum::BaseFmt`]
///
pub fn plot_fmt<D, E>(
title: impl Display,
xname: impl Display,
yname: impl Display,
tickx: D,
ticky: E,
) -> impl BaseFmt<X = D::Num, Y = E::Num>
where
D: TickFormat,
E: TickFormat,
{
///
/// A simple plot formatter that is composed of
/// display objects as TickFormats.
///
struct SimplePlotFormatter<A, B, C, D, E> {
title: A,
xname: B,
yname: C,
tickx: D,
ticky: E,
}
impl<A, B, C, D, E> BaseFmt for SimplePlotFormatter<A, B, C, D, E>
where
A: Display,
B: Display,
C: Display,
D: TickFormat,
E: TickFormat,
{
type X = D::Num;
type Y = E::Num;
fn write_title(&mut self, writer: &mut dyn fmt::Write) -> fmt::Result {
write!(writer, "{}", self.title)
}
fn write_xname(&mut self, writer: &mut dyn fmt::Write) -> fmt::Result {
write!(writer, "{}", self.xname)
}
fn write_yname(&mut self, writer: &mut dyn fmt::Write) -> fmt::Result {
write!(writer, "{}", self.yname)
}
fn write_xtick(&mut self, writer: &mut dyn fmt::Write, val: &Self::X) -> fmt::Result {
self.tickx.write_tick(writer, val)
}
fn write_ytick(&mut self, writer: &mut dyn fmt::Write, val: &Self::Y) -> fmt::Result {
self.ticky.write_tick(writer, val)
}
fn write_xwher(&mut self, writer: &mut dyn fmt::Write) -> fmt::Result {
self.tickx.write_where(writer)
}
fn write_ywher(&mut self, writer: &mut dyn fmt::Write) -> fmt::Result {
self.ticky.write_where(writer)
}
}
SimplePlotFormatter {
title,
xname,
yname,
tickx,
ticky,
}
}
///
/// Iterate over the specified range over num iterations.
///
pub fn range_iter(
range: [f64; 2],
num: usize,
) -> impl ExactSizeIterator<Item = f64> + Clone + Send + Sync + std::iter::FusedIterator {
let [min, max] = range;
let diff = max - min;
let divf = num as f64;
(0..num).map(move |x| min + (x as f64 / divf) * diff)
}