Skip to main content

devpage_design/display/
run.rs

1//! A run's children in the index: each child a space of its own — its
2//! chunks in its own coordinates under an R-tree of its own — placed along
3//! the run by the prefix sums of the sizes before it. The run is one entry in
4//! the space it sits in; a query inside it finds the first child that
5//! reaches the rectangle by a search of the sums, widened by the furthest any
6//! child paints past its box, and walks on until a child starts past it. A
7//! child that changes size moves no other child's entry: their places are
8//! the sums.
9
10use std::collections::{BTreeMap, HashMap};
11
12use kurbo::{Rect as KRect, Vec2};
13
14use crate::prefix_sums::{Item, Summary, Sums};
15use crate::tree::OrderKey;
16
17use super::*;
18
19/// A run, by its slot.
20pub type RunId = u32;
21
22/// Where a chunk's coordinates are: the design's own, or a run's child's,
23/// whose origin is the child's box's top-left.
24#[derive(Clone, Debug, PartialEq, Eq, Hash)]
25pub enum Space {
26    Root,
27    Item(RunId, OrderKey),
28}
29
30/// A run's children placed along it, and what they paint.
31pub struct Run {
32    /// The space its own node is in.
33    pub space: Space,
34    /// Its node's unrounded top-left and its rounded one, in its space.
35    pub raw: Vec2,
36    pub at: Vec2,
37    /// Where its space's origin was in the design's coordinates when it was
38    /// last placed: its children are placed in single precision there, as
39    /// taffy places them, so they round where a whole tree's do.
40    pub origin: Vec2,
41    /// Whether its children go along x; else along y.
42    pub row: bool,
43    /// A frame with no layout: each child at a place of its own, found by
44    /// an R-tree of their boxes rather than by the sums.
45    pub free: bool,
46    pub(super) boxes: rstar::RTree<Boxed>,
47    pub(super) free_reach: [Counted; 4],
48    pub(super) free_cover: [Counted; 4],
49    /// Where its first child starts along the run, from its node's unrounded
50    /// top-left: the padding and any `justify` offset.
51    pub start: f64,
52    /// Where its children's line starts across, from the same: the padding.
53    pub across: f64,
54    /// How far apart its children are: its gap, and any room
55    /// `"space-between"` spreads between them — one number for the run, so
56    /// a change of it moves no child's entry.
57    pub gap: f64,
58    /// The breadth of its children's line.
59    pub line: f64,
60    /// What cuts it in its space: its clipping frames' boxes.
61    pub cut: Option<KRect>,
62    pub(super) items: Sums<OrderKey, Placed>,
63    pub(super) spaces: HashMap<OrderKey, SpaceIndex>,
64    /// The furthest any child reaches past its box along the run, and each
65    /// child's reach and cover across, from the line's start: counted sets.
66    pub(super) overhang: Counted,
67    pub(super) reach_across: (Counted, Counted),
68    pub(super) cover_across: (Counted, Counted),
69    /// Its entry in its space's index, and what it adds to its space's cover.
70    pub(super) entry: Option<KRect>,
71    pub(super) cover: Option<KRect>,
72}
73
74/// One space's index: its chunks and runs by their bounds in its own
75/// coordinates, and the extent of what its chunks cover.
76#[derive(Default)]
77pub struct SpaceIndex {
78    pub(super) index: rstar::RTree<Entry>,
79    pub(super) covers: Extent,
80}
81
82/// A child of a run as the run's sums see it: its size along the run, where
83/// it sits across, and what it reaches and covers along the run from its
84/// unrounded start.
85#[derive(Clone, Debug, PartialEq)]
86pub struct Placed {
87    pub size: f64,
88    /// In a free run, its unrounded top-left from the run's node's.
89    pub at: Option<Vec2>,
90    /// In a free run, what it reaches and covers in the run's space.
91    pub(super) boxed: Option<KRect>,
92    pub(super) cover_box: Option<KRect>,
93    /// Its unrounded place across, from the line's start.
94    pub across: f64,
95    /// Its rounded top-left less its unrounded one: fixed while what the
96    /// run places it at keeps its fraction of a pixel.
97    pub delta: Vec2,
98    pub(super) reach: Option<(f64, f64)>,
99    pub(super) covered: Option<(f64, f64)>,
100    /// What it put in the run's counted sets, to take out again.
101    pub(super) counted: Counts,
102}
103
104/// A child's entries in its run's counted sets.
105#[derive(Clone, Debug, PartialEq, Default)]
106pub struct Counts {
107    overhang: Option<f64>,
108    reach: Option<(f64, f64)>,
109    cover: Option<(f64, f64)>,
110}
111
112impl Placed {
113    /// A free run's child, at `at` from the run's node's top-left.
114    pub fn at(at: Vec2, delta: Vec2) -> Placed {
115        Placed {
116            at: Some(at),
117            ..Placed::new(0.0, 0.0, delta)
118        }
119    }
120
121    pub fn new(size: f64, across: f64, delta: Vec2) -> Placed {
122        Placed {
123            at: None,
124            boxed: None,
125            cover_box: None,
126            size,
127            across,
128            delta,
129            reach: None,
130            covered: None,
131            counted: Counts::default(),
132        }
133    }
134}
135
136/// A stretch of a run's children: how many, and the sum of their sizes —
137/// how far they advance less the gaps, which are the run's.
138#[derive(Clone, Debug, Default)]
139pub struct Span {
140    pub n: usize,
141    pub length: f64,
142}
143
144impl Summary for Span {
145    fn then(&self, next: &Self) -> Self {
146        Span {
147            n: self.n + next.n,
148            length: self.length + next.length,
149        }
150    }
151}
152
153impl Item for Placed {
154    type Sum = Span;
155    fn summary(&self) -> Span {
156        Span {
157            n: 1,
158            length: self.size,
159        }
160    }
161}
162
163/// A free run's child's entry in the run's R-tree: what it reaches.
164#[derive(Clone, Debug, PartialEq)]
165pub(super) struct Boxed {
166    pub(super) key: OrderKey,
167    pub(super) bounds: KRect,
168}
169
170impl rstar::RTreeObject for Boxed {
171    type Envelope = rstar::AABB<[f64; 2]>;
172    fn envelope(&self) -> Self::Envelope {
173        rstar::AABB::from_corners(
174            [self.bounds.x0, self.bounds.y0],
175            [self.bounds.x1, self.bounds.y1],
176        )
177    }
178}
179
180/// A multiset of numbers that knows its least and greatest in log time.
181#[derive(Default)]
182pub struct Counted(BTreeMap<i64, usize>);
183
184impl Counted {
185    pub(super) fn add(&mut self, v: f64) {
186        *self.0.entry(key(v)).or_default() += 1;
187    }
188    pub(super) fn remove(&mut self, v: f64) {
189        let k = key(v);
190        if let Some(n) = self.0.get_mut(&k) {
191            *n -= 1;
192            if *n == 0 {
193                self.0.remove(&k);
194            }
195        }
196    }
197    pub(super) fn least(&self) -> Option<f64> {
198        self.0.keys().next().map(|k| *k as f64 / 1e6)
199    }
200    pub(super) fn greatest(&self) -> Option<f64> {
201        self.0.keys().next_back().map(|k| *k as f64 / 1e6)
202    }
203}
204
205/// A rectangle's span along a run and across it.
206fn spans(row: bool, r: KRect) -> ((f64, f64), (f64, f64)) {
207    if row {
208        ((r.x0, r.x1), (r.y0, r.y1))
209    } else {
210        ((r.y0, r.y1), (r.x0, r.x1))
211    }
212}
213
214fn rect(row: bool, along: (f64, f64), across: (f64, f64)) -> KRect {
215    if row {
216        KRect::new(along.0, across.0, along.1, across.1)
217    } else {
218        KRect::new(across.0, along.0, across.1, along.1)
219    }
220}
221
222impl Run {
223    pub fn new(space: Space) -> Run {
224        Run {
225            space,
226            raw: Vec2::ZERO,
227            at: Vec2::ZERO,
228            origin: Vec2::ZERO,
229            row: false,
230            free: false,
231            boxes: rstar::RTree::new(),
232            free_reach: Default::default(),
233            free_cover: Default::default(),
234            start: 0.0,
235            across: 0.0,
236            gap: 0.0,
237            line: 0.0,
238            cut: None,
239            items: Sums::new(),
240            spaces: HashMap::new(),
241            overhang: Counted::default(),
242            reach_across: Default::default(),
243            cover_across: Default::default(),
244            entry: None,
245            cover: None,
246        }
247    }
248
249    pub fn len(&self) -> usize {
250        self.items.len()
251    }
252
253    pub fn is_empty(&self) -> bool {
254        self.items.is_empty()
255    }
256
257    /// Where the run places a child: its unrounded top-left in the run's
258    /// space, and its rounded one — each edge where taffy's pass puts it,
259    /// measured from the run's node, a box in the flow.
260    /// Where a free run places a child at `at` from its node: its unrounded
261    /// top-left and its rounded one, measured from the run's node, as taffy
262    /// rounds a box placed in its parent.
263    pub fn place_at(&self, at: Vec2) -> (Vec2, Vec2) {
264        let o = self.origin;
265        let (rx, ry) = ((o.x + self.raw.x) as f32, (o.y + self.raw.y) as f32);
266        let raw = (rx + at.x as f32, ry + at.y as f32);
267        let snap32 = |v: f32| (v + 0.5).floor();
268        let rounded = (
269            (o.x + self.at.x) as f32 + snap32(raw.0) - snap32(rx),
270            (o.y + self.at.y) as f32 + snap32(raw.1) - snap32(ry),
271        );
272        (
273            Vec2::new(raw.0 as f64 - o.x, raw.1 as f64 - o.y),
274            Vec2::new(rounded.0 as f64 - o.x, rounded.1 as f64 - o.y),
275        )
276    }
277
278    pub fn place(&self, before: f64, across: f64) -> (Vec2, Vec2) {
279        // In single precision, as taffy places a box and its rounding pass
280        // rounds it: the child's place within the run, then the run's.
281        let along = (self.start + before) as f32;
282        let across = (self.across + across) as f32;
283        let (dx, dy) = if self.row {
284            (along, across)
285        } else {
286            (across, along)
287        };
288        let o = self.origin;
289        let (rx, ry) = ((o.x + self.raw.x) as f32, (o.y + self.raw.y) as f32);
290        let raw = (rx + dx, ry + dy);
291        let snap32 = |v: f32| (v + 0.5).floor();
292        let at = (
293            (o.x + self.at.x) as f32 + snap32(raw.0) - snap32(rx),
294            (o.y + self.at.y) as f32 + snap32(raw.1) - snap32(ry),
295        );
296        (
297            Vec2::new(raw.0 as f64 - o.x, raw.1 as f64 - o.y),
298            Vec2::new(at.0 as f64 - o.x, at.1 as f64 - o.y),
299        )
300    }
301
302    /// The child at `key`: how far along the run the children before it
303    /// advance, and how it is placed.
304    pub fn child(&self, key: &OrderKey) -> Option<(f64, &Placed)> {
305        let placed = self.items.get(key)?;
306        Some((self.prefix(key), placed))
307    }
308
309    /// How far along the run the children before `key` advance, gaps and
310    /// all.
311    pub fn prefix(&self, key: &OrderKey) -> f64 {
312        let s = self.items.before(key);
313        s.length + s.n as f64 * self.gap
314    }
315
316    /// The keys of the children after `key`, in order.
317    pub fn keys_after(&self, key: &OrderKey) -> Vec<OrderKey> {
318        self.items
319            .from(key)
320            .filter(|(k, _)| *k != key)
321            .map(|(k, _)| k.clone())
322            .collect()
323    }
324
325    /// Its entry's bounds in its space's index.
326    pub fn entry(&self) -> Option<KRect> {
327        self.entry
328    }
329
330    /// Where the child at `key` is: its rounded top-left in the run's space.
331    pub fn child_at(&self, key: &OrderKey) -> Option<Vec2> {
332        if self.free {
333            let p = self.items.get(key)?;
334            return Some(self.place_at(p.at.unwrap_or(Vec2::ZERO)).1);
335        }
336        let (before, p) = self.child(key)?;
337        Some(self.place(before, p.across).1)
338    }
339
340    /// The children in order, each with how far the ones before it advance.
341    pub fn children(&self) -> impl Iterator<Item = (&OrderKey, f64, &Placed)> {
342        let mut before = 0.0;
343        let gap = self.gap;
344        self.items.iter().map(move |(k, p)| {
345            let b = before;
346            before += p.size + gap;
347            (k, b, p)
348        })
349    }
350
351    /// The children that can touch `r`, in the run's space, in order: from
352    /// the first whose box ends past `r`'s start, less the furthest any child
353    /// reaches past its box, to the last that starts before its end, plus
354    /// that.
355    pub(super) fn touching(&self, r: KRect) -> Vec<(&OrderKey, Vec2)> {
356        self.touching_rounded(r)
357            .into_iter()
358            .map(|(k, at, _)| (k, at))
359            .collect()
360    }
361
362    /// [`Run::touching`], each child with whether it still rounds as its
363    /// space was made: whether its rounded place less its unrounded one is
364    /// what it was when its boxes and chunks were made. A child the sums
365    /// moved by a fraction of a pixel doesn't, and is rounded anew before it
366    /// is read.
367    pub(super) fn touching_rounded(&self, r: KRect) -> Vec<(&OrderKey, Vec2, bool)> {
368        if self.free {
369            let env = rstar::AABB::from_corners([r.x0, r.y0], [r.x1, r.y1]);
370            let mut found: Vec<&Boxed> = self.boxes.locate_in_envelope_intersecting(&env).collect();
371            found.sort_by(|a, b| a.key.cmp(&b.key));
372            return found
373                .into_iter()
374                .filter_map(|b| {
375                    let (k, p) = (&b.key, self.items.get(&b.key)?);
376                    let (raw, rounded) = self.place_at(p.at.unwrap_or(Vec2::ZERO));
377                    let fresh = ((rounded - raw) - p.delta).hypot() < 1e-3;
378                    Some((k, rounded, fresh))
379                })
380                .collect();
381        }
382        let ((a0, a1), _) = spans(self.row, r);
383        let base = if self.row { self.raw.x } else { self.raw.y } + self.start;
384        let over = self.overhang.greatest().unwrap_or(0.0).max(0.0);
385        let (from, to) = (a0 - base - over, a1 - base + over);
386        let gap = self.gap;
387        let Some((first, _, before)) = self
388            .items
389            .seek(|s| s.length + s.n.saturating_sub(1) as f64 * gap >= from)
390        else {
391            return Vec::new();
392        };
393        let mut out = Vec::new();
394        let mut at = before.length + before.n as f64 * gap;
395        for (k, p) in self.items.from(first) {
396            if at > to {
397                break;
398            }
399            let (raw, rounded) = self.place(at, p.across);
400            let fresh = ((rounded - raw) - p.delta).hypot() < 1e-3;
401            out.push((k, rounded, fresh));
402            at += p.size + gap;
403        }
404        out
405    }
406
407    /// What the run's children reach and cover in its space, from its sums
408    /// and counted sets; `None` where nothing. Along the run, the least is
409    /// found walking in from the first child and the greatest from the
410    /// last, each only while a child could still reach past the best found
411    /// — no further than the furthest any child reaches past its box.
412    pub(super) fn extents(&self) -> (Option<KRect>, Option<KRect>) {
413        if self.free {
414            let make = |c: &[Counted; 4]| {
415                let (Some(x0), Some(y0), Some(x1), Some(y1)) =
416                    (c[0].least(), c[1].least(), c[2].greatest(), c[3].greatest())
417                else {
418                    return None;
419                };
420                Some(KRect::new(x0, y0, x1, y1) + self.raw)
421            };
422            let cover = make(&self.free_cover).and_then(|r| super::chunk::cut_to(r, self.cut));
423            return (make(&self.free_reach), cover);
424        }
425        let base = if self.row { self.raw.x } else { self.raw.y } + self.start;
426        let across = if self.row { self.raw.y } else { self.raw.x } + self.across;
427        let over = self.overhang.greatest().unwrap_or(0.0).max(0.0);
428        let along = |of: fn(&Placed) -> Option<(f64, f64)>| -> Option<(f64, f64)> {
429            let gap = self.gap;
430            let mut lo = f64::INFINITY;
431            let mut at = 0.0;
432            for (_, p) in self.items.iter() {
433                if at - over > lo {
434                    break;
435                }
436                if let Some((l, _)) = of(p) {
437                    lo = lo.min(at + l);
438                }
439                at += p.size + gap;
440            }
441            let total = self.items.total();
442            let mut hi = f64::NEG_INFINITY;
443            let mut end = total.length + total.n.saturating_sub(1) as f64 * gap;
444            for (_, p) in self.items.rev() {
445                let start = end - p.size;
446                if end + over < hi {
447                    break;
448                }
449                if let Some((_, h)) = of(p) {
450                    hi = hi.max(start + h);
451                }
452                end = start - gap;
453            }
454            (lo <= hi).then_some((lo, hi))
455        };
456        let make = |along: Option<(f64, f64)>, lo: &Counted, hi: &Counted| {
457            let along = along?;
458            let (Some(c0), Some(c1)) = (lo.least(), hi.greatest()) else {
459                return None;
460            };
461            Some(rect(
462                self.row,
463                (base + along.0, base + along.1),
464                (across + c0, across + c1),
465            ))
466        };
467        let reach = make(
468            along(|p| p.reach),
469            &self.reach_across.0,
470            &self.reach_across.1,
471        );
472        let cover = make(
473            along(|p| p.covered),
474            &self.cover_across.0,
475            &self.cover_across.1,
476        )
477        .and_then(|r| super::chunk::cut_to(r, self.cut));
478        (reach, cover)
479    }
480
481    /// Puts what a child is, from its space's index and covers, in the sums
482    /// and the counted sets.
483    pub(super) fn set(&mut self, key: OrderKey, mut placed: Placed) {
484        self.unset(&key);
485        if self.free {
486            let local = self.spaces.entry(key.clone()).or_default();
487            let env = local.index.root().envelope();
488            let reach = (env.lower()[0] <= env.upper()[0]).then(|| {
489                KRect::new(
490                    env.lower()[0],
491                    env.lower()[1],
492                    env.upper()[0],
493                    env.upper()[1],
494                )
495            });
496            let covered = local.covers.rect();
497            // In the run's own coordinates, from its node's unrounded
498            // top-left: the child's rounded place less that.
499            let at = placed.at.unwrap_or(Vec2::ZERO) + placed.delta;
500            placed.boxed = reach.map(|r| r + at);
501            placed.cover_box = covered.map(|r| r + at);
502            if let Some(b) = placed.boxed {
503                self.boxes.insert(Boxed {
504                    key: key.clone(),
505                    bounds: b,
506                });
507                for (c, v) in self.free_reach.iter_mut().zip([b.x0, b.y0, b.x1, b.y1]) {
508                    c.add(v);
509                }
510            }
511            if let Some(b) = placed.cover_box {
512                for (c, v) in self.free_cover.iter_mut().zip([b.x0, b.y0, b.x1, b.y1]) {
513                    c.add(v);
514                }
515            }
516            self.items.insert(key, placed);
517            return;
518        }
519        let local = self.spaces.entry(key.clone()).or_default();
520        let reach = local.index.root().envelope();
521        let reach = (reach.lower()[0] <= reach.upper()[0]).then(|| {
522            KRect::new(
523                reach.lower()[0],
524                reach.lower()[1],
525                reach.upper()[0],
526                reach.upper()[1],
527            )
528        });
529        let covered = local.covers.rect();
530        let d = if self.row {
531            (placed.delta.x, placed.delta.y)
532        } else {
533            (placed.delta.y, placed.delta.x)
534        };
535        let along = |r: KRect| {
536            let ((a0, a1), _) = spans(self.row, r);
537            (a0 + d.0, a1 + d.0)
538        };
539        let across = |r: KRect| {
540            let (_, (c0, c1)) = spans(self.row, r);
541            (placed.across + d.1 + c0, placed.across + d.1 + c1)
542        };
543        placed.reach = reach.map(along);
544        placed.covered = covered.map(along);
545        placed.counted = Counts::default();
546        if let Some(r) = reach {
547            let (lo, hi) = along(r);
548            let o = (-lo).max(hi - placed.size).max(0.0);
549            self.overhang.add(o);
550            let c = across(r);
551            self.reach_across.0.add(c.0);
552            self.reach_across.1.add(c.1);
553            placed.counted.overhang = Some(o);
554            placed.counted.reach = Some(c);
555        }
556        if let Some(r) = covered {
557            let c = across(r);
558            self.cover_across.0.add(c.0);
559            self.cover_across.1.add(c.1);
560            placed.counted.cover = Some(c);
561        }
562        self.items.insert(key, placed);
563    }
564
565    /// Takes a child out of the sums and the counted sets, keeping its
566    /// space; returns how it was placed.
567    pub(super) fn unset(&mut self, key: &OrderKey) -> Option<Placed> {
568        let p = self.items.remove(key)?;
569        if let Some(b) = p.boxed {
570            self.boxes.remove(&Boxed {
571                key: key.clone(),
572                bounds: b,
573            });
574            for (c, v) in self.free_reach.iter_mut().zip([b.x0, b.y0, b.x1, b.y1]) {
575                c.remove(v);
576            }
577        }
578        if let Some(b) = p.cover_box {
579            for (c, v) in self.free_cover.iter_mut().zip([b.x0, b.y0, b.x1, b.y1]) {
580                c.remove(v);
581            }
582        }
583        if let Some(o) = p.counted.overhang {
584            self.overhang.remove(o);
585        }
586        if let Some((c0, c1)) = p.counted.reach {
587            self.reach_across.0.remove(c0);
588            self.reach_across.1.remove(c1);
589        }
590        if let Some((c0, c1)) = p.counted.cover {
591            self.cover_across.0.remove(c0);
592            self.cover_across.1.remove(c1);
593        }
594        Some(p)
595    }
596}