Skip to main content

devpage_design/display/
list.rs

1//! The list: every chunk by paint order and the top-level R-tree over them
2//! in the design's coordinates — what touches a rectangle, in log time —
3//! with the extent that keeps the drawing's area without a walk.
4
5use std::collections::{BTreeMap, HashMap};
6
7use kurbo::{BezPath, Rect as KRect, Shape, Vec2};
8use rstar::{AABB, RTree, RTreeObject};
9
10use crate::render::{Draw, Frame};
11
12use super::*;
13
14/// What an entry of a space's index is: a chunk, by its order, or a run
15/// whose children are spaces of their own.
16#[derive(Clone, PartialEq, Debug)]
17pub(super) enum What {
18    Chunk(Order),
19    Run(RunId),
20}
21
22/// An entry in a space's index.
23#[derive(Clone, PartialEq)]
24pub(super) struct Entry {
25    pub(super) what: What,
26    pub(super) bounds: KRect,
27}
28
29impl RTreeObject for Entry {
30    type Envelope = AABB<[f64; 2]>;
31    fn envelope(&self) -> Self::Envelope {
32        aabb(self.bounds)
33    }
34}
35
36/// A chunk a query found: its order, where its space's origin is in the
37/// design's coordinates, and what cuts it from outside its space.
38pub(super) struct Found<'a> {
39    pub(super) order: &'a Order,
40    pub(super) at: Vec2,
41    pub(super) cut: Option<KRect>,
42}
43
44/// A multiset of edges that knows its least and greatest in log time: the
45/// drawing's area kept from its chunks' covers as they come and go.
46#[derive(Default)]
47pub(super) struct Extent {
48    x0: BTreeMap<i64, usize>,
49    y0: BTreeMap<i64, usize>,
50    x1: BTreeMap<i64, usize>,
51    y1: BTreeMap<i64, usize>,
52}
53
54/// An edge as a key: to a millionth of a pixel, exactly ordered.
55pub(super) fn key(v: f64) -> i64 {
56    (v * 1e6).round() as i64
57}
58
59impl Extent {
60    pub(super) fn add(&mut self, r: KRect) {
61        for (m, v) in [
62            (&mut self.x0, r.x0),
63            (&mut self.y0, r.y0),
64            (&mut self.x1, r.x1),
65            (&mut self.y1, r.y1),
66        ] {
67            *m.entry(key(v)).or_default() += 1;
68        }
69    }
70
71    pub(super) fn remove(&mut self, r: KRect) {
72        for (m, v) in [
73            (&mut self.x0, r.x0),
74            (&mut self.y0, r.y0),
75            (&mut self.x1, r.x1),
76            (&mut self.y1, r.y1),
77        ] {
78            let k = key(v);
79            if let Some(n) = m.get_mut(&k) {
80                *n -= 1;
81                if *n == 0 {
82                    m.remove(&k);
83                }
84            }
85        }
86    }
87
88    pub(super) fn rect(&self) -> Option<KRect> {
89        let low = |m: &BTreeMap<i64, usize>| m.keys().next().map(|k| *k as f64 / 1e6);
90        let high = |m: &BTreeMap<i64, usize>| m.keys().next_back().map(|k| *k as f64 / 1e6);
91        Some(KRect::new(
92            low(&self.x0)?,
93            low(&self.y0)?,
94            high(&self.x1)?,
95            high(&self.y1)?,
96        ))
97    }
98}
99
100/// The display list: chunks by paint order, the property trees, and the
101/// index over the chunks — the design's own space, and a space for each
102/// child of each run.
103pub struct List {
104    pub trees: Trees,
105    pub(super) chunks: BTreeMap<Order, Chunk>,
106    pub(super) root: SpaceIndex,
107    pub(super) runs: Vec<Option<Run>>,
108    pub(super) free_runs: Vec<RunId>,
109    /// Children whose spaces changed since their runs last summed them.
110    pub(super) stale: std::collections::BTreeSet<(RunId, crate::tree::OrderKey)>,
111    /// The root of the transform tree: the design's own coordinates.
112    pub root_transform: Id,
113    /// The root of the clip tree: the drawing's own area.
114    pub root_clip: Id,
115    /// The drawing's area.
116    pub area: Frame,
117}
118
119impl Default for List {
120    fn default() -> Self {
121        List::new()
122    }
123}
124
125impl List {
126    /// An empty list: the root transform, and the root clip — to an empty
127    /// area until [`List::set_area`].
128    pub fn new() -> List {
129        let mut trees = Trees::default();
130        let root_transform = trees.transforms.add(Transform {
131            parent: None,
132            offset: Vec2::ZERO,
133            item: None,
134        });
135        let root_clip = trees.clips.add(Clip {
136            parent: None,
137            transform: root_transform,
138            path: BezPath::new(),
139        });
140        List {
141            trees,
142            chunks: BTreeMap::new(),
143            root: SpaceIndex::default(),
144            runs: Vec::new(),
145            free_runs: Vec::new(),
146            stale: Default::default(),
147            root_transform,
148            root_clip,
149            area: Frame {
150                x: 0.0,
151                y: 0.0,
152                width: 0.0,
153                height: 0.0,
154            },
155        }
156    }
157
158    /// A list of one chunk at the origin, `area` its drawing, built by
159    /// `build`: for painting a few commands by themselves.
160    pub fn of(area: Frame, build: impl FnOnce(&mut Builder)) -> List {
161        let mut list = List::new();
162        list.set_area(area);
163        let (t, c) = (list.root_transform, list.root_clip);
164        let mut b = Builder::new(&mut list.trees, t, c, None);
165        build(&mut b);
166        let made = b.finish();
167        list.put(
168            Vec::new(),
169            Chunk::new(t, Space::Root, Vec2::ZERO, made, Vec::new(), None, None),
170        );
171        list
172    }
173
174    /// Puts the chunks made for a new scene in, those in the design's own
175    /// space bulk-loaded into its index with what is in it already.
176    pub fn load(&mut self, chunks: Vec<(Order, Chunk)>) {
177        let mut entries: Vec<Entry> = std::mem::take(&mut self.root.index).into_iter().collect();
178        for (order, chunk) in chunks {
179            if chunk.space != Space::Root {
180                self.put(order, chunk);
181                continue;
182            }
183            if let Some(r) = chunk.cover() {
184                self.root.covers.add(r);
185            }
186            if let Some(bounds) = chunk.reach() {
187                entries.push(Entry {
188                    what: What::Chunk(order.clone()),
189                    bounds,
190                });
191            }
192            self.chunks.insert(order, chunk);
193        }
194        self.root.index = RTree::bulk_load(entries);
195        self.flush();
196    }
197
198    /// The drawing's area becomes `area`, and the root clip with it.
199    pub fn set_area(&mut self, area: Frame) {
200        self.area = area;
201        self.trees.clips.get_mut(self.root_clip).path = frame_rect(area).to_path(0.1);
202    }
203
204    /// What the chunks cover together, before rounding.
205    pub fn covered(&self) -> Option<KRect> {
206        self.root.covers.rect()
207    }
208
209    pub fn chunk(&self, order: &Order) -> Option<&Chunk> {
210        self.chunks.get(order)
211    }
212
213    /// Every chunk, in paint order.
214    pub fn chunks(&self) -> impl Iterator<Item = (&Order, &Chunk)> {
215        self.chunks.iter()
216    }
217
218    /// The index of a space.
219    fn local(&mut self, space: &Space) -> &mut SpaceIndex {
220        match space {
221            Space::Root => &mut self.root,
222            Space::Item(r, k) => self.runs[*r as usize]
223                .as_mut()
224                .expect("a live run")
225                .spaces
226                .entry(k.clone())
227                .or_default(),
228        }
229    }
230
231    /// Takes the chunk at `order` out of the list and its space's index.
232    pub fn take(&mut self, order: &Order) -> Option<Chunk> {
233        let chunk = self.chunks.remove(order)?;
234        let (reach, cover, space) = (chunk.reach(), chunk.cover(), chunk.space.clone());
235        if self.space_lives(&space) {
236            let local = self.local(&space);
237            if let Some(bounds) = reach {
238                local.index.remove(&Entry {
239                    what: What::Chunk(order.clone()),
240                    bounds,
241                });
242            }
243            if let Some(r) = cover {
244                local.covers.remove(r);
245            }
246            self.touch(&space);
247        }
248        Some(chunk)
249    }
250
251    /// Puts a chunk in the list and its space's index.
252    pub fn put(&mut self, order: Order, chunk: Chunk) {
253        let (reach, cover, space) = (chunk.reach(), chunk.cover(), chunk.space.clone());
254        let local = self.local(&space);
255        if let Some(bounds) = reach {
256            local.index.insert(Entry {
257                what: What::Chunk(order.clone()),
258                bounds,
259            });
260        }
261        if let Some(r) = cover {
262            local.covers.add(r);
263        }
264        self.touch(&space);
265        self.chunks.insert(order, chunk);
266    }
267
268    fn space_lives(&self, space: &Space) -> bool {
269        match space {
270            Space::Root => true,
271            Space::Item(r, _) => self.runs.get(*r as usize).is_some_and(|r| r.is_some()),
272        }
273    }
274
275    /// Marks a space's run child as needing its run to sum it again.
276    fn touch(&mut self, space: &Space) {
277        if let Space::Item(r, k) = space {
278            self.stale.insert((*r, k.clone()));
279        }
280    }
281
282    /// A new run, in `space`, with no children yet.
283    pub fn add_run(&mut self, space: Space) -> RunId {
284        let run = Run::new(space);
285        match self.free_runs.pop() {
286            Some(id) => {
287                self.runs[id as usize] = Some(run);
288                id
289            }
290            None => {
291                self.runs.push(Some(run));
292                (self.runs.len() - 1) as RunId
293            }
294        }
295    }
296
297    pub fn run(&self, id: RunId) -> &Run {
298        self.runs[id as usize].as_ref().expect("a live run")
299    }
300
301    pub fn run_mut(&mut self, id: RunId) -> &mut Run {
302        self.runs[id as usize].as_mut().expect("a live run")
303    }
304
305    /// Takes a run out, its entry and cover with it; its children's chunks
306    /// must be gone already.
307    pub fn remove_run(&mut self, id: RunId) {
308        self.place_run(id, None, None);
309        let space = self.run(id).space.clone();
310        self.runs[id as usize] = None;
311        self.free_runs.push(id);
312        self.stale.retain(|(r, _)| *r != id);
313        self.touch(&space);
314    }
315
316    /// Puts a run's child in its sums, as `placed` — or, with `None`, takes
317    /// it out, its space with it.
318    pub fn place_child(&mut self, id: RunId, key: crate::tree::OrderKey, placed: Option<Placed>) {
319        let run = self.run_mut(id);
320        match placed {
321            Some(p) => run.set(key.clone(), p),
322            None => {
323                run.unset(&key);
324                run.spaces.remove(&key);
325            }
326        }
327        self.stale.remove(&(id, key));
328        self.resum(id);
329    }
330
331    /// The run's entry and cover in its space made again from its sums —
332    /// after where it places its children changed.
333    pub fn resum(&mut self, id: RunId) {
334        let (reach, cover) = self.run(id).extents();
335        self.place_run(id, reach, cover);
336    }
337
338    fn place_run(&mut self, id: RunId, reach: Option<KRect>, cover: Option<KRect>) {
339        let (space, old_reach, old_cover) = {
340            let run = self.run(id);
341            (run.space.clone(), run.entry, run.cover)
342        };
343        if old_reach == reach && old_cover == cover {
344            return;
345        }
346        let local = self.local(&space);
347        if let Some(bounds) = old_reach {
348            local.index.remove(&Entry {
349                what: What::Run(id),
350                bounds,
351            });
352        }
353        if let Some(r) = old_cover {
354            local.covers.remove(r);
355        }
356        if let Some(bounds) = reach {
357            local.index.insert(Entry {
358                what: What::Run(id),
359                bounds,
360            });
361        }
362        if let Some(r) = cover {
363            local.covers.add(r);
364        }
365        let run = self.run_mut(id);
366        run.entry = reach;
367        run.cover = cover;
368        self.touch(&space);
369    }
370
371    /// Sums again every run child whose space changed, and every run around
372    /// it, out to the design's own space.
373    pub fn flush(&mut self) {
374        while let Some((r, k)) = self.stale.pop_last() {
375            if !self.space_lives(&Space::Item(r, k.clone())) {
376                continue;
377            }
378            let run = self.run_mut(r);
379            if let Some(p) = run.items.get(&k).cloned() {
380                run.set(k, p);
381            }
382            self.resum(r);
383        }
384    }
385
386    /// Frees the property-tree nodes a chunk's own layers made.
387    pub fn free(&mut self, made: &Made) {
388        for &c in &made.clips {
389            self.trees.clips.remove(c);
390        }
391        for &e in &made.effects {
392            self.trees.effects.remove(e);
393        }
394    }
395
396    /// The chunks whose entries touch `rect`, in paint order — those smaller
397    /// than `pixel` both ways skipped — through every run's children that
398    /// can touch it.
399    pub(super) fn found(&self, rect: KRect, pixel: f64) -> Vec<Found<'_>> {
400        let mut out = Vec::new();
401        self.found_in(&self.root, Vec2::ZERO, None, rect, pixel, &mut out);
402        out.sort_by(|a, b| a.order.cmp(b.order));
403        out
404    }
405
406    fn found_in<'a>(
407        &'a self,
408        local: &'a SpaceIndex,
409        at: Vec2,
410        cut: Option<KRect>,
411        rect: KRect,
412        pixel: f64,
413        out: &mut Vec<Found<'a>>,
414    ) {
415        let inside = rect - at;
416        for e in local.index.locate_in_envelope_intersecting(&aabb(inside)) {
417            if e.bounds.width() < pixel && e.bounds.height() < pixel {
418                continue;
419            }
420            match &e.what {
421                What::Chunk(order) => out.push(Found { order, at, cut }),
422                What::Run(id) => {
423                    let run = self.run(*id);
424                    let outer = run.cut.map(|c| c + at);
425                    let cut = match (cut, outer) {
426                        (Some(a), Some(b)) => Some(a.intersect(b)),
427                        (a, b) => a.or(b),
428                    };
429                    let rect = match outer {
430                        Some(c) => rect.intersect(c),
431                        None => rect,
432                    };
433                    if rect.width() < 0.0 || rect.height() < 0.0 {
434                        continue;
435                    }
436                    for (k, child) in run.touching(rect - at) {
437                        if let Some(space) = run.spaces.get(k) {
438                            self.found_in(space, at + child, cut, rect, pixel, out);
439                        }
440                    }
441                }
442            }
443        }
444    }
445
446    /// The run children that touch `rect` — in the design's coordinates —
447    /// and no longer round as their spaces were made: what is rounded anew
448    /// before `rect` is painted, hit or read. Only what touches `rect` is
449    /// looked at.
450    pub fn unrounded(&self, rect: KRect) -> Vec<(RunId, crate::tree::OrderKey)> {
451        let mut out = Vec::new();
452        self.unrounded_in(&self.root, Vec2::ZERO, rect, &mut out);
453        out
454    }
455
456    fn unrounded_in(
457        &self,
458        local: &SpaceIndex,
459        at: Vec2,
460        rect: KRect,
461        out: &mut Vec<(RunId, crate::tree::OrderKey)>,
462    ) {
463        for e in local
464            .index
465            .locate_in_envelope_intersecting(&aabb(rect - at))
466        {
467            let What::Run(id) = &e.what else { continue };
468            let run = self.run(*id);
469            let rect = match run.cut {
470                Some(c) => rect.intersect(c + at),
471                None => rect,
472            };
473            if rect.width() < 0.0 || rect.height() < 0.0 {
474                continue;
475            }
476            for (k, child, fresh) in run.touching_rounded(rect - at) {
477                if !fresh {
478                    out.push((*id, k.clone()));
479                } else if let Some(space) = run.spaces.get(k) {
480                    self.unrounded_in(space, at + child, rect, out);
481                }
482            }
483        }
484    }
485
486    /// The commands that touch `rect`, in the design's coordinates, in paint
487    /// order — a chunk smaller than `pixel` both ways skipped — each with its
488    /// chunk and where the chunk's origin is in the design's coordinates.
489    pub fn query(&self, rect: KRect, pixel: f64) -> Vec<(Vec2, &Chunk, &Command)> {
490        let mut out = Vec::new();
491        for f in self.found(rect, pixel) {
492            let chunk = &self.chunks[f.order];
493            let origin = f.at + chunk.origin;
494            let local = rect - origin;
495            let mut at: Vec<usize> = chunk
496                .index
497                .locate_in_envelope_intersecting(&aabb(local))
498                .map(|l| l.at)
499                .collect();
500            at.sort_unstable();
501            out.extend(at.into_iter().map(|i| (origin, chunk, &chunk.commands[i])));
502        }
503        out
504    }
505
506    /// Where a space's origin is in the design's coordinates.
507    pub fn space_at(&self, space: &Space) -> Vec2 {
508        match space {
509            Space::Root => Vec2::ZERO,
510            Space::Item(r, k) => {
511                let run = self.run(*r);
512                self.space_at(&run.space) + run.child_at(k).unwrap_or(Vec2::ZERO)
513            }
514        }
515    }
516
517    /// Where `transform`'s origin is in the design's coordinates.
518    pub(super) fn world(&self, mut transform: Id) -> Vec2 {
519        let mut at = Vec2::ZERO;
520        loop {
521            let t = self.trees.transforms.get(transform);
522            at += t.offset;
523            if let Some((r, k)) = &t.item {
524                return at + self.space_at(&Space::Item(*r, k.clone()));
525            }
526            match t.parent {
527                Some(p) => transform = p,
528                None => return at,
529            }
530        }
531    }
532
533    /// Every image the list paints swapped for the blob `last_of` names for
534    /// it, where it names one.
535    pub fn swap_images(&mut self, last_of: &HashMap<u64, peniko::ImageData>) {
536        for chunk in self.chunks.values_mut() {
537            for c in &mut chunk.commands {
538                if let Draw::Fill(_, crate::render::Paint::Image(data, _)) = &mut c.draw
539                    && let Some(old) = last_of.get(&data.data.id())
540                {
541                    *data = old.clone();
542                }
543            }
544        }
545    }
546}
547
548pub(super) fn frame_rect(f: Frame) -> KRect {
549    KRect::new(
550        f.x as f64,
551        f.y as f64,
552        (f.x + f.width) as f64,
553        (f.y + f.height) as f64,
554    )
555}
556
557#[cfg(test)]
558mod tests;