leptos_helios/graph_features/
visualization.rs

1//! Interactive Graph Manipulation
2//!
3//! This module provides interactive graph manipulation capabilities for visualization.
4
5use super::{GraphEdge, GraphNode};
6use std::collections::HashSet;
7
8/// Interactive graph manipulation system
9#[derive(Debug, Clone)]
10pub struct GraphManipulator {
11    pub nodes: Vec<GraphNode>,
12    pub edges: Vec<GraphEdge>,
13    pub selected_nodes: HashSet<String>,
14    pub selected_edges: HashSet<String>,
15    pub is_dragging: bool,
16    pub drag_start_x: f64,
17    pub drag_start_y: f64,
18    pub drag_offset_x: f64,
19    pub drag_offset_y: f64,
20}
21
22impl GraphManipulator {
23    /// Create a new graph manipulator
24    pub fn new() -> Self {
25        Self {
26            nodes: Vec::new(),
27            edges: Vec::new(),
28            selected_nodes: HashSet::new(),
29            selected_edges: HashSet::new(),
30            is_dragging: false,
31            drag_start_x: 0.0,
32            drag_start_y: 0.0,
33            drag_offset_x: 0.0,
34            drag_offset_y: 0.0,
35        }
36    }
37
38    /// Add a node to the graph
39    pub fn add_node(&mut self, node: GraphNode) {
40        self.nodes.push(node);
41    }
42
43    /// Add an edge to the graph
44    pub fn add_edge(&mut self, edge: GraphEdge) {
45        self.edges.push(edge);
46    }
47
48    /// Select a node
49    pub fn select_node(&mut self, node_id: &str) {
50        self.selected_nodes.insert(node_id.to_string());
51    }
52
53    /// Deselect a node
54    pub fn deselect_node(&mut self, node_id: &str) {
55        self.selected_nodes.remove(node_id);
56    }
57
58    /// Select multiple nodes
59    pub fn select_nodes(&mut self, node_ids: &[String]) {
60        for node_id in node_ids {
61            self.selected_nodes.insert(node_id.clone());
62        }
63    }
64
65    /// Clear all selections
66    pub fn clear_selection(&mut self) {
67        self.selected_nodes.clear();
68        self.selected_edges.clear();
69    }
70
71    /// Start dragging a node
72    pub fn start_drag(&mut self, node_id: &str, x: f64, y: f64) {
73        if let Some(node) = self.nodes.iter().find(|n| n.id == node_id) {
74            self.is_dragging = true;
75            self.drag_start_x = x;
76            self.drag_start_y = y;
77            self.drag_offset_x = x - node.x;
78            self.drag_offset_y = y - node.y;
79        }
80    }
81
82    /// Drag to a new position
83    pub fn drag_to(&mut self, x: f64, y: f64) {
84        if self.is_dragging {
85            let dx = x - self.drag_start_x;
86            let dy = y - self.drag_start_y;
87
88            for node_id in &self.selected_nodes {
89                if let Some(node) = self.nodes.iter_mut().find(|n| n.id == *node_id) {
90                    node.x += dx;
91                    node.y += dy;
92                }
93            }
94
95            self.drag_start_x = x;
96            self.drag_start_y = y;
97        }
98    }
99
100    /// End dragging
101    pub fn end_drag(&mut self) {
102        self.is_dragging = false;
103        self.drag_offset_x = 0.0;
104        self.drag_offset_y = 0.0;
105    }
106
107    /// Create an edge between two nodes
108    pub fn create_edge(&mut self, source: &str, target: &str, weight: f64) -> Option<GraphEdge> {
109        if self.nodes.iter().any(|n| n.id == source)
110            && self.nodes.iter().any(|n| n.id == target)
111            && source != target
112        {
113            let edge = GraphEdge::new(source, target, weight);
114            self.edges.push(edge.clone());
115            Some(edge)
116        } else {
117            None
118        }
119    }
120
121    /// Delete a node and its associated edges
122    pub fn delete_node(&mut self, node_id: &str) {
123        self.nodes.retain(|n| n.id != node_id);
124        self.edges
125            .retain(|e| e.source != node_id && e.target != node_id);
126        self.selected_nodes.remove(node_id);
127    }
128
129    /// Delete an edge
130    pub fn delete_edge(&mut self, source: &str, target: &str) {
131        self.edges
132            .retain(|e| !(e.source == source && e.target == target));
133    }
134
135    /// Duplicate a node
136    pub fn duplicate_node(&mut self, node_id: &str) -> Option<GraphNode> {
137        if let Some(node) = self.nodes.iter().find(|n| n.id == node_id) {
138            let new_id = format!("{}_copy", node_id);
139            let new_node = GraphNode::new(&new_id, node.x + 20.0, node.y + 20.0);
140            self.nodes.push(new_node.clone());
141            Some(new_node)
142        } else {
143            None
144        }
145    }
146
147    /// Get a node by ID
148    pub fn get_node(&self, node_id: &str) -> Option<&GraphNode> {
149        self.nodes.iter().find(|n| n.id == node_id)
150    }
151
152    /// Get a mutable reference to a node by ID
153    pub fn get_node_mut(&mut self, node_id: &str) -> Option<&mut GraphNode> {
154        self.nodes.iter_mut().find(|n| n.id == node_id)
155    }
156
157    /// Get node position
158    pub fn get_node_position(&self, node_id: &str) -> (f64, f64) {
159        if let Some(node) = self.nodes.iter().find(|n| n.id == node_id) {
160            (node.x, node.y)
161        } else {
162            (0.0, 0.0)
163        }
164    }
165
166    /// Set node position
167    pub fn set_node_position(&mut self, node_id: &str, x: f64, y: f64) {
168        if let Some(node) = self.nodes.iter_mut().find(|n| n.id == node_id) {
169            node.x = x;
170            node.y = y;
171        }
172    }
173
174    /// Find nodes within a radius of a point
175    pub fn find_nodes_in_radius(&self, x: f64, y: f64, radius: f64) -> Vec<String> {
176        self.nodes
177            .iter()
178            .filter(|node| {
179                let dx = node.x - x;
180                let dy = node.y - y;
181                (dx * dx + dy * dy).sqrt() <= radius
182            })
183            .map(|node| node.id.clone())
184            .collect()
185    }
186
187    /// Get all connected nodes
188    pub fn get_connected_nodes(&self, node_id: &str) -> Vec<String> {
189        let mut connected = Vec::new();
190
191        for edge in &self.edges {
192            if edge.source == node_id {
193                connected.push(edge.target.clone());
194            } else if edge.target == node_id {
195                connected.push(edge.source.clone());
196            }
197        }
198
199        connected
200    }
201}