eb_bars/lib.rs
1// Copyright 2025 Developers of eb_bars.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! EB - Bars: Plotting library for Rust providing a simple way to create barcharts in svg format.
10//!
11//! # Quick Start
12//!
13//! The simplest usecase (which you never really might want) is written like so.
14//! ```
15//! use eb_bars::BarPlot;
16//!
17//! // Start out with an empty plot.
18//! let mut plot = BarPlot::new();
19//!
20//! // Add a set of values.
21//! plot.add_values(&[5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6]);
22//!
23//! // Render to svg format.
24//! let svg: String = plot.to_svg(1600, 1000);
25//! ```
26//!
27//! # But the above "Quick Start" looks bad and boring
28//!
29//! As the above example stands, the largest number will have its bar take up the full height of the window.
30//! Also, the lowest value will be a bar of zero height e.g. no bar at all.
31//! The problem is that we have not set a specific scaling for the values.
32//! This means that the bars are all scaled based on the minimum and maximum value.
33//! Let's improve it a bit in the next section by adding a scale.
34//!
35//! ```
36//! use eb_bars::BarPlot;
37//!
38//! let mut plot = BarPlot::new();
39//!
40//! // Add same values as before.
41//! plot.add_values(&[5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6]);
42//!
43//! // Here, we are setting a scale range for better visibility/scaling of bars.
44//! plot.set_scale_range(0, 20, 2);
45//! // The bars will look better, but the numbers on the scale won't be visible.
46//! // We need to shrink the plot window relative to the full window.
47//! // Keep in mind that all size and offset values are based of a percentage.
48//! // Setting a width to 100 means it takes up the whole width.
49//! // Same goes for the height.
50//!
51//! // Let's shrink the plot size.
52//! plot.set_plot_window_size(95.0, 85.0, 93.0, 50.0);
53//! // We have now set the width at 95% and moved it 85% right from the left side.
54//! // We also set the height at 93% and moved it 50% down from the top.
55//! // First two parameters affect the width and the left/right offset respectively.
56//! // The last two parameters affect the height and the top/bottom offset respectively.
57//!
58//! // Let's render the svg.
59//! let svg: String = plot.to_svg(1600, 1000);
60//! ```
61//!
62//! It is still kinda boring, so please checkout all the tweaks available in [`BarPlot`].
63//!
64//! # Important note
65//!
66//! If the method name is prefixed `add_`, then calling it multiple times will _add_ stuff to the plot.
67//! This goes for adding multiple sets of values (categories) and adding colors to those values etc..
68//!
69//! If the method name is prefixed `set_`, then calling it multiple times will _override_ the previous one.
70//! You most certainly never want to call these more than once, unless there is a good reason to.
71//!
72//! Check out [`BarPlot`] for all implementations.
73//!
74//! # Panics and error handling.
75//!
76//! This library has very limited error handling at the moment. Actually, it has none.
77//! There are some assertions here and there that will provoke a panic on invalid input.
78//! That way, you can try-and-re-try your code until it works.
79//!
80//! Once everything works, it is very unlikely that it will panic on continous use in your application.
81//! However, if you pass values that are generated from a source that you do not have full control over,
82//! then the task of making sure the input is sanitized and double checked lies on your end and your code.
83
84mod svg;
85
86type Percentage = f64;
87
88const VERSION: &str = "0.7.1";
89const REPOSITORY: &str = "https://github.com/emilbratt/eb_bars";
90
91const DEFAULT_SIZE: (u32, u32) = (1600, 1000);
92
93const DEFAULT_BAR_COLOR: &str = "rgb(112, 153, 182)";
94const DEFAULT_BASE_COLOR: &str = "rgb(197, 197, 197)";
95const DEFAULT_BAR_GAP: Percentage = 0.0;
96const DEFAULT_BIN_GAP: Percentage = 10.0;
97
98const DEFAULT_FONT_SIZE: Percentage = 100.0;
99const DEFAULT_LEGEND_POSITION: (Percentage, Percentage) = (90.0, 20.0);
100const DEFAULT_TEXT_SIDE_OFFSET: Percentage = 35.0;
101const DEFAULT_TICK_LENGTH: Percentage = 10.0;
102
103
104#[derive(Debug, Default)]
105enum BinMarkerPosition {
106 Left,
107 #[default]
108 Middle,
109 Right,
110}
111
112#[derive(Debug, Default)]
113struct PlotLegend<'a> {
114 categories: Option<&'a[&'a str]>,
115 position: Option<(Percentage, Percentage)>,
116}
117
118#[derive(Debug)]
119enum BarColorLayout<'a> {
120 Category(Vec<&'a str>), // Each category has its own color.
121 Indexed(Vec<Vec<&'a str>>), // Every bar has its own selected color.
122 Threshold((&'a str, &'a str, &'a str, &'a str)), // Every bar is given its color based on its value.
123 Uniform(&'a str), // All bars are the same color.
124}
125
126impl Default for BarColorLayout<'_> {
127 fn default() -> Self {
128 Self::Uniform(DEFAULT_BAR_COLOR)
129 }
130}
131
132#[derive(Debug, Default)]
133struct BarColors<'a> {
134 layout: BarColorLayout<'a>,
135 overrides: Vec<(usize, usize, &'a str)>,
136}
137
138#[derive(Debug)]
139struct Colors<'a> {
140 background: Option<&'a str>,
141 bars: BarColors<'a>,
142 line: &'a str,
143 text: &'a str,
144 tick: &'a str,
145}
146
147impl Default for Colors<'_> {
148 fn default() -> Self {
149 Self {
150 background: None,
151 bars: BarColors::default(),
152 line: DEFAULT_BASE_COLOR,
153 text: DEFAULT_BASE_COLOR,
154 tick: DEFAULT_BASE_COLOR,
155 }
156 }
157}
158
159#[derive(Debug, Default)]
160struct Show {
161 window_border: bool,
162 plot_border: bool,
163 horizontal_lines: bool,
164 vertical_lines: bool,
165}
166
167#[derive(Debug, Default)]
168struct PlotText<'a> {
169 left: Option<&'a str>,
170 left_offset: Option<Percentage>,
171
172 right: Option<&'a str>,
173 right_offset: Option<Percentage>,
174
175 top: Option<&'a str>,
176 top_offset: Option<Percentage>,
177
178 bottom: Option<&'a str>,
179 bottom_offset: Option<Percentage>,
180}
181
182#[derive(Debug)]
183struct PlotLayout {
184 bar_gap: Percentage,
185 bin_gap: Percentage,
186 bin_marker_position: BinMarkerPosition,
187 font_size: Percentage,
188 plot_window_scale: Option<(Percentage, Percentage, Percentage, Percentage)>,
189 scale_range: Option<(i64, i64, usize)>,
190 x_axis_tick_length: Percentage,
191 y_axis_tick_length: Percentage,
192 negative_bars_go_down: bool,
193}
194
195impl Default for PlotLayout {
196 fn default() -> Self {
197 Self {
198 bin_gap: DEFAULT_BIN_GAP,
199 bar_gap: DEFAULT_BAR_GAP,
200 bin_marker_position: BinMarkerPosition::default(),
201 font_size: DEFAULT_FONT_SIZE,
202 plot_window_scale: None,
203 scale_range: None,
204 x_axis_tick_length: DEFAULT_TICK_LENGTH,
205 y_axis_tick_length: DEFAULT_TICK_LENGTH,
206 negative_bars_go_down: false,
207 }
208 }
209}
210
211#[derive(Debug)]
212enum LinesAt<'a> {
213 Horizontal(f64, &'a str),
214 Vertical(f64, &'a str),
215}
216
217#[derive(Debug)]
218pub struct BarPlot<'a> {
219 values: Vec<&'a [f64]>,
220 markers: Option<&'a [&'a str]>,
221 lines_at: Vec<LinesAt<'a>>,
222 size: (u32, u32),
223 colors: Colors<'a>,
224 legend: PlotLegend<'a>,
225 layout: PlotLayout,
226 show: Show,
227 plot_text: PlotText<'a>,
228}
229
230// FIXME: add new with default, allow for now with attribute below..
231#[allow(clippy::new_without_default)]
232impl <'a>BarPlot<'a> {
233
234 /// Instantiate a new plot.
235 ///
236 /// # Example
237 ///
238 /// ```
239 /// use eb_bars::BarPlot;
240 ///
241 /// let mut plot = BarPlot::new();
242 /// ```
243 pub fn new() -> Self {
244 Self {
245 values: Vec::new(),
246 markers: None,
247 lines_at: Vec::new(),
248 size: DEFAULT_SIZE,
249 colors: Colors::default(),
250 legend: PlotLegend::default(),
251 layout: PlotLayout::default(),
252 show: Show::default(),
253 plot_text: PlotText::default(),
254 }
255 }
256
257 /// Adding a set of values (bars) to the plot.
258 ///
259 /// # Takes an array slice of f64.
260 ///
261 /// All valus must be f64.
262 /// If you have a `Vec<u32>`, make sure to convert it to a `Vec<f64>` before passing it.
263 /// Then you can pass the vector as a reference.
264 ///
265 /// # This method is required.
266 ///
267 /// There must be at least one set of values to produce a plot. :)
268 ///
269 /// # Grouped bars
270 ///
271 /// Calling this method more than once will create `groups` for values of same index.
272 /// This means that the first datapoint of each added dataset will be the first group,
273 /// the second datapoint of each added dataset will be the second group and so on..
274 /// E.g. calling this method 5 times will add groups of 5 bars in each bin.
275 ///
276 /// # Short summary
277 ///
278 /// * Must be called at least once. A plot without values does not make any sense.. :)
279 /// * If called multiple times, each bin will contain a group with values of the same index.
280 /// * All arrays passed after first call must be of the `exact` same length as the first array.
281 ///
282 /// # Example
283 ///
284 /// ```
285 /// use eb_bars::BarPlot;
286 ///
287 /// let mut plot = BarPlot::new();
288 ///
289 /// let apples: Vec<f64> = vec![5., 16., 17., 8., 3.];
290 /// let oranges: Vec<f64> = vec![7., 6., 7., 16., 9.];
291 /// // The first group contains 5 apples and 7 oranges.
292 /// // The second one 16 and 6 respectively.
293 /// // The last group contains 3 apples and 9 oranges.
294 ///
295 /// // Add the first set of values.
296 /// plot.add_values(&apples);
297 ///
298 /// // Add the second set of values.
299 /// plot.add_values(&oranges);
300 ///
301 /// let svg: String = plot.to_svg(1600, 1000);
302 /// ```
303 pub fn add_values(&mut self, values: &'a [f64]) {
304 if !self.values.is_empty() {
305 // This makes sure that all new values are of same length e.g. same count as previous values.
306 // This is needed so that all bins have the same amount of bars in them,
307 // ..as long as we add more than 1 category, that is..
308 let exp_count = self.values[0].len();
309 let count = values.len();
310 assert_eq!(
311 exp_count,
312 count,
313 "Added values should be same count as old, expected {exp_count}, got {count}"
314 );
315 }
316 self.values.push(values);
317 }
318
319 /// Set a fill color as background.
320 ///
321 /// By default, the image will be fully transparent where there is nothing drawn on it.
322 /// Adding a background color might be a good idea for better visual presentation depending on usecase.
323 ///
324 /// # Accepted color conventions.
325 ///
326 /// * As its name such as "Red".
327 /// * As an RGB value such as "rgb(29, 28, 27)".
328 /// * As a HEX value such as "#1111FA".
329 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
330 ///
331 /// # Example
332 ///
333 /// ```
334 /// use eb_bars::BarPlot;
335 ///
336 /// let mut plot = BarPlot::new();
337 ///
338 /// plot.add_values(&[1., 2., 3.,]);
339 ///
340 /// plot.set_background_color("Black");
341 ///
342 /// let svg: String = plot.to_svg(1600, 1000);
343 /// ```
344 pub fn set_background_color(&mut self, color: &'a str) {
345 self.colors.background = Some(color);
346 }
347
348 /// Set color for the lines.
349 ///
350 /// By default, all lines are drawn with a `default` color.
351 /// You can `override` this by setting your own color.
352 ///
353 /// # Accepted color conventions.
354 ///
355 /// * As its name such as "Red".
356 /// * As an RGB value such as "rgb(29, 28, 27)".
357 /// * As a HEX value such as "#1111FA".
358 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
359 ///
360 /// # Example
361 ///
362 /// ```
363 /// use eb_bars::BarPlot;
364 ///
365 /// let mut plot = BarPlot::new();
366 ///
367 /// plot.add_values(&[1., 2., 3.,]);
368 ///
369 /// plot.set_line_color("Yellow");
370 ///
371 /// let svg: String = plot.to_svg(1600, 1000);
372 /// ```
373 pub fn set_line_color(&mut self, color: &'a str) {
374 self.colors.line = color;
375 }
376
377 /// Set color for text/numbers.
378 ///
379 /// By default, all text are drawn with a `default` color.
380 /// You can `override` this by setting your own color.
381 ///
382 /// # Accepted color conventions.
383 ///
384 /// * As its name such as "Red".
385 /// * As an RGB value such as "rgb(29, 28, 27)".
386 /// * As a HEX value such as "#1111FA".
387 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
388 ///
389 /// # Example
390 ///
391 /// ```
392 /// use eb_bars::BarPlot;
393 ///
394 /// let mut plot = BarPlot::new();
395 ///
396 /// plot.add_values(&[1., 2., 3.,]);
397 ///
398 /// plot.set_text_color("LightBlue");
399 ///
400 /// let svg: String = plot.to_svg(1600, 1000);
401 /// ```
402 pub fn set_text_color(&mut self, color: &'a str) {
403 self.colors.text = color;
404 }
405
406 /// Set color for x-ticks and y-ticks.
407 ///
408 /// By default, all ticks are drawn with a `default` color.
409 /// You can `override` this by setting your own color.
410 ///
411 /// # Accepted color conventions.
412 ///
413 /// * As its name such as "Red".
414 /// * As an RGB value such as "rgb(29, 28, 27)".
415 /// * As a HEX value such as "#1111FA".
416 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
417 ///
418 /// # Example
419 ///
420 /// ```
421 /// use eb_bars::BarPlot;
422 ///
423 /// let mut plot = BarPlot::new();
424 ///
425 /// plot.add_values(&[1., 2., 3.,]);
426 ///
427 /// plot.set_text_color("LightBlue");
428 ///
429 /// let svg: String = plot.to_svg(1600, 1000);
430 /// ```
431 pub fn set_tick_color(&mut self, color: &'a str) {
432 self.colors.tick = color;
433 }
434
435 /// Set a single color for all bars.
436 ///
437 /// By default, all bars are drawn with a `default` color.
438 /// You can `override` this by setting a new uniform color value.
439 ///
440 /// # Accepted color conventions.
441 ///
442 /// * As its name such as "Red".
443 /// * As an RGB value such as "rgb(29, 28, 27)".
444 /// * As a HEX value such as "#1111FA".
445 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
446 ///
447 /// # Example
448 ///
449 /// ```
450 /// use eb_bars::BarPlot;
451 ///
452 /// let mut plot = BarPlot::new();
453 ///
454 /// plot.add_values(&[1., 2., 3.,]);
455 ///
456 /// plot.set_bar_colors_by_uniform("Green");
457 ///
458 /// let svg: String = plot.to_svg(1600, 1000);
459 /// ```
460 pub fn set_bar_colors_by_uniform(&mut self, color: &'a str) {
461 self.colors.bars.layout = BarColorLayout::Uniform(color);
462 }
463
464 /// Set bar colors by threshold.
465 ///
466 /// By default, all bars are drawn with a `default` color.
467 /// You can `override` this by setting different colors for different thresholds.
468 /// The threshold is as follows: minumum value, less than average, greater than or equal to average and max.
469 ///
470 /// # Accepted color conventions.
471 ///
472 /// * As its name such as "Red".
473 /// * As an RGB value such as "rgb(29, 28, 27)".
474 /// * As a HEX value such as "#1111FA".
475 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
476 ///
477 /// # Avoid if having more than one set of values.
478 ///
479 /// When you add multiple sets of values e.g. calling [`BarPlot::add_values`] multiple times,
480 /// then you might want to use [`BarPlot::add_bar_colors_by_category`] instead.
481 ///
482 /// # Example
483 ///
484 /// ```
485 /// use eb_bars::BarPlot;
486 ///
487 /// let mut plot = BarPlot::new();
488 ///
489 /// plot.add_values(&[1., 2., 3.,]);
490 ///
491 /// // Each color represent how signifacant a value is.
492 /// let min = "Red"; // The lowest value will have its bar colored red.
493 /// let low = "Orange"; // Low values will have their bars be orange.
494 /// let high = "Yellow"; // High values will be yellow.
495 /// let max = "Green"; // Max value (tallest bar) will be green.
496 ///
497 /// plot.set_bar_colors_by_threshold(min, low, high, max);
498 ///
499 /// let svg: String = plot.to_svg(1600, 1000);
500 /// ```
501 pub fn set_bar_colors_by_threshold(&mut self, min: &'a str, low: &'a str, high: &'a str, max: &'a str) {
502 self.colors.bars.layout = BarColorLayout::Threshold((min, low, high, max));
503 }
504
505 /// Add color to last added values.
506 ///
507 /// By default, all bars are drawn with a `default` color.
508 /// You can `override` this by setting different colors for different categories.
509 /// Keep in mind that this only make sens to use if you have called [`BarPlot::add_values`]
510 /// at least 2 times. The two datasets are treated as two distinct categories.
511 /// Note: see [`BarPlot::set_legend`] for displaying the names with their respective color.
512 ///
513 /// # How it operates
514 /// The category added first will have its bars colored first.
515 /// Then, any consecutive call will apply proportional to all consecutive calls to [`BarPlot::add_values`].
516 ///
517 /// In simple terms: if you call [`BarPlot::add_values`] 5 times, then it is required to call this method
518 /// `exactly` 5 times. Each time with its own particular color.
519 ///
520 /// # Accepted color conventions.
521 ///
522 /// * As its name such as "Red".
523 /// * As an RGB value such as "rgb(29, 28, 27)".
524 /// * As a HEX value such as "#1111FA".
525 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
526 ///
527 /// # Avoid if having only one set of values.
528 ///
529 /// If you only add one set of values e.g. calling [`BarPlot::add_values`] one time,
530 /// then it makes no sense using this method. Use any of the other ways to set colors.
531 ///
532 /// # Example
533 ///
534 /// ```
535 /// use eb_bars::BarPlot;
536 ///
537 /// let mut plot = BarPlot::new();
538 ///
539 /// let apples: Vec<f64> = vec![5., 16., 17., 8., 3.];
540 /// let oranges: Vec<f64> = vec![7., 6., 7., 16., 9.];
541 ///
542 /// // Add first set of values.
543 /// plot.add_values(&apples);
544 /// // Adding a second set of values.
545 /// plot.add_values(&oranges);
546 ///
547 /// // First call adds a color to the first category. Red bacuse apples are often red.
548 /// plot.add_bar_colors_by_category("Red");
549 /// // Second call adds a color to the second category. Orange because; oranges are orange. :)
550 /// plot.add_bar_colors_by_category("Orange");
551 ///
552 /// let svg: String = plot.to_svg(1600, 1000);
553 /// ```
554 pub fn add_bar_colors_by_category(&mut self, color: &'a str) {
555 if let BarColorLayout::Category(v) = &mut self.colors.bars.layout {
556 v.push(color);
557 } else {
558 self.colors.bars.layout = BarColorLayout::Category(vec![color]);
559 }
560 }
561
562 /// Add a set of colors for every bar in last added category.
563 ///
564 /// By default, all bars are drawn with a `default` color.
565 /// You can `override` this by adding an array of colors which have same length as the values.
566 ///
567 /// # How it operates
568 /// The category added first will have its bars colored first.
569 /// Then, any consecutive call will apply proportional to all consecutive calls to [`BarPlot::add_values`].
570 ///
571 /// In simple terms: if you call [`BarPlot::add_values`] 5 times, then it is required to call this method
572 /// `exactly` 5 times. Each time with its own particular set of colors.
573 ///
574 /// # Accepted color conventions.
575 ///
576 /// * As its name such as "Red".
577 /// * As an RGB value such as "rgb(29, 28, 27)".
578 /// * As a HEX value such as "#1111FA".
579 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
580 ///
581 /// This method is meant for users that want full control over the bar colors.
582 ///
583 /// # Example
584 ///
585 /// ```
586 /// use eb_bars::BarPlot;
587 ///
588 /// let mut plot = BarPlot::new();
589 ///
590 /// let apples: Vec<f64> = vec![5., 16., 17., 8., 3.];
591 /// let oranges: Vec<f64> = vec![7., 6., 7., 16., 9.];
592 ///
593 /// // Add first set of values.
594 /// plot.add_values(&apples);
595 /// // Adding a second set of values.
596 /// plot.add_values(&oranges);
597 ///
598 /// // First call adds a color to the first category. Red bacuse apples are often red.
599 /// let clr_red = vec!["Red", "Red", "Red", "Red", "Red"];
600 /// plot.add_bar_colors_from_vec(clr_red);
601 /// // Second call adds a color to the second category. Orange because; oranges are orange. :)
602 /// let clr_orange = vec!["Orange", "Orange", "Orange", "Orange", "Orange"];
603 /// plot.add_bar_colors_from_vec(clr_orange);
604 ///
605 /// let svg: String = plot.to_svg(1600, 1000);
606 /// ```
607 pub fn add_bar_colors_from_vec(&mut self, colors: Vec<&'a str>) {
608 if let BarColorLayout::Indexed(v) = &mut self.colors.bars.layout {
609 v.push(colors);
610 } else {
611 self.colors.bars.layout = BarColorLayout::Indexed(vec![colors]);
612 }
613 }
614
615 /// Override any bar with any color.
616 ///
617 /// This one will override the category index and the value (bar) index with a color.
618 /// Say you added 3 sets of values by calling [`BarPlot::add_values`] 3 times.
619 /// This means you have 3 categories times `X` values where `X` is the length of array holding
620 /// each of set of values.
621 ///
622 /// # How it operates
623 /// The category added first will have its category and bars colored zero indexed.
624 /// The first bar in the first category will have index 0-0. The next bar will have 0-1.
625 /// You call this method as many times as you need, passing one color at the time.
626 ///
627 /// Calling [`BarPlot::add_values`] 5 times with 3 values for each set of values,
628 /// then the index for category will be 0-4 (5 indexes ) and the index for values will be 0-2 (3 indexes).
629 ///
630 /// # Accepted color conventions.
631 ///
632 /// * As its name such as "Red".
633 /// * As an RGB value such as "rgb(29, 28, 27)".
634 /// * As a HEX value such as "#1111FA".
635 /// * As an HSL value such as "hsl(30, 3.80%, 10.20%)".
636 ///
637 /// # Avoid if having only one set of values.
638 ///
639 /// If you only add one set of values e.g. calling [`BarPlot::add_values`] one time,
640 /// then it makes no sense using this method. Use any of the other ways to set colors.
641 ///
642 /// # Example
643 ///
644 /// ```
645 /// use eb_bars::BarPlot;
646 ///
647 /// let mut plot = BarPlot::new();
648 ///
649 /// let apples: Vec<f64> = vec![5., 16., 17., 8., 3.];
650 /// let oranges: Vec<f64> = vec![7., 6., 7., 16., 9.];
651 ///
652 /// // Add first set of values.
653 /// plot.add_values(&apples);
654 /// // Adding a second set of values.
655 /// plot.add_values(&oranges);
656 ///
657 /// // First call adds a color to the first category. Red bacuse apples are often red.
658 /// let clr_red = vec!["Red", "Red", "Red", "Red", "Red"];
659 /// plot.add_bar_colors_from_vec(clr_red);
660 /// // Second call adds a color to the second category. Orange because; oranges are orange. :)
661 /// let clr_orange = vec!["Orange", "Orange", "Orange", "Orange", "Orange"];
662 /// plot.add_bar_colors_from_vec(clr_orange);
663 ///
664 /// // Setting the first apple = green.
665 /// plot.add_bar_color_override(0, 0, "Green");
666 /// // Setting the last orange to blue.. :)
667 /// plot.add_bar_color_override(1, 4, "Blue");
668 ///
669 /// let svg: String = plot.to_svg(1600, 1000);
670 /// ```
671 pub fn add_bar_color_override(&mut self, category: usize, bar: usize, color: &'a str) {
672 // Will always select the bar from the last added category e.g. after most recent BarPlot.add_values() call.
673 assert!(
674 !self.values.is_empty(),
675 "Can't override bar '{bar}' with color '{color}', because no bars (values) have been added yet."
676 );
677 self.colors.bars.overrides.push((category, bar, color));
678 }
679
680 /// Show horizontal grid lines.
681 ///
682 /// # Important
683 ///
684 /// Call [`BarPlot::set_scale_range`] first, otherwise there are no values to base the grid on.
685 ///
686 /// # Example
687 ///
688 /// ```
689 /// use eb_bars::BarPlot;
690 ///
691 /// let mut plot = BarPlot::new();
692 ///
693 /// plot.add_values(&[5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6]);
694 ///
695 /// // Needed for horizontal (y-grid) lines.
696 /// plot.set_scale_range(0, 20, 2);
697 ///
698 /// plot.set_show_horizontal_lines();
699 ///
700 /// let svg: String = plot.to_svg(1600, 1000);
701 /// ```
702 pub fn set_show_horizontal_lines(&mut self) {
703 self.show.horizontal_lines = true;
704 }
705
706 /// Add custom horizontal grid lines.
707 ///
708 /// # Example
709 ///
710 /// ```
711 /// use eb_bars::BarPlot;
712 ///
713 /// let mut plot = BarPlot::new();
714 ///
715 /// let values = [5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6];
716 /// plot.add_values(&values);
717 ///
718 /// // Add a horizontal lines.
719 /// let line_color = "rgb(126, 255, 165)";
720 /// plot.add_horizontal_line_at(25.0, line_color);
721 /// plot.add_horizontal_line_at(50.0, line_color);
722 /// plot.add_horizontal_line_at(75.0, line_color);
723 ///
724 /// let svg: String = plot.to_svg(1600, 1000);
725 /// ```
726 pub fn add_horizontal_line_at(&mut self, p: Percentage, color: &'a str) {
727 self.lines_at.push(LinesAt::Horizontal(p, color));
728 }
729
730 /// Show vertical grid lines.
731 ///
732 /// # Important
733 ///
734 /// Call [`BarPlot::set_bin_markers`] first, otherwise there are no values to base the grid on.
735 ///
736 /// # Example
737 ///
738 /// ```
739 /// use eb_bars::BarPlot;
740 ///
741 /// let mut plot = BarPlot::new();
742 ///
743 /// let values = [5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6];
744 /// plot.add_values(&values);
745 ///
746 /// // Needed for vertical (x-grid) lines.
747 /// let markers: Vec<String> = (0..values.len()).map(|i| (i).to_string()).collect();
748 /// let markers = markers.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
749 /// plot.set_bin_markers(&markers);
750 ///
751 /// plot.set_show_vertical_lines();
752 ///
753 /// let svg: String = plot.to_svg(1600, 1000);
754 /// ```
755 pub fn set_show_vertical_lines(&mut self) {
756 self.show.vertical_lines = true;
757 }
758
759 /// Add custom vertical grid lines.
760 ///
761 /// # Example
762 ///
763 /// ```
764 /// use eb_bars::BarPlot;
765 ///
766 /// let mut plot = BarPlot::new();
767 ///
768 /// let values = [5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6];
769 /// plot.add_values(&values);
770 ///
771 /// // Add vertical lines.
772 /// let line_color = "rgb(126, 255, 165)";
773 /// plot.add_vertical_line_at(25.0, line_color);
774 /// plot.add_vertical_line_at(50.0, line_color);
775 /// plot.add_vertical_line_at(75.0, line_color);
776 ///
777 /// let svg: String = plot.to_svg(1600, 1000);
778 /// ```
779 pub fn add_vertical_line_at(&mut self, p: Percentage, color: &'a str) {
780
781 self.lines_at.push(LinesAt::Vertical(p, color));
782 }
783
784 /// Set size of the barplot size (relative to the canvas/frame).
785 ///
786 /// By default, the barchart part of the image will take up the full width and length of the frame.
787 /// However, by adding literally anything to the barplot, you most likely want to change the size of the plot.
788 /// If you skip this method, the text, ticks, markers, legend etc. will not be inside the viewbox of the canvas.
789 ///
790 /// # How it operates
791 ///
792 /// We only work with percentage values when defining sizes. A width of 100 means it takes up 100% of the width.
793 /// A height of 100 will do the same, but for height.
794 /// Same goes for offset. A horizontal offset of 10 means that the offset is pushed 10% from the left.
795 /// A vertical offset of 10 means that the offset is pushed 10% from the top.
796 /// An offset with value 100 means the offset is pushed all the way to the right (horizontal) or bottom (vertical).
797 ///
798 /// # Example
799 ///
800 /// ```
801 /// use eb_bars::BarPlot;
802 ///
803 /// let mut plot = BarPlot::new();
804 ///
805 /// plot.add_values(&[1., 2., 3.,]);
806 ///
807 /// let width = 90.0; // Set width to 90%,
808 /// let horizontal_offset = 65.0; // Move 65% right
809 /// let height = 85.0; // Set height to 85%
810 /// let vertical_offset = 40.0; // Move 40% down.
811 /// plot.set_plot_window_size(width, horizontal_offset, height, vertical_offset);
812 ///
813 /// let svg: String = plot.to_svg(1600, 1000);
814 /// ```
815 pub fn set_plot_window_size(
816 &mut self,
817 x_length: Percentage,
818 x_offset: Percentage,
819 y_length: Percentage,
820 y_offset: Percentage
821 ) {
822 assert!(x_length <= 100.0 && x_offset <= 100.0, "plot window width cannot exceed 100%");
823 assert!(y_length <= 100.0 && y_offset <= 100.0, "plot window height cannot exceed 100%");
824
825 self.layout.plot_window_scale = Some((x_length, x_offset, y_length, y_offset));
826 }
827
828 /// Set a scale for the barchart.
829 ///
830 /// By default, the scale is calculated using the maximum and minimum value throughout all data.
831 /// However, by setting the scale manually we get a more presentable plot.
832 /// Pass whole numbers (can be negative) as the scale minimum and scale maximum.
833 /// The 3rd parameter is the gap between each number on the scale e.g. the step.
834 /// The step must be a positive number as it is an increasing number starting from minimum.
835 ///
836 /// # Example
837 ///
838 /// ```
839 /// use eb_bars::BarPlot;
840 ///
841 /// let mut plot = BarPlot::new();
842 ///
843 /// plot.add_values(&[5.0, 16.4, 17.1, 13.7, 8.9, 3.9, 6.3, 9.6]);
844 ///
845 /// let min = 0;
846 /// let max = 20;
847 /// let step = 2;
848 ///
849 /// plot.set_scale_range(min, max, step);
850 ///
851 /// let svg: String = plot.to_svg(1600, 1000);
852 /// ```
853 pub fn set_scale_range(&mut self, min: i64, max: i64, step: u64) {
854 self.layout.scale_range = Some((min, max, step as usize));
855 }
856
857 /// Set the labels and markers for each bin/bucket on the x-axis.
858 ///
859 /// Note: Passing an array with fewer bin markers than added values will cause some bins to be un-labeled.
860 /// To make sure everything is correct, it is recommended to pass the same amount of markers as values.
861 ///
862 /// # Example
863 ///
864 /// ```
865 /// use eb_bars::BarPlot;
866 ///
867 /// let mut plot = BarPlot::new();
868 ///
869 /// let absence_boys = [5., 3., 8., 4., 7.];
870 /// let absence_girls = [4., 1., 2., 3., 1.];
871 /// let weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday",];
872 ///
873 /// plot.add_values(&absence_boys);
874 /// plot.add_values(&absence_girls);
875 /// plot.set_bin_markers(&weekdays);
876 ///
877 /// let svg: String = plot.to_svg(1600, 1000);
878 /// ```
879 pub fn set_bin_markers(&mut self, markers: &'a [&'a str]) {
880 self.markers = Some(markers);
881 }
882
883 /// Place the bin markers at the middle of each bin/bucket instead of to the left.
884 ///
885 /// By default, the bin markers are placed on the left side in between each bin.
886 /// You can have the markers placed either `left`, `middle` or `right` depending on usecase.
887 ///
888 /// Note: check out [`BarPlot::set_bin_markers_left`] and [`BarPlot::set_bin_markers_right`].
889 ///
890 /// # Example
891 ///
892 /// ```
893 /// use eb_bars::BarPlot;
894 ///
895 /// let mut plot = BarPlot::new();
896 ///
897 /// let values = [1., 2., 3.];
898 /// plot.add_values(&values);
899 ///
900 /// let markers: Vec<String> = (0..values.len()).map(|i| (i).to_string()).collect();
901 /// let markers = markers.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
902 /// plot.set_bin_markers(&markers);
903 ///
904 /// plot.set_bin_markers_middle();
905 ///
906 /// let svg: String = plot.to_svg(1600, 1000);
907 /// ```
908 pub fn set_bin_markers_middle(&mut self) {
909 self.layout.bin_marker_position = BinMarkerPosition::Middle;
910 }
911
912 /// Place the bin markers to the left of each bin/bucket.
913 ///
914 /// By default, the bin markers are already placed on the left side in between each bin.
915 /// This method can be used to _reset_ an eventual change.
916 /// You can have the markers placed either `left`, `middle` or `right` depending on usecase.
917 ///
918 /// Note: check out [`BarPlot::set_bin_markers_middle`] and [`BarPlot::set_bin_markers_right`].
919 ///
920 /// # Example
921 ///
922 /// ```
923 /// use eb_bars::BarPlot;
924 ///
925 /// let mut plot = BarPlot::new();
926 ///
927 /// let values = [1., 2., 3.];
928 /// plot.add_values(&values);
929 ///
930 /// let markers: Vec<String> = (0..values.len()).map(|i| (i).to_string()).collect();
931 /// let markers = markers.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
932 /// plot.set_bin_markers(&markers);
933 ///
934 /// // Setting markers at middle.
935 /// plot.set_bin_markers_middle();
936 /// // Then back to left side.
937 /// plot.set_bin_markers_left();
938 ///
939 /// let svg: String = plot.to_svg(1600, 1000);
940 /// ```
941 pub fn set_bin_markers_left(&mut self) {
942 self.layout.bin_marker_position = BinMarkerPosition::Left;
943 }
944
945 /// Place the bin markers to the right of each bin/bucket instead of to the left.
946 ///
947 /// By default, the bin markers are placed on the left side in between each bin.
948 /// You can have the markers placed either `left`, `middle` or `right` depending on usecase.
949 ///
950 /// Note: check out [`BarPlot::set_bin_markers_left`] and [`BarPlot::set_bin_markers_middle`].
951 ///
952 /// # Example
953 ///
954 /// ```
955 /// use eb_bars::BarPlot;
956 ///
957 /// let mut plot = BarPlot::new();
958 ///
959 /// let values = [1., 2., 3.];
960 /// plot.add_values(&values);
961 ///
962 /// let markers: Vec<String> = (0..values.len()).map(|i| (i).to_string()).collect();
963 /// let markers = markers.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
964 /// plot.set_bin_markers(&markers);
965 ///
966 /// plot.set_bin_markers_right();
967 ///
968 /// let svg: String = plot.to_svg(1600, 1000);
969 /// ```
970 pub fn set_bin_markers_right(&mut self) {
971 self.layout.bin_marker_position = BinMarkerPosition::Right;
972 }
973
974 /// Introduce a `gap` between every bar.
975 ///
976 /// The gap is calculated using a percentage.
977 /// A gap of 0 means there is no gap/air between bars.
978 /// A gap of 50 means that the bar and the gap will take up the same width.
979 /// A gap of 100 means that the gap will take up all space and so the bar becomes invisible.
980 ///
981 /// Note: check out [`BarPlot::set_bin_gap`] for gap only betweem bins.
982 ///
983 /// # Example
984 ///
985 /// ```
986 /// use eb_bars::BarPlot;
987 ///
988 /// let mut plot = BarPlot::new();
989 ///
990 /// plot.add_values(&[1., 2., 3.]);
991 /// plot.add_values(&[4., 5., 6.]);
992 ///
993 /// let gap = 30.0; // The gap will take up 30% of the space, leaving 70% for bar.
994 /// plot.set_bar_gap(gap);
995 ///
996 /// let svg: String = plot.to_svg(1600, 1000);
997 /// ```
998 pub fn set_bar_gap(&mut self, gap: Percentage) {
999 self.layout.bar_gap = gap;
1000 }
1001
1002 /// Introduce a `gap` between every bin/bucket.
1003 ///
1004 /// The gap is calculated using a percentage.
1005 /// A gap of 0 means there is no gap/air between bins.
1006 /// A gap of 50 means that the bin and the gap will take up the same width.
1007 /// A gap of 100 means that the gap will take up all space and so the bin squashed into nothing.
1008 ///
1009 /// Note: check out [`BarPlot::set_bar_gap`] for gap betweem bars.
1010 ///
1011 /// # Example
1012 ///
1013 /// ```
1014 /// use eb_bars::BarPlot;
1015 ///
1016 /// let mut plot = BarPlot::new();
1017 ///
1018 /// plot.add_values(&[1., 2., 3.]);
1019 /// plot.add_values(&[4., 5., 6.]);
1020 ///
1021 /// let gap = 30.0; // The gap will take up 30% of the space, leaving 70% for bin.
1022 /// plot.set_bin_gap(gap);
1023 ///
1024 /// let svg: String = plot.to_svg(1600, 1000);
1025 /// ```
1026 pub fn set_bin_gap(&mut self, gap: Percentage) {
1027 self.layout.bin_gap = gap;
1028 }
1029
1030 /// Set length for ticks on the y axis.
1031 ///
1032 /// The length is calculated using a percentage.
1033 /// A length of 0 means the tick will not be generated.
1034 /// A length of 10 means that the tick will be of a _somewhat_ _normal_ length.
1035 /// A length of greater than 10 means that the tick will be long.
1036 /// Try different lengths to find the best length for your usecase.
1037 ///
1038 /// # Example
1039 ///
1040 /// ```
1041 /// use eb_bars::BarPlot;
1042 ///
1043 /// let mut plot = BarPlot::new();
1044 ///
1045 /// plot.add_values(&[1., 2., 3.]);
1046 ///
1047 /// let len = 20.0; // The tick length will be of considerate length.
1048 /// plot.set_y_axis_tick_length(len);
1049 ///
1050 /// let svg: String = plot.to_svg(1600, 1000);
1051 /// ```
1052 pub fn set_y_axis_tick_length(&mut self, p: Percentage) {
1053 self.layout.y_axis_tick_length = p;
1054 }
1055
1056 /// Set length for ticks on the x axis.
1057 ///
1058 /// The length is calculated using a percentage.
1059 /// A length of 0 means the tick will not be generated.
1060 /// A length of 10 means that the tick will be of a _somewhat_ _normal_ length.
1061 /// A length of greater than 10 means that the tick will be long.
1062 /// Try different lengths to find the best length for your usecase.
1063 ///
1064 /// # Example
1065 ///
1066 /// ```
1067 /// use eb_bars::BarPlot;
1068 ///
1069 /// let mut plot = BarPlot::new();
1070 ///
1071 /// plot.add_values(&[1., 2., 3.]);
1072 ///
1073 /// let len = 20.0; // The tick length will be of considerate length.
1074 /// plot.set_x_axis_tick_length(len);
1075 ///
1076 /// let svg: String = plot.to_svg(1600, 1000);
1077 /// ```
1078 pub fn set_x_axis_tick_length(&mut self, p: Percentage) {
1079 self.layout.x_axis_tick_length = p;
1080 }
1081
1082 /// Anchor bars at zero instead of the floor.
1083 /// This will make negative bars grow downwards.
1084 /// If your dataset contains only negative values, then this might not make sense to use.
1085 /// Rather, `use this when your dataset is likely to contain both positive and negative values`.
1086 ///
1087 /// An area where it makes sens is when visualizing temperature differences. :)
1088 ///
1089 /// By default, bars are anchored at the floor of the barchart.
1090 /// However, you might want negative values to stand out by having them point downwards.
1091 /// This method will apply anchoring bars at the zero line instead of the floor.
1092 ///
1093 /// # Important
1094 /// Call [`BarPlot::set_scale_range`] first, and make sure to set `min < 0` and `max >= 0`.
1095 /// Otherwise, you ~might~ will get a barchart that looks goofy. Consider yourself warned.
1096 ///
1097 /// # Example
1098 ///
1099 /// ```
1100 /// use eb_bars::BarPlot;
1101 ///
1102 /// let mut plot = BarPlot::new();
1103 ///
1104 /// plot.add_values(&[5.0, -16.4, 17.1, 13.7, 8.9, 3.9, -6.3, 9.6]);
1105 ///
1106 /// // Adding some extra tweaks that makes this example more clear.
1107 /// let min = -20;
1108 /// let max = 20;
1109 /// let step = 2;
1110 /// plot.set_scale_range(min, max, step);
1111 /// plot.set_plot_window_size(90.0, 80.0, 83.0, 50.0);
1112 ///
1113 /// // Negative bars will now grow downwards instead of upwards. :)
1114 /// plot.set_negative_bars_go_down();
1115 ///
1116 /// let svg: String = plot.to_svg(1600, 1000);
1117 /// ```
1118 pub fn set_negative_bars_go_down(&mut self) {
1119 self.layout.negative_bars_go_down = true;
1120 }
1121
1122 /// Apply text on the left side of the plot window.
1123 ///
1124 /// By default, the plot window takes up the whole window.
1125 /// For the text to be visible, we need to scale down the plot first with [`BarPlot::set_plot_window_size`].
1126 ///
1127 /// The text will by default be offset from the plot window with a sane value.
1128 /// If you want to override this value, you can set a custom offset with [`BarPlot::set_text_left_offset`].
1129 ///
1130 /// # Example
1131 ///
1132 /// ```
1133 /// use eb_bars::BarPlot;
1134 ///
1135 /// let mut plot = BarPlot::new();
1136 ///
1137 /// plot.add_values(&[1., 2., 3.]);
1138 ///
1139 /// // Scale down the plot figure size so text can become be visible.
1140 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1141 ///
1142 /// plot.set_text_left("This is some text.");
1143 ///
1144 /// let svg: String = plot.to_svg(1600, 1000);
1145 /// ```
1146 pub fn set_text_left(&mut self, text: &'a str) {
1147 self.plot_text.left = Some(text);
1148 }
1149
1150 /// Apply offset for text on the left side of the plot window.
1151 ///
1152 /// The offset is calculated percent wise from the frame towards the plot window.
1153 /// The higher the percentage the closer the text moves towards the plot window.
1154 ///
1155 /// Note: you need to explicitly apply text first with [`BarPlot::set_text_lef.
1156 ///
1157 /// # Example
1158 ///
1159 /// ```
1160 /// use eb_bars::BarPlot;
1161 ///
1162 /// let mut plot = BarPlot::new();
1163 ///
1164 /// plot.add_values(&[1., 2., 3.]);
1165 ///
1166 /// // Scale down the plot figure size so text can become be visible.
1167 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1168 ///
1169 /// plot.set_text_left("This is some text.");
1170 ///
1171 /// let percent = 30.0;
1172 /// plot.set_text_left_offset(percent);
1173 ///
1174 /// let svg: String = plot.to_svg(1600, 1000);
1175 /// ```
1176 pub fn set_text_left_offset(&mut self, offset: Percentage) {
1177 self.plot_text.left_offset = Some(offset);
1178 }
1179
1180 /// Apply text on the right side of the plot window.
1181 ///
1182 /// By default, the plot window takes up the whole window.
1183 /// For the text to be visible, we need to scale down the plot first with [`BarPlot::set_plot_window_size`].
1184 ///
1185 /// The text will by default be offset from the plot window with a sane value.
1186 /// If you want to override this value, you can set a custom offset with [`BarPlot::set_text_right_offset`].
1187 ///
1188 /// # Example
1189 ///
1190 /// ```
1191 /// use eb_bars::BarPlot;
1192 ///
1193 /// let mut plot = BarPlot::new();
1194 ///
1195 /// plot.add_values(&[1., 2., 3.]);
1196 ///
1197 /// // Scale down the plot figure size so text can become be visible.
1198 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1199 ///
1200 /// plot.set_text_right("This is some text.");
1201 ///
1202 /// let svg: String = plot.to_svg(1600, 1000);
1203 /// ```
1204 pub fn set_text_right(&mut self, text: &'a str) {
1205 self.plot_text.right = Some(text);
1206 }
1207
1208 /// Apply offset for text on the right side of the plot window.
1209 ///
1210 /// The offset is calculated percent wise from the frame towards the plot window.
1211 /// The higher the percentage the closer the text moves towards the plot window.
1212 ///
1213 /// Note: you need to explicitly apply text first with [`BarPlot::set_text_right`].
1214 ///
1215 /// # Example
1216 ///
1217 /// ```
1218 /// use eb_bars::BarPlot;
1219 ///
1220 /// let mut plot = BarPlot::new();
1221 ///
1222 /// plot.add_values(&[1., 2., 3.]);
1223 ///
1224 /// // Scale down the plot figure size so text can become be visible.
1225 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1226 ///
1227 /// plot.set_text_right("This is some text.");
1228 ///
1229 /// let percent = 30.0;
1230 /// plot.set_text_right_offset(percent);
1231 ///
1232 /// let svg: String = plot.to_svg(1600, 1000);
1233 /// ```
1234 pub fn set_text_right_offset(&mut self, offset: Percentage) {
1235 self.plot_text.right_offset = Some(offset);
1236 }
1237
1238 /// Apply text underneath the plot window.
1239 ///
1240 /// By default, the plot window takes up the whole window.
1241 /// For the text to be visible, we need to scale down the plot first with [`BarPlot::set_plot_window_size`].
1242 ///
1243 /// The text will by default be offset from the plot window with a sane value.
1244 /// If you want to override this value, you can set a custom offset with [`BarPlot::set_text_bottom_offset`].
1245 ///
1246 /// # Example
1247 ///
1248 /// ```
1249 /// use eb_bars::BarPlot;
1250 ///
1251 /// let mut plot = BarPlot::new();
1252 ///
1253 /// plot.add_values(&[1., 2., 3.]);
1254 ///
1255 /// // Scale down the plot figure size so text can become be visible.
1256 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1257 ///
1258 /// plot.set_text_bottom("This is some text.");
1259 ///
1260 /// let svg: String = plot.to_svg(1600, 1000);
1261 /// ```
1262 pub fn set_text_bottom(&mut self, text: &'a str) {
1263 self.plot_text.bottom = Some(text);
1264 }
1265
1266 /// Apply offset for text underneath the plot window.
1267 ///
1268 /// The offset is calculated percent wise from the frame towards the plot window.
1269 /// The higher the percentage the closer the text moves towards the plot window.
1270 ///
1271 /// Note: you need to explicitly apply text first with [`BarPlot::set_text_bottom`].
1272 ///
1273 /// # Example
1274 ///
1275 /// ```
1276 /// use eb_bars::BarPlot;
1277 ///
1278 /// let mut plot = BarPlot::new();
1279 ///
1280 /// plot.add_values(&[1., 2., 3.]);
1281 ///
1282 /// // Scale down the plot figure size so text can become be visible.
1283 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1284 ///
1285 /// plot.set_text_bottom("This is some text.");
1286 ///
1287 /// let percent = 30.0;
1288 /// plot.set_text_bottom_offset(percent);
1289 ///
1290 /// let svg: String = plot.to_svg(1600, 1000);
1291 /// ```
1292 pub fn set_text_bottom_offset(&mut self, offset: Percentage) {
1293 self.plot_text.bottom_offset = Some(offset);
1294 }
1295
1296 /// Apply text above the plot window.
1297 ///
1298 /// By default, the plot window takes up the whole window.
1299 /// For the text to be visible, we need to scale down the plot first with [`BarPlot::set_plot_window_size`].
1300 ///
1301 /// The text will by default be offset from the plot window with a sane value.
1302 /// If you want to override this value, you can set a custom offset with [`BarPlot::set_text_top_offset`].
1303 ///
1304 /// # Example
1305 ///
1306 /// ```
1307 /// use eb_bars::BarPlot;
1308 ///
1309 /// let mut plot = BarPlot::new();
1310 ///
1311 /// plot.add_values(&[1., 2., 3.]);
1312 ///
1313 /// // Scale down the plot figure size so text can become be visible.
1314 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1315 ///
1316 /// plot.set_text_top("This is some text.");
1317 ///
1318 /// let svg: String = plot.to_svg(1600, 1000);
1319 /// ```
1320 pub fn set_text_top(&mut self, text: &'a str) {
1321 self.plot_text.top = Some(text);
1322 }
1323
1324 /// Apply offset for text above the plot window.
1325 ///
1326 /// The offset is calculated percent wise from the frame towards the plot window.
1327 /// The higher the percentage the closer the text moves towards the plot window.
1328 ///
1329 /// Note: you need to explicitly apply text first with [`BarPlot::set_text_top`].
1330 ///
1331 /// # Example
1332 ///
1333 /// ```
1334 /// use eb_bars::BarPlot;
1335 ///
1336 /// let mut plot = BarPlot::new();
1337 ///
1338 /// plot.add_values(&[1., 2., 3.]);
1339 ///
1340 /// // Scale down the plot figure size so text can become be visible.
1341 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1342 ///
1343 /// plot.set_text_top("This is some text.");
1344 ///
1345 /// let percent = 30.0;
1346 /// plot.set_text_top_offset(percent);
1347 ///
1348 /// let svg: String = plot.to_svg(1600, 1000);
1349 /// ```
1350 pub fn set_text_top_offset(&mut self, offset: Percentage) {
1351 self.plot_text.top_offset = Some(offset);
1352 }
1353
1354 /// Apply a legend with category names and their corresponding colors.
1355 ///
1356 /// When calling [`BarPlot::add_values`] multiple times each time adding a set of values (categories),
1357 /// you most likely want a legend to label each category by name and color.
1358 ///
1359 /// Note: The legend will use the colors added with [`BarPlot::add_bar_colors_by_category`],
1360 /// remember to call this method once for each category added.
1361 ///
1362 /// # Example
1363 ///
1364 /// ```
1365 /// use eb_bars::BarPlot;
1366 ///
1367 /// let mut plot = BarPlot::new();
1368 ///
1369 /// let apples: Vec<f64> = vec![5., 16., 17., 8., 3.];
1370 /// let oranges: Vec<f64> = vec![7., 6., 7., 16., 9.];
1371 ///
1372 /// // Add first set of values.
1373 /// plot.add_values(&apples);
1374 /// // Adding a second set of values.
1375 /// plot.add_values(&oranges);
1376 ///
1377 /// // First call adds a color to the first category. Red bacuse apples are often red.
1378 /// plot.add_bar_colors_by_category("Red");
1379 /// // Second call adds a color to the second category. Orange because; oranges are orange. :)
1380 /// plot.add_bar_colors_by_category("Orange");
1381 ///
1382 /// // Adding the labels (colors are already added) for legend.
1383 /// let categories = ["Apples", "Oranges"];
1384 /// // Applying the legend.
1385 /// plot.set_legend(&categories);
1386 ///
1387 /// let svg: String = plot.to_svg(1600, 1000);
1388 /// ```
1389 pub fn set_legend(&mut self, categories: &'a [&'a str]) {
1390 self.legend.categories = Some(categories);
1391 }
1392
1393 /// Set legend position.
1394 ///
1395 /// When calling [`BarPlot::set_legend`], its position is calculated using percentage.
1396 /// By default, the legend is positioned on the far right side and close to the top.
1397 /// You can override the default position by passing two values.
1398 /// First value is the absolute offset from left to right,
1399 /// the second value is the absolute offset from top to bottom.
1400 ///
1401 /// An offset X = 50 and offset Y = 50 will roughly position the legend in the center of the canvas.
1402 /// More precisely, the top left corner of the legend itself will be pinned on that position.
1403 ///
1404 /// Note: you might want to resize the plot window to accomodate for the legend if you want it
1405 /// to be drawn outside the plot, otherwise it will be drawn on top of the plot figure.
1406 /// Check out [`BarPlot::set_plot_window_size`] for that.
1407 ///
1408 /// # Example
1409 ///
1410 /// ```
1411 /// use eb_bars::BarPlot;
1412 ///
1413 /// let mut plot = BarPlot::new();
1414 ///
1415 /// let apples: Vec<f64> = vec![5., 16., 17., 8., 3.];
1416 /// let oranges: Vec<f64> = vec![7., 6., 7., 16., 9.];
1417 ///
1418 /// // Add first set of values.
1419 /// plot.add_values(&apples);
1420 /// // Adding a second set of values.
1421 /// plot.add_values(&oranges);
1422 ///
1423 /// // First call adds a color to the first category. Red bacuse apples are often red.
1424 /// plot.add_bar_colors_by_category("Red");
1425 /// // Second call adds a color to the second category. Orange because; oranges are orange. :)
1426 /// plot.add_bar_colors_by_category("Orange");
1427 ///
1428 /// // Adding the labels (colors are already added) for legend.
1429 /// let categories = ["Apples", "Oranges"];
1430 /// // Applying the legend.
1431 /// plot.set_legend(&categories);
1432 ///
1433 /// // Make room for legend on the right side by shrinking plot window.
1434 /// plot.set_plot_window_size(80.0, 30.0, 85.0, 40.0);
1435 ///
1436 /// let offset_x = 90.0;
1437 /// let offset_y = 22.0;
1438 /// plot.set_legend_position(offset_x, offset_y);
1439 ///
1440 /// let svg: String = plot.to_svg(1600, 1000);
1441 /// ```
1442 pub fn set_legend_position(&mut self, x: Percentage, y: Percentage) {
1443 self.legend.position = Some((x,y));
1444 }
1445
1446 /// Apply a custom font-size for all text.
1447 ///
1448 /// By default, all text and numbers are rendered with a default font-size. This can be overriden.
1449 /// The font-size is calculated using a percentage value.
1450 /// A font-size of size 100 (100%) will not affect the text as it is the default.
1451 /// You can either increase the size by passing a >100 value or decrease it with <100.
1452 ///
1453 /// # Example
1454 ///
1455 /// ```
1456 /// use eb_bars::BarPlot;
1457 ///
1458 /// let mut plot = BarPlot::new();
1459 ///
1460 /// plot.add_values(&[1., 2., 3.,]);
1461 ///
1462 /// // Scale down the plot figure size so text can become be visible.
1463 /// plot.set_plot_window_size(85.0, 65.0, 80.0, 40.0);
1464 ///
1465 /// // Apply some text.
1466 /// plot.set_text_left("This is some text.");
1467 ///
1468 /// // Increase font-size using a percentage greater than 100%.
1469 /// let size = 130.0;
1470 /// plot.set_font_size(size);
1471 ///
1472 /// let svg: String = plot.to_svg(1600, 1000);
1473 /// ```
1474 pub fn set_font_size(&mut self, p: Percentage) {
1475 self.layout.font_size = p;
1476 }
1477
1478 /// Apply a border around the canvas.
1479 ///
1480 /// # Example
1481 ///
1482 /// ```
1483 /// use eb_bars::BarPlot;
1484 ///
1485 /// let mut plot = BarPlot::new();
1486 ///
1487 /// plot.add_values(&[1., 2., 3.]);
1488 ///
1489 /// // Shrink plot so that it becomes clear that the border goes around the canvas.
1490 /// plot.set_plot_window_size(80.0, 30.0, 85.0, 40.0);
1491 ///
1492 /// plot.set_show_window_border();
1493 ///
1494 /// let svg: String = plot.to_svg(1600, 1000);
1495 /// ```
1496 pub fn set_show_window_border(&mut self) {
1497 self.show.window_border = true;
1498 }
1499
1500 /// Apply a border around the plot.
1501 ///
1502 /// # Example
1503 ///
1504 /// ```
1505 /// use eb_bars::BarPlot;
1506 ///
1507 /// let mut plot = BarPlot::new();
1508 ///
1509 /// plot.add_values(&[1., 2., 3.]);
1510 ///
1511 /// // Shrink plot so that it becomes clear that the border goes around the plot window.
1512 /// plot.set_plot_window_size(80.0, 30.0, 85.0, 40.0);
1513 ///
1514 /// plot.set_show_plot_border();
1515 ///
1516 /// let svg: String = plot.to_svg(1600, 1000);
1517 /// ```
1518 pub fn set_show_plot_border(&mut self) {
1519 self.show.plot_border = true;
1520 }
1521
1522 /// Generate the final svg image of all applied content.
1523 ///
1524 /// When you are satisfied with all the tweaks in above methods, it is time to generate the final svg.
1525 /// Keep in mind that you can still add or re-apply changes on the same object after calling this method.
1526 /// Changes will be applied and included on the following call to this method.
1527 ///
1528 /// # Example
1529 ///
1530 /// ```
1531 /// use eb_bars::BarPlot;
1532 ///
1533 /// let mut plot = BarPlot::new();
1534 ///
1535 /// plot.add_values(&[1., 2., 3.]);
1536 ///
1537 /// let mut svg: String = plot.to_svg(1600, 1000);
1538 ///
1539 /// // Add a new configuration (just resize plot window).
1540 /// plot.set_plot_window_size(80.0, 30.0, 85.0, 40.0);
1541 ///
1542 /// // Lets overwrite the old plot with this new configuration.
1543 /// svg = plot.to_svg(1600, 1000);
1544 /// ```
1545 pub fn to_svg(&mut self, width: u32, height: u32) -> String {
1546 assert!(!self.values.is_empty(), "Can not generate plot without any values..");
1547
1548 self.size = (width, height);
1549
1550 let n_categories = self.values.len();
1551 match &mut self.colors.bars.layout {
1552 BarColorLayout::Category(colors) => {
1553 let n_colors = colors.len();
1554 assert_eq!(
1555 n_categories,
1556 n_colors,
1557 "Got {n_categories} categories and {n_colors} colors.",
1558 );
1559 }
1560 BarColorLayout::Indexed(matrix) => {
1561 let n_color_vectors = matrix.len();
1562 assert_eq!(
1563 n_categories,
1564 n_color_vectors,
1565 "Got {n_categories} categories and {n_color_vectors} color vectors.",
1566 );
1567
1568 for (i, colors) in matrix.iter().enumerate() {
1569 let values = self.values[i].len();
1570 let n_colors = colors.len();
1571 assert_eq!(
1572 values,
1573 n_colors,
1574 "Got {values} values and {n_colors} colors for category {i}.",
1575 );
1576 }
1577 }
1578 // No need to do assertion on remaining variants..
1579 _ => (),
1580 }
1581
1582 svg::render(self)
1583 }
1584}
1585
1586#[cfg(test)]
1587mod tests {
1588 // All other tests are in directory ./tests
1589
1590 use std::fs;
1591
1592 use toml;
1593
1594 use super::{VERSION, REPOSITORY};
1595
1596 #[test]
1597 fn version_and_repo() {
1598 // When updating Cargo.toml, make sure to update corresponding values in src files as well.
1599 let contents = fs::read_to_string("Cargo.toml").unwrap();
1600 let value = contents.parse::<toml::Table>().unwrap();
1601
1602 let version = value["package"]["version"].as_str().unwrap();
1603 assert_eq!(version, VERSION);
1604
1605 let repository = value["package"]["repository"].as_str().unwrap();
1606 assert_eq!(repository, REPOSITORY);
1607 }
1608}