plotters_layout/lib.rs
1//! # plotters-layout
2//!
3//! Layout utility library for [plotters](::plotters) crate.
4//!
5//! ## Creating a chart whose plotting area has specified size
6//!
7//! ```
8//! use plotters::prelude::*;
9//! use plotters_layout::ChartLayout;
10//! use plotters::backend::{RGBPixel, PixelFormat};
11//!
12//! let mut layout = ChartLayout::new();
13//! layout.caption("Graph Title", ("sans-serif", 40))?
14//! .margin(4)
15//! .x_label_area_size(40)
16//! .y_label_area_size(40);
17//! let (w, h): (u32, u32) = layout.desired_image_size((200, 160));
18//! let mut buf = vec![0u8; (w * h) as usize * RGBPixel::PIXEL_SIZE];
19//! let graph = BitMapBackend::with_buffer(&mut buf, (w, h));
20//! let root_area = graph.into_drawing_area();
21//! let builder = layout.bind(&root_area)?;
22//! let chart = builder.build_cartesian_2d(0f64..20f64, 0f64..16f64)?;
23//! assert_eq!(chart.plotting_area().dim_in_pixel(), (200, 160));
24//! # Ok::<(), Box<dyn std::error::Error>>(())
25//! ```
26//!
27//! ## Adjusting aspect ratio
28//!
29//! ```
30//! use plotters::prelude::*;
31//! use plotters_layout::{centering_ranges, ChartLayout};
32//!
33//! let min_range = (-200f64..200f64, -100f64..100f64);
34//!
35//! let mut buf = String::new();
36//! let graph = SVGBackend::with_string(&mut buf, (1280, 720));
37//! let root_area = graph.into_drawing_area();
38//!
39//! let mut builder = ChartLayout::new()
40//! .caption("Graph Title", ("sans-serif", 40))?
41//! .margin(4)
42//! .x_label_area_size(40)
43//! .y_label_area_size(40)
44//! .bind(&root_area)?;
45//!
46//! let (width, height) = builder.estimate_plot_area_size();
47//! let (x_range, y_range) = centering_ranges(&min_range, &(width as f64, height as f64));
48//!
49//! // (x_range, y_range) and (width, height) has same aspect ratio
50//! let inner_ratio = (x_range.end - x_range.start) / (y_range.end - y_range.start);
51//! let outer_ratio = width as f64 / height as f64;
52//! assert!((inner_ratio - outer_ratio).abs() < 1e-8);
53//!
54//! let chart = builder.build_cartesian_2d(x_range, y_range)?;
55//! assert_eq!(chart.plotting_area().dim_in_pixel(), (width, height));
56//!
57//! # Ok::<(), Box<dyn std::error::Error>>(())
58//! ```
59
60mod chart;
61
62use std::ops::{Add, Div, Mul, Range, Sub};
63
64pub use crate::chart::*;
65
66pub fn centering_ranges<T, S>(
67 minimum: &(Range<T>, Range<T>),
68 destination: &(S, S),
69) -> (Range<T>, Range<T>)
70where
71 T: Copy + PartialOrd + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
72 S: Copy + Into<T>,
73{
74 let sx = minimum.0.end - minimum.0.start;
75 let sy = minimum.1.end - minimum.1.start;
76 let dx = destination.0.into();
77 let dy = destination.1.into();
78 let half = dx / (dx + dx); // == 0.5
79 if sx * dy < sy * dx {
80 // sx -> sy * dx / dy
81 let radius = sy * dx / dy * half;
82 let center = (minimum.0.start + minimum.0.end) * half;
83 let s0 = (center - radius)..(radius + center);
84 (s0, minimum.1.clone())
85 } else {
86 // sy -> sx * dy / dx
87 let radius = sx * dy / dx * half;
88 let center = (minimum.1.end + minimum.1.start) * half;
89 let s1 = (center - radius)..(radius + center);
90 (minimum.0.clone(), s1)
91 }
92}