use crate::data::{Data, Field, Inverse, Name, Reference, Row, Table, Value};
use crate::expr::cpu::Rows;
use crate::host::Key;
use crate::scene::mark::Style;
use crate::{Edges, Mark, Port, Vertices};
pub(crate) struct VertexMark<'a> {
pub(crate) mark: &'a Vertices,
pub(crate) data: &'a Data,
pub(crate) table: &'a Table,
pub(crate) first: usize,
}
pub(crate) struct EdgeMark<'a> {
pub(crate) mark: &'a Edges,
pub(crate) data: &'a Data,
pub(crate) table: &'a Table,
}
pub(crate) fn marks<'a>(input: &super::Input<'a>) -> (Vec<VertexMark<'a>>, Vec<EdgeMark<'a>>) {
let (mut vertices, mut edges): (Vec<VertexMark<'a>>, Vec<EdgeMark<'a>>) = (Vec::new(), Vec::new());
for mark in &input.scene.marks {
let style = mark.style();
let Some(data) = input.data.iter().find(|d| *d.name() == style.data) else { continue };
let Some(table) = data.table(&style.table) else { continue };
match mark {
Mark::Vertices(v) => {
let shown = VertexMark { mark: v, data, table, first: 0 };
match taken(vertices.iter().map(|vm| &vm.mark.style), style) {
None => vertices.push(shown),
Some(at) if replaces(&vertices[at].mark.style, style) => vertices[at] = shown,
Some(_) => {}
}
}
Mark::Edges(e) => {
let shown = EdgeMark { mark: e, data, table };
match taken(edges.iter().map(|em| &em.mark.style), style) {
None => edges.push(shown),
Some(at) if replaces(&edges[at].mark.style, style) => edges[at] = shown,
Some(_) => {}
}
}
Mark::Faces(_) | Mark::Lines(_) | Mark::Areas(_) => {}
}
}
let mut first = 0;
for vm in &mut vertices {
vm.first = first;
first += vm.table.len();
}
(vertices, edges)
}
fn taken<'s>(mut styles: impl Iterator<Item = &'s Style>, style: &Style) -> Option<usize> {
styles.position(|s| s.data == style.data && s.table == style.table)
}
fn replaces(before: &Style, style: &Style) -> bool {
before.name.is_some() && style.name.is_none()
}
pub(crate) fn drawing(vertex_marks: &[VertexMark<'_>], data: &Name, table: &Name) -> Option<usize> {
vertex_marks.iter().position(|vm| vm.data.name() == data && vm.table.name() == table)
}
pub(crate) fn position(mark: &Vertices, rows: &Rows<'_>, row: usize) -> Option<[f64; 2]> {
match &mark.position {
Some([x, y]) => Some([rows.number(x, row)?, rows.number(y, row)?]),
None => match rows.table.value(row, mark.point_field(), f64::INFINITY)? {
Value::Point(p) if p.iter().all(|v| v.is_finite()) => Some(p),
_ => None,
},
}
}
pub(crate) fn key_of(row: &Row) -> Key {
(row.table.clone(), row.id.clone())
}
pub(crate) fn links(marks: &[VertexMark<'_>], edge_marks: &[EdgeMark<'_>]) -> Vec<[u32; 2]> {
let codes: Vec<Vec<u32>> = marks.iter().map(|vm| port_codes(vm.mark)).collect();
let mut links = Vec::new();
for em in edge_marks {
let joins = Joins::new(marks, &codes, em);
for row in 0..em.table.len() {
if let Some([a, b]) = joins.ends(row) {
if a != b {
links.push([a as u32, b as u32]);
}
continue;
}
let terminals: Vec<(u32, bool)> =
joins.terminals(marks, row).into_iter().map(|(v, _, output)| (v as u32, output)).collect();
links.extend(net_links(&terminals));
}
}
links
}
pub(crate) struct Joins<'a> {
ends: Option<[(&'a Reference, usize); 2]>,
ports: Vec<Naming<'a>>,
}
struct Naming<'a> {
mark: usize,
code: u32,
output: bool,
inverse: &'a Inverse,
}
impl<'a> Joins<'a> {
pub(crate) fn new(marks: &[VertexMark<'a>], codes: &[Vec<u32>], em: &EdgeMark<'a>) -> Joins<'a> {
let table: &'a Table = em.table;
let end = |field: &str| {
let Some(Field::Reference(r)) = table.field_named(field) else { return None };
let m = drawing(marks, em.data.name(), &r.to)?;
Some((r, marks[m].first))
};
if let Some((from, to)) = &em.mark.ends {
return Joins { ends: end(from).zip(end(to)).map(|(a, b)| [a, b]), ports: Vec::new() };
}
let mut ports = Vec::new();
for (m, vm) in marks.iter().enumerate().filter(|(_, vm)| vm.data.name() == em.data.name()) {
let vertices: &'a Table = vm.table;
for (k, (name, port)) in vm.mark.ports.iter().enumerate() {
if let Some(Field::Reference(r)) = vertices.field_named(name)
&& r.to == *table.name()
{
ports.push(Naming { mark: m, code: codes[m][k], output: port.output, inverse: r.inverse() });
}
}
}
Joins { ends: None, ports }
}
pub(crate) fn ends(&self, row: usize) -> Option<[usize; 2]> {
let [(a, first_a), (b, first_b)] = self.ends?;
Some([first_a + a.target(row)? as usize, first_b + b.target(row)? as usize])
}
pub(crate) fn terminals(&self, marks: &[VertexMark<'_>], row: usize) -> Vec<(usize, u32, bool)> {
let mut terminals = Vec::new();
for naming in &self.ports {
let first = marks[naming.mark].first;
terminals.extend(naming.inverse.of(row).iter().map(|&v| (first + v as usize, naming.code, naming.output)));
}
terminals
}
}
pub(crate) fn port_codes(mark: &Vertices) -> Vec<u32> {
Port::codes(&mark.ports.iter().map(|(_, port)| port).collect::<Vec<_>>())
}
fn net_links(ends: &[(u32, bool)]) -> Vec<[u32; 2]> {
let (mut drivers, mut readers): (Vec<u32>, Vec<u32>) = (Vec::new(), Vec::new());
for &(vertex, drives) in ends {
if drives { drivers.push(vertex) } else { readers.push(vertex) }
}
if drivers.is_empty() || readers.is_empty() {
drivers = ends.iter().take(1).map(|e| e.0).collect();
readers = ends.iter().skip(1).map(|e| e.0).collect();
}
let (Some(&driver), Some(&reader)) = (drivers.first(), readers.first()) else { return Vec::new() };
let links = drivers.iter().map(|&d| [d, reader]).chain(readers.iter().skip(1).map(|&r| [driver, r]));
links.filter(|[a, b]| a != b).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nets_link_their_drivers_to_their_readers() {
assert_eq!(net_links(&[(0, false), (1, true), (2, false)]), vec![[1, 0], [1, 2]]);
assert_eq!(net_links(&[(4, false), (5, false)]), vec![[4, 5]], "without a driver, the first drives");
}
}