poloto/build/
mod.rs

1//!
2//! Tools for assembling plots
3//!
4//!
5
6use std::iter::{Fuse, FusedIterator};
7
8use super::*;
9
10pub mod bar;
11pub mod crop;
12pub mod output_zip;
13pub mod unwrapper;
14use marker::Area;
15
16pub mod marker;
17pub mod plotit;
18
19use plotit::*;
20use unwrapper::Unwrapper;
21
22///
23/// Determine how to interpret the plot's point data when rendering.
24///
25#[derive(Copy, Clone, Debug)]
26pub enum PlotType {
27    Scatter,
28    Line,
29    Histo,
30    LineFill,
31    LineFillRaw,
32    Bars,
33}
34
35///
36/// Determine if this is a plot or just text.
37///
38#[derive(Copy, Clone, Debug)]
39pub enum PlotMetaType {
40    Plot(PlotType),
41    Text,
42}
43
44///
45/// Display label helper for chaining.
46///
47#[derive(Copy, Clone)]
48pub enum ChainDisplay<A, B> {
49    A(A),
50    B(B),
51}
52impl<A: Display, B: Display> fmt::Display for ChainDisplay<A, B> {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        match self {
55            ChainDisplay::A(a) => write!(f, "{}", a),
56            ChainDisplay::B(b) => write!(f, "{}", b),
57        }
58    }
59}
60
61///
62/// Chain two iterators that produce plot tags.
63///
64#[derive(Clone)]
65pub struct Chain<A, B> {
66    a: Fuse<A>,
67    b: Fuse<B>,
68}
69
70impl<L: Point, D1: Display, D2: Display, A, B> FusedIterator for Chain<A, B>
71where
72    A: Iterator<Item = PlotTag<L, D1>>,
73    B: Iterator<Item = PlotTag<L, D2>>,
74{
75}
76
77impl<L: Point, D1: Display, D2: Display, A, B> Iterator for Chain<A, B>
78where
79    A: Iterator<Item = PlotTag<L, D1>>,
80    B: Iterator<Item = PlotTag<L, D2>>,
81{
82    type Item = PlotTag<L, ChainDisplay<D1, D2>>;
83    fn size_hint(&self) -> (usize, Option<usize>) {
84        let (a, b) = self.a.size_hint();
85        let (c, d) = self.b.size_hint();
86
87        let k = match (b, d) {
88            (Some(a), Some(b)) => Some(a + b),
89            (Some(a), _) => Some(a),
90            (_, Some(b)) => Some(b),
91            (_, _) => None,
92        };
93        (a + c, k)
94    }
95    fn next(&mut self) -> Option<Self::Item> {
96        if let Some(a) = self.a.next() {
97            Some(match a {
98                PlotTag::Start {
99                    name,
100                    typ,
101                    size_hint,
102                } => PlotTag::Start {
103                    name: ChainDisplay::A(name),
104                    typ,
105                    size_hint,
106                },
107                PlotTag::Plot(p) => PlotTag::Plot(p),
108                PlotTag::Finish() => PlotTag::Finish(),
109            })
110        } else {
111            self.b.next().map(|a| match a {
112                PlotTag::Start {
113                    name,
114                    typ,
115                    size_hint,
116                } => PlotTag::Start {
117                    name: ChainDisplay::B(name),
118                    typ,
119                    size_hint,
120                },
121                PlotTag::Plot(p) => PlotTag::Plot(p),
122                PlotTag::Finish() => PlotTag::Finish(),
123            })
124        }
125    }
126}
127
128// pub fn chain<L:Point,A:PlotIterator<L=L>,B:PlotIterator<L=L>>(a:A,b:B)->PlotRes<impl Iterator<Item=PlotTag<L,ChainDisplay<A::D,B::D>>>,L>{
129//     let PlotRes {
130//         area: curr_area,
131//         it: p1,
132//     } = a.unpack();
133//     let PlotRes {
134//         area: other_area,
135//         it: p,
136//     } = b.unpack();
137//     let mut area = curr_area;
138//     area.grow_area(&other_area);
139
140//     let a=p1.map(|a|{
141//         match a {
142//             PlotTag::Start {
143//                 name,
144//                 typ,
145//                 size_hint,
146//             } => PlotTag::Start {
147//                 name: ChainDisplay::A(name),
148//                 typ,
149//                 size_hint,
150//             },
151//             PlotTag::Plot(p) => PlotTag::Plot(p),
152//             PlotTag::Finish() => PlotTag::Finish(),
153//         }
154//     });
155//     let b=p.map(|a|{
156//         match a {
157//             PlotTag::Start {
158//                 name,
159//                 typ,
160//                 size_hint,
161//             } => PlotTag::Start {
162//                 name: ChainDisplay::B(name),
163//                 typ,
164//                 size_hint,
165//             },
166//             PlotTag::Plot(p) => PlotTag::Plot(p),
167//             PlotTag::Finish() => PlotTag::Finish(),
168//         }
169//     });
170
171//     PlotRes {
172//         area,
173//         it: a.chain(b),
174//     }
175// }
176
177pub trait PlotIterator {
178    type L: Point;
179    type P: Iterator<Item = PlotTag<Self::L, Self::D>>;
180    type D: Display;
181    fn unpack(self) -> PlotRes<Self::P, Self::L>;
182
183    fn chain<P: PlotIterator<L = Self::L>>(self, other: P) -> PlotRes<Chain<Self::P, P::P>, Self::L>
184    where
185        Self: Sized,
186    {
187        let PlotRes {
188            area: curr_area,
189            it: p1,
190            num_plots: n1,
191        } = self.unpack();
192        let PlotRes {
193            area: other_area,
194            it: p,
195            num_plots: n2,
196        } = other.unpack();
197        let mut area = curr_area;
198        area.grow_area(&other_area);
199        PlotRes {
200            area,
201            it: Chain {
202                a: p1.fuse(),
203                b: p.fuse(),
204            },
205            num_plots: n1 + n2,
206        }
207    }
208
209    fn dyn_box<'a>(self) -> PlotRes<DynIt<'a, Self::L, Self::D>, Self::L>
210    where
211        Self::P: 'a,
212        Self: Sized,
213    {
214        let PlotRes {
215            area,
216            it,
217            num_plots,
218        } = self.unpack();
219        PlotRes {
220            it: Box::new(it),
221            area,
222            num_plots,
223        }
224    }
225}
226
227type DynIt<'a, L, D> = Box<dyn Iterator<Item = PlotTag<L, D>> + 'a>;
228
229#[derive(Copy, Clone)]
230pub struct PlotRes<I: Iterator, L: Point> {
231    pub(crate) area: Area<L::X, L::Y>,
232    pub(crate) num_plots: usize,
233    pub(crate) it: I,
234}
235
236impl<P: Iterator<Item = PlotTag<L, D>>, L: Point, D: Display> PlotIterator for PlotRes<P, L> {
237    type L = L;
238    type P = P;
239    type D = D;
240
241    fn unpack(self) -> PlotRes<Self::P, Self::L> {
242        self
243    }
244}
245
246pub trait Point {
247    type X: PlotNum;
248    type Y: PlotNum;
249    fn get(&self) -> (&Self::X, &Self::Y);
250}
251impl<X: PlotNum, Y: PlotNum> Point for (X, Y) {
252    type X = X;
253    type Y = Y;
254
255    fn get(&self) -> (&Self::X, &Self::Y) {
256        (&self.0, &self.1)
257    }
258}
259
260#[derive(Clone)]
261pub enum PlotTag<L: Point, D: Display> {
262    Start {
263        name: D,
264        typ: PlotMetaType,
265        size_hint: (usize, Option<usize>),
266    },
267    Plot(L),
268    Finish(),
269}
270
271///
272/// Ensure that the origin point is within view.
273///
274pub fn origin<L: Point>() -> PlotRes<std::iter::Empty<PlotTag<L, &'static str>>, L>
275where
276    L::X: HasZero,
277    L::Y: HasZero,
278{
279    markers(Some(L::X::zero()), Some(L::Y::zero()))
280}
281
282///
283/// Ensure the list of marker values are within view.
284///
285pub fn markers<XI: IntoIterator<Item = L::X>, YI: IntoIterator<Item = L::Y>, L: Point>(
286    x: XI,
287    y: YI,
288) -> PlotRes<std::iter::Empty<PlotTag<L, &'static str>>, L> {
289    let mut area = Area::new();
290    for a in x {
291        area.grow(Some(&a), None);
292    }
293    for a in y {
294        area.grow(None, Some(&a));
295    }
296
297    PlotRes {
298        area,
299        it: std::iter::empty(),
300        num_plots: 0,
301    }
302}
303
304///
305/// A plot iterator that will be cloned to find the min max bounds.
306///
307pub fn cloned<L: Point, I: IntoIterator>(it: I) -> ClonedPlotIt<I::IntoIter>
308where
309    I::IntoIter: Clone,
310    I::Item: build::unwrapper::Unwrapper<Item = L>,
311{
312    ClonedPlotIt::new(it.into_iter())
313}
314
315// use std::iter::Map;
316// pub struct PlotIterCreator<I:Iterator> where I::Item:Point{
317//     it:std::iter::Chain<std::iter::Chain<std::iter::Once<build::PlotTag<I::Item>>, Map<I, fn(I::Item) -> build::PlotTag<I::Item>>>, std::iter::Once<build::PlotTag<I::Item>>>
318// }
319
320// impl<I: Iterator<Item = L>, L: Point> PlotIterCreator<I> {
321//     fn new(name: String, typ: PlotMetaType, it: I) -> Self {
322//         let start=std::iter::once(PlotTag::Start { name, typ});
323//         let mid:Map<I,fn(L)->PlotTag<L>>=it.map(PlotTag::Plot);
324//         let end = std::iter::once(PlotTag::Finish());
325
326//         PlotIterCreator{
327//             it:start.chain(mid).chain(end)
328//         }
329//     }
330
331// }
332// impl<I: FusedIterator<Item=L>, L: Point> FusedIterator for PlotIterCreator<I> {
333// }
334
335// impl<I: Iterator<Item = L>, L: Point> Iterator for PlotIterCreator<I> {
336//     type Item=PlotTag<L>;
337//     fn next(&mut self) -> Option<Self::Item> {
338//         self.it.next()
339//     }
340// }
341
342pub struct SinglePlotBuilder<D> {
343    label: D,
344}
345
346#[derive(Clone)]
347pub struct PlotIterCreator<I, D> {
348    start: Option<(PlotMetaType, D)>,
349    it: Fuse<I>,
350    posted_finish: bool,
351}
352impl<I: Iterator<Item = L>, L: Point, D: Display> PlotIterCreator<I, D> {
353    fn new(label: D, typ: PlotMetaType, it: I) -> Self {
354        Self {
355            start: Some((typ, label)),
356            it: it.fuse(),
357            posted_finish: false,
358        }
359    }
360}
361
362impl<I: ExactSizeIterator<Item = L>, L: Point, D: Display> ExactSizeIterator
363    for PlotIterCreator<I, D>
364{
365}
366impl<I: Iterator<Item = L>, L: Point, D: Display> FusedIterator for PlotIterCreator<I, D> {}
367impl<I: Iterator<Item = L>, L: Point, D: Display> Iterator for PlotIterCreator<I, D> {
368    type Item = PlotTag<L, D>;
369    fn next(&mut self) -> Option<PlotTag<L, D>> {
370        if let Some((typ, name)) = self.start.take() {
371            Some(PlotTag::Start {
372                typ,
373                name,
374                size_hint: self.size_hint(),
375            })
376        } else if let Some(l) = self.it.next() {
377            Some(PlotTag::Plot(l))
378        } else if !self.posted_finish {
379            self.posted_finish = true;
380            Some(PlotTag::Finish())
381        } else {
382            None
383        }
384    }
385    fn size_hint(&self) -> (usize, Option<usize>) {
386        let (a, b) = self.it.size_hint();
387        (a + 2, b.map(|b| b + 2))
388    }
389}
390
391impl<D: Display> SinglePlotBuilder<D> {
392    fn gen<P: PlotIt>(self, it: P, typ: PlotMetaType) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
393        let mut area = Area::new();
394        let it = it.unpack(&mut area);
395
396        PlotRes {
397            area,
398            it: PlotIterCreator::new(self.label, typ, it),
399            num_plots: 1,
400        }
401    }
402    /// Create a line from plots using a SVG path element.
403    /// The path element belongs to the `.poloto[N]fill` css class.  
404    ///
405    pub fn line<P: PlotIt>(self, it: P) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
406        self.gen(it, PlotMetaType::Plot(PlotType::Line))
407    }
408
409    pub(crate) fn bars<P: PlotIt>(self, it: P) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
410        self.gen(it, PlotMetaType::Plot(PlotType::Bars))
411    }
412
413    /// Create a scatter plot from plots, using a SVG path with lines with zero length.
414    /// Each point can be sized using the stroke width.
415    /// The path belongs to the CSS classes `poloto_scatter` and `.poloto[N]stroke` css class
416    /// with the latter class overriding the former.
417    pub fn scatter<P: PlotIt>(self, it: P) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
418        self.gen(it, PlotMetaType::Plot(PlotType::Scatter))
419    }
420
421    /// Create a histogram from plots using SVG rect elements.
422    /// Each bar's left side will line up with a point.
423    /// Each rect element belongs to the `.poloto[N]fill` css class.
424    pub fn histogram<P: PlotIt>(self, it: P) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
425        self.gen(it, PlotMetaType::Plot(PlotType::Histo))
426    }
427
428    /// Create a line from plots that will be filled underneath using a SVG path element.
429    /// The path element belongs to the `.poloto[N]fill` css class.
430    pub fn line_fill<P: PlotIt>(self, it: P) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
431        self.gen(it, PlotMetaType::Plot(PlotType::LineFill))
432    }
433
434    /// Create a line from plots that will be filled using a SVG path element.
435    /// The first and last points will be connected and then filled in.
436    /// The path element belongs to the `.poloto[N]fill` css class.
437    pub fn line_fill_raw<P: PlotIt>(self, it: P) -> PlotRes<PlotIterCreator<P::It, D>, P::L> {
438        self.gen(it, PlotMetaType::Plot(PlotType::LineFillRaw))
439    }
440
441    ///
442    /// Write some text in the legend. This doesnt increment the plot number.
443    ///
444    pub fn text<L: Point>(self) -> PlotRes<PlotIterCreator<std::iter::Empty<L>, D>, L> {
445        let area = Area::new();
446        PlotRes {
447            area,
448            it: PlotIterCreator::new(self.label, PlotMetaType::Text, std::iter::empty()),
449            num_plots: 1,
450        }
451    }
452}
453
454///
455/// Start creating one plot.
456///
457pub fn plot<D: Display>(label: D) -> SinglePlotBuilder<D> {
458    SinglePlotBuilder { label }
459}
460
461impl<I: IntoIterator<Item = P>, P: PlotIterator<L = L>, L: Point> PlotIterator for I {
462    type L = L;
463    type P = std::iter::Flatten<std::vec::IntoIter<P::P>>;
464    type D = P::D;
465    fn unpack(self) -> PlotRes<Self::P, Self::L> {
466        let mut total = 0;
467        let (areas, its): (Vec<_>, Vec<_>) = self
468            .into_iter()
469            .map(|x| {
470                let PlotRes {
471                    area,
472                    it,
473                    num_plots,
474                } = x.unpack();
475                total += num_plots;
476                (area, it)
477            })
478            .unzip();
479
480        let mut area = Area::new();
481        for a in areas {
482            area.grow_area(&a);
483        }
484
485        let it = its.into_iter().flatten();
486
487        PlotRes {
488            area,
489            it,
490            num_plots: total,
491        }
492    }
493}