use std::collections::{BTreeMap, HashMap};
use kurbo::{Rect as KRect, Vec2};
use crate::sums::{Item, Summary, Sums};
use crate::tree::OrderKey;
use super::*;
pub type RunId = u32;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Space {
Root,
Item(RunId, OrderKey),
}
pub struct Run {
pub space: Space,
pub raw: Vec2,
pub at: Vec2,
pub origin: Vec2,
pub row: bool,
pub start: f64,
pub across: f64,
pub gap: f64,
pub line: f64,
pub cut: Option<KRect>,
pub(super) items: Sums<OrderKey, Placed>,
pub(super) spaces: HashMap<OrderKey, SpaceIndex>,
pub(super) overhang: Counted,
pub(super) reach_across: (Counted, Counted),
pub(super) cover_across: (Counted, Counted),
pub(super) entry: Option<KRect>,
pub(super) cover: Option<KRect>,
}
#[derive(Default)]
pub struct SpaceIndex {
pub(super) index: rstar::RTree<Entry>,
pub(super) covers: Extent,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Placed {
pub size: f64,
pub gap: f64,
pub across: f64,
pub delta: Vec2,
pub(super) reach: Option<(f64, f64)>,
pub(super) covered: Option<(f64, f64)>,
pub(super) counted: Counts,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Counts {
overhang: Option<f64>,
reach: Option<(f64, f64)>,
cover: Option<(f64, f64)>,
}
impl Placed {
pub fn new(size: f64, gap: f64, across: f64, delta: Vec2) -> Placed {
Placed {
size,
gap,
across,
delta,
reach: None,
covered: None,
counted: Counts::default(),
}
}
}
#[derive(Clone, Debug)]
pub struct Span {
pub n: usize,
pub length: f64,
pub reach: (f64, f64),
pub covered: (f64, f64),
}
impl Default for Span {
fn default() -> Self {
Span {
n: 0,
length: 0.0,
reach: (f64::INFINITY, f64::NEG_INFINITY),
covered: (f64::INFINITY, f64::NEG_INFINITY),
}
}
}
impl Summary for Span {
fn then(&self, next: &Self) -> Self {
let shift = |(lo, hi): (f64, f64), (nlo, nhi): (f64, f64)| {
(lo.min(self.length + nlo), hi.max(self.length + nhi))
};
Span {
n: self.n + next.n,
length: self.length + next.length,
reach: shift(self.reach, next.reach),
covered: shift(self.covered, next.covered),
}
}
}
impl Item for Placed {
type Sum = Span;
fn summary(&self) -> Span {
let none = (f64::INFINITY, f64::NEG_INFINITY);
Span {
n: 1,
length: self.size + self.gap,
reach: self.reach.unwrap_or(none),
covered: self.covered.unwrap_or(none),
}
}
}
#[derive(Default)]
pub struct Counted(BTreeMap<i64, usize>);
impl Counted {
pub(super) fn add(&mut self, v: f64) {
*self.0.entry(key(v)).or_default() += 1;
}
pub(super) fn remove(&mut self, v: f64) {
let k = key(v);
if let Some(n) = self.0.get_mut(&k) {
*n -= 1;
if *n == 0 {
self.0.remove(&k);
}
}
}
pub(super) fn least(&self) -> Option<f64> {
self.0.keys().next().map(|k| *k as f64 / 1e6)
}
pub(super) fn greatest(&self) -> Option<f64> {
self.0.keys().next_back().map(|k| *k as f64 / 1e6)
}
}
fn spans(row: bool, r: KRect) -> ((f64, f64), (f64, f64)) {
if row {
((r.x0, r.x1), (r.y0, r.y1))
} else {
((r.y0, r.y1), (r.x0, r.x1))
}
}
fn rect(row: bool, along: (f64, f64), across: (f64, f64)) -> KRect {
if row {
KRect::new(along.0, across.0, along.1, across.1)
} else {
KRect::new(across.0, along.0, across.1, along.1)
}
}
impl Run {
pub fn new(space: Space) -> Run {
Run {
space,
raw: Vec2::ZERO,
at: Vec2::ZERO,
origin: Vec2::ZERO,
row: false,
start: 0.0,
across: 0.0,
gap: 0.0,
line: 0.0,
cut: None,
items: Sums::new(),
spaces: HashMap::new(),
overhang: Counted::default(),
reach_across: Default::default(),
cover_across: Default::default(),
entry: None,
cover: None,
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn place(&self, before: f64, across: f64) -> (Vec2, Vec2) {
let along = (self.start + before) as f32;
let across = (self.across + across) as f32;
let (dx, dy) = if self.row {
(along, across)
} else {
(across, along)
};
let o = self.origin;
let (rx, ry) = ((o.x + self.raw.x) as f32, (o.y + self.raw.y) as f32);
let raw = (rx + dx, ry + dy);
let snap32 = |v: f32| (v + 0.5).floor();
let at = (
(o.x + self.at.x) as f32 + snap32(raw.0) - snap32(rx),
(o.y + self.at.y) as f32 + snap32(raw.1) - snap32(ry),
);
(
Vec2::new(raw.0 as f64 - o.x, raw.1 as f64 - o.y),
Vec2::new(at.0 as f64 - o.x, at.1 as f64 - o.y),
)
}
pub fn child(&self, key: &OrderKey) -> Option<(f64, &Placed)> {
let placed = self.items.get(key)?;
Some((self.items.before(key).length, placed))
}
pub fn prefix(&self, key: &OrderKey) -> f64 {
self.items.before(key).length
}
pub fn keys_after(&self, key: &OrderKey) -> Vec<OrderKey> {
self.items
.from(key)
.filter(|(k, _)| *k != key)
.map(|(k, _)| k.clone())
.collect()
}
pub fn entry(&self) -> Option<KRect> {
self.entry
}
pub fn child_at(&self, key: &OrderKey) -> Option<Vec2> {
let (before, p) = self.child(key)?;
Some(self.place(before, p.across).1)
}
pub fn children(&self) -> impl Iterator<Item = (&OrderKey, f64, &Placed)> {
let mut before = 0.0;
self.items.iter().map(move |(k, p)| {
let b = before;
before += p.size + p.gap;
(k, b, p)
})
}
pub(super) fn touching(&self, r: KRect) -> Vec<(&OrderKey, Vec2)> {
self.touching_rounded(r)
.into_iter()
.map(|(k, at, _)| (k, at))
.collect()
}
pub(super) fn touching_rounded(&self, r: KRect) -> Vec<(&OrderKey, Vec2, bool)> {
let ((a0, a1), _) = spans(self.row, r);
let base = if self.row { self.raw.x } else { self.raw.y } + self.start;
let over = self.overhang.greatest().unwrap_or(0.0).max(0.0);
let (from, to) = (a0 - base - over, a1 - base + over);
let Some((first, _, before)) = self.items.seek(|s| s.length - s_gap(s, self.gap) >= from)
else {
return Vec::new();
};
let mut out = Vec::new();
let mut at = before.length;
for (k, p) in self.items.from(first) {
if at > to {
break;
}
let (raw, rounded) = self.place(at, p.across);
let fresh = ((rounded - raw) - p.delta).hypot() < 1e-3;
out.push((k, rounded, fresh));
at += p.size + p.gap;
}
out
}
pub(super) fn extents(&self) -> (Option<KRect>, Option<KRect>) {
let total = self.items.total();
let base = if self.row { self.raw.x } else { self.raw.y } + self.start;
let across = if self.row { self.raw.y } else { self.raw.x } + self.across;
let make = |along: (f64, f64), lo: &Counted, hi: &Counted| {
let (Some(c0), Some(c1)) = (lo.least(), hi.greatest()) else {
return None;
};
if along.0.partial_cmp(&along.1) == Some(std::cmp::Ordering::Greater)
|| along.0.is_nan()
{
return None;
}
Some(rect(
self.row,
(base + along.0, base + along.1),
(across + c0, across + c1),
))
};
let reach = make(total.reach, &self.reach_across.0, &self.reach_across.1);
let cover = make(total.covered, &self.cover_across.0, &self.cover_across.1)
.and_then(|r| super::chunk::cut_to(r, self.cut));
(reach, cover)
}
pub(super) fn set(&mut self, key: OrderKey, mut placed: Placed) {
self.unset(&key);
let local = self.spaces.entry(key.clone()).or_default();
let reach = local.index.root().envelope();
let reach = (reach.lower()[0] <= reach.upper()[0]).then(|| {
KRect::new(
reach.lower()[0],
reach.lower()[1],
reach.upper()[0],
reach.upper()[1],
)
});
let covered = local.covers.rect();
let d = if self.row {
(placed.delta.x, placed.delta.y)
} else {
(placed.delta.y, placed.delta.x)
};
let along = |r: KRect| {
let ((a0, a1), _) = spans(self.row, r);
(a0 + d.0, a1 + d.0)
};
let across = |r: KRect| {
let (_, (c0, c1)) = spans(self.row, r);
(placed.across + d.1 + c0, placed.across + d.1 + c1)
};
placed.reach = reach.map(along);
placed.covered = covered.map(along);
placed.counted = Counts::default();
if let Some(r) = reach {
let (lo, hi) = along(r);
let o = (-lo).max(hi - placed.size).max(0.0);
self.overhang.add(o);
let c = across(r);
self.reach_across.0.add(c.0);
self.reach_across.1.add(c.1);
placed.counted.overhang = Some(o);
placed.counted.reach = Some(c);
}
if let Some(r) = covered {
let c = across(r);
self.cover_across.0.add(c.0);
self.cover_across.1.add(c.1);
placed.counted.cover = Some(c);
}
self.items.insert(key, placed);
}
pub(super) fn unset(&mut self, key: &OrderKey) -> Option<Placed> {
let p = self.items.remove(key)?;
if let Some(o) = p.counted.overhang {
self.overhang.remove(o);
}
if let Some((c0, c1)) = p.counted.reach {
self.reach_across.0.remove(c0);
self.reach_across.1.remove(c1);
}
if let Some((c0, c1)) = p.counted.cover {
self.cover_across.0.remove(c0);
self.cover_across.1.remove(c1);
}
Some(p)
}
}
fn s_gap(s: &Span, gap: f64) -> f64 {
if s.n == 0 { 0.0 } else { gap }
}