use std::collections::{BTreeMap, HashMap};
use kurbo::{BezPath, Rect as KRect, Shape, Vec2};
use rstar::{AABB, RTree, RTreeObject};
use crate::render::{Draw, Frame};
use super::*;
#[derive(Clone, PartialEq, Debug)]
pub(super) enum What {
Chunk(Order),
Run(RunId),
}
#[derive(Clone, PartialEq)]
pub(super) struct Entry {
pub(super) what: What,
pub(super) bounds: KRect,
}
impl RTreeObject for Entry {
type Envelope = AABB<[f64; 2]>;
fn envelope(&self) -> Self::Envelope {
aabb(self.bounds)
}
}
pub(super) struct Found<'a> {
pub(super) order: &'a Order,
pub(super) at: Vec2,
pub(super) cut: Option<KRect>,
}
#[derive(Default)]
pub(super) struct Extent {
x0: BTreeMap<i64, usize>,
y0: BTreeMap<i64, usize>,
x1: BTreeMap<i64, usize>,
y1: BTreeMap<i64, usize>,
}
pub(super) fn key(v: f64) -> i64 {
(v * 1e6).round() as i64
}
impl Extent {
pub(super) fn add(&mut self, r: KRect) {
for (m, v) in [
(&mut self.x0, r.x0),
(&mut self.y0, r.y0),
(&mut self.x1, r.x1),
(&mut self.y1, r.y1),
] {
*m.entry(key(v)).or_default() += 1;
}
}
pub(super) fn remove(&mut self, r: KRect) {
for (m, v) in [
(&mut self.x0, r.x0),
(&mut self.y0, r.y0),
(&mut self.x1, r.x1),
(&mut self.y1, r.y1),
] {
let k = key(v);
if let Some(n) = m.get_mut(&k) {
*n -= 1;
if *n == 0 {
m.remove(&k);
}
}
}
}
pub(super) fn rect(&self) -> Option<KRect> {
let low = |m: &BTreeMap<i64, usize>| m.keys().next().map(|k| *k as f64 / 1e6);
let high = |m: &BTreeMap<i64, usize>| m.keys().next_back().map(|k| *k as f64 / 1e6);
Some(KRect::new(
low(&self.x0)?,
low(&self.y0)?,
high(&self.x1)?,
high(&self.y1)?,
))
}
}
pub struct List {
pub trees: Trees,
pub(super) chunks: BTreeMap<Order, Chunk>,
pub(super) root: SpaceIndex,
pub(super) runs: Vec<Option<Run>>,
pub(super) free_runs: Vec<RunId>,
pub(super) stale: std::collections::BTreeSet<(RunId, crate::tree::OrderKey)>,
pub root_transform: Id,
pub root_clip: Id,
pub area: Frame,
}
impl Default for List {
fn default() -> Self {
List::new()
}
}
impl List {
pub fn new() -> List {
let mut trees = Trees::default();
let root_transform = trees.transforms.add(Transform {
parent: None,
offset: Vec2::ZERO,
item: None,
});
let root_clip = trees.clips.add(Clip {
parent: None,
transform: root_transform,
path: BezPath::new(),
});
List {
trees,
chunks: BTreeMap::new(),
root: SpaceIndex::default(),
runs: Vec::new(),
free_runs: Vec::new(),
stale: Default::default(),
root_transform,
root_clip,
area: Frame {
x: 0.0,
y: 0.0,
width: 0.0,
height: 0.0,
},
}
}
pub fn of(area: Frame, build: impl FnOnce(&mut Builder)) -> List {
let mut list = List::new();
list.set_area(area);
let (t, c) = (list.root_transform, list.root_clip);
let mut b = Builder::new(&mut list.trees, t, c, None);
build(&mut b);
let made = b.finish();
list.put(
Vec::new(),
Chunk::new(t, Space::Root, Vec2::ZERO, made, Vec::new(), None, None),
);
list
}
pub fn load(&mut self, chunks: Vec<(Order, Chunk)>) {
let mut entries: Vec<Entry> = std::mem::take(&mut self.root.index).into_iter().collect();
for (order, chunk) in chunks {
if chunk.space != Space::Root {
self.put(order, chunk);
continue;
}
if let Some(r) = chunk.cover() {
self.root.covers.add(r);
}
if let Some(bounds) = chunk.reach() {
entries.push(Entry {
what: What::Chunk(order.clone()),
bounds,
});
}
self.chunks.insert(order, chunk);
}
self.root.index = RTree::bulk_load(entries);
self.flush();
}
pub fn set_area(&mut self, area: Frame) {
self.area = area;
self.trees.clips.get_mut(self.root_clip).path = frame_rect(area).to_path(0.1);
}
pub fn covered(&self) -> Option<KRect> {
self.root.covers.rect()
}
pub fn chunk(&self, order: &Order) -> Option<&Chunk> {
self.chunks.get(order)
}
pub fn chunks(&self) -> impl Iterator<Item = (&Order, &Chunk)> {
self.chunks.iter()
}
fn local(&mut self, space: &Space) -> &mut SpaceIndex {
match space {
Space::Root => &mut self.root,
Space::Item(r, k) => self.runs[*r as usize]
.as_mut()
.expect("a live run")
.spaces
.entry(k.clone())
.or_default(),
}
}
pub fn take(&mut self, order: &Order) -> Option<Chunk> {
let chunk = self.chunks.remove(order)?;
let (reach, cover, space) = (chunk.reach(), chunk.cover(), chunk.space.clone());
if self.space_lives(&space) {
let local = self.local(&space);
if let Some(bounds) = reach {
local.index.remove(&Entry {
what: What::Chunk(order.clone()),
bounds,
});
}
if let Some(r) = cover {
local.covers.remove(r);
}
self.touch(&space);
}
Some(chunk)
}
pub fn put(&mut self, order: Order, chunk: Chunk) {
let (reach, cover, space) = (chunk.reach(), chunk.cover(), chunk.space.clone());
let local = self.local(&space);
if let Some(bounds) = reach {
local.index.insert(Entry {
what: What::Chunk(order.clone()),
bounds,
});
}
if let Some(r) = cover {
local.covers.add(r);
}
self.touch(&space);
self.chunks.insert(order, chunk);
}
fn space_lives(&self, space: &Space) -> bool {
match space {
Space::Root => true,
Space::Item(r, _) => self.runs.get(*r as usize).is_some_and(|r| r.is_some()),
}
}
fn touch(&mut self, space: &Space) {
if let Space::Item(r, k) = space {
self.stale.insert((*r, k.clone()));
}
}
pub fn add_run(&mut self, space: Space) -> RunId {
let run = Run::new(space);
match self.free_runs.pop() {
Some(id) => {
self.runs[id as usize] = Some(run);
id
}
None => {
self.runs.push(Some(run));
(self.runs.len() - 1) as RunId
}
}
}
pub fn run(&self, id: RunId) -> &Run {
self.runs[id as usize].as_ref().expect("a live run")
}
pub fn run_mut(&mut self, id: RunId) -> &mut Run {
self.runs[id as usize].as_mut().expect("a live run")
}
pub fn remove_run(&mut self, id: RunId) {
self.place_run(id, None, None);
let space = self.run(id).space.clone();
self.runs[id as usize] = None;
self.free_runs.push(id);
self.stale.retain(|(r, _)| *r != id);
self.touch(&space);
}
pub fn place_child(&mut self, id: RunId, key: crate::tree::OrderKey, placed: Option<Placed>) {
let run = self.run_mut(id);
match placed {
Some(p) => run.set(key.clone(), p),
None => {
run.unset(&key);
run.spaces.remove(&key);
}
}
self.stale.remove(&(id, key));
self.resum(id);
}
pub fn resum(&mut self, id: RunId) {
let (reach, cover) = self.run(id).extents();
self.place_run(id, reach, cover);
}
fn place_run(&mut self, id: RunId, reach: Option<KRect>, cover: Option<KRect>) {
let (space, old_reach, old_cover) = {
let run = self.run(id);
(run.space.clone(), run.entry, run.cover)
};
if old_reach == reach && old_cover == cover {
return;
}
let local = self.local(&space);
if let Some(bounds) = old_reach {
local.index.remove(&Entry {
what: What::Run(id),
bounds,
});
}
if let Some(r) = old_cover {
local.covers.remove(r);
}
if let Some(bounds) = reach {
local.index.insert(Entry {
what: What::Run(id),
bounds,
});
}
if let Some(r) = cover {
local.covers.add(r);
}
let run = self.run_mut(id);
run.entry = reach;
run.cover = cover;
self.touch(&space);
}
pub fn flush(&mut self) {
while let Some((r, k)) = self.stale.pop_last() {
if !self.space_lives(&Space::Item(r, k.clone())) {
continue;
}
let run = self.run_mut(r);
if let Some(p) = run.items.get(&k).cloned() {
run.set(k, p);
}
self.resum(r);
}
}
pub fn free(&mut self, made: &Made) {
for &c in &made.clips {
self.trees.clips.remove(c);
}
for &e in &made.effects {
self.trees.effects.remove(e);
}
}
pub(super) fn found(&self, rect: KRect, pixel: f64) -> Vec<Found<'_>> {
let mut out = Vec::new();
self.found_in(&self.root, Vec2::ZERO, None, rect, pixel, &mut out);
out.sort_by(|a, b| a.order.cmp(b.order));
out
}
fn found_in<'a>(
&'a self,
local: &'a SpaceIndex,
at: Vec2,
cut: Option<KRect>,
rect: KRect,
pixel: f64,
out: &mut Vec<Found<'a>>,
) {
let inside = rect - at;
for e in local.index.locate_in_envelope_intersecting(&aabb(inside)) {
if e.bounds.width() < pixel && e.bounds.height() < pixel {
continue;
}
match &e.what {
What::Chunk(order) => out.push(Found { order, at, cut }),
What::Run(id) => {
let run = self.run(*id);
let outer = run.cut.map(|c| c + at);
let cut = match (cut, outer) {
(Some(a), Some(b)) => Some(a.intersect(b)),
(a, b) => a.or(b),
};
let rect = match outer {
Some(c) => rect.intersect(c),
None => rect,
};
if rect.width() < 0.0 || rect.height() < 0.0 {
continue;
}
for (k, child) in run.touching(rect - at) {
if let Some(space) = run.spaces.get(k) {
self.found_in(space, at + child, cut, rect, pixel, out);
}
}
}
}
}
}
pub fn unrounded(&self, rect: KRect) -> Vec<(RunId, crate::tree::OrderKey)> {
let mut out = Vec::new();
self.unrounded_in(&self.root, Vec2::ZERO, rect, &mut out);
out
}
fn unrounded_in(
&self,
local: &SpaceIndex,
at: Vec2,
rect: KRect,
out: &mut Vec<(RunId, crate::tree::OrderKey)>,
) {
for e in local
.index
.locate_in_envelope_intersecting(&aabb(rect - at))
{
let What::Run(id) = &e.what else { continue };
let run = self.run(*id);
let rect = match run.cut {
Some(c) => rect.intersect(c + at),
None => rect,
};
if rect.width() < 0.0 || rect.height() < 0.0 {
continue;
}
for (k, child, fresh) in run.touching_rounded(rect - at) {
if !fresh {
out.push((*id, k.clone()));
} else if let Some(space) = run.spaces.get(k) {
self.unrounded_in(space, at + child, rect, out);
}
}
}
}
pub fn query(&self, rect: KRect, pixel: f64) -> Vec<(Vec2, &Chunk, &Command)> {
let mut out = Vec::new();
for f in self.found(rect, pixel) {
let chunk = &self.chunks[f.order];
let origin = f.at + chunk.origin;
let local = rect - origin;
let mut at: Vec<usize> = chunk
.index
.locate_in_envelope_intersecting(&aabb(local))
.map(|l| l.at)
.collect();
at.sort_unstable();
out.extend(at.into_iter().map(|i| (origin, chunk, &chunk.commands[i])));
}
out
}
pub fn space_at(&self, space: &Space) -> Vec2 {
match space {
Space::Root => Vec2::ZERO,
Space::Item(r, k) => {
let run = self.run(*r);
self.space_at(&run.space) + run.child_at(k).unwrap_or(Vec2::ZERO)
}
}
}
pub(super) fn world(&self, mut transform: Id) -> Vec2 {
let mut at = Vec2::ZERO;
loop {
let t = self.trees.transforms.get(transform);
at += t.offset;
if let Some((r, k)) = &t.item {
return at + self.space_at(&Space::Item(*r, k.clone()));
}
match t.parent {
Some(p) => transform = p,
None => return at,
}
}
}
pub fn swap_images(&mut self, last_of: &HashMap<u64, peniko::ImageData>) {
for chunk in self.chunks.values_mut() {
for c in &mut chunk.commands {
if let Draw::Fill(_, crate::render::Paint::Image(data, _)) = &mut c.draw
&& let Some(old) = last_of.get(&data.data.id())
{
*data = old.clone();
}
}
}
}
}
pub(super) fn frame_rect(f: Frame) -> KRect {
KRect::new(
f.x as f64,
f.y as f64,
(f.x + f.width) as f64,
(f.y + f.height) as f64,
)
}
#[cfg(test)]
mod tests;