1
|
|
/*!
|
2
|
|
The `page` module provides structures for laying out and rendering multiple views.
|
3
|
|
*/
|
4
|
|
|
5
|
|
use std::ffi::OsStr;
|
6
|
|
use std::path::Path;
|
7
|
|
|
8
|
|
use svg;
|
9
|
|
use svg::Document;
|
10
|
|
use svg::Node;
|
11
|
|
|
12
|
|
use crate::errors::Result;
|
13
|
|
use crate::view::View;
|
14
|
|
|
15
|
|
use failure::ResultExt;
|
16
|
|
|
17
|
|
/**
|
18
|
|
A single page page laying out the views in a grid
|
19
|
|
*/
|
20
|
|
pub struct Page<'a> {
|
21
|
|
views: Vec<&'a dyn View>,
|
22
|
|
num_views: u32,
|
23
|
|
dimensions: (u32, u32),
|
24
|
|
}
|
25
|
|
|
26
|
|
impl<'a> Page<'a> {
|
27
|
|
/**
|
28
|
|
Creates an empty page container for plots to be added to
|
29
|
|
*/
|
30
|
2
|
pub fn empty() -> Self {
|
31
|
|
Page {
|
32
|
2
|
views: Vec::new(),
|
33
|
|
num_views: 0,
|
34
|
2
|
dimensions: (600, 400),
|
35
|
|
}
|
36
|
|
}
|
37
|
|
|
38
|
|
/**
|
39
|
|
Creates a plot containing a single view
|
40
|
|
*/
|
41
|
2
|
pub fn single(view: &'a dyn View) -> Self {
|
42
|
2
|
Page::empty().add_plot(view)
|
43
|
|
}
|
44
|
|
|
45
|
|
/// Set the dimensions of the plot.
|
46
|
0
|
pub fn dimensions(mut self, x: u32, y: u32) -> Self {
|
47
|
0
|
self.dimensions = (x, y);
|
48
|
0
|
self
|
49
|
|
}
|
50
|
|
|
51
|
|
/// Add a view to the plot
|
52
|
2
|
pub fn add_plot(mut self, view: &'a dyn View) -> Self {
|
53
|
2
|
self.views.push(view);
|
54
|
2
|
self.num_views += 1;
|
55
|
2
|
self
|
56
|
|
}
|
57
|
|
|
58
|
|
/**
|
59
|
|
Render the plot to an svg document
|
60
|
|
*/
|
61
|
2
|
pub fn to_svg(&self) -> Result<svg::Document> {
|
62
|
2
|
let (width, height) = self.dimensions;
|
63
|
2
|
let mut document = Document::new().set("viewBox", (0, 0, width, height));
|
64
|
|
|
65
|
2
|
let x_margin = 120; // should actually depend on y-axis label font size
|
66
|
2
|
let y_margin = 60;
|
67
|
2
|
let x_offset = 0.6 * f64::from(x_margin);
|
68
|
2
|
let y_offset = 0.6 * f64::from(y_margin);
|
69
|
|
|
70
|
|
// TODO put multiple views in correct places
|
71
|
2
|
for &view in &self.views {
|
72
|
2
|
let view_group = view
|
73
|
2
|
.to_svg(f64::from(width - x_margin), f64::from(height - y_margin))?
|
74
|
0
|
.set(
|
75
|
0
|
"transform",
|
76
|
2
|
format!("translate({}, {})", x_offset, f64::from(height) - y_offset),
|
77
|
|
);
|
78
|
2
|
document.append(view_group);
|
79
|
|
}
|
80
|
2
|
Ok(document)
|
81
|
|
}
|
82
|
|
|
83
|
|
/**
|
84
|
|
Render the plot to an `String`
|
85
|
|
*/
|
86
|
0
|
pub fn to_text(&self) -> Result<String> {
|
87
|
0
|
let (width, height) = self.dimensions;
|
88
|
|
// TODO compose multiple views into a page
|
89
|
0
|
let view = self.views[0];
|
90
|
0
|
view.to_text(width, height)
|
91
|
|
}
|
92
|
|
|
93
|
|
/**
|
94
|
|
Save the plot to a file.
|
95
|
|
|
96
|
|
The type of file will be based on the file extension.
|
97
|
|
*/
|
98
|
|
|
99
|
2
|
pub fn save<P>(&self, path: P) -> Result<()>
|
100
|
|
where
|
101
|
|
P: AsRef<Path>,
|
102
|
|
{
|
103
|
2
|
match path.as_ref().extension().and_then(OsStr::to_str) {
|
104
|
2
|
Some("svg") => svg::save(path, &self.to_svg()?)
|
105
|
|
.context("saving svg")
|
106
|
2
|
.map_err(From::from),
|
107
|
0
|
_ => Ok(()),
|
108
|
|
}
|
109
|
|
}
|
110
|
|
}
|