use rustc_hash::FxHashMap;
use super::graph::{key_of, links, marks, position};
use super::{Derive, Input, Jobs, Tables, answers, beside, kept};
use crate::data::{Name, Row, Value, Values};
use crate::expr::cpu::Rows;
use crate::host::{Job, Key, Placement};
use crate::{Layout, Pending};
#[derive(Default)]
pub(crate) struct Places {
tables: Tables,
laid: FxHashMap<Row, [f64; 2]>,
stamp: u64,
generation: u64,
arranging: bool,
arranged: Option<Vec<(Row, [f64; 2])>>,
placing: Option<Placing>,
landed: u64,
}
struct Placing {
pending: Pending<Vec<u8>>,
generation: u64,
rows: FxHashMap<Key, Row>,
}
impl Derive for Places {
fn update(&mut self, input: &Input<'_>, jobs: &mut Jobs<'_>) -> bool {
if matches!(input.scene.layout, Layout::None) {
let had = !self.tables.is_empty();
self.clear();
if self.arranging {
self.arranging = false;
self.arranged = Some(Vec::new());
}
return had;
}
let mut changed = false;
for _ in 0..4 {
self.settle();
let mut hasher = rustc_hash::FxHasher::default();
std::hash::Hash::hash(&(input.scene_version, self.landed, self.generation), &mut hasher);
input.structure(&mut hasher);
let stamp = std::hash::Hasher::finish(&hasher).max(1);
if stamp == self.stamp {
break;
}
self.stamp = stamp;
self.build(input, jobs);
changed = true;
}
changed
}
fn busy(&self) -> bool {
self.placing.is_some()
}
fn tables(&self) -> &Tables {
&self.tables
}
fn clear(&mut self) {
self.tables.clear();
self.stamp = 0;
}
}
impl Places {
pub(crate) fn arrange(&mut self) {
self.arranging = true;
self.placing = None;
self.generation += 1;
self.stamp = 0;
}
pub(crate) fn take_arranged(&mut self) -> Option<Vec<(Row, [f64; 2])>> {
self.arranged.take()
}
fn settle(&mut self) {
let Some(answer) = self.placing.as_ref().and_then(|p| p.pending.take()) else { return };
let placing = self.placing.take().expect("a layout under way");
if placing.generation == self.generation {
let mut placed = Vec::new();
for (key, values) in answer.map(|bytes| answers(&bytes, &["x", "y"])).unwrap_or_default() {
if let (Some(row), [Some(Value::Number(x)), Some(Value::Number(y))]) =
(placing.rows.get(&key), &values[..])
{
self.laid.insert(row.clone(), [*x, *y]);
placed.push((row.clone(), [*x, *y]));
}
}
if std::mem::take(&mut self.arranging) {
self.arranged = Some(placed);
}
}
self.landed += 1;
}
fn build(&mut self, input: &Input<'_>, jobs: &Jobs<'_>) {
let (vertex_marks, edge_marks) = marks(input);
let mut keys = Vec::new();
let mut pins = Vec::new();
for vm in &vertex_marks {
let rows = Rows::new(vm.table, input.data);
for row in 0..vm.table.len() {
let id = vm.table.ids.id(row).expect("every row has an id");
keys.push(Row { data: vm.data.name().clone(), table: vm.table.name().clone(), id });
pins.push(position(vm.mark, &rows, row));
}
}
let arranging = self.arranging;
let wanted = |i: usize| pins[i].is_none() && (arranging || !self.laid.contains_key(&keys[i]));
if self.placing.is_none() && (0..keys.len()).any(wanted) {
let pinned: Vec<Option<[f64; 2]>> = (0..keys.len())
.map(|i| pins[i].or_else(|| (!arranging).then(|| self.laid.get(&keys[i]).copied()).flatten()))
.collect();
let mut rows = FxHashMap::default();
let job_keys: Vec<Option<Key>> = (0..keys.len())
.map(|i| {
let key = key_of(&keys[i]);
(wanted(i) && rows.insert(key.clone(), keys[i].clone()).is_none()).then_some(key)
})
.collect();
let (sizes, groups, pads) =
(vec![[0.0; 2]; keys.len()], vec![None; keys.len()], vec![[0.0; 4]; keys.len()]);
let links = links(&vertex_marks, &edge_marks);
let placement = Placement {
sizes: &sizes,
pinned: &pinned,
groups: &groups,
pads: &pads,
links: &links,
keys: &job_keys,
};
let pending = jobs.spawn(Job::layout(&input.scene.layout, &placement));
self.placing = Some(Placing { pending, generation: self.generation, rows });
} else if self.arranging && self.placing.is_none() {
self.arranging = false;
self.arranged = Some(Vec::new());
}
let mut tables = Tables::default();
let mut at = 0;
for vm in &vertex_marks {
let own = at..at + vm.table.len();
at = own.end;
let place = |i: usize| pins[i].or_else(|| self.laid.get(&keys[i]).copied());
let column = |k: usize| Values::from(own.clone().map(|i| place(i).map(|p| p[k])).collect::<Vec<_>>());
let key: (Name, Name) = (vm.data.name().clone(), vm.table.name().clone());
let before = self.tables.get(&key);
let fields = [("x", column(0)), ("y", column(1))]
.map(|(name, values)| (Name::from(name), kept(name, &values, vm.table, before)));
tables.insert(key, beside(vm.table, fields));
}
self.tables = tables;
}
}