1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// Pre-existing warnings allowed at crate level (not part of current refactoring)
//! # iced_nodegraph
//!
//! A high-performance node graph editor widget for the [Iced](https://github.com/iced-rs/iced) GUI framework,
//! featuring SDF-based rendering and type-safe coordinate transformations.
//!
//! ## Features
//!
//! - **Nodes** - Draggable containers for your custom widgets
//! - **Pins** - Connection points on nodes with type checking and visual feedback
//! - **Edges** - Connect pins to build data flow graphs with type-safe [`PinRef`]
//! - **Interactive Connections** - Drag to connect, click edges to re-route (cable-like unplugging)
//! - **Selection** - Multi-select with box selection, clone (Ctrl+D), delete (Delete key)
//! - **Zoom & Pan** - Smooth infinite canvas navigation with [`Camera2D`]
//! - **SDF Rendering** - High-performance visualization via signed-distance fields (`iced_nodegraph_sdf`)
//! - **Spatial Index** - A GPU tile index culls geometry per pixel, scaling to large graphs
//! - **Pin Feedback** - Valid drop targets pulse while dragging an edge
//! - **Theme Support** - Integrates with Iced's theming system
//!
//! ## Quick Start
//!
//! ```rust,no_run
//! use iced_nodegraph::{NodeGraph, PinRef, edge, node, node_graph};
//! use iced::{Element, Theme, Point, Vector};
//! use iced::widget::text;
//! use iced_wgpu::Renderer;
//!
//! #[derive(Debug, Clone)]
//! enum Message {
//! EdgeConnected { from: PinRef<usize, usize>, to: PinRef<usize, usize> },
//! NodesMoved { delta: Vector, node_ids: Vec<usize> },
//! }
//!
//! fn view(edges: &[(PinRef<usize, usize>, PinRef<usize, usize>)]) -> Element<'_, Message, Theme, Renderer> {
//! let mut ng = node_graph()
//! .on_connect(|from, to| Message::EdgeConnected { from, to })
//! .on_move(|delta, node_ids| Message::NodesMoved { delta, node_ids });
//!
//! // Add nodes with IDs
//! ng.push_node(node(0, Point::new(100.0, 100.0), text("Node A")));
//! ng.push_node(node(1, Point::new(300.0, 100.0), text("Node B")));
//!
//! // Add edges with type-safe PinRef; edge! defaults the id to ()
//! for (from, to) in edges {
//! ng.push_edge(edge!(*from, *to));
//! }
//!
//! ng.into()
//! }
//! ```
//!
//! ## Core Types
//!
//! ### [`PinRef`]
//! Type-safe identifier for a pin connection, generic over your node/pin id types:
//! ```rust
//! use iced_nodegraph::PinRef;
//!
//! let pin = PinRef::new(0, 1); // node 0, pin 1
//! assert_eq!(pin.node_id, 0);
//! assert_eq!(pin.pin_id, 1);
//! ```
//!
//! ### [`Camera2D`]
//! Programmatic access to zoom and pan state.
//!
//! ## Styling
//!
//! Node, edge and pin styles are concrete flat structs. Override fields with
//! struct-update over a theme-derived default inside a `.style()` closure:
//!
//! ```ignore
//! node(0, pos, body).style(|theme, status| NodeStyle {
//! fill_color: ColorQuad::solid(Color::from_rgb(0.2, 0.3, 0.5)),
//! ..default_node_style(theme, status)
//! });
//! ```
//!
//! ### Ready-made presets
//!
//! Reach for these before hand-rolling a look:
//! - Nodes: [`NodeStyle::input`] (blue), [`NodeStyle::process`] (green),
//! [`NodeStyle::output`] (orange).
//! - Edges: [`EdgeStyle::error`] (red animated marching-ants with a border ring),
//! [`EdgeStyle::disabled`] (gray dashed), [`EdgeStyle::highlighted`] (yellow with
//! a soft ring), [`EdgeStyle::data_flow`] (blue), [`EdgeStyle::debug`] (dotted
//! cyan straight line).
//!
//! ```ignore
//! edge!(from, to).style(|_theme, _status, _from, _to| EdgeStyle::error());
//! ```
//!
//! ### Stroke patterns
//!
//! [`Pattern`] (re-exported from `iced_nodegraph_sdf`) controls every stroke:
//! `Pattern::solid(width)`, `Pattern::dashed(width, dash, gap)`,
//! `Pattern::dotted(spacing, radius)`, plus `.flow(speed)` to animate it along the
//! stroke. An animated pattern self-drives redraws - no host frame loop needed.
//!
//! ### Per-node status
//!
//! The style closure intentionally does not receive the node id: your `view` loop
//! already has it (and any per-node status). Derive the status there and pass it in;
//! a shared function keeps it DRY across nodes:
//!
//! ```ignore
//! for n in &self.nodes {
//! let working = self.is_working(n.id);
//! ng.push_node(node(n.id, n.pos, body).style(move |theme, status| {
//! let base = default_node_style(theme, status);
//! if working {
//! NodeStyle { border_pattern: Pattern::dashed(2.0, 6.0, 4.0).flow(40.0), ..base }
//! } else {
//! base
//! }
//! }));
//! }
//! ```
//!
//! ## Demonstration Projects
//!
//! ### [hello_world](https://github.com/tuco86/iced_nodegraph/tree/main/demos/hello_world)
//! Basic node graph with command palette:
//! - Node creation and positioning
//! - Pin connections with type colors
//! - Camera controls (pan/zoom)
//! - Theme switching with live preview
//! - Email processing workflow example
//!
//! ```bash
//! cargo run -p demo_hello_world
//! ```
//!
//! ### [styling](https://github.com/tuco86/iced_nodegraph/tree/main/demos/styling)
//! Visual customization showcase:
//! - Custom node styles (colors, borders, opacity)
//! - Live style controls with sliders
//! - Preset styles (Input, Process, Output, Comment)
//! - Theme switching
//!
//! ```bash
//! cargo run -p demo_styling
//! ```
//!
//! ### [500_nodes](https://github.com/tuco86/iced_nodegraph/tree/main/demos/500_nodes)
//! Performance benchmark with 500+ nodes:
//! - Procedural shader graph generation
//! - Stress tests GPU rendering pipeline
//! - Multiple node types and connection patterns
//!
//! ```bash
//! cargo run -p demo_500_nodes
//! ```
//!
//! ### [shader_editor](https://github.com/tuco86/iced_nodegraph/tree/main/demos/shader_editor)
//! Visual WGSL shader editor:
//! - Math, Vector, Color, Texture nodes
//! - Real-time shader compilation
//! - Command palette for node spawning
//!
//! ```bash
//! cargo run -p demo_shader_editor
//! ```
//!
//! ## Platform Support
//!
//! ### Native (Windows, macOS, Linux)
//! Full WGPU rendering via signed-distance fields (`iced_nodegraph_sdf`).
//!
//! ### WebAssembly (Browser)
//! WebGPU only - there is no WebGL fallback. Chrome/Chromium is recommended.
//!
//! ## Architecture
//!
//! ### Coordinate System
//!
//! The widget uses two distinct coordinate spaces with compile-time type safety via the
//! [`euclid`](https://docs.rs/euclid) crate:
//!
//! - **Screen Space** - Pixel coordinates from user input (mouse, viewport)
//! - **World Space** - Virtual infinite canvas where nodes exist
//!
//! Transformations use mathematically consistent formulas:
//!
//! - **Screen -> World**: `world = screen / zoom - position`
//! - **World -> Screen**: `screen = (world + position) * zoom`
//! - **Zoom at Cursor**: `new_pos = old_pos + cursor_screen * (1/new_zoom - 1/old_zoom)`
//!
//! See [`Camera2D`] for implementation details and comprehensive test coverage.
//!
//! ### SDF Rendering
//!
//! Nodes, edges, pins and overlays are drawn with signed-distance fields via the
//! in-tree `iced_nodegraph_sdf` crate - there are no hand-written vertex or
//! fragment shaders in this crate:
//! - Layered compositing for correct draw order (shadows, edges, node bodies)
//! - A GPU tile index culls geometry per pixel, scaling to large graphs
//! - Cross-platform (native WGPU + WebGPU)
//!
//! ## Interaction
//!
//! | Action | Input |
//! |--------|-------|
//! | Pan canvas | Right mouse drag |
//! | Zoom | Mouse wheel (maintains cursor position) |
//! | Connect pins | Left-click source pin, drag to target |
//! | Re-route edge | Click on edge endpoint to unplug |
//! | Move node | Left-click and drag node |
//! | Box select | Left-click on empty space, drag |
//! | Clone selection | Ctrl+D |
//! | Delete selection | Delete key |
//! | Add to selection | Shift+click |
//!
//! ### Plug Behavior
//!
//! Edge connections behave like physical plugs:
//! - **Snap**: When dragging an edge close to a compatible pin, it "snaps" and
//! `EdgeConnected` fires immediately (not on mouse release)
//! - **Unsnap**: Moving away from the snapped pin fires `EdgeDisconnected`
//! - **Release**: Releasing the mouse while snapped keeps the connection;
//! releasing while not snapped discards the drag
//!
//! ### What the host owns
//!
//! The widget is stateless between frames and never mutates your data model, so a
//! few invariants are yours to enforce:
//!
//! - **Edge dedupe.** `on_connect` fires on every snap during a drag (not on
//! release), so one drag can report several connections. The default
//! [`can_connect`](NodeGraph::can_connect) already rejects a second edge into an
//! occupied input; for replace-on-drop instead, drop that rule (see
//! [`connection`]) and remove the prior edge whose input matches in your
//! `on_connect` handler - `to` is always the input pin.
//! - **Unique node ids.** Lookups resolve to the first match, so reuse renders a
//! node doubled. Prefer a stable id from your data - a database key, `uuid::Uuid`,
//! or a typed newtype - over a hand-managed counter (collision-proof, and it
//! survives multi-client collaboration); debug builds assert uniqueness.
//! - **Applying moves/deletes/clones.** `on_move` / `on_delete` / `on_clone` report
//! intent; your model applies it and feeds the result back on the next `view`.
//!
//! ## Diagnostics
//!
//! The widget is stateless between frames - the host owns nodes, edges and
//! selection - so there are no `node_count()` / `edges()` query methods. For
//! per-frame metrics (element counts total/in-view/culled and CPU op timings),
//! register a callback with [`NodeGraph::on_info`]; it delivers a [`GraphInfo`]
//! each redraw.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
// Re-export iced_nodegraph_sdf pattern types for downstream crates
pub use Pattern;
pub use PatternType as SdfPatternType;
// Re-export iced for downstream crates
pub use iced;