use std::collections::BTreeSet;
use crate::artist::{Artist, Grid, ScatterColor, ScatterSize};
use crate::edit::{Edit, NodeTree, Transaction};
use crate::figure::Figure;
use crate::ids::{DataId, NodeId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Selection {
pub node: NodeId,
pub indices: Option<BTreeSet<usize>>,
}
impl Selection {
pub fn node(node: NodeId) -> Self {
Self {
node,
indices: None,
}
}
pub fn points(node: NodeId, indices: impl IntoIterator<Item = usize>) -> Self {
Self {
node,
indices: Some(indices.into_iter().collect()),
}
}
pub fn updated(&self, before: &Figure, transaction: &Transaction) -> Option<Selection> {
let mut tree = NodeTree::of(before);
let arrays = arrays_of(before, self.node);
let mut indices = self.indices.clone();
let mut window = arrays.as_ref().and_then(|arrays| {
let array = before.data.get(&arrays.primary)?;
let (&entries, rest) = array.shape.split_first()?;
Some((entries, rest.iter().product::<usize>()))
});
for edit in &transaction.edits {
match edit {
Edit::Remove { node } => {
if tree.subtree(*node).contains(&self.node) {
return None;
}
tree.apply(edit);
}
Edit::PutData { id, .. } => {
if arrays
.as_ref()
.is_some_and(|arrays| arrays.all.contains(id))
{
indices = None;
}
}
Edit::AppendData { id, array, retain } => {
let primary = arrays.as_ref().is_some_and(|arrays| arrays.primary == *id);
if let Some((entries, values)) = &mut window
&& primary
{
*entries += array.shape.first().copied().unwrap_or_default();
let kept = retain.map_or(*entries, |retain| {
usize::try_from(retain).unwrap_or(usize::MAX)
});
if kept < *entries {
let discarded = (*entries - kept) * *values;
*entries = kept;
if let Some(indices) = &mut indices {
*indices = indices
.iter()
.filter_map(|i| i.checked_sub(discarded))
.collect();
}
}
}
}
_ => tree.apply(edit),
}
}
Some(Selection {
node: self.node,
indices,
})
}
}
struct Arrays {
primary: DataId,
all: Vec<DataId>,
}
fn arrays_of(figure: &Figure, node: NodeId) -> Option<Arrays> {
let (_, artist) = figure.artist(node)?;
let mut all = Vec::new();
let primary = match artist {
Artist::Line(a) => {
all.extend([a.x, a.y]);
all.extend(a.z);
a.x
}
Artist::Scatter(a) => {
all.extend([a.x, a.y]);
all.extend(a.z);
if let ScatterSize::Data { data } = a.size {
all.push(data);
}
if let ScatterColor::Data { data } = a.color {
all.push(data);
}
a.x
}
Artist::Quiver(a) => {
all.extend([a.x, a.y, a.u, a.v]);
all.extend(a.z);
all.extend(a.w);
a.x
}
Artist::Contour(a) => {
let (x, y) = grid_data(a.grid);
all.extend([x, y, a.z]);
a.z
}
Artist::Surface(a) => {
let (x, y) = grid_data(a.grid);
all.extend([x, y, a.z]);
all.extend(a.c);
a.z
}
};
Some(Arrays { primary, all })
}
fn grid_data(grid: Grid) -> (DataId, DataId) {
match grid {
Grid::Rectilinear { x, y } | Grid::Curvilinear { x, y } => (x, y),
}
}