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
use crate::GeomBatch;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Axis {
Horizontal,
Vertical,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Alignment {
Center,
Top,
Left,
}
#[derive(Debug)]
pub struct GeomBatchStack {
batches: Vec<GeomBatch>,
axis: Axis,
alignment: Alignment,
spacing: f64,
}
impl Default for GeomBatchStack {
fn default() -> Self {
GeomBatchStack {
batches: vec![],
axis: Axis::Horizontal,
alignment: Alignment::Center,
spacing: 0.0,
}
}
}
impl GeomBatchStack {
pub fn horizontal(batches: Vec<GeomBatch>) -> Self {
GeomBatchStack {
batches,
axis: Axis::Horizontal,
..Default::default()
}
}
pub fn vertical(batches: Vec<GeomBatch>) -> Self {
GeomBatchStack {
batches,
axis: Axis::Vertical,
..Default::default()
}
}
pub fn push(&mut self, geom_batch: GeomBatch) {
self.batches.push(geom_batch);
}
pub fn append(&mut self, geom_batches: &mut Vec<GeomBatch>) {
self.batches.append(geom_batches);
}
pub fn set_axis(&mut self, new_value: Axis) {
self.axis = new_value;
}
pub fn set_alignment(&mut self, new_value: Alignment) {
self.alignment = new_value;
}
pub fn set_spacing(&mut self, spacing: impl Into<f64>) -> &mut Self {
self.spacing = spacing.into();
self
}
pub fn batch(self) -> GeomBatch {
if self.batches.is_empty() {
return GeomBatch::new();
}
let max_bound_for_axis = self
.batches
.iter()
.map(GeomBatch::get_bounds)
.max_by(|b1, b2| match self.axis {
Axis::Vertical => b1.width().partial_cmp(&b2.width()).unwrap(),
Axis::Horizontal => b1.height().partial_cmp(&b2.height()).unwrap(),
})
.unwrap();
let mut stack_batch = GeomBatch::new();
let mut stack_offset = 0.0;
for mut batch in self.batches {
let bounds = batch.get_bounds();
let alignment_inset = match (self.alignment, self.axis) {
(Alignment::Center, Axis::Vertical) => {
(max_bound_for_axis.width() - bounds.width()) / 2.0
}
(Alignment::Center, Axis::Horizontal) => {
(max_bound_for_axis.height() - bounds.height()) / 2.0
}
(Alignment::Top, Axis::Vertical) => {
panic!("cannot top-align items in a vertical stack")
}
(Alignment::Top, Axis::Horizontal) => 0.0,
(Alignment::Left, Axis::Horizontal) => {
panic!("cannot left-align items in a horizontal stack")
}
(Alignment::Left, Axis::Vertical) => 0.0,
};
let (dx, dy) = match self.axis {
Axis::Vertical => (alignment_inset, stack_offset),
Axis::Horizontal => (stack_offset, alignment_inset),
};
batch = batch.translate(dx, dy);
stack_batch.append(batch);
stack_offset += self.spacing;
match self.axis {
Axis::Vertical => stack_offset += bounds.height(),
Axis::Horizontal => stack_offset += bounds.width(),
}
}
stack_batch
}
}