1use console_engine::pixel;
2use console_engine::screen::Screen;
3use console_engine::Color;
4use std::fmt;
5use std::iter::successors;
6
7use crate::function::Function;
8use crate::graph::{GraphOptions, GraphWidths};
9use crate::traits::AsF64;
10
11pub struct MultiGraph<X: AsF64, Y: AsF64, F: Fn(X) -> Y> {
12 functions: Vec<Function<X, Y, F>>,
13 widths: GraphWidths,
14 height: u32,
15 graph_height: u32,
16 options: MultiGraphOptions,
17}
18
19pub struct MultiGraphOptions(Vec<GraphOptions>);
20
21impl Default for MultiGraphOptions {
22 fn default() -> MultiGraphOptions {
23 let colors = [
24 Color::Red,
25 Color::Blue,
26 Color::Green,
27 Color::Magenta,
28 Color::Cyan,
29 Color::Yellow,
30 Color::DarkRed,
31 Color::DarkBlue,
32 Color::DarkGreen,
33 Color::DarkMagenta,
34 Color::DarkCyan,
35 Color::DarkYellow,
36 ];
37 MultiGraphOptions(
38 (0..12)
39 .map(|i| GraphOptions {
40 color: colors[i].into(),
41 ..GraphOptions::default()
42 })
43 .collect(),
44 )
45 }
46}
47
48impl<X: AsF64, Y: AsF64, F: Fn(X) -> Y> MultiGraph<X, Y, F> {
49 pub fn new(
50 f: Vec<Function<X, Y, F>>,
51 width: u32,
52 set_height: Option<u32>,
53 ) -> MultiGraph<X, Y, F> {
54 MultiGraph::with_options(f, width, set_height, MultiGraphOptions::default())
55 }
56
57 pub fn new_screen(f: Vec<Function<X, Y, F>>) -> MultiGraph<X, Y, F> {
58 MultiGraph::with_options_screen(f, MultiGraphOptions::default())
59 }
60
61 pub fn with_options(
62 fs: Vec<Function<X, Y, F>>,
63 width: u32,
64 set_height: Option<u32>,
65 options: MultiGraphOptions,
66 ) -> MultiGraph<X, Y, F> {
67 let max = fs
69 .iter()
70 .map(|f| {
71 f.rng(0, width)
72 .into_iter()
73 .reduce(f64::max)
74 .unwrap_or_default()
75 })
76 .reduce(f64::max)
77 .unwrap_or_default()
78 .round() as u32;
79 let max_height_digits = successors(Some(max), |&n| (n >= 10).then(|| n / 10)).count() as u32;
81
82 let height = match set_height {
83 Some(h) => h,
84 None => max + 1,
85 };
86 MultiGraph {
87 functions: fs,
88 widths: GraphWidths {
89 total: width,
90 graph: width - max_height_digits,
91 height_legend: max_height_digits,
92 },
93 height,
94 graph_height: height - 1,
95 options,
96 }
97 }
98
99 pub fn with_options_screen(
100 f: Vec<Function<X, Y, F>>,
101 options: MultiGraphOptions,
102 ) -> MultiGraph<X, Y, F> {
103 let w_screen = console_engine::crossterm::terminal::size().unwrap().0;
104 MultiGraph::with_options(f, w_screen as u32, None, options)
105 }
106
107 pub fn draw(&self) {
108 let mut scr = Screen::new(self.widths.total, self.height);
109
110 self.draw_axis(&mut scr);
111 if self.options.0.get(0).unwrap().height_legend {
112 self.draw_height_legend(&mut scr);
113 }
114 self.draw_functions(&mut scr);
115
116 scr.draw();
117 }
118
119 fn draw_axis(&self, scr: &mut Screen) {
120 scr.h_line(
122 (self.widths.height_legend + 1) as i32,
123 self.graph_height as i32,
124 self.widths.graph as i32,
125 pixel::pxl('_'),
126 );
127 scr.v_line(
128 self.widths.height_legend as i32,
129 0,
130 self.height as i32,
131 pixel::pxl('|'),
132 );
133 }
134
135 fn draw_height_legend(&self, scr: &mut Screen) {
136 for h in 0..=self.graph_height {
137 for (index, digit) in h.to_string().chars().enumerate() {
138 scr.set_pxl(
139 index as i32,
140 (self.graph_height - h) as i32,
141 pixel::pxl(digit),
142 );
143 }
144 }
145 }
146
147 fn draw_functions(&self, scr: &mut Screen) {
148 for (i, f) in self.functions.iter().enumerate() {
149 for (x, y) in f
151 .rng_x(0, self.widths.graph)
152 .into_iter()
153 .map(|(x, y)| (x, y.round() as u32))
154 {
155 scr.set_pxl(
156 (x + self.widths.height_legend) as i32,
157 (self.graph_height - y) as i32, pixel::pxl_fg(
160 self.options.0.get(i).unwrap().character.as_char(),
161 self.options.0.get(i).unwrap().color.into(),
162 ),
163 )
164 }
165 }
166 }
167}
168
169impl<X: AsF64, Y: AsF64, F: Fn(X) -> Y> fmt::Display for MultiGraph<X, Y, F> {
170 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
171 self.draw();
172 Ok(())
173 }
174}