use macroquad::prelude::Vec2;
use crate::layout;
use crate::movie::Movie;
pub struct ArrayView {
pub id: String,
pub n: usize,
}
impl ArrayView {
pub fn cell(&self, i: usize) -> String {
format!("{}{}", self.id, i)
}
pub fn label(&self, i: usize) -> String {
format!("{}{}.label", self.id, i)
}
pub fn index(&self, i: usize) -> String {
format!("{}{}.idx", self.id, i)
}
}
pub fn array(m: &mut Movie, id: &str, labels: &[&str], center: Vec2) -> ArrayView {
m.scene().cells(
id,
labels.len(),
center,
Vec2::new(64.0, 64.0),
10.0,
Some(labels),
);
ArrayView {
id: id.into(),
n: labels.len(),
}
}
pub fn bit_array(m: &mut Movie, id: &str, n: usize, center: Vec2) -> ArrayView {
let zeros = vec!["0"; n];
m.scene()
.cells(id, n, center, Vec2::new(48.0, 48.0), 6.0, Some(&zeros));
ArrayView { id: id.into(), n }
}
pub fn hash_table(m: &mut Movie, id: &str, n: usize, center: Vec2) -> ArrayView {
m.scene()
.cells(id, n, center, Vec2::new(64.0, 64.0), 6.0, None);
ArrayView { id: id.into(), n }
}
pub struct LinkedListView {
pub id: String,
pub n: usize,
pub pos: Vec<Vec2>,
}
impl LinkedListView {
pub fn node(&self, i: usize) -> String {
format!("{}.n{}", self.id, i)
}
pub fn label(&self, i: usize) -> String {
format!("{}.n{}.label", self.id, i)
}
pub fn next(&self, i: usize) -> String {
format!("{}.a{}", self.id, i)
}
}
pub fn linked_list(m: &mut Movie, id: &str, labels: &[&str], start: Vec2) -> LinkedListView {
let (w, h, gap) = (72.0, 48.0, 48.0);
let stride = w + gap;
let pos: Vec<Vec2> = (0..labels.len())
.map(|i| Vec2::new(start.x + stride * i as f32, start.y))
.collect();
{
let mut s = m.scene();
for (i, label) in labels.iter().enumerate() {
s.rect(&format!("{id}.n{i}"), pos[i], w, h)
.tag(id)
.label(label);
}
for i in 0..labels.len().saturating_sub(1) {
s.arrow(
&format!("{id}.a{i}"),
pos[i] + Vec2::new(w / 2.0, 0.0),
pos[i + 1] - Vec2::new(w / 2.0, 0.0),
)
.tag(id);
}
}
LinkedListView {
id: id.into(),
n: labels.len(),
pos,
}
}
pub struct TreeView {
pub id: String,
pub n: usize,
pub pos: Vec<Vec2>,
parents: Vec<Option<usize>>,
}
impl TreeView {
pub fn node(&self, i: usize) -> String {
format!("{}.n{}", self.id, i)
}
pub fn label(&self, i: usize) -> String {
format!("{}.n{}.label", self.id, i)
}
pub fn edge(&self, i: usize) -> Option<String> {
self.parents[i].map(|_| format!("{}.e{}", self.id, i))
}
}
pub fn tree(
m: &mut Movie,
id: &str,
parents: &[Option<usize>],
labels: &[&str],
top: Vec2,
dx: f32,
dy: f32,
) -> TreeView {
assert_eq!(parents.len(), labels.len(), "one label per node");
let pos = layout::tree(parents, top, dx, dy);
{
let mut s = m.scene();
for (i, p) in parents.iter().enumerate() {
if let Some(p) = p {
s.line(&format!("{id}.e{i}"), pos[i], pos[*p]).z(-1).tag(id);
}
}
for (i, label) in labels.iter().enumerate() {
s.circle(&format!("{id}.n{i}"), pos[i], 26.0)
.tag(id)
.label(label);
}
}
TreeView {
id: id.into(),
n: parents.len(),
pos,
parents: parents.to_vec(),
}
}
pub struct GraphView {
pub id: String,
pub n: usize,
pub pos: Vec<Vec2>,
}
impl GraphView {
pub fn node(&self, i: usize) -> String {
format!("{}.n{}", self.id, i)
}
pub fn label(&self, i: usize) -> String {
format!("{}.n{}.label", self.id, i)
}
pub fn edge(&self, a: usize, b: usize) -> String {
format!("{}.e{}-{}", self.id, a, b)
}
}
pub fn graph(
m: &mut Movie,
id: &str,
labels: &[&str],
edges: &[(usize, usize)],
positions: Option<&[Vec2]>,
) -> GraphView {
let n = labels.len();
let pos: Vec<Vec2> = match positions {
Some(p) => {
assert_eq!(p.len(), n, "one position per node");
p.to_vec()
}
None => {
let center = Vec2::new(m.width as f32 / 2.0, m.height as f32 / 2.0 + 30.0);
let radius = (m.height as f32) * 0.30;
layout::graph(n, edges, center, radius, 1)
}
};
{
let mut s = m.scene();
for (a, b) in edges {
s.line(&format!("{id}.e{a}-{b}"), pos[*a], pos[*b])
.z(-1)
.tag(id);
}
for (i, label) in labels.iter().enumerate() {
s.circle(&format!("{id}.n{i}"), pos[i], 26.0)
.tag(id)
.label(label);
}
}
GraphView {
id: id.into(),
n,
pos,
}
}
pub struct HashRingView {
pub id: String,
pub n: usize,
pub center: Vec2,
pub r: f32,
fractions: Vec<f32>,
}
impl HashRingView {
pub fn ring(&self) -> String {
format!("{}.ring", self.id)
}
pub fn node(&self, i: usize) -> String {
format!("{}.n{}", self.id, i)
}
pub fn label(&self, i: usize) -> String {
format!("{}.n{}.label", self.id, i)
}
pub fn at(&self, frac: f32) -> Vec2 {
ring_point(self.center, self.r, frac)
}
pub fn node_pos(&self, i: usize) -> Vec2 {
self.at(self.fractions[i])
}
}
fn ring_point(center: Vec2, r: f32, frac: f32) -> Vec2 {
let a = std::f32::consts::TAU * frac - std::f32::consts::FRAC_PI_2;
center + Vec2::new(a.cos(), a.sin()) * r
}
pub fn hash_ring(
m: &mut Movie,
id: &str,
center: Vec2,
r: f32,
nodes: &[(f32, &str)],
) -> HashRingView {
let faded = m.theme.faded;
{
let mut s = m.scene();
s.circle(&format!("{id}.ring"), center, r)
.outlined()
.outline_color(faded)
.stroke(2.0)
.tag(id);
for (i, (frac, label)) in nodes.iter().enumerate() {
s.circle(&format!("{id}.n{i}"), ring_point(center, r, *frac), 20.0)
.tag(id)
.label(label);
}
}
HashRingView {
id: id.into(),
n: nodes.len(),
center,
r,
fractions: nodes.iter().map(|(f, _)| *f).collect(),
}
}
pub struct LsmLevelsView {
pub id: String,
pub counts: Vec<usize>,
pub pos: Vec<Vec<Vec2>>,
}
impl LsmLevelsView {
pub fn block(&self, l: usize, i: usize) -> String {
format!("{}.l{}b{}", self.id, l, i)
}
pub fn label(&self, l: usize, i: usize) -> String {
format!("{}.l{}b{}.label", self.id, l, i)
}
pub fn level_tag(&self, l: usize) -> String {
format!("{}.l{}.tag", self.id, l)
}
}
pub fn lsm_levels(m: &mut Movie, id: &str, counts: &[usize], top: Vec2) -> LsmLevelsView {
let (bw, bh) = (64.0, 38.0);
let (dx, dy) = (bw + 10.0, bh + 26.0);
let pos = layout::levels(counts, top, dx, dy);
let faded = m.theme.faded;
let widest = counts.iter().copied().max().unwrap_or(0) as f32;
let tag_x = top.x - dx * (widest - 1.0) / 2.0 - bw / 2.0 - 40.0;
{
let mut s = m.scene();
for (l, row) in pos.iter().enumerate() {
s.text(
&format!("{id}.l{l}.tag"),
Vec2::new(tag_x, top.y + l as f32 * dy),
&format!("L{l}"),
)
.size(18.0)
.color(faded)
.tag(id);
for (i, p) in row.iter().enumerate() {
s.rect(&format!("{id}.l{l}b{i}"), *p, bw, bh)
.tag(id)
.label("");
}
}
}
LsmLevelsView {
id: id.into(),
counts: counts.to_vec(),
pos,
}
}
pub struct SkipListView {
pub id: String,
pub lanes: usize,
base: Vec2,
dx: f32,
dy: f32,
}
impl SkipListView {
pub fn node(&self, k: usize, c: usize) -> String {
format!("{}.l{}n{}", self.id, k, c)
}
pub fn label(&self, k: usize, c: usize) -> String {
format!("{}.l{}n{}.label", self.id, k, c)
}
pub fn next(&self, k: usize, c: usize) -> String {
format!("{}.l{}a{}", self.id, k, c)
}
pub fn slot(&self, k: usize, c: usize) -> Vec2 {
self.base + Vec2::new(self.dx * c as f32, -self.dy * k as f32)
}
}
pub fn skip_list(
m: &mut Movie,
id: &str,
values: &[&str],
lanes: &[Vec<usize>],
base: Vec2,
) -> SkipListView {
let (w, h) = (56.0, 40.0);
let (dx, dy) = (w + 26.0, h + 30.0);
let all: Vec<usize> = (0..values.len()).collect();
{
let mut s = m.scene();
for (k, cols) in std::iter::once(&all).chain(lanes.iter()).enumerate() {
let y = base.y - dy * k as f32;
for (j, &c) in cols.iter().enumerate() {
assert!(
c < values.len(),
"lane {k} references column {c} out of range"
);
let p = Vec2::new(base.x + dx * c as f32, y);
s.rect(&format!("{id}.l{k}n{c}"), p, w, h)
.tag(id)
.label(values[c]);
if let Some(&next_c) = cols.get(j + 1) {
s.arrow(
&format!("{id}.l{k}a{c}"),
p + Vec2::new(w / 2.0, 0.0),
Vec2::new(base.x + dx * next_c as f32 - w / 2.0, y),
)
.tag(id);
}
}
}
}
SkipListView {
id: id.into(),
lanes: lanes.len(),
base,
dx,
dy,
}
}