1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! Definition of the Frame struct
use cairo::Context;
use palette::Rgba;
/// ## Frame
///
/// A frame defined by its boundaries.
#[derive(Clone, Debug)]
pub struct Frame {
left: f64,
right: f64,
top: f64,
bottom: f64,
is_left_updated: bool,
is_right_updated: bool,
is_bottom_updated: bool,
is_top_updated: bool,
display_border: bool,
color: Rgba,
thickness: f64
}
impl Frame {
/// Return a new, default frame.
pub fn new() -> Frame {
Frame {
left: 0.0,
right: 1.0,
bottom: 0.0,
top: 1.0,
is_left_updated: false,
is_right_updated: false,
is_bottom_updated: false,
is_top_updated: false,
display_border: false,
color: Rgba::new(0.0, 0.0, 0.0, 1.0),
thickness: 0.0,
}
}
/// Return a new frame from given coordinate values.
pub fn from_sides(left: f64, right: f64, bottom: f64, top: f64) -> Frame {
Frame {
left: left,
right: right,
bottom: bottom,
top: top,
is_left_updated: false,
is_right_updated: false,
is_bottom_updated: false,
is_top_updated: false,
display_border: false,
color: Rgba::new(0.0, 0.0, 0.0, 1.0),
thickness: 0.0,
}
}
/// Update an already created frame
pub fn set(&mut self, left: f64, right: f64, bottom: f64, top: f64) {
self.left = left;
self.right = right;
self.bottom = bottom;
self.top = top;
self.is_left_updated = true;
self.is_right_updated = true;
self.is_bottom_updated = true;
self.is_top_updated = true;
}
/// Update the left horisontal frame coordinate
pub fn set_left(&mut self, val: f64) {
self.left = val;
self.is_left_updated = true;
}
/// Update the right horisontal frame coordinate
pub fn set_right(&mut self, val: f64) {
self.right = val;
self.is_right_updated = true;
}
/// Update the bottom vertical frame coordinate
pub fn set_bottom(&mut self, val: f64) {
self.bottom = val;
self.is_bottom_updated = true;
}
/// Update the top vertical frame coordinate
pub fn set_top(&mut self, val: f64) {
self.top = val;
self.is_top_updated = true;
}
/// Is the left horisontal frame coordinate updated after the default set?
pub fn is_left_updated(&self) -> bool {
self.is_left_updated
}
/// Is the right horisontal frame coordinate updated after the default set?
pub fn is_right_updated(&self) -> bool {
self.is_right_updated
}
/// Is the bottom vertical frame coordinate updated after the default set?
pub fn is_bottom_updated(&self) -> bool {
self.is_bottom_updated
}
/// Is the top vertical frame coordinate updated after the default set?
pub fn is_top_updated(&self) -> bool {
self.is_top_updated
}
/// Return the left horisontal frame coordinate
pub fn left(&self) -> f64 {
self.left
}
/// Return the right horisontal frame coordinate
pub fn right(&self) -> f64 {
self.right
}
/// Return the bottom vertical frame coordinate
pub fn bottom(&self) -> f64 {
self.bottom
}
/// Return the top vertical frame coordinate
pub fn top(&self) -> f64 {
self.top
}
/// Return the hight of the frame
pub fn height(&self) -> f64 {
self.top - self.bottom
}
/// Return the width of the frame
pub fn width(&self) -> f64 {
self.right - self.left
}
/// Return the diagonal length of the frame
pub fn diag_len(&self) -> f64 {
let delta_x = self.right - self.left;
let delta_y = self.top - self.bottom;
(delta_x * delta_x + delta_y * delta_y).sqrt()
}
/// Whether or not to display the border (equivalent to thickness = 0.0)
pub fn display_border(&mut self, val: bool) {
self.display_border = val;
}
/// Set the color of the frame border
pub fn set_color(&mut self, color: Rgba) {
self.color = color;
}
/// Set the line width of the frame border
pub fn set_thickness(&mut self, val: f64) {
self.thickness = val;
}
/// Returns this frame mapped to a different `reference_frame`. This is useful when one wants
/// to map the local frame (`self`) to a global frame (`reference_frame`).
///
/// Each coordinate x on any side S (top, left, bottom, right) is then mapped as
///
/// ```text,no_run
/// x -> ((old_max - x)*new_min + (x - old_min)*new_max) / (old_max - old_min)
/// ```
///
/// Since the local frame is bounded by (0, 1) on each side, this simplifies to
///
/// ```text,no_run
/// x -> new_min + (new_max - new_min) * x
/// ```
pub fn relative_to(&self, reference: &Frame) -> Frame {
let new_left = reference.left() + reference.width() * self.left();
let new_right = reference.left() + reference.width() * self.right();
let new_bottom = reference.bottom() + reference.height() * self.bottom();
let new_top = reference.bottom() + reference.height() * self.top();
Frame::from_sides(new_left, new_right, new_bottom, new_top)
}
/// Scale the frame border thickness with factor
pub fn scale_size(&mut self, factor: f64) {
self.thickness *= factor;
}
/// Draw a border around the frame
pub fn draw(&self, cr: &Context, fig_rel_height: f64, fig_rel_width: f64) {
if self.display_border {
cr.set_source_rgba(self.color.red as f64, self.color.green as f64,
self.color.blue as f64, self.color.alpha as f64);
// Move to bottom left corner
cr.move_to(self.left, self.bottom);
// Bottom left to bottom right
cr.set_line_width(self.thickness * fig_rel_width);
cr.rel_line_to(self.width(), 0.0);
// Bottom right to top right
cr.set_line_width(self.thickness * fig_rel_height);
cr.rel_line_to(0.0, self.height());
// Top right to top left
cr.set_line_width(self.thickness * fig_rel_width);
cr.rel_line_to(-self.width(), 0.0);
// Top left to bottom left
cr.set_line_width(self.thickness * fig_rel_height);
cr.close_path();
//cr.rectangle(self.left(), self.bottom(), self.width(), self.height());
cr.stroke();
}
}
}