1use crate::render::svg::*;
2use crate::shape::axis::{Axis, AxisPosition};
3use crate::view::View;
4use crate::{BandScale, Error, LinearScale};
5use std::path::Path;
6use svg::Node;
7
8const DEFAULT_MARGIN_TOP: i32 = 90;
9const DEFAULT_MARGIN_BOTTOM: i32 = 50;
10const DEFAULT_MARGIN_LEFT: i32 = 60;
11const DEFAULT_MARGIN_RIGHT: i32 = 40;
12const DEFAULT_WIDTH: i32 = 800;
13const DEFAULT_HEIGHT: i32 = 600;
14
15const DEFAULT_TITLE_FONT_SIZE: &str = "24px";
16const DEFAULT_TITLE_Y_TRANSFORM: i32 = 25;
17
18pub struct Chart<'a> {
21 margin_top: i32,
22 margin_bottom: i32,
23 margin_left: i32,
24 margin_right: i32,
25 width: i32,
26 height: i32,
27 x_axis_top: Option<Axis>,
28 x_axis_bottom: Option<Axis>,
29 y_axis_left: Option<Axis>,
30 y_axis_right: Option<Axis>,
31 views: Vec<&'a dyn View>,
32 title: String,
33}
34
35impl<'a> Chart<'a> {
36 pub fn new() -> Self {
38 Chart {
39 margin_top: DEFAULT_MARGIN_TOP,
40 margin_bottom: DEFAULT_MARGIN_BOTTOM,
41 margin_left: DEFAULT_MARGIN_LEFT,
42 margin_right: DEFAULT_MARGIN_RIGHT,
43 width: DEFAULT_WIDTH,
44 height: DEFAULT_HEIGHT,
45 x_axis_top: None,
46 x_axis_bottom: None,
47 y_axis_left: None,
48 y_axis_right: None,
49 views: Vec::new(),
50 title: String::new(),
51 }
52 }
53
54 pub fn view_width(&self) -> i32 {
56 self.width - self.margin_left - self.margin_right
57 }
58
59 pub fn view_height(&self) -> i32 {
61 self.height - self.margin_top - self.margin_bottom
62 }
63
64 pub fn set_margin_top(mut self, margin_top: i32) -> Self {
66 self.margin_top = margin_top;
67 self
68 }
69
70 pub fn set_margin_bottom(mut self, margin_bottom: i32) -> Self {
72 self.margin_bottom = margin_bottom;
73 self
74 }
75
76 pub fn set_margin_left(mut self, margin_left: i32) -> Self {
78 self.margin_left = margin_left;
79 self
80 }
81
82 pub fn set_margin_right(mut self, margin_right: i32) -> Self {
84 self.margin_right = margin_right;
85 self
86 }
87
88 pub fn set_width(mut self, width: i32) -> Self {
90 self.width = width;
91 self
92 }
93
94 pub fn set_height(mut self, height: i32) -> Self {
96 self.height = height;
97 self
98 }
99
100 pub fn set_axis_top_band(mut self, scale: BandScale) -> Self {
102 self.x_axis_top = Some(Axis::new(
103 &scale,
104 AxisPosition::Top,
105 self.view_width(),
106 self.view_height(),
107 ));
108 self
109 }
110
111 pub fn set_axis_top_linear(mut self, scale: LinearScale) -> Self {
113 self.x_axis_top = Some(Axis::new(
114 &scale,
115 AxisPosition::Top,
116 self.view_width(),
117 self.view_height(),
118 ));
119 self
120 }
121
122 pub fn set_axis_bottom_band(mut self, scale: BandScale) -> Self {
124 self.x_axis_bottom = Some(Axis::new(
125 &scale,
126 AxisPosition::Bottom,
127 self.view_width(),
128 self.view_height(),
129 ));
130 self
131 }
132
133 pub fn set_axis_bottom_linear(mut self, scale: LinearScale) -> Self {
135 self.x_axis_bottom = Some(Axis::new(
136 &scale,
137 AxisPosition::Bottom,
138 self.view_width(),
139 self.view_height(),
140 ));
141 self
142 }
143
144 pub fn set_axis_left_band(mut self, scale: BandScale) -> Self {
146 self.y_axis_left = Some(Axis::new(
147 &scale,
148 AxisPosition::Left,
149 self.view_width(),
150 self.view_height(),
151 ));
152 self
153 }
154
155 pub fn set_axis_left_linear(mut self, scale: LinearScale) -> Self {
157 self.y_axis_left = Some(Axis::new(
158 &scale,
159 AxisPosition::Left,
160 self.view_width(),
161 self.view_height(),
162 ));
163 self
164 }
165
166 pub fn set_axis_right_band(mut self, scale: BandScale) -> Self {
168 self.y_axis_right = Some(Axis::new(
169 &scale,
170 AxisPosition::Right,
171 self.view_width(),
172 self.view_height(),
173 ));
174 self
175 }
176
177 pub fn set_axis_right_linear(mut self, scale: LinearScale) -> Self {
179 self.y_axis_right = Some(Axis::new(
180 &scale,
181 AxisPosition::Right,
182 self.view_width(),
183 self.view_height(),
184 ));
185 self
186 }
187
188 pub fn set_axis_top_label(mut self, label: &str) -> Self {
190 if let Some(ref mut axis) = self.x_axis_top {
191 axis.set_label(label);
192 }
193 self
194 }
195
196 pub fn set_axis_bottom_label(mut self, label: &str) -> Self {
198 if let Some(ref mut axis) = self.x_axis_bottom {
199 axis.set_label(label);
200 }
201 self
202 }
203
204 pub fn set_axis_left_label(mut self, label: &str) -> Self {
206 if let Some(ref mut axis) = self.y_axis_left {
207 axis.set_label(label);
208 }
209 self
210 }
211
212 pub fn set_axis_right_label(mut self, label: &str) -> Self {
214 if let Some(ref mut axis) = self.y_axis_right {
215 axis.set_label(label);
216 }
217 self
218 }
219
220 pub fn set_title(mut self, title: &str) -> Self {
222 self.title = title.to_string();
223 self
224 }
225
226 pub fn add_view(mut self, view: &'a dyn View) -> Self {
228 self.views.push(view);
229 self
230 }
231
232 pub fn set_views(mut self, views: Vec<&'a dyn View>) -> Self {
234 self.views = views;
235 self
236 }
237
238 pub fn to_svg(&self) -> svg::Document {
240 let mut res = svg::node::element::Group::new().set(CLASS_ATTR, CLASS_CHART);
241
242 if let Some(ref axis) = self.x_axis_top {
244 let mut axis_group = axis.to_svg();
245 axis_group.assign(
246 TRANSFORM_ATTR,
247 translate_x_y(self.margin_left, self.margin_top),
248 );
249 res.append(axis_group);
250 };
251 if let Some(ref axis) = self.x_axis_bottom {
252 let mut axis_group = axis.to_svg();
253 axis_group.assign(
254 TRANSFORM_ATTR,
255 translate_x_y(self.margin_left, self.height - self.margin_bottom),
256 );
257 res.append(axis_group);
258 };
259 if let Some(ref axis) = self.y_axis_left {
260 let mut axis_group = axis.to_svg();
261 axis_group.assign(
262 TRANSFORM_ATTR,
263 translate_x_y(self.margin_left, self.margin_top),
264 );
265 res.append(axis_group);
266 };
267 if let Some(ref axis) = self.y_axis_right {
268 let mut axis_group = axis.to_svg();
269 axis_group.assign(
270 TRANSFORM_ATTR,
271 translate_x_y(self.width - self.margin_right, self.margin_top),
272 );
273 res.append(axis_group);
274 };
275
276 let mut views_group = svg::node::element::Group::new()
278 .set(CLASS_ATTR, CLASS_VIEWS)
279 .set(
280 TRANSFORM_ATTR,
281 translate_x_y(self.margin_left, self.margin_top),
282 );
283 for view in self.views.iter() {
284 views_group.append(view.to_svg());
285 }
286 res.append(views_group);
287
288 if !self.title.is_empty() {
290 let title_group = svg::node::element::Group::new()
291 .set(CLASS_ATTR, CLASS_TITLE)
292 .set(
293 TRANSFORM_ATTR,
294 translate_x_y(self.width / 2, DEFAULT_TITLE_Y_TRANSFORM),
295 )
296 .add(
297 svg::node::element::Text::new()
298 .set(X_ATTR, START)
299 .set(Y_ATTR, START)
300 .set(DY_ATTR, DEFAULT_DY)
301 .set(FILL_ATTR, DEFAULT_FONT_COLOR)
302 .set(TEXT_ANCHOR_ATTR, TEXT_ANCHOR_MIDDLE)
303 .set(FONT_SIZE_ATTR, DEFAULT_TITLE_FONT_SIZE)
304 .set(FONT_FAMILY_ATTR, DEFAULT_FONT_FAMILY)
305 .add(svg::node::Text::new(&self.title)),
306 );
307 res.append(title_group);
308 }
309
310 svg::Document::new()
311 .set(WIDTH_ATTR, self.width)
312 .set(HEIGHT_ATTR, self.height)
313 .set(VIEW_BOX_ATTR, (START, START, self.width, self.height))
314 .add(res)
315 }
316
317 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
319 svg::save(path, &self.to_svg())?;
320
321 Ok(())
322 }
323}
324
325impl<'a> Default for Chart<'a> {
326 fn default() -> Self {
327 Self::new()
328 }
329}