Skip to main content

kyu_visualizer/
state.rs

1//! Application state types.
2//!
3//! These are stored as `State<T>` via `ctx.use_state_keyed()` and
4//! their signal IDs are wired to stateful container `.deps()`.
5
6use hashbrown::HashMap;
7
8/// 2D vector for positions and velocities.
9#[derive(Clone, Copy, Default, Debug)]
10pub struct Vec2 {
11    pub x: f32,
12    pub y: f32,
13}
14
15impl Vec2 {
16    pub fn new(x: f32, y: f32) -> Self {
17        Self { x, y }
18    }
19
20    pub fn length(self) -> f32 {
21        (self.x * self.x + self.y * self.y).sqrt()
22    }
23
24    pub fn normalized(self) -> Self {
25        let len = self.length();
26        if len < 1e-6 {
27            Self::default()
28        } else {
29            Self {
30                x: self.x / len,
31                y: self.y / len,
32            }
33        }
34    }
35}
36
37impl std::ops::Add for Vec2 {
38    type Output = Self;
39    fn add(self, rhs: Self) -> Self {
40        Self {
41            x: self.x + rhs.x,
42            y: self.y + rhs.y,
43        }
44    }
45}
46
47impl std::ops::AddAssign for Vec2 {
48    fn add_assign(&mut self, rhs: Self) {
49        self.x += rhs.x;
50        self.y += rhs.y;
51    }
52}
53
54impl std::ops::Sub for Vec2 {
55    type Output = Self;
56    fn sub(self, rhs: Self) -> Self {
57        Self {
58            x: self.x - rhs.x,
59            y: self.y - rhs.y,
60        }
61    }
62}
63
64impl std::ops::Mul<f32> for Vec2 {
65    type Output = Self;
66    fn mul(self, s: f32) -> Self {
67        Self {
68            x: self.x * s,
69            y: self.y * s,
70        }
71    }
72}
73
74impl std::ops::Div<f32> for Vec2 {
75    type Output = Self;
76    fn div(self, s: f32) -> Self {
77        Self {
78            x: self.x / s,
79            y: self.y / s,
80        }
81    }
82}
83
84/// The full graph data loaded into the visualizer.
85#[derive(Clone, Default)]
86pub struct GraphData {
87    pub nodes: Vec<NodeVisual>,
88    pub edges: Vec<EdgeVisual>,
89    /// Maps node id ("{table}:{pk}") → index in `nodes` for O(1) dedup.
90    pub node_index: HashMap<String, usize>,
91}
92
93impl GraphData {
94    pub fn new() -> Self {
95        Self::default()
96    }
97
98    pub fn clear(&mut self) {
99        self.nodes.clear();
100        self.edges.clear();
101        self.node_index.clear();
102    }
103}
104
105/// A single node in the visual graph.
106#[derive(Clone, Debug)]
107pub struct NodeVisual {
108    /// Unique id: "{TableName}:{pk_value}"
109    pub id: String,
110    /// Node table name (e.g. "Person").
111    pub label: String,
112    /// Pre-formatted (key, value) pairs for inspector display.
113    pub properties: Vec<(String, String)>,
114    /// Position in world coordinates.
115    pub pos: Vec2,
116    /// Velocity for force-directed layout.
117    pub vel: Vec2,
118    /// If true, FR layout skips displacement for this node.
119    pub pinned: bool,
120    /// Index into `theme::NODE_PALETTE`.
121    pub color_idx: u8,
122}
123
124/// A single edge in the visual graph.
125#[derive(Clone, Debug)]
126pub struct EdgeVisual {
127    /// Index into `GraphData.nodes` for the source node.
128    pub src: usize,
129    /// Index into `GraphData.nodes` for the target node.
130    pub dst: usize,
131    /// Relationship table name (e.g. "KNOWS").
132    pub rel_type: String,
133    /// Pre-formatted (key, value) pairs for inspector display.
134    pub properties: Vec<(String, String)>,
135}
136
137/// Camera state for pan and zoom.
138#[derive(Clone, Copy, Debug)]
139pub struct CameraState {
140    pub offset_x: f32,
141    pub offset_y: f32,
142    pub zoom: f32,
143}
144
145impl Default for CameraState {
146    fn default() -> Self {
147        Self {
148            offset_x: 0.0,
149            offset_y: 0.0,
150            zoom: 1.0,
151        }
152    }
153}
154
155impl CameraState {
156    /// Clamp zoom to [0.1, 5.0].
157    pub fn clamp_zoom(&mut self) {
158        self.zoom = self.zoom.clamp(0.1, 5.0);
159    }
160}
161
162/// Currently selected element in the graph.
163#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
164pub enum Selection {
165    #[default]
166    None,
167    Node(usize),
168    Edge(usize),
169}
170
171/// Schema data loaded from the catalog.
172#[derive(Clone, Default, Debug)]
173pub struct SchemaData {
174    pub node_tables: Vec<SchemaTable>,
175    pub rel_tables: Vec<SchemaTable>,
176}
177
178/// A table entry for the schema browser.
179#[derive(Clone, Debug)]
180pub struct SchemaTable {
181    pub name: String,
182    pub num_rows: u64,
183    pub properties: Vec<(String, String)>, // (name, type)
184}