use core::fmt::Write;
pub use tagger;
mod util;
pub mod prelude {
pub use super::iter::PlotIterator;
pub use super::move_format;
}
use core::fmt;
mod render;
pub use render::StyleBuilder;
use iter::DoubleIterator;
pub mod iter;
pub mod default_tags {
pub use super::render::NUM_COLORS;
use core::fmt;
pub const CLASS: &str = "poloto";
pub const WIDTH: f64 = 800.0;
pub const HEIGHT: f64 = 500.0;
pub const XMLNS: &str = "http://www.w3.org/2000/svg";
pub fn default_svg_attrs<'a, 'b, T: fmt::Write>(
w: &'a mut tagger::AttributeWriter<'b, T>,
) -> Result<&'a mut tagger::AttributeWriter<'b, T>, fmt::Error> {
use tagger::prelude::*;
w.attr("class", CLASS)?
.attr("width", WIDTH)?
.attr("height", HEIGHT)?
.with_attr("viewBox", wr!("0 0 {} {}", WIDTH, HEIGHT))?
.attr("xmlns", XMLNS)
}
}
trait PlotTrait {
fn write_name(&self, a: &mut fmt::Formatter) -> fmt::Result;
fn iter_first(&mut self) -> &mut dyn Iterator<Item = [f64; 2]>;
fn iter_second(&mut self) -> &mut dyn Iterator<Item = [f64; 2]>;
}
use fmt::Display;
struct Wrapper2<D: DoubleIterator, F: Display> {
a: Option<D>,
b: Option<D::Next>,
func: F,
}
impl<I: DoubleIterator<Item = [f64; 2]>, F: Display> Wrapper2<I, F> {
fn new(it: I, func: F) -> Self {
Wrapper2 {
a: Some(it),
b: None,
func,
}
}
}
impl<D: DoubleIterator<Item = [f64; 2]>, F: Display> PlotTrait for Wrapper2<D, F> {
fn write_name(&self, a: &mut fmt::Formatter) -> fmt::Result {
self.func.fmt(a)
}
fn iter_first(&mut self) -> &mut dyn Iterator<Item = [f64; 2]> {
self.a.as_mut().unwrap()
}
fn iter_second(&mut self) -> &mut dyn Iterator<Item = [f64; 2]> {
self.b = Some(self.a.take().unwrap().finish_first());
self.b.as_mut().unwrap()
}
}
enum PlotType {
Scatter,
Line,
Histo,
LineFill,
}
struct Plot<'a> {
plot_type: PlotType,
plots: Box<dyn PlotTrait + 'a>,
}
#[macro_export]
macro_rules! move_format {
($($arg:tt)*) => {
$crate::moveable_format(move |w| write!(w,$($arg)*))
}
}
pub fn concatenate_display(
spacing: impl fmt::Display,
a: impl fmt::Display,
b: impl fmt::Display,
) -> impl fmt::Display {
struct Foo<A, B, C> {
spacing: A,
a: B,
b: C,
}
impl<A, B, C> fmt::Display for Foo<A, B, C>
where
A: fmt::Display,
B: fmt::Display,
C: fmt::Display,
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.a.fmt(formatter)?;
self.spacing.fmt(formatter)?;
self.b.fmt(formatter)
}
}
Foo { spacing, a, b }
}
pub fn moveable_format(func: impl Fn(&mut fmt::Formatter) -> fmt::Result) -> impl fmt::Display {
struct Foo<F>(F);
impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Display for Foo<F> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
(self.0)(formatter)
}
}
Foo(func)
}
struct NamesStruct<A, B, C,D> {
title: A,
xname: B,
yname: C,
header: D
}
impl<A: Display, B: Display, C: Display,D:Display> Names for NamesStruct<A, B, C,D> {
fn write_header(&self, fm: &mut fmt::Formatter) -> fmt::Result {
self.header.fmt(fm)
}
fn write_title(&self, fm: &mut fmt::Formatter) -> fmt::Result {
self.title.fmt(fm)
}
fn write_xname(&self, fm: &mut fmt::Formatter) -> fmt::Result {
self.xname.fmt(fm)
}
fn write_yname(&self, fm: &mut fmt::Formatter) -> fmt::Result {
self.yname.fmt(fm)
}
}
pub trait Names {
fn write_header(&self, fm: &mut fmt::Formatter) -> fmt::Result;
fn write_title(&self, fm: &mut fmt::Formatter) -> fmt::Result;
fn write_xname(&self, fm: &mut fmt::Formatter) -> fmt::Result;
fn write_yname(&self, fm: &mut fmt::Formatter) -> fmt::Result;
}
pub fn plot<'a>(
title: impl Display + 'a,
xname: impl Display + 'a,
yname: impl Display + 'a,
) -> Plotter<'a,impl Names> {
PlotterBuilder::new()
.with_data(DataBuilder::new().push_css_default())
.build(title, xname, yname)
}
#[derive(Copy, Clone)]
enum SvgTagOption {
Svg,
NoSvg,
}
pub struct DataBuilder<D: Display> {
header: D,
}
impl Default for DataBuilder<&'static str> {
fn default() -> Self {
Self::new()
}
}
impl DataBuilder<&'static str> {
pub fn new() -> Self {
DataBuilder { header: "" }
}
}
impl<D: Display> DataBuilder<D> {
pub fn push_css_default(self) -> DataBuilder<impl Display> {
DataBuilder {
header: concatenate_display("", self.header, StyleBuilder::new().build()),
}
}
pub fn push_default_css_with_variable(self) -> DataBuilder<impl Display> {
DataBuilder {
header: concatenate_display(
"",
self.header,
StyleBuilder::new().build_with_css_variables(),
),
}
}
pub fn push(self, a: impl fmt::Display) -> DataBuilder<impl Display> {
DataBuilder {
header: concatenate_display("", self.header, a),
}
}
fn finish(self) -> D {
self.header
}
}
pub struct PlotterBuilder<D: fmt::Display> {
data: DataBuilder<D>,
svgtag: bool,
}
impl Default for PlotterBuilder<&'static str> {
fn default() -> Self {
Self::new()
}
}
impl PlotterBuilder<&'static str> {
pub fn new() -> Self {
PlotterBuilder {
data: DataBuilder::new(),
svgtag: true,
}
}
pub fn with_data<J: Display>(self, data: DataBuilder<J>) -> PlotterBuilder<J> {
PlotterBuilder {
data,
svgtag: self.svgtag,
}
}
pub fn with_svg(mut self, svg: bool) -> Self {
self.svgtag = svg;
self
}
}
impl<'a, D: Display + 'a> PlotterBuilder<D> {
pub fn build(
self,
title: impl Display + 'a,
xname: impl Display + 'a,
yname: impl Display + 'a,
) -> Plotter<'a,impl Names> {
let svgtag = if self.svgtag {
SvgTagOption::Svg
} else {
SvgTagOption::NoSvg
};
Plotter {
names: NamesStruct {
title,
xname,
yname,
header:self.data.finish()
},
plots: Vec::new(),
svgtag,
}
}
}
pub struct Plotter<'a,D:Names> {
names:D,
plots: Vec<Plot<'a>>,
svgtag: SvgTagOption,
}
impl<'a,D:Names> Plotter<'a,D> {
pub fn line(
&mut self,
name: impl Display + 'a,
plots: impl DoubleIterator<Item = [f64; 2]> + 'a,
) -> &mut Self {
self.plots.push(Plot {
plot_type: PlotType::Line,
plots: Box::new(Wrapper2::new(plots, name)),
});
self
}
pub fn line_fill(
&mut self,
name: impl Display + 'a,
plots: impl DoubleIterator<Item = [f64; 2]> + 'a,
) -> &mut Self {
self.plots.push(Plot {
plot_type: PlotType::LineFill,
plots: Box::new(Wrapper2::new(plots, name)),
});
self
}
pub fn scatter(
&mut self,
name: impl Display + 'a,
plots: impl DoubleIterator<Item = [f64; 2]> + 'a,
) -> &mut Self {
self.plots.push(Plot {
plot_type: PlotType::Scatter,
plots: Box::new(Wrapper2::new(plots, name)),
});
self
}
pub fn histogram(
&mut self,
name: impl Display + 'a,
plots: impl DoubleIterator<Item = [f64; 2]> + 'a,
) -> &mut Self {
self.plots.push(Plot {
plot_type: PlotType::Histo,
plots: Box::new(Wrapper2::new(plots, name)),
});
self
}
pub fn render_to_string(self) -> Result<String, fmt::Error> {
let mut s = String::new();
self.render(&mut s)?;
Ok(s)
}
pub fn render_fmt(self, f: &mut fmt::Formatter) -> fmt::Result {
self.render(f)?;
Ok(())
}
pub fn render_io<T: std::io::Write>(self, writer: T) -> Result<T, fmt::Error> {
self.render(tagger::upgrade(writer)).map(|x| x.inner)
}
pub fn render<T: fmt::Write>(self, writer: T) -> Result<T, fmt::Error> {
let Plotter {
names,
plots,
svgtag,
} = self;
let mut root = tagger::Element::new(writer);
use default_tags::*;
match svgtag {
SvgTagOption::Svg => {
root.elem("svg", |writer| {
let svg = writer.write(|w| default_svg_attrs(w))?;
render::render(svg.get_writer(), plots, names)?;
Ok(svg)
})?;
}
SvgTagOption::NoSvg => {
render::render(root.get_writer(), plots, names)?;
}
}
Ok(root.into_writer())
}
}