Skip to main content

kas_core/layout/
grid_solver.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Row / column solver
7
8use std::marker::PhantomData;
9
10use super::{AxisInfo, SizeRules};
11use super::{GridStorage, RowTemp, RulesSetter, RulesSolver};
12use crate::cast::{Cast, Conv};
13use crate::geom::{Coord, Offset, Rect, Size};
14
15/// Bound on [`GridSolver`] type parameters
16pub trait DefaultWithLen {
17    /// Construct with default elements of given length; panic on failure
18    fn default_with_len(len: usize) -> Self;
19}
20impl<T: Copy + Default, const N: usize> DefaultWithLen for [T; N] {
21    fn default_with_len(len: usize) -> Self {
22        assert_eq!(len, N);
23        [Default::default(); N]
24    }
25}
26impl<T: Clone + Default> DefaultWithLen for Vec<T> {
27    fn default_with_len(len: usize) -> Self {
28        let mut v = Vec::new();
29        v.resize_with(len, Default::default);
30        v
31    }
32}
33
34/// Grid dimensions
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
36pub struct GridDimensions {
37    /// The number of columns
38    ///
39    /// This is one greater than the maximum [`GridCellInfo::last_col`] value.
40    pub cols: u32,
41    /// The number of cells spanning more than one column
42    pub col_spans: u32,
43    /// The number of rows
44    ///
45    /// This is one greater than the maximum [`GridCellInfo::last_row`] value.
46    pub rows: u32,
47    /// The number of cells spanning more than one row
48    pub row_spans: u32,
49}
50
51/// Grid cell index and span information
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
53pub struct GridCellInfo {
54    /// Column index (first column when in a span)
55    pub col: u32,
56    /// Last column index of span (`last_col = col` without span)
57    pub last_col: u32,
58    /// Row index (first row when in a span)
59    pub row: u32,
60    /// One-past-last index of row span (`last_row = row` without span)
61    pub last_row: u32,
62}
63
64impl GridCellInfo {
65    /// Construct from row and column
66    pub fn new(col: u32, row: u32) -> Self {
67        GridCellInfo {
68            col,
69            last_col: col,
70            row,
71            last_row: row,
72        }
73    }
74}
75
76/// A [`RulesSolver`] for grids supporting cell-spans
77///
78/// This implementation relies on the caller to provide storage for solver data.
79pub struct GridSolver<CSR, RSR, S: GridStorage> {
80    axis: AxisInfo,
81    col_spans: CSR,
82    row_spans: RSR,
83    next_col_span: usize,
84    next_row_span: usize,
85    _s: PhantomData<S>,
86}
87
88impl<CSR: DefaultWithLen, RSR: DefaultWithLen, S: GridStorage> GridSolver<CSR, RSR, S> {
89    /// Construct.
90    ///
91    /// Argument order is consistent with other [`RulesSolver`]s.
92    ///
93    /// - `axis`: `AxisInfo` instance passed into `size_rules`
94    /// - `dim`: grid dimensions
95    /// - `storage`: reference to persistent storage
96    pub fn new(axis: AxisInfo, dim: GridDimensions, storage: &mut S) -> Self {
97        let col_spans = CSR::default_with_len(dim.col_spans.cast());
98        let row_spans = RSR::default_with_len(dim.row_spans.cast());
99
100        storage.set_dims(dim.cols.cast(), dim.rows.cast());
101
102        let mut solver = GridSolver {
103            axis,
104            col_spans,
105            row_spans,
106            next_col_span: 0,
107            next_row_span: 0,
108            _s: Default::default(),
109        };
110        solver.prepare(storage);
111        solver
112    }
113
114    fn prepare(&mut self, storage: &mut S) {
115        if self.axis.has_fixed {
116            if self.axis.is_vertical() {
117                let (widths, rules) = storage.widths_and_rules();
118                SizeRules::solve_widths(widths, rules, self.axis.other_axis);
119            } else {
120                let (heights, rules) = storage.heights_and_rules();
121                SizeRules::solve_widths(heights, rules, self.axis.other_axis);
122            }
123        }
124
125        if self.axis.is_horizontal() {
126            for n in 0..storage.width_rules().len() {
127                storage.width_rules()[n] = SizeRules::EMPTY;
128            }
129        } else {
130            for n in 0..storage.height_rules().len() {
131                storage.height_rules()[n] = SizeRules::EMPTY;
132            }
133        }
134    }
135}
136
137impl<CSR, RSR, S: GridStorage> RulesSolver for GridSolver<CSR, RSR, S>
138where
139    CSR: AsRef<[(SizeRules, u32, u32)]> + AsMut<[(SizeRules, u32, u32)]>,
140    RSR: AsRef<[(SizeRules, u32, u32)]> + AsMut<[(SizeRules, u32, u32)]>,
141{
142    type Storage = S;
143    type ChildInfo = GridCellInfo;
144
145    fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
146        &mut self,
147        storage: &mut Self::Storage,
148        info: Self::ChildInfo,
149        child_rules: CR,
150    ) {
151        if self.axis.has_fixed {
152            if self.axis.is_horizontal() {
153                self.axis.other_axis = ((info.row + 1)..=info.last_row)
154                    .fold(storage.heights()[usize::conv(info.row)], |h, i| {
155                        h + storage.heights()[usize::conv(i)]
156                    });
157            } else {
158                self.axis.other_axis = ((info.col + 1)..=info.last_col)
159                    .fold(storage.widths()[usize::conv(info.col)], |w, i| {
160                        w + storage.widths()[usize::conv(i)]
161                    });
162            }
163        }
164        let child_rules = child_rules(self.axis);
165        if self.axis.is_horizontal() {
166            if info.last_col > info.col {
167                let span = &mut self.col_spans.as_mut()[self.next_col_span];
168                span.0.max_with(child_rules);
169                span.1 = info.col;
170                span.2 = info.last_col + 1;
171                self.next_col_span += 1;
172            } else {
173                storage.width_rules()[usize::conv(info.col)].max_with(child_rules);
174            }
175        } else if info.last_row > info.row {
176            let span = &mut self.row_spans.as_mut()[self.next_row_span];
177            span.0.max_with(child_rules);
178            span.1 = info.row;
179            span.2 = info.last_row + 1;
180            self.next_row_span += 1;
181        } else {
182            storage.height_rules()[usize::conv(info.row)].max_with(child_rules);
183        };
184    }
185
186    fn finish(mut self, storage: &mut Self::Storage) -> SizeRules {
187        fn calculate(widths: &mut [SizeRules], spans: &mut [(SizeRules, u32, u32)]) -> SizeRules {
188            // spans: &mut [(rules, begin, end)]
189
190            // To avoid losing Stretch, we distribute this first
191            const BASE_WEIGHT: u32 = 100;
192            const SPAN_WEIGHT: u32 = 10;
193            let mut scores: Vec<u32> = widths
194                .iter()
195                .map(|w| w.stretch() as u32 * BASE_WEIGHT)
196                .collect();
197            for span in spans.iter() {
198                let w = span.0.stretch() as u32 * SPAN_WEIGHT;
199                for score in &mut scores[(usize::conv(span.1))..(usize::conv(span.2))] {
200                    *score += w;
201                }
202            }
203            for span in spans.iter() {
204                let range = (usize::conv(span.1))..(usize::conv(span.2));
205                span.0
206                    .distribute_stretch_over_by(&mut widths[range.clone()], &scores[range]);
207            }
208
209            // Sort spans to apply smallest first
210            spans.sort_by_key(|span| span.2.saturating_sub(span.1));
211
212            // We are left with non-overlapping spans.
213            // For each span, we ensure cell widths are sufficiently large.
214            for span in spans {
215                let rules = span.0;
216                let begin = usize::conv(span.1);
217                let end = usize::conv(span.2);
218                rules.distribute_span_over(&mut widths[begin..end]);
219            }
220
221            SizeRules::sum(widths)
222        }
223
224        if self.axis.is_horizontal() {
225            calculate(storage.width_rules(), self.col_spans.as_mut())
226        } else {
227            calculate(storage.height_rules(), self.row_spans.as_mut())
228        }
229    }
230}
231
232/// A [`RulesSetter`] for grids supporting cell-spans
233pub struct GridSetter<CT: RowTemp, RT: RowTemp, S: GridStorage> {
234    w_offsets: CT,
235    h_offsets: RT,
236    pos: Coord,
237    _s: PhantomData<S>,
238}
239
240impl<CT: RowTemp, RT: RowTemp, S: GridStorage> GridSetter<CT, RT, S> {
241    /// Construct
242    ///
243    /// Argument order is consistent with other [`RulesSetter`]s.
244    ///
245    /// -   `rect`: the [`Rect`] within which to position children
246    /// -   `dim`: grid dimensions
247    /// -   `storage`: access to the solver's storage
248    pub fn new(rect: Rect, dim: GridDimensions, storage: &mut S) -> Self {
249        let (cols, rows) = (dim.cols.cast(), dim.rows.cast());
250        let mut w_offsets = CT::default();
251        w_offsets.set_len(cols);
252        let mut h_offsets = RT::default();
253        h_offsets.set_len(rows);
254
255        storage.set_dims(cols, rows);
256
257        if cols > 0 {
258            let (widths, rules) = storage.widths_and_rules();
259            let target = rect.size.0;
260            SizeRules::solve_widths(widths, rules, target);
261
262            w_offsets.as_mut()[0] = 0;
263            for i in 1..w_offsets.as_mut().len() {
264                let i1 = i - 1;
265                let m1 = storage.width_rules()[i1].margins_i32().1;
266                let m0 = storage.width_rules()[i].margins_i32().0;
267                w_offsets.as_mut()[i] = w_offsets.as_mut()[i1] + storage.widths()[i1] + m1.max(m0);
268            }
269        }
270
271        if rows > 0 {
272            let (heights, rules) = storage.heights_and_rules();
273            let target = rect.size.1;
274            SizeRules::solve_widths(heights, rules, target);
275
276            h_offsets.as_mut()[0] = 0;
277            for i in 1..h_offsets.as_mut().len() {
278                let i1 = i - 1;
279                let m1 = storage.height_rules()[i1].margins_i32().1;
280                let m0 = storage.height_rules()[i].margins_i32().0;
281                h_offsets.as_mut()[i] = h_offsets.as_mut()[i1] + storage.heights()[i1] + m1.max(m0);
282            }
283        }
284
285        GridSetter {
286            w_offsets,
287            h_offsets,
288            pos: rect.pos,
289            _s: Default::default(),
290        }
291    }
292}
293
294impl<CT: RowTemp, RT: RowTemp, S: GridStorage> RulesSetter for GridSetter<CT, RT, S> {
295    type Storage = S;
296    type ChildInfo = GridCellInfo;
297
298    fn child_rect(&mut self, storage: &mut Self::Storage, info: Self::ChildInfo) -> Rect {
299        let x = self.w_offsets.as_mut()[usize::conv(info.col)];
300        let y = self.h_offsets.as_mut()[usize::conv(info.row)];
301        let pos = self.pos + Offset(x, y);
302
303        let i1 = usize::conv(info.last_col);
304        let w = storage.widths()[i1] + self.w_offsets.as_mut()[i1]
305            - self.w_offsets.as_mut()[usize::conv(info.col)];
306        let i1 = usize::conv(info.last_row);
307        let h = storage.heights()[i1] + self.h_offsets.as_mut()[i1]
308            - self.h_offsets.as_mut()[usize::conv(info.row)];
309        let size = Size(w, h);
310
311        Rect { pos, size }
312    }
313}