atagen

monolith

public
Star0
Watch0
a76842e12aca6c84e9870fd7ac94268136311b8d
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/layout/src/tree.rsRust728 lines23.2 KB
1
use monolith_planner::Rect;
2
use slotmap::{SlotMap, new_key_type};
3
use std::collections::HashMap;
4
use std::hash::Hash;
5
6
new_key_type! { pub struct NodeId; }
7
8
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9
pub enum Axis {
10
    Horizontal,
11
    Vertical,
12
}
13
14
impl Axis {
15
    pub fn other(self) -> Self {
16
        match self {
17
            Self::Horizontal => Self::Vertical,
18
            Self::Vertical => Self::Horizontal,
19
        }
20
    }
21
}
22
23
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24
pub enum Dir {
25
    Left,
26
    Right,
27
    Up,
28
    Down,
29
}
30
31
impl Dir {
32
    pub fn axis(self) -> Axis {
33
        match self {
34
            Self::Left | Self::Right => Axis::Horizontal,
35
            Self::Up | Self::Down => Axis::Vertical,
36
        }
37
    }
38
39
    pub fn forward(self) -> bool {
40
        matches!(self, Self::Right | Self::Down)
41
    }
42
}
43
44
const MIN_SHARE: f64 = 0.05;
45
46
#[derive(Clone, Debug)]
47
struct Child {
48
    node: NodeId,
49
    weight: f64,
50
}
51
52
#[derive(Clone, Debug)]
53
enum Kind<W> {
54
    Leaf(W),
55
    Container {
56
        axis: Axis,
57
        children: Vec<Child>,
58
        expanded: Option<NodeId>,
59
    },
60
}
61
62
#[derive(Clone, Debug)]
63
struct Node<W> {
64
    parent: Option<NodeId>,
65
    kind: Kind<W>,
66
}
67
68
/// One output's tiling tree. Every tiled window of every tag lives here, the
69
/// caller's visibility filter decides which ones take space.
70
///
71
/// Children run on the axis opposite their parent. Containers hold at least
72
/// two children, hidden ones included, and a container left with one child
73
/// is replaced by it.
74
#[derive(Clone, Debug)]
75
pub struct Tree<W> {
76
    nodes: SlotMap<NodeId, Node<W>>,
77
    root: Option<NodeId>,
78
    leaves: HashMap<W, NodeId>,
79
    stack: Axis,
80
}
81
82
impl<W: Copy + Eq + Hash> Default for Tree<W> {
83
    fn default() -> Self {
84
        Self {
85
            nodes: SlotMap::with_key(),
86
            root: None,
87
            leaves: HashMap::new(),
88
            stack: Axis::Vertical,
89
        }
90
    }
91
}
92
93
impl<W: Copy + Eq + Hash> Tree<W> {
94
    /// The axis tag rows stack along.
95
    pub fn stack_axis(&self) -> Axis {
96
        self.stack
97
    }
98
99
    pub fn contains(&self, w: W) -> bool {
100
        self.leaves.contains_key(&w)
101
    }
102
103
    pub fn is_empty(&self) -> bool {
104
        self.root.is_none()
105
    }
106
107
    pub fn windows(&self) -> impl Iterator<Item = W> + '_ {
108
        self.leaves.keys().copied()
109
    }
110
111
    /// Inserts `w` as the next sibling of `anchor`, or as a new row if
112
    /// `anchor` is not in the tree.
113
    pub fn insert_after(&mut self, w: W, anchor: W) {
114
        let Some(&a) = self.leaves.get(&anchor) else {
115
            return self.insert_row(w);
116
        };
117
        let n = self.new_leaf(w);
118
        match self.position(a) {
119
            Some((p, i)) if Some(p) == self.root && self.axis(p) == Some(self.stack) => {
120
                let weight = self.unlink(p, i).weight;
121
                let c = self.new_container(self.stack.other());
122
                self.attach(c, 0, a, 1.0);
123
                self.attach(c, 1, n, 1.0);
124
                self.attach(p, i, c, weight);
125
            }
126
            Some((p, i)) => {
127
                let weight = self.mean_weight(p);
128
                self.attach(p, i + 1, n, weight);
129
            }
130
            None => {
131
                let c = self.new_container(self.stack.other());
132
                self.attach(c, 0, a, 1.0);
133
                self.attach(c, 1, n, 1.0);
134
                self.root = Some(c);
135
            }
136
        }
137
    }
