leptos_helios/graph_features/
visualization.rs1use super::{GraphEdge, GraphNode};
6use std::collections::HashSet;
7
8#[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 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 pub fn add_node(&mut self, node: GraphNode) {
40 self.nodes.push(node);
41 }
42
43 pub fn add_edge(&mut self, edge: GraphEdge) {
45 self.edges.push(edge);
46 }
47
48 pub fn select_node(&mut self, node_id: &str) {
50 self.selected_nodes.insert(node_id.to_string());
51 }
52
53 pub fn deselect_node(&mut self, node_id: &str) {
55 self.selected_nodes.remove(node_id);
56 }
57
58 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 pub fn clear_selection(&mut self) {
67 self.selected_nodes.clear();
68 self.selected_edges.clear();
69 }
70
71 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 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 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 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 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 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 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 pub fn get_node(&self, node_id: &str) -> Option<&GraphNode> {
149 self.nodes.iter().find(|n| n.id == node_id)
150 }
151
152 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 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 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 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 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}