1use crate::theme::{LegendPosition, Theme};
2
3use super::Rect;
4
5pub struct PlotLayout {
7 pub total: Rect,
8 pub plot_area: Rect,
9 pub title_area: Rect,
10 pub subtitle_area: Rect,
11 pub caption_area: Rect,
12 pub x_axis_area: Rect,
13 pub y_axis_area: Rect,
14 pub legend_area: Rect,
15}
16
17impl PlotLayout {
18 pub fn compute(
20 width: f64,
21 height: f64,
22 theme: &Theme,
23 has_title: bool,
24 has_legend: bool,
25 ) -> Self {
26 Self::compute_full(
27 width, height, theme, has_title, false, false, has_legend, false, None, None,
28 )
29 }
30
31 #[allow(clippy::too_many_arguments)]
38 pub fn compute_full(
39 width: f64,
40 height: f64,
41 theme: &Theme,
42 has_title: bool,
43 has_subtitle: bool,
44 has_caption: bool,
45 has_legend: bool,
46 x_axis_top: bool,
47 y_label_width: Option<f64>,
48 x_label_height: Option<f64>,
49 ) -> Self {
50 let margin = &theme.plot_margin;
51
52 let title_height = if has_title {
53 theme.title.size * 2.0
54 } else {
55 0.0
56 };
57
58 let subtitle_height = if has_subtitle {
59 theme.subtitle.size * 1.5
60 } else {
61 0.0
62 };
63
64 let caption_height = if has_caption {
65 theme.caption.size * 1.8
66 } else {
67 0.0
68 };
69
70 let x_axis_height = theme.axis_ticks_length
71 + if theme.axis_text_x.visible {
72 if theme.axis_text_x.angle.abs() > 10.0 {
75 x_label_height.unwrap_or(theme.axis_text_x.size * 5.0)
76 } else {
77 (theme.axis_text_x.size + 4.0) * theme.axis_text_x_dodge.max(1) as f64
79 }
80 } else {
81 0.0
82 }
83 + if theme.axis_title_x.visible {
84 theme.axis_title_x.size + 8.0
85 } else {
86 0.0
87 };
88
89 let y_axis_width = theme.axis_ticks_length
90 + if theme.axis_text_y.visible {
91 y_label_width.unwrap_or(theme.axis_text_y.size * 3.5 + 4.0)
92 } else {
93 0.0
94 }
95 + if theme.axis_title_y.visible {
96 theme.axis_title_y.size + 8.0
97 } else {
98 0.0
99 };
100
101 let legend_size = if has_legend {
102 theme.legend_margin.left
103 + theme.legend_key_width
104 + theme.legend_spacing
105 + theme.legend_text.size * 6.0
106 + theme.legend_margin.right
107 } else {
108 0.0
109 };
110
111 let legend_band = if has_legend {
114 theme.legend_margin.top
115 + theme.legend_key_height
116 + theme.legend_spacing
117 + theme.legend_margin.bottom
118 } else {
119 0.0
120 };
121 let (legend_right, legend_left, legend_top, legend_bottom) = if has_legend {
123 match theme.legend_position {
124 LegendPosition::Right => (legend_size, 0.0, 0.0, 0.0),
125 LegendPosition::Left => (0.0, legend_size, 0.0, 0.0),
126 LegendPosition::Top => (0.0, 0.0, legend_band, 0.0),
127 LegendPosition::Bottom => (0.0, 0.0, 0.0, legend_band),
128 LegendPosition::None | LegendPosition::Inside(..) => (0.0, 0.0, 0.0, 0.0),
130 }
131 } else {
132 (0.0, 0.0, 0.0, 0.0)
133 };
134
135 let plot_x = margin.left + y_axis_width + legend_left;
136 let plot_y = margin.top
138 + title_height
139 + subtitle_height
140 + legend_top
141 + if x_axis_top { x_axis_height } else { 0.0 };
142 let plot_width =
143 width - margin.left - margin.right - y_axis_width - legend_right - legend_left;
144 let plot_height = height
145 - margin.top
146 - margin.bottom
147 - title_height
148 - subtitle_height
149 - caption_height
150 - x_axis_height
151 - legend_top
152 - legend_bottom;
153
154 let mut plot_width = plot_width.max(50.0);
155 let mut plot_height = plot_height.max(50.0);
156 let mut plot_x = plot_x;
157 let mut plot_y = plot_y;
158
159 if let Some(r) = theme.aspect_ratio {
162 if r > 0.0 {
163 let (avail_w, avail_h) = (plot_width, plot_height);
164 if avail_w * r <= avail_h {
165 plot_height = avail_w * r;
166 plot_y += (avail_h - plot_height) / 2.0;
167 } else {
168 plot_width = avail_h / r;
169 plot_x += (avail_w - plot_width) / 2.0;
170 }
171 }
172 }
173
174 PlotLayout {
175 total: Rect {
176 x: 0.0,
177 y: 0.0,
178 width,
179 height,
180 },
181 plot_area: Rect {
182 x: plot_x,
183 y: plot_y,
184 width: plot_width,
185 height: plot_height,
186 },
187 title_area: Rect {
188 x: plot_x,
189 y: margin.top,
190 width: plot_width,
191 height: title_height,
192 },
193 subtitle_area: Rect {
194 x: plot_x,
195 y: margin.top + title_height,
196 width: plot_width,
197 height: subtitle_height,
198 },
199 caption_area: Rect {
200 x: plot_x,
201 y: plot_y + plot_height + x_axis_height,
202 width: plot_width,
203 height: caption_height,
204 },
205 x_axis_area: Rect {
206 x: plot_x,
207 y: plot_y + plot_height,
208 width: plot_width,
209 height: x_axis_height,
210 },
211 y_axis_area: Rect {
212 x: margin.left + legend_left,
213 y: plot_y,
214 width: y_axis_width,
215 height: plot_height,
216 },
217 legend_area: Rect {
218 x: plot_x + plot_width,
219 y: plot_y,
220 width: legend_size,
221 height: plot_height,
222 },
223 }
224 }
225}