pub(crate) mod graph;
mod lanes;
pub(crate) mod places;
pub(crate) mod pyramid;
mod shapes;
pub(crate) mod sight;
mod stars;
pub(crate) mod tiled;
pub(crate) mod tiles;
mod tiling;
pub(crate) use lanes::{answer as lanes_answer, spread};
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};
use std::task::Waker;
use rustc_hash::{FxHashMap, FxHasher};
use crate::data::{Column, Data, Field, Name, Op, Page, Patch, Table, Value, Values, Version};
use crate::gpu::readback::Readback;
use crate::gpu::work::{Count, Counters};
use crate::host::{Host, Job, Key, Request};
use crate::sheet::Sheet;
use crate::{Pending, Result, Scene, Space};
use places::Places;
use tiles::TileSets;
pub(crate) fn stamp_of(inputs: impl Hash) -> u64 {
let mut hasher = FxHasher::default();
inputs.hash(&mut hasher);
hasher.finish().max(1)
}
pub(crate) trait Answer {
fn take(&self) -> Option<Result<Vec<u8>>>;
}
impl Answer for Readback {
fn take(&self) -> Option<Result<Vec<u8>>> {
Readback::take(self)
}
}
impl Answer for Pending<Vec<u8>> {
fn take(&self) -> Option<Result<Vec<u8>>> {
Pending::take(self)
}
}
pub(crate) struct Derived<T, A: Answer = Readback> {
pub(crate) value: T,
stamp: u64,
working: Option<(u64, A)>,
}
impl<T: Default, A: Answer> Default for Derived<T, A> {
fn default() -> Self {
Derived { value: T::default(), stamp: 0, working: None }
}
}
impl<T, A: Answer> Derived<T, A> {
pub(crate) fn stamp(&self) -> u64 {
self.stamp
}
pub(crate) fn due(&self, stamp: u64) -> bool {
stamp != self.stamp && self.working.as_ref().is_none_or(|(s, _)| *s != stamp)
}
pub(crate) fn make(&mut self, stamp: u64, value: T) {
self.value = value;
self.stamp = stamp;
self.working = None;
}
pub(crate) fn mark(&mut self, stamp: u64) {
self.stamp = stamp;
self.working = None;
}
pub(crate) fn start(&mut self, stamp: u64, work: A) {
self.working = Some((stamp, work));
}
pub(crate) fn landed(&mut self) -> Option<(u64, Vec<u8>)> {
let (stamp, work) = self.working.as_ref()?;
let stamp = *stamp;
let landed = work.take()?;
self.working = None;
let Ok(bytes) = landed else {
self.stamp = stamp;
return None;
};
Some((stamp, bytes))
}
pub(crate) fn busy(&self) -> bool {
self.working.is_some()
}
}
const FRAME_WORK: u32 = 4000;
pub(crate) struct Jobs<'a> {
errors: &'a Arc<Mutex<Vec<crate::Error>>>,
host: Option<&'a dyn Host>,
waker: &'a Waker,
counters: &'a Counters,
left: u32,
}
impl<'a> Jobs<'a> {
pub(crate) fn new(
host: Option<&'a dyn Host>,
waker: &'a Waker,
counters: &'a Counters,
errors: &'a Arc<Mutex<Vec<crate::Error>>>,
) -> Jobs<'a> {
Jobs { host, waker, counters, errors, left: FRAME_WORK }
}
pub(crate) fn fits(&mut self, cost: u64) -> bool {
let fits = cost <= u64::from(self.left);
if fits {
self.left -= cost as u32;
}
fits
}
pub(crate) fn left(&self) -> u64 {
u64::from(self.left)
}
pub(crate) fn spend(&mut self, cost: u64) {
self.left = self.left.saturating_sub(cost.min(u64::from(u32::MAX)) as u32);
}
pub(crate) fn spawn(&self, job: Job) -> Pending<Vec<u8>> {
self.counters.add(Count::Jobs, 1);
let pending = match self.host {
Some(host) => host.spawn(job),
None => Pending::ready(Ok(job.run())),
};
pending.report_errors(self.errors);
pending.notify(self.waker);
pending
}
pub(crate) fn fetch(&self, request: &Request) -> Pending<Vec<u8>> {
let pending = match self.host {
Some(host) => host.fetch(request),
None => Pending::ready(Err(crate::Error::Unsupported("fetching needs a host".into()))),
};
pending.notify(self.waker);
pending
}
}
pub(crate) struct Input<'a> {
pub(crate) scene: &'a Scene,
pub(crate) scene_version: u64,
pub(crate) data: &'a [Data],
pub(crate) host: Option<&'a dyn Host>,
pub(crate) dragging: bool,
}
impl Input<'_> {
pub(crate) fn data(&self, name: &str) -> Option<&Data> {
self.data.iter().find(|d| d.name() == name)
}
pub(crate) fn structure(&self, hasher: &mut impl Hasher) {
self.reads(&mut |read| match read {
Read::Rows { data, table, rows } => (data, table, rows.structure, rows.order).hash(hasher),
Read::Field { data, table, name, field, .. } => (data, table, name, field.map(Field::version)).hash(hasher),
});
}
pub(crate) fn reads<'a>(&'a self, read: &mut impl FnMut(Read<'a>)) {
for mark in &self.scene.marks {
let style = mark.style();
let Some(rows) = self.data(&style.data).and_then(|d| d.table(&style.table)) else { continue };
read(Read::Rows { data: &style.data, table: &style.table, rows });
let own = (&style.data, &style.table);
match mark {
crate::Mark::Vertices(v) => {
match &v.position {
Some([x, y]) => {
self.read_expr(own, x, true, read);
self.read_expr(own, y, true, read);
}
None => self.read_field(own, v.point_field(), read),
}
for size in v.size.iter().flatten().chain(&v.radius) {
self.read_expr(own, &size.expr, true, read);
}
if let Some(label) = &style.label {
self.read_expr(own, &label.text, true, read);
}
for (reference, _) in &v.ports {
self.read_field(own, reference, read);
}
if let Some((members, reference)) = &v.contains {
self.read_field((&style.data, members), reference, read);
}
}
crate::Mark::Edges(e) => {
for name in e.ends.iter().flat_map(|(from, to)| [from, to]).chain(&e.bends) {
self.read_field(own, name, read);
}
if let Some((members, reference)) = &e.joins {
self.read_field((&style.data, members), reference, read);
}
}
crate::Mark::Faces(_) | crate::Mark::Lines(_) | crate::Mark::Areas(_) => {}
}
}
}
fn read_expr<'a>(
&'a self,
table: (&'a Name, &'a Name),
expr: &'a crate::Expr,
own: bool,
read: &mut impl FnMut(Read<'a>),
) {
let own = own && !expr.fits();
expr.for_each_field(&mut |f| {
let (data, of) = f.of.as_ref().map_or(table, |(d, t)| (d, t));
let field = self.data(data).and_then(|d| d.table(of)).and_then(|t| t.field_named(&f.name));
read(Read::Field { data, table: of, name: f.name.as_str(), field, own: own && f.of.is_none() });
});
}
fn read_field<'a>(&'a self, (data, table): (&'a Name, &'a Name), name: &'a str, read: &mut impl FnMut(Read<'a>)) {
let field = self.data(data).and_then(|d| d.table(table)).and_then(|t| t.field_named(name));
read(Read::Field { data, table, name, field, own: false });
}
}
pub(crate) enum Read<'a> {
Rows { data: &'a Name, table: &'a Name, rows: &'a Table },
Field { data: &'a Name, table: &'a Name, name: &'a str, field: Option<&'a Field>, own: bool },
}
pub(crate) type Tables = FxHashMap<(Name, Name), Table>;
pub(crate) trait Derive {
fn update(&mut self, input: &Input<'_>, jobs: &mut Jobs<'_>) -> bool;
fn busy(&self) -> bool;
fn complete(&self) -> bool {
!self.busy()
}
fn tables(&self) -> &Tables;
fn clear(&mut self);
fn take_errors(&mut self) -> Vec<crate::Error> {
Vec::new()
}
}
#[derive(Default)]
pub(crate) struct Derivations {
pub(crate) sheet: Sheet,
lanes: lanes::Lanes,
places: Places,
stars: stars::Stars,
shapes: shapes::Shapes,
pub(crate) tiles: TileSets,
pub(crate) tiled: tiled::Tiled,
}
pub(crate) type Beside<'a> = [Option<&'a Table>; 2];
impl Derivations {
pub(crate) fn update(&mut self, input: &Input<'_>, jobs: &mut Jobs<'_>) -> bool {
let mut changed = if matches!(input.scene.space, Space::Diagram(_)) {
self.lanes.clear();
self.places.clear();
self.stars.clear();
self.sheet.update(input, jobs)
} else {
self.sheet.clear();
let placed = self.places.update(input, jobs);
self.lanes.update(input, jobs) | placed | self.stars.update(input, self.places.tables())
};
changed |= self.shapes.update(input, jobs);
changed
}
pub(crate) fn arrange(&mut self, diagram: bool) {
if diagram { self.sheet.arrange() } else { self.places.arrange() }
}
pub(crate) fn take_arranged(&mut self) -> Option<Vec<(crate::Row, [f64; 2])>> {
self.sheet.take_arranged().or_else(|| self.places.take_arranged())
}
pub(crate) fn complete(&self) -> bool {
self.sheet.complete()
&& self.lanes.complete()
&& self.places.complete()
&& self.shapes.complete()
&& self.tiles.complete()
&& self.tiled.complete()
}
pub(crate) fn shaping(&self) -> bool {
self.shapes.busy()
}
pub(crate) fn take_errors(&mut self) -> Vec<crate::Error> {
let mut errors = self.sheet.take_errors();
errors.extend(self.places.take_errors());
errors.extend(self.tiles.take_errors());
errors
}
pub(crate) fn beside(&self, data: &Name, table: &Name) -> Beside<'_> {
let key = (data.clone(), table.clone());
let first = [self.sheet.tables(), self.places.tables(), self.stars.tables(), self.lanes.tables()]
.into_iter()
.find_map(|tables| tables.get(&key));
[first, self.shapes.tables().get(&key)]
}
}
pub(crate) fn beside(source: &Table, fields: impl IntoIterator<Item = (Name, Field)>) -> Table {
let mut table = Table {
name: source.name.clone(),
ids: source.ids.clone(),
index: source.index.clone(),
fields: Vec::new(),
structure: source.structure,
order: source.order,
};
for (name, field) in fields {
table.insert(name, field);
}
table
}
pub(crate) fn kept(name: &str, values: &Values, source: &Table, before: Option<&Table>) -> Field {
let fresh = Column::new(values);
let old = before.filter(|b| b.structure == source.structure).and_then(|b| match b.field_named(name) {
Some(Field::Values(column, version)) if column.len() == fresh.len() => Some((column, *version)),
_ => None,
});
let Some((column, version)) = old else { return Field::Values(fresh, Version::next()) };
let pages: Vec<Page> = fresh
.pages()
.iter()
.zip(column.pages())
.map(|(new, old)| if new.values.same(&old.values) { old.clone() } else { new.clone() })
.collect();
if pages.iter().zip(column.pages()).all(|(page, old)| page.id == old.id) {
return Field::Values(column.clone(), version);
}
Field::Values(Column::of_pages(pages, column.like()), Version::next())
}
pub(crate) fn answers(bytes: &[u8], fields: &[&str]) -> Vec<(Key, Vec<Option<Value>>)> {
let Ok(patch) = Patch::from_bytes(bytes) else { return Vec::new() };
let mut out = Vec::new();
for op in patch.ops {
let Op::Set(table) = op else { continue };
for row in 0..table.len() {
let Some(id) = table.ids.id(row) else { continue };
let values = fields.iter().map(|f| table.value(row, f, f64::INFINITY)).collect();
out.push(((table.name.clone(), id), values));
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::PAGE_ROWS;
#[test]
fn kept_keeps_the_pages_that_hold_the_same() {
let rows = PAGE_ROWS + 10;
let source = Table::new("T", crate::Ids::count(rows as u32));
let numbers = |changed: f64| {
Values::from((0..rows).map(|r| if r == rows - 1 { changed } else { 1.0 }).collect::<Vec<f64>>())
};
let first = beside(&source, [(Name::from("x"), kept("x", &numbers(1.0), &source, None))]);
let same = kept("x", &numbers(1.0), &source, Some(&first));
assert_eq!(same.version(), first.field_named("x").expect("x").version(), "nothing changed");
let Field::Values(before, _) = first.field_named("x").expect("x") else { panic!("values") };
let Field::Values(after, _) = kept("x", &numbers(2.0), &source, Some(&first)) else { panic!("values") };
assert_eq!(after.pages()[0].id, before.pages()[0].id, "the first page held the same");
assert_ne!(after.pages()[1].id, before.pages()[1].id, "the last page changed");
}
}