1pub fn distribute_grow(items: &mut [(f64, f64)], remaining: f64) {
9 let total_grow: f64 = items.iter().map(|(_, g)| g).sum();
11 if total_grow <= 0.0 || remaining <= 0.0 {
12 return;
13 }
14 for (width, grow) in items.iter_mut() {
15 *width += remaining * (*grow / total_grow);
16 }
17}
18
19#[derive(Debug, Clone)]
21pub struct WrapLine {
22 pub start: usize,
24 pub end: usize,
26}
27
28pub fn partition_into_lines(
31 base_widths: &[f64],
32 column_gap: f64,
33 available_width: f64,
34) -> Vec<WrapLine> {
35 if base_widths.is_empty() {
36 return vec![];
37 }
38
39 let mut lines = Vec::new();
40 let mut line_start = 0;
41 let mut line_width = 0.0;
42
43 for (i, &w) in base_widths.iter().enumerate() {
44 let needed = if i == line_start { w } else { column_gap + w };
45 if i > line_start && line_width + needed > available_width {
46 lines.push(WrapLine {
47 start: line_start,
48 end: i,
49 });
50 line_start = i;
51 line_width = w;
52 } else {
53 line_width += needed;
54 }
55 }
56
57 if line_start < base_widths.len() {
59 lines.push(WrapLine {
60 start: line_start,
61 end: base_widths.len(),
62 });
63 }
64
65 lines
66}
67
68pub fn distribute_shrink(items: &mut [(f64, f64)], overflow: f64) {
70 let total_shrink_weighted: f64 = items.iter().map(|(w, s)| w * s).sum();
72 if total_shrink_weighted <= 0.0 || overflow >= 0.0 {
73 return;
74 }
75 let overflow = overflow.abs();
76 for (width, shrink) in items.iter_mut() {
77 let factor = (*width * *shrink) / total_shrink_weighted;
78 *width -= overflow * factor;
79 *width = width.max(0.0);
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_grow_distribution() {
89 let mut items = vec![(100.0, 1.0), (100.0, 2.0)];
90 distribute_grow(&mut items, 90.0);
91 assert!((items[0].0 - 130.0).abs() < 0.01);
92 assert!((items[1].0 - 160.0).abs() < 0.01);
93 }
94
95 #[test]
96 fn test_shrink_distribution() {
97 let mut items = vec![(200.0, 1.0), (100.0, 1.0)];
98 distribute_shrink(&mut items, -60.0);
99 assert!(items[0].0 < 200.0);
101 assert!(items[1].0 < 100.0);
102 assert!((items[0].0 + items[1].0 - 240.0).abs() < 0.01);
103 }
104
105 #[test]
106 fn test_partition_single_line_fits() {
107 let widths = vec![100.0, 100.0, 100.0];
108 let lines = partition_into_lines(&widths, 10.0, 400.0);
109 assert_eq!(lines.len(), 1);
110 assert_eq!(lines[0].start, 0);
111 assert_eq!(lines[0].end, 3);
112 }
113
114 #[test]
115 fn test_partition_two_line_split() {
116 let widths = vec![100.0, 100.0, 100.0];
118 let lines = partition_into_lines(&widths, 10.0, 250.0);
119 assert_eq!(lines.len(), 2);
120 assert_eq!(lines[0].start, 0);
121 assert_eq!(lines[0].end, 2); assert_eq!(lines[1].start, 2);
123 assert_eq!(lines[1].end, 3);
124 }
125
126 #[test]
127 fn test_partition_oversized_item() {
128 let widths = vec![500.0];
130 let lines = partition_into_lines(&widths, 10.0, 200.0);
131 assert_eq!(lines.len(), 1);
132 assert_eq!(lines[0].start, 0);
133 assert_eq!(lines[0].end, 1);
134 }
135
136 #[test]
137 fn test_partition_empty_input() {
138 let lines = partition_into_lines(&[], 10.0, 200.0);
139 assert!(lines.is_empty());
140 }
141
142 #[test]
143 fn test_partition_exact_fit() {
144 let widths = vec![100.0, 100.0];
146 let lines = partition_into_lines(&widths, 10.0, 210.0);
147 assert_eq!(lines.len(), 1);
148 }
149}