138
139
    /// Appends `w` as a row of its own along the stacking axis.
140
    pub fn insert_row(&mut self, w: W) {
141
        let n = self.new_leaf(w);
142
        match self.root {
143
            None => self.root = Some(n),
144
            Some(r) if self.axis(r) == Some(self.stack) => {
145
                let weight = self.mean_weight(r);
146
                let end = self.children(r).len();
147
                self.attach(r, end, n, weight);
148
            }
149
            Some(r) => {
150
                let c = self.new_container(self.stack);
151
                self.attach(c, 0, r, 1.0);
152
                self.attach(c, 1, n, 1.0);
153
                self.root = Some(c);
154
            }
155
        }
156
    }
157
158
    pub fn remove(&mut self, w: W) -> bool {
159
        let Some(n) = self.leaves.remove(&w) else {
160
            return false;
161
        };
162
        self.detach(n);
163
        self.nodes.remove(n);
164
        true
165
    }
166
167
    pub fn retain(&mut self, mut keep: impl FnMut(W) -> bool) {
168
        let gone: Vec<W> = self.windows().filter(|&w| !keep(w)).collect();
169
        for w in gone {
170
            self.remove(w);
171
        }
172
    }
173
174
    /// Moves a window one step in `dir`. Leaves swap slots with the next
175
    /// visible sibling, enter a sibling container, or step out beside their
176
    /// container. Hidden windows never move.
177
    pub fn move_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
178
        let Some(&n) = self.leaves.get(&w) else {
179
            return false;
180
        };
181
        self.move_node(n, dir, &vis, true)
182
    }
183
184
    /// Moves the container holding `w` as a rigid block. A window sitting
185
    /// directly in the root is its own block.
186
    pub fn move_container(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
187
        let Some(&n) = self.leaves.get(&w) else {
188
            return false;
189
        };
190
        let block = self.nodes[n]
191
            .parent
192
            .filter(|&p| Some(p) != self.root)
193
            .unwrap_or(n);
194
        self.move_node(block, dir, &vis, false)
195
    }
196
197
    /// Moves a window to the end of the next visible lane in `dir`, stepping
198
    /// up a level when its container has none that way. A lone window there
199
    /// becomes a stack with it.
200
    pub fn merge_window(&mut self, w: W, dir: Dir, vis: impl Fn(W) -> bool) -> bool {
201
        let Some(&n) = self.leaves.get(&w) else {
202
            return false;
203
        };
204
        let mut cur = n;
205
        while let Some((p, i)) = self.position(cur) {
206
            if self.axis(p) == Some(dir.axis())
207
                && let Some(j) = self.next_shown(p, i, dir, &vis)
208
            {
209
                self.join_lane(n, p, j);
210
                return true;
211
            }
212
            cur = p;
213
        }
214
        false
215
    }
216
217
    /// Whether every container above `w` is expanded towards it.
218
    pub fn maximized(&self, w: W) -> bool {
219
        let Some(&leaf) = self.leaves.get(&w) else {
220
            return false;
221
        };
222
        let mut cur = leaf;
223
        let mut any = false;
224
        while let Some(p) = self.nodes[cur].parent {
225
            if self.expanded(p) != Some(cur) {
226
                return false;
227
            }
228
            any = true;
229
            cur = p;
230
        }
231
        any
232
    }
233
234
    /// Transposes the whole tree.
235
    pub fn flip(&mut self) {
236
        self.stack = self.stack.other();
237
        for node in self.nodes.values_mut() {
238
            if let Kind::Container { axis, .. } = &mut node.kind {
239
                *axis = axis.other();
240
            }
241
        }
242
    }
243
244
    /// Toggles expansion for `count` levels of `w`'s ancestry, starting
245
    /// `skip` levels up. Level 0 expands the window inside its container.
246
    pub fn toggle_expand(&mut self, w: W, skip: usize, count: usize) -> bool {
247
        let Some(&leaf) = self.leaves.get(&w) else {
248
            return false;
249
        };
250
        let mut path = Vec::new();
251
        let mut cur = leaf;
252
        while let Some(p) = self.nodes[cur].parent {
253
            path.push((p, cur));
254
            cur = p;
255
        }
256
        let path: Vec<_> = path.into_iter().skip(skip).take(count).collect();
257
        if path.is_empty() {
258
            return false;
259
        }
260
        let all = path.iter().all(|&(p, c)| self.expanded(p) == Some(c));
261
        for (p, c) in path {
262
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind {
263
                *expanded = (!all).then_some(c);
264
            }
265
        }
266
        true
267
    }
