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