268
269
    /// Grows `w` by `delta`, a fraction of the visible length of the nearest
270
    /// container along `axis` where it has a visible sibling. Hidden siblings
271
    /// keep their weights, and with them their share.
272
    pub fn resize(&mut self, w: W, axis: Axis, delta: f64, vis: impl Fn(W) -> bool) -> bool {
273
        let Some(&leaf) = self.leaves.get(&w) else {
274
            return false;
275
        };
276
        let mut cur = leaf;
277
        while let Some((p, i)) = self.position(cur) {
278
            if self.axis(p) == Some(axis) {
279
                let shown: Vec<usize> = (0..self.children(p).len())
280
                    .filter(|&j| self.visible(self.children(p)[j].node, &vis))
281
                    .collect();
282
                if shown.len() >= 2 {
283
                    let children = self.children_mut(p);
284
                    let sum: f64 = shown.iter().map(|&j| children[j].weight).sum();
285
                    let old = children[i].weight;
286
                    let max = 1.0 - MIN_SHARE * (shown.len() - 1) as f64;
287
                    let new = (old / sum + delta).clamp(MIN_SHARE, max) * sum;
288
                    let scale = (sum - new) / (sum - old);
289
                    for j in shown {
290
                        children[j].weight = if j == i {
291
                            new
292
                        } else {
293
                            children[j].weight * scale
294
                        };
295
                    }
296
                    return true;
297
                }
298
            }
299
            cur = p;
300
        }
301
        false
302
    }
303
304
    /// Lays the tree out in `area`. Hidden windows come back as zero-size
305
    /// rects on the boundary where they would reappear.
306
    pub fn arrange(&self, area: Rect, gap: f64, vis: impl Fn(W) -> bool) -> Vec<(W, Rect)> {
307
        let mut out = Vec::with_capacity(self.leaves.len());
308
        if let Some(r) = self.root {
309
            let area = if self.visible(r, &vis) {
310
                area
311
            } else {
312
                Rect::new(area.x + area.w / 2.0, area.y, 0.0, area.h)
313
            };
314
            self.place(r, area, gap, &vis, &mut out);
315
        }
316
        out
317
    }
318
319
    fn place(
320
        &self,
321
        n: NodeId,
322
        r: Rect,
323
        gap: f64,
324
        vis: &impl Fn(W) -> bool,
325
        out: &mut Vec<(W, Rect)>,
326
    ) {
327
        let (axis, children, expanded) = match &self.nodes[n].kind {
328
            Kind::Leaf(w) => return out.push((*w, r)),
329
            Kind::Container {
330
                axis,
331
                children,
332
                expanded,
333
            } => (*axis, children, *expanded),
334
        };
335
        let shown: Vec<bool> = children.iter().map(|c| self.visible(c.node, vis)).collect();
336
        let (start, len) = span(r, axis);
337
        if let Some(e) = expanded.and_then(|e| children.iter().position(|c| c.node == e))
338
            && shown[e]
339
        {
340
            for (i, c) in children.iter().enumerate() {
341
                let sub = match i.cmp(&e) {
342
                    std::cmp::Ordering::Equal => r,
343
                    std::cmp::Ordering::Less => with_span(r, axis, start, 0.0),
344
                    std::cmp::Ordering::Greater => with_span(r, axis, start + len, 0.0),
345
                };
346
                self.place(c.node, sub, gap, vis, out);
347
            }
348
            return;
349
        }
350
        let count = shown.iter().filter(|s| **s).count();
351
        let total: f64 = children
352
            .iter()
353
            .zip(&shown)
354
            .filter(|(_, s)| **s)
355
            .map(|(c, _)| c.weight)
356
            .sum();
357
        let gaps = count.saturating_sub(1) as f64;
358
        let gap = if gaps > 0.0 { gap.min(len / gaps) } else { gap };
359
        let avail = len - gap * gaps;
360
        let last = shown.iter().rposition(|s| *s);
361
        let mut pos = start;
362
        let mut seen = false;
363
        for (i, c) in children.iter().enumerate() {
364
            if shown[i] {
365
                if seen {
366
                    pos += gap;
367
                }
368
                let l = avail * c.weight / total;
369
                self.place(c.node, with_span(r, axis, pos, l), gap, vis, out);
370
                pos += l;
371
                seen = true;
372
            } else {
373
                let between = seen && last.is_some_and(|l| l > i);
374
                let at = if between { pos + gap / 2.0 } else { pos };
375
                self.place(c.node, with_span(r, axis, at, 0.0), gap, vis, out);
376
            }
377
        }
378
    }
379
380
    fn move_node(&mut self, n: NodeId, dir: Dir, vis: &impl Fn(W) -> bool, enter: bool) -> bool {
381
        let mut cur = n;
382
        while let Some((p, i)) = self.position(cur) {
383
            if self.axis(p) == Some(dir.axis()) {
384
                let hidden_lane = self
385
                    .adjacent(p, i, dir)
386
                    .filter(|&j| enter && !self.visible(self.children(p)[j].node, vis));
387
                if cur != n {
388
                    if let Some(j) = hidden_lane {
389
                        self.join_lane(n, p, j);
390
                    } else {
391
                        let at = if dir.forward() { i + 1 } else { i };
392
                        self.relocate(n, p, at);
393
                    }
394
                    return true;
395
                }
396
                if let Some(j) = self.next_shown(p, i, dir, vis) {
397
                    let sib = self.children(p)[j].node;
398
                    if enter && self.axis(sib).is_some() {
399
                        let end = self.children(sib).len();
400
                        self.relocate(n, sib, end);
401
                    } else {
402
                        let children = self.children_mut(p);
403
                        children[i].node = sib;
404
                        children[j].node = n;
405
                    }
406
                    return true;
407
                }
408
                if let Some(j) = hidden_lane {
409
                    self.join_lane(n, p, j);
410
                    return true;
411
                }
412
            }
413
            cur = p;
414
        }
415
        if cur == n || self.axis(cur) == Some(dir.axis()) {
416
            return false;
417
        }
418
        let (old, i) = self.position(n).expect("n is below the root");
419
        self.unlink(old, i);
420
        self.normalize(old);
421
        let r = self.root.expect("the rest of the tree survives");
422
        if self.axis(r) == Some(dir.axis()) {
423
            let weight = self.mean_weight(r);
424
            let at = if dir.forward() {
425
                self.children(r).len()
426
            } else {
427
                0
428
            };
429
            self.attach(r, at, n, weight);
430
        } else {
431
            let c = self.new_container(dir.axis());
432
            self.attach(c, 0, r, 1.0);
433
            let at = if dir.forward() {
434
                self.children(c).len()
435
            } else {
436
                0
437
            };
438
            self.attach(c, at, n, 1.0);
439
            self.root = Some(c);
440
        }
441
        true
442
    }
443
444
    fn next_shown(&self, p: NodeId, i: usize, dir: Dir, vis: &impl Fn(W) -> bool) -> Option<usize> {
445
        let children = self.children(p);
446
        if dir.forward() {
447
            (i + 1..children.len()).find(|&j| self.visible(children[j].node, vis))
448
        } else {
449
            (0..i).rev().find(|&j| self.visible(children[j].node, vis))
450
        }
451
    }
452
453
    fn adjacent(&self, p: NodeId, i: usize, dir: Dir) -> Option<usize> {
454
        let j = if dir.forward() {
455
            i + 1
456
        } else {
457
            i.checked_sub(1)?
458
        };
459
        (j < self.children(p).len()).then_some(j)
460
    }
461
462
    /// Moves `n` to the end of the lane at `p`'s child `j`. A lone window
463
    /// there becomes a lane of its own, perpendicular to `p`.
464
    fn join_lane(&mut self, n: NodeId, p: NodeId, j: usize) {
465
        let lane = self.children(p)[j].node;
466
        let lane = if self.axis(lane).is_some() {
467
            lane
468
        } else {
469
            let c = self.new_container(self.axis(p).expect("p is a container").other());
470
            self.children_mut(p)[j].node = c;
471
            self.nodes[c].parent = Some(p);
472
            if let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
473
                && *expanded == Some(lane)
474
            {
475
                *expanded = Some(c);
476
            }
477
            self.attach(c, 0, lane, 1.0);
478
            c
479
        };
480
        let end = self.children(lane).len();
481
        self.relocate(n, lane, end);
482
    }
483
484
    fn relocate(&mut self, n: NodeId, p: NodeId, at: usize) {
485
        let (old, i) = self.position(n).expect("relocated nodes have a parent");
486
        let weight = self.mean_weight(p);
487
        let at = if old == p && i < at { at - 1 } else { at };
488
        self.unlink(old, i);
489
        self.attach(p, at, n, weight);
490
        self.normalize(old);
491
    }
492
493
    /// Places `node` among `p`'s children. A container running along `p`'s
494
    /// axis is dissolved into `p` so axes keep alternating.
495
    fn attach(&mut self, p: NodeId, at: usize, node: NodeId, weight: f64) {
496
        if self.axis(node) == self.axis(p) {
497
            let Some(Node {
498
                kind: Kind::Container { children, .. },
499
                ..
500
            }) = self.nodes.remove(node)
501
            else {
502
                unreachable!("only containers have an axis");
503
            };
504
            let total: f64 = children.iter().map(|c| c.weight).sum();
505
            for (k, c) in children.into_iter().enumerate() {
506
                self.nodes[c.node].parent = Some(p);
507
                let weight = weight * c.weight / total;
508
                self.children_mut(p).insert(
509
                    at + k,
510
                    Child {
511
                        node: c.node,
512
                        weight,
513
                    },
514
                );
515
            }
516
        } else {
517
            self.nodes[node].parent = Some(p);
518
            self.children_mut(p).insert(at, Child { node, weight });
519
        }
520
    }
521
522
    fn unlink(&mut self, p: NodeId, i: usize) -> Child {
523
        let Kind::Container {
524
            children, expanded, ..
525
        } = &mut self.nodes[p].kind
526
        else {
527
            unreachable!("parents are containers");
528
        };
529
        let child = children.remove(i);
530
        if *expanded == Some(child.node) {
531
            *expanded = None;
532
        }
533
        self.nodes[child.node].parent = None;
534
        child
535
    }
536
537
    fn detach(&mut self, n: NodeId) {
538
        match self.position(n) {
539
            Some((p, i)) => {
540
                self.unlink(p, i);
541
                self.normalize(p);
542
            }
543
            None => self.root = None,
544
        }
545
    }
546
547
    /// Repairs a container that lost a child.
548
    fn normalize(&mut self, c: NodeId) {
549
        if self.children(c).len() >= 2 {
550
            return;
551
        }
552
        let pos = self.position(c);
553
        let was_expanded = pos.is_some_and(|(p, _)| self.expanded(p) == Some(c));
554
        let Some(Node {
555
            kind: Kind::Container { children, .. },
556
            ..
557
        }) = self.nodes.remove(c)
558
        else {
559
            unreachable!("only containers are normalized");
560
        };
561
        let only = children.into_iter().next();
562
        match (only, pos) {
563
            (None, Some((p, i))) => {
564
                self.unlink_dead(p, i);
565
                self.normalize(p);
566
            }
567
            (None, None) => self.root = None,
568
            (Some(only), Some((p, i))) => {
569
                let weight = self.unlink_dead(p, i).weight;
570
                let leaf_like = self.axis(only.node) != self.axis(p);
571
                self.attach(p, i, only.node, weight);
572
                if was_expanded
573
                    && leaf_like
574
                    && let Kind::Container { expanded, .. } = &mut self.nodes[p].kind
575
                {
576
                    *expanded = Some(only.node);
577
                }
578
            }
579
            (Some(only), None) => {
580
                self.nodes[only.node].parent = None;
581
                self.root = Some(only.node);
582
            }
583
        }
584
    }
585
586
    /// Like `unlink`, for a child whose node is already gone.
587
    fn unlink_dead(&mut self, p: NodeId, i: usize) -> Child {
588
        let Kind::Container {
589
            children, expanded, ..
590
        } = &mut self.nodes[p].kind
591
        else {
592
            unreachable!("parents are containers");
593
        };
594
        let child = children.remove(i);
595
        if *expanded == Some(child.node) {
596
            *expanded = None;
597
        }
598
        child
599
    }
600
601
    fn new_leaf(&mut self, w: W) -> NodeId {
602
        let n = self.nodes.insert(Node {
603
            parent: None,
604
            kind: Kind::Leaf(w),
605
        });
606
        self.leaves.insert(w, n);
607
        n
608
    }
609
610
    fn new_container(&mut self, axis: Axis) -> NodeId {
611
        self.nodes.insert(Node {
612
            parent: None,
613
            kind: Kind::Container {
614
                axis,
615
                children: Vec::new(),
616
                expanded: None,
617
            },
618
        })
619
    }
620
621
    fn position(&self, n: NodeId) -> Option<(NodeId, usize)> {
622
        let p = self.nodes[n].parent?;
623
        let i = self.children(p).iter().position(|c| c.node == n)?;
624
        Some((p, i))
625
    }
626
627
    fn axis(&self, n: NodeId) -> Option<Axis> {
628
        match &self.nodes[n].kind {
629
            Kind::Container { axis, .. } => Some(*axis),
630
            Kind::Leaf(_) => None,
631
        }
632
    }
633
634
    fn expanded(&self, n: NodeId) -> Option<NodeId> {
635
        match &self.nodes[n].kind {
636
            Kind::Container { expanded, .. } => *expanded,
637
            Kind::Leaf(_) => None,
638
        }
639
    }
640
641
    fn children(&self, n: NodeId) -> &[Child] {
642
        match &self.nodes[n].kind {
643
            Kind::Container { children, .. } => children,
644
            Kind::Leaf(_) => &[],
645
        }
646
    }
647
648
    fn children_mut(&mut self, n: NodeId) -> &mut Vec<Child> {
649
        match &mut self.nodes[n].kind {
650
            Kind::Container { children, .. } => children,
651
            Kind::Leaf(_) => unreachable!("leaves have no children"),
652
        }
653
    }
654
655
    fn mean_weight(&self, p: NodeId) -> f64 {
656
        let children = self.children(p);
657
        if children.is_empty() {
658
            1.0
659
        } else {
660
            children.iter().map(|c| c.weight).sum::<f64>() / children.len() as f64
661
        }
662
    }
663
664
    fn visible(&self, n: NodeId, vis: &impl Fn(W) -> bool) -> bool {
665
        match &self.nodes[n].kind {
666
            Kind::Leaf(w) => vis(*w),
667
            Kind::Container { children, .. } => children.iter().any(|c| self.visible(c.node, vis)),
668
        }
669
    }
670
671
    #[cfg(test)]
672
    pub(crate) fn check(&self) {
673
        let reachable = match self.root {
674
            Some(r) => {
675
                assert!(self.nodes[r].parent.is_none());
676
                self.check_node(r)
677
            }
678
            None => 0,
679
        };
680
        assert_eq!(reachable, self.nodes.len(), "orphaned nodes");
681
        for (&w, &n) in &self.leaves {
682
            assert!(matches!(self.nodes[n].kind, Kind::Leaf(x) if x == w));
683
        }
684
    }
685
686
    #[cfg(test)]
687
    fn check_node(&self, n: NodeId) -> usize {
688
        match &self.nodes[n].kind {
689
            Kind::Leaf(_) => 1,
690
            Kind::Container {
691
                axis,
692
                children,
693
                expanded,
694
            } => {
695
                assert!(
696
                    children.len() >= 2,
697
                    "container with {} children",
698
                    children.len()
699
                );
700
                if let Some(e) = expanded {
701
                    assert!(children.iter().any(|c| c.node == *e));
702
                }
703
                let mut count = 1;
704
                for c in children {
705
                    assert!(c.weight > 0.0 && c.weight.is_finite());
706
                    assert_eq!(self.nodes[c.node].parent, Some(n));
707
                    assert_ne!(self.axis(c.node), Some(*axis), "axes must alternate");
708
                    count += self.check_node(c.node);
709
                }
710
                count
711
            }
712
        }
713
    }
714
}
715
716
fn span(r: Rect, axis: Axis) -> (f64, f64) {
717
    match axis {
718
        Axis::Horizontal => (r.x, r.w),
719
        Axis::Vertical => (r.y, r.h),
720
    }
721
}
722
723
fn with_span(r: Rect, axis: Axis, start: f64, len: f64) -> Rect {
724
    match axis {
725
        Axis::Horizontal => Rect::new(start, r.y, len, r.h),
726
        Axis::Vertical => Rect::new(r.x, start, r.w, len),
727
    }
728
}