1use derivative::Derivative;
47use egui::UiBuilder;
48use std::collections::HashMap;
49
50mod link;
51mod node;
52mod pin;
53mod style;
54
55use link::*;
56use node::*;
57use pin::*;
58
59pub use {
60 link::LinkArgs,
61 node::{NodeArgs, NodeConstructor},
62 pin::{AttributeFlags, PinArgs, PinShape},
63 style::{ColorStyle, Style, StyleFlags, StyleVar},
64};
65
66#[derive(Derivative)]
68#[derivative(Default, Debug)]
69pub struct Context {
70 node_idx_submission_order: Vec<usize>,
71 node_indices_overlapping_with_mouse: Vec<usize>,
72 occluded_pin_indices: Vec<usize>,
73
74 canvas_origin_screen_space: egui::Vec2,
75 #[derivative(Default(value = "[[0.0; 2].into(); 2].into()"))]
76 canvas_rect_screen_space: egui::Rect,
77
78 #[derivative(Debug = "ignore")]
79 pub io: IO,
80 #[derivative(Debug = "ignore")]
81 pub style: Style,
82 color_modifier_stack: Vec<ColorStyleElement>,
83 style_modifier_stack: Vec<StyleElement>,
84 text_buffer: String,
85
86 current_attribute_flags: usize,
87 attribute_flag_stack: Vec<usize>,
88
89 hovered_node_index: Option<usize>,
90 interactive_node_index: Option<usize>,
91 hovered_link_idx: Option<usize>,
92 hovered_pin_index: Option<usize>,
93 hovered_pin_flags: usize,
94 ui_element_hovered: bool,
95
96 deleted_link_idx: Option<usize>,
97 snap_link_idx: Option<usize>,
98
99 element_state_change: usize,
100
101 active_attribute_id: usize,
102 active_attribute: bool,
103
104 mouse_pos: egui::Pos2,
105 mouse_delta: egui::Vec2,
106
107 left_mouse_clicked: bool,
108 left_mouse_released: bool,
109 alt_mouse_clicked: bool,
110 left_mouse_dragging: bool,
111 alt_mouse_dragging: bool,
112 mouse_in_canvas: bool,
113 link_detatch_with_modifier_click: bool,
114
115 nodes: ObjectPool<NodeData>,
116 pins: ObjectPool<PinData>,
117 links: ObjectPool<LinkData>,
118 nodes_map: HashMap<usize, usize>,
119 nodes_free: Vec<usize>,
120
121 node_depth_order: Vec<usize>,
122
123 panning: egui::Vec2,
124
125 selected_node_indices: Vec<usize>,
126 selected_link_indices: Vec<usize>,
127
128 #[derivative(Default(value = "ClickInteractionType::None"))]
129 click_interaction_type: ClickInteractionType,
130 click_interaction_state: ClickInteractionState,
131}
132
133impl Context {
134 pub fn show<'a>(
136 &mut self,
137 nodes: impl IntoIterator<Item = NodeConstructor<'a>>,
138 links: impl IntoIterator<Item = (usize, usize, usize, LinkArgs)>,
139 ui: &mut egui::Ui,
140 ) -> egui::Response {
141 let rect = ui.available_rect_before_wrap();
142 self.canvas_rect_screen_space = rect;
143 self.canvas_origin_screen_space = self.canvas_rect_screen_space.min.to_vec2();
144 {
145 self.nodes.reset();
146 self.pins.reset();
147 self.links.reset();
148
149 self.hovered_node_index.take();
150 self.interactive_node_index.take();
151 self.hovered_link_idx.take();
152 self.hovered_pin_flags = AttributeFlags::None as usize;
153 self.deleted_link_idx.take();
154 self.snap_link_idx.take();
155
156 self.node_indices_overlapping_with_mouse.clear();
157 self.element_state_change = ElementStateChange::None as usize;
158
159 self.active_attribute = false;
160 }
161
162 {
163 ui.set_min_size(self.canvas_rect_screen_space.size());
164 let mut ui = ui.new_child(
165 UiBuilder::new()
166 .max_rect(self.canvas_rect_screen_space)
167 .layout(egui::Layout::top_down(egui::Align::Center)),
168 );
169 {
170 let ui = &mut ui;
171 let screen_rect = ui.ctx().screen_rect();
172 ui.set_clip_rect(self.canvas_rect_screen_space.intersect(screen_rect));
173 ui.painter().rect_filled(
174 self.canvas_rect_screen_space,
175 0.0,
176 self.style.colors[ColorStyle::GridBackground as usize],
177 );
178
179 if (self.style.flags & StyleFlags::GridLines as usize) != 0 {
180 self.draw_grid(self.canvas_rect_screen_space.size(), ui);
181 }
182
183 let links = links.into_iter().collect::<Vec<_>>();
184 for (id, start, end, args) in links {
185 self.add_link(id, start, end, args, ui);
186 }
187
188 let mut nodes = nodes
189 .into_iter()
190 .map(|x| (self.node_pool_find_or_create_index(x.id, x.pos), x))
191 .collect::<HashMap<_, _>>();
192 for idx in self.node_depth_order.clone() {
193 if let Some(node_builder) = nodes.remove(&idx) {
194 self.add_node(idx, node_builder, ui);
195 }
196 }
197 }
198 let response = ui.interact(
199 self.canvas_rect_screen_space,
200 ui.id().with("Input"),
201 egui::Sense::click_and_drag(),
202 );
203 let io = ui.ctx().input(|i| i.clone());
204 let mouse_pos = if let Some(mouse_pos) = response.hover_pos() {
205 self.mouse_in_canvas = true;
206 mouse_pos
207 } else {
208 self.mouse_in_canvas = false;
209 self.mouse_pos
210 };
211 self.mouse_delta = mouse_pos - self.mouse_pos;
212 self.mouse_pos = mouse_pos;
213 let left_mouse_clicked = io.pointer.button_down(egui::PointerButton::Primary);
214 self.left_mouse_released =
215 (self.left_mouse_clicked || self.left_mouse_dragging) && !left_mouse_clicked;
216 self.left_mouse_dragging =
217 (self.left_mouse_clicked || self.left_mouse_dragging) && left_mouse_clicked;
218 self.left_mouse_clicked =
219 left_mouse_clicked && !(self.left_mouse_clicked || self.left_mouse_dragging);
220
221 let alt_mouse_clicked = self.io.emulate_three_button_mouse.is_active(&io.modifiers)
222 || self.io.alt_mouse_button.is_some_and(|x| io.pointer.button_down(x));
223 self.alt_mouse_dragging =
224 (self.alt_mouse_clicked || self.alt_mouse_dragging) && alt_mouse_clicked;
225 self.alt_mouse_clicked =
226 alt_mouse_clicked && !(self.alt_mouse_clicked || self.alt_mouse_dragging);
227 self.link_detatch_with_modifier_click =
228 self.io.link_detatch_with_modifier_click.is_active(&io.modifiers);
229 {
230 let ui = &mut ui;
231 if self.mouse_in_canvas {
232 self.resolve_occluded_pins();
233 self.resolve_hovered_pin();
234
235 if self.hovered_pin_index.is_none() {
236 self.resolve_hovered_node();
237 }
238
239 if self.hovered_node_index.is_none() {
240 self.resolve_hovered_link();
241 }
242 }
243
244 for node_idx in self.node_depth_order.clone() {
245 if self.nodes.in_use[node_idx] {
246 self.draw_node(node_idx, ui);
247 }
248 }
249
250 for (link_idx, in_use) in self.links.in_use.clone().into_iter().enumerate() {
251 if in_use {
252 self.draw_link(link_idx, ui);
253 }
254 }
255
256 if self.left_mouse_clicked || self.alt_mouse_clicked {
257 self.begin_canvas_interaction();
258 }
259
260 self.click_interaction_update(ui);
261
262 self.node_pool_update();
263 self.pins.update();
264 self.links.update();
265 }
266 ui.painter().rect_stroke(
267 self.canvas_rect_screen_space,
268 0.0,
269 (1.0, self.style.colors[ColorStyle::GridLine as usize]),
270 egui::StrokeKind::Inside,
271 );
272 response
273 }
274 }
275
276 pub fn attribute_flag_push(&mut self, flag: AttributeFlags) {
279 self.attribute_flag_stack.push(self.current_attribute_flags);
280 self.current_attribute_flags |= flag as usize;
281 }
282
283 pub fn attribute_flag_pop(&mut self) {
285 if let Some(flags) = self.attribute_flag_stack.pop() {
286 self.current_attribute_flags = flags;
287 }
288 }
289
290 pub fn color_style_push(&mut self, item: ColorStyle, color: egui::Color32) {
292 self.color_modifier_stack.push(ColorStyleElement::new(
293 self.style.colors[item as usize],
294 item,
295 ));
296 self.style.colors[item as usize] = color;
297 }
298
299 pub fn color_style_pop(&mut self) {
301 if let Some(elem) = self.color_modifier_stack.pop() {
302 self.style.colors[elem.item as usize] = elem.color;
303 }
304 }
305
306 pub fn style_var_push(&mut self, item: StyleVar, value: f32) {
308 let style_var = self.lookup_style_var(item);
309 let elem = StyleElement::new(*style_var, item);
310 *style_var = value;
311 self.style_modifier_stack.push(elem);
312 }
313
314 pub fn style_var_pop(&mut self) {
316 if let Some(elem) = self.style_modifier_stack.pop() {
317 let style_var = self.lookup_style_var(elem.item);
318 *style_var = elem.value;
319 }
320 }
321
322 pub fn set_node_pos_screen_space(&mut self, node_id: usize, screen_space_pos: egui::Pos2) {
323 let idx = self.node_pool_find_or_create_index(node_id, None);
324 self.nodes.pool[idx].origin = self.screen_space_to_grid_space(screen_space_pos);
325 }
326
327 pub fn set_node_pos_editor_space(&mut self, node_id: usize, editor_space_pos: egui::Pos2) {
328 let idx = self.node_pool_find_or_create_index(node_id, None);
329 self.nodes.pool[idx].origin = self.editor_space_to_grid_spcae(editor_space_pos);
330 }
331
332 pub fn set_node_pos_grid_space(&mut self, node_id: usize, grid_pos: egui::Pos2) {
333 let idx = self.node_pool_find_or_create_index(node_id, None);
334 self.nodes.pool[idx].origin = grid_pos;
335 }
336
337 pub fn set_node_draggable(&mut self, node_id: usize, draggable: bool) {
338 let idx = self.node_pool_find_or_create_index(node_id, None);
339 self.nodes.pool[idx].draggable = draggable;
340 }
341
342 pub fn get_node_pos_screen_space(&self, node_id: usize) -> Option<egui::Pos2> {
343 self.nodes.find(node_id).map(|x| self.grid_space_to_screen_space(self.nodes.pool[x].origin))
344 }
345
346 pub fn get_node_pos_editor_space(&self, node_id: usize) -> Option<egui::Pos2> {
347 self.nodes.find(node_id).map(|x| self.grid_space_to_editor_spcae(self.nodes.pool[x].origin))
348 }
349
350 pub fn get_node_pos_grid_space(&self, node_id: usize) -> Option<egui::Pos2> {
351 self.nodes.find(node_id).map(|x| self.nodes.pool[x].origin)
352 }
353
354 pub fn node_hovered(&self) -> Option<usize> {
356 self.hovered_node_index.map(|x| self.nodes.pool[x].id)
357 }
358
359 pub fn link_hovered(&self) -> Option<usize> {
361 self.hovered_link_idx.map(|x| self.links.pool[x].id)
362 }
363
364 pub fn pin_hovered(&self) -> Option<usize> {
366 self.hovered_pin_index.map(|x| self.pins.pool[x].id)
367 }
368
369 pub fn num_selected_nodes(&self) -> usize {
370 self.selected_link_indices.len()
371 }
372
373 pub fn get_selected_nodes(&self) -> Vec<usize> {
374 self.selected_node_indices.iter().map(|x| self.nodes.pool[*x].id).collect()
375 }
376
377 pub fn get_selected_links(&self) -> Vec<usize> {
378 self.selected_link_indices.iter().map(|x| self.links.pool[*x].id).collect()
379 }
380
381 pub fn clear_node_selection(&mut self) {
382 self.selected_node_indices.clear()
383 }
384
385 pub fn clear_link_selection(&mut self) {
386 self.selected_link_indices.clear()
387 }
388
389 pub fn active_attribute(&self) -> Option<usize> {
391 if self.active_attribute {
392 Some(self.active_attribute_id)
393 } else {
394 None
395 }
396 }
397
398 pub fn link_started(&self) -> Option<usize> {
400 if (self.element_state_change & ElementStateChange::LinkStarted as usize) != 0 {
401 Some(self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx].id)
402 } else {
403 None
404 }
405 }
406
407 pub fn link_dropped(&self, including_detached_links: bool) -> Option<usize> {
409 if (self.element_state_change & ElementStateChange::LinkDropped as usize) != 0
410 && (including_detached_links
411 || self.click_interaction_state.link_creation.link_creation_type
412 != LinkCreationType::FromDetach)
413 {
414 Some(self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx].id)
415 } else {
416 None
417 }
418 }
419
420 pub fn link_created(&self) -> Option<(usize, usize, bool)> {
423 if (self.element_state_change & ElementStateChange::LinkCreated as usize) != 0 {
424 let (start_pin_id, end_pin_id) = {
425 let start_pin =
426 &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
427 let end_pin = &self.pins.pool
428 [self.click_interaction_state.link_creation.end_pin_index.unwrap()];
429 if start_pin.kind == AttributeType::Output {
430 (start_pin.id, end_pin.id)
431 } else {
432 (end_pin.id, start_pin.id)
433 }
434 };
435 let created_from_snap =
436 self.click_interaction_type == ClickInteractionType::LinkCreation;
437 Some((start_pin_id, end_pin_id, created_from_snap))
438 } else {
439 None
440 }
441 }
442
443 pub fn link_created_node(&self) -> Option<(usize, usize, usize, usize, bool)> {
446 if (self.element_state_change & ElementStateChange::LinkCreated as usize) != 0 {
447 let (start_pin_id, start_node_id, end_pin_id, end_node_id) = {
448 let start_pin =
449 &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
450 let end_pin = &self.pins.pool
451 [self.click_interaction_state.link_creation.end_pin_index.unwrap()];
452 let start_node = &self.nodes.pool[start_pin.parent_node_idx];
453 let end_node = &self.nodes.pool[end_pin.parent_node_idx];
454 if start_pin.kind == AttributeType::Output {
455 (start_pin.id, start_node.id, end_pin.id, end_node.id)
456 } else {
457 (end_pin.id, end_node.id, start_pin.id, start_node.id)
458 }
459 };
460 let created_from_snap =
461 self.click_interaction_type == ClickInteractionType::LinkCreation;
462 Some((
463 start_pin_id,
464 start_node_id,
465 end_pin_id,
466 end_node_id,
467 created_from_snap,
468 ))
469 } else {
470 None
471 }
472 }
473
474 pub fn link_destroyed(&self) -> Option<usize> {
476 self.deleted_link_idx
477 }
478
479 pub fn get_panning(&self) -> egui::Vec2 {
480 self.panning
481 }
482
483 pub fn reset_panniing(&mut self, panning: egui::Vec2) {
484 self.panning = panning;
485 }
486
487 pub fn get_node_dimensions(&self, id: usize) -> Option<egui::Vec2> {
488 self.nodes.find(id).map(|x| self.nodes.pool[x].rect.size())
489 }
490}
491
492impl Context {
493 fn add_node(
494 &mut self,
495 idx: usize,
496 NodeConstructor {
497 id,
498 title,
499 attributes,
500 pos: _,
501 args,
502 }: NodeConstructor<'_>,
503 ui: &mut egui::Ui,
504 ) {
505 let node = &mut self.nodes.pool[idx];
506 self.style.format_node(node, args);
507 node.background_shape.replace(ui.painter().add(egui::Shape::Noop));
508 node.id = id;
509 let node_origin = node.origin;
510 let node_size = node.size;
511 let title_space = node.layout_style.padding.y;
512
513 let response = ui.scope_builder(
514 UiBuilder::new().max_rect(egui::Rect::from_min_size(
515 self.grid_space_to_screen_space(node_origin),
516 node_size,
517 )),
518 |ui| {
519 let mut title_info = None;
520 if let Some(title) = title {
521 let titlebar_shape = ui.painter().add(egui::Shape::Noop);
522 let response = ui.allocate_ui(ui.available_size(), title);
523 let title_bar_content_rect = response.response.rect;
524 title_info.replace((titlebar_shape, title_bar_content_rect));
525 ui.add_space(title_space);
526 }
527 let outline_shape = ui.painter().add(egui::Shape::Noop);
528 for (id, kind, args, attribute) in attributes {
529 let response = ui.allocate_ui(ui.available_size(), attribute);
530 let shape = ui.painter().add(egui::Shape::Noop);
531 let response = response.response.union(response.inner);
532 self.add_attribute(id, kind, args, response, idx, shape);
533 }
534 (title_info, outline_shape)
535 },
536 );
537 let node = &mut self.nodes.pool[idx];
538 let (title_info, outline_shape) = response.inner;
539 if let Some((titlebar_shape, title_bar_content_rect)) = title_info {
540 node.titlebar_shape.replace(titlebar_shape);
541 node.title_bar_content_rect = title_bar_content_rect;
542 }
543 node.outline_shape.replace(outline_shape);
544 node.rect = response.response.rect.expand2(node.layout_style.padding);
545 if response.response.hovered() || ui.rect_contains_pointer(node.rect) {
546 self.node_indices_overlapping_with_mouse.push(idx);
547 }
548 }
549
550 fn add_attribute(
551 &mut self,
552 id: usize,
553 kind: AttributeType,
554 args: PinArgs,
555 response: egui::Response,
556 node_idx: usize,
557 shape: egui::layers::ShapeIdx,
558 ) {
559 if kind != AttributeType::None {
560 let pin_idx = self.pins.find_or_create_index(id);
561 let pin = &mut self.pins.pool[pin_idx];
562 pin.id = id;
563 pin.parent_node_idx = node_idx;
564 pin.kind = kind;
565 pin.shape_gui.replace(shape);
566 self.style.format_pin(pin, args, self.current_attribute_flags);
567 self.pins.pool[pin_idx].attribute_rect = response.rect;
568 self.nodes.pool[node_idx].pin_indices.push(pin_idx);
569 }
570
571 if response.is_pointer_button_down_on() {
572 self.active_attribute = true;
573 self.active_attribute_id = id;
574 self.interactive_node_index.replace(node_idx);
575 }
576 }
577
578 fn add_link(
579 &mut self,
580 id: usize,
581 start_attr_id: usize,
582 end_attr_id: usize,
583 args: LinkArgs,
584 ui: &mut egui::Ui,
585 ) {
586 let link_idx = self.links.find_or_create_index(id);
587 let link = &mut self.links.pool[link_idx];
588 link.id = id;
589 link.start_pin_index = self.pins.find_or_create_index(start_attr_id);
590 link.end_pin_index = self.pins.find_or_create_index(end_attr_id);
591 link.shape.replace(ui.painter().add(egui::Shape::Noop));
592 self.style.format_link(link, args);
593
594 if (self.click_interaction_type == ClickInteractionType::LinkCreation
595 && (self.pins.pool[link.end_pin_index].flags
596 & AttributeFlags::EnableLinkCreationOnSnap as usize)
597 != 0
598 && self.click_interaction_state.link_creation.start_pin_idx == link.start_pin_index
599 && self.click_interaction_state.link_creation.end_pin_index == Some(link.end_pin_index))
600 || (self.click_interaction_state.link_creation.start_pin_idx == link.end_pin_index
601 && self.click_interaction_state.link_creation.end_pin_index
602 == Some(link.start_pin_index))
603 {
604 self.snap_link_idx.replace(link_idx);
605 }
606 }
607
608 fn lookup_style_var(&mut self, item: StyleVar) -> &mut f32 {
609 match item {
610 StyleVar::GridSpacing => &mut self.style.grid_spacing,
611 StyleVar::NodeCornerRounding => &mut self.style.node_corner_rounding,
612 StyleVar::NodePaddingHorizontal => &mut self.style.node_padding_horizontal,
613 StyleVar::NodePaddingVertical => &mut self.style.node_padding_vertical,
614 StyleVar::NodeBorderThickness => &mut self.style.node_border_thickness,
615 StyleVar::LinkThickness => &mut self.style.link_thickness,
616 StyleVar::LinkLineSegmentsPerLength => &mut self.style.link_line_segments_per_length,
617 StyleVar::LinkHoverDistance => &mut self.style.link_hover_distance,
618 StyleVar::PinCircleRadius => &mut self.style.pin_circle_radius,
619 StyleVar::PinQuadSideLength => &mut self.style.pin_quad_side_length,
620 StyleVar::PinTriangleSideLength => &mut self.style.pin_triangle_side_length,
621 StyleVar::PinLineThickness => &mut self.style.pin_line_thickness,
622 StyleVar::PinHoverRadius => &mut self.style.pin_hover_radius,
623 StyleVar::PinOffset => &mut self.style.pin_offset,
624 }
625 }
626
627 fn draw_grid(&self, canvas_size: egui::Vec2, ui: &mut egui::Ui) {
628 let mut x = self.panning.x.rem_euclid(self.style.grid_spacing);
629 while x < canvas_size.x {
630 ui.painter().line_segment(
631 [
632 self.editor_space_to_screen_space([x, 0.0].into()),
633 self.editor_space_to_screen_space([x, canvas_size.y].into()),
634 ],
635 (1.0, self.style.colors[ColorStyle::GridLine as usize]),
636 );
637 x += self.style.grid_spacing;
638 }
639
640 let mut y = self.panning.y.rem_euclid(self.style.grid_spacing);
641 while y < canvas_size.y {
642 ui.painter().line_segment(
643 [
644 self.editor_space_to_screen_space([0.0, y].into()),
645 self.editor_space_to_screen_space([canvas_size.x, y].into()),
646 ],
647 (1.0, self.style.colors[ColorStyle::GridLine as usize]),
648 );
649 y += self.style.grid_spacing;
650 }
651 }
652
653 fn screen_space_to_grid_space(&self, v: egui::Pos2) -> egui::Pos2 {
654 v - self.canvas_origin_screen_space - self.panning
655 }
656
657 fn grid_space_to_screen_space(&self, v: egui::Pos2) -> egui::Pos2 {
658 v + self.canvas_origin_screen_space + self.panning
659 }
660
661 fn grid_space_to_editor_spcae(&self, v: egui::Pos2) -> egui::Pos2 {
662 v + self.panning
663 }
664
665 fn editor_space_to_grid_spcae(&self, v: egui::Pos2) -> egui::Pos2 {
666 v - self.panning
667 }
668
669 fn editor_space_to_screen_space(&self, v: egui::Pos2) -> egui::Pos2 {
670 v + self.canvas_origin_screen_space
671 }
672
673 fn get_screen_space_pin_coordinates(&self, pin: &PinData) -> egui::Pos2 {
674 let parent_node_rect = self.nodes.pool[pin.parent_node_idx].rect;
675 self.style.get_screen_space_pin_coordinates(
676 &parent_node_rect,
677 &pin.attribute_rect,
678 pin.kind,
679 )
680 }
681
682 fn resolve_occluded_pins(&mut self) {
683 self.occluded_pin_indices.clear();
684 let depth_stack = &self.node_depth_order;
685 if depth_stack.len() < 2 {
686 return;
687 }
688
689 for depth_idx in 0..(depth_stack.len() - 1) {
690 let node_below = &self.nodes.pool[depth_stack[depth_idx]];
691 for next_depth in &depth_stack[(depth_idx + 1)..(depth_stack.len())] {
692 let rect_above = self.nodes.pool[*next_depth].rect;
693 for idx in node_below.pin_indices.iter() {
694 let pin_pos = self.pins.pool[*idx].pos;
695 if rect_above.contains(pin_pos) {
696 self.occluded_pin_indices.push(*idx);
697 }
698 }
699 }
700 }
701 }
702
703 fn resolve_hovered_pin(&mut self) {
704 let mut smallest_distance = f32::MAX;
705 self.hovered_pin_index.take();
706
707 let hover_radius_sqr = self.style.pin_hover_radius.powi(2);
708 for idx in 0..self.pins.pool.len() {
709 if !self.pins.in_use[idx] || self.occluded_pin_indices.contains(&idx) {
710 continue;
711 }
712
713 let pin_pos = self.pins.pool[idx].pos;
714 let distance_sqr = (pin_pos - self.mouse_pos).length_sq();
715 if distance_sqr < hover_radius_sqr && distance_sqr < smallest_distance {
716 smallest_distance = distance_sqr;
717 self.hovered_pin_index.replace(idx);
718 }
719 }
720 }
721
722 fn resolve_hovered_node(&mut self) {
723 match self.node_indices_overlapping_with_mouse.len() {
724 0 => {
725 self.hovered_node_index.take();
726 }
727 1 => {
728 self.hovered_node_index.replace(self.node_indices_overlapping_with_mouse[0]);
729 }
730 _ => {
731 let mut largest_depth_idx = -1;
732
733 for node_idx in self.node_indices_overlapping_with_mouse.iter() {
734 for (depth_idx, depth_node_idx) in self.node_depth_order.iter().enumerate() {
735 if *depth_node_idx == *node_idx && depth_idx as isize > largest_depth_idx {
736 largest_depth_idx = depth_idx as isize;
737 self.hovered_node_index.replace(*node_idx);
738 }
739 }
740 }
741 }
742 }
743 }
744
745 fn resolve_hovered_link(&mut self) {
746 let mut smallest_distance = f32::MAX;
747 self.hovered_link_idx.take();
748
749 for idx in 0..self.links.pool.len() {
750 if !self.links.in_use[idx] {
751 continue;
752 }
753
754 let link = &self.links.pool[idx];
755 if self.hovered_pin_index == Some(link.start_pin_index)
756 || self.hovered_pin_index == Some(link.end_pin_index)
757 {
758 self.hovered_link_idx.replace(idx);
759 return;
760 }
761
762 let start_pin = &self.pins.pool[link.start_pin_index];
763 let end_pin = &self.pins.pool[link.end_pin_index];
764
765 let link_data = LinkBezierData::get_link_renderable(
766 start_pin.pos,
767 end_pin.pos,
768 start_pin.kind,
769 self.style.link_line_segments_per_length,
770 );
771 let link_rect = link_data
772 .bezier
773 .get_containing_rect_for_bezier_curve(self.style.link_hover_distance);
774
775 if link_rect.contains(self.mouse_pos) {
776 let distance = link_data.get_distance_to_cubic_bezier(&self.mouse_pos);
777 if distance < self.style.link_hover_distance && distance < smallest_distance {
778 smallest_distance = distance;
779 self.hovered_link_idx.replace(idx);
780 }
781 }
782 }
783 }
784
785 fn draw_link(&mut self, link_idx: usize, ui: &mut egui::Ui) {
786 let link = &mut self.links.pool[link_idx];
787 let start_pin = &self.pins.pool[link.start_pin_index];
788 let end_pin = &self.pins.pool[link.end_pin_index];
789 let link_data = LinkBezierData::get_link_renderable(
790 start_pin.pos,
791 end_pin.pos,
792 start_pin.kind,
793 self.style.link_line_segments_per_length,
794 );
795 let link_shape = link.shape.take().unwrap();
796 let link_hovered = self.hovered_link_idx == Some(link_idx)
797 && self.click_interaction_type != ClickInteractionType::BoxSelection;
798
799 if link_hovered && self.left_mouse_clicked {
800 self.begin_link_interaction(link_idx);
801 }
802
803 if self.deleted_link_idx == Some(link_idx) {
804 return;
805 }
806
807 let link = &self.links.pool[link_idx];
808 let mut link_color = link.color_style.base;
809 if self.selected_link_indices.contains(&link_idx) {
810 link_color = link.color_style.selected;
811 } else if link_hovered {
812 link_color = link.color_style.hovered;
813 }
814
815 ui.painter().set(
816 link_shape,
817 link_data.draw((self.style.link_thickness, link_color)),
818 );
819 }
820
821 fn draw_node(&mut self, node_idx: usize, ui: &mut egui::Ui) {
822 let node = &mut self.nodes.pool[node_idx];
823
824 let node_hovered = self.hovered_node_index == Some(node_idx)
825 && self.click_interaction_type != ClickInteractionType::BoxSelection;
826
827 let mut node_background = node.color_style.background;
828 let mut titlebar_background = node.color_style.titlebar;
829
830 if self.selected_node_indices.contains(&node_idx) {
831 node_background = node.color_style.background_selected;
832 titlebar_background = node.color_style.titlebar_selected;
833 } else if node_hovered {
834 node_background = node.color_style.background_hovered;
835 titlebar_background = node.color_style.titlebar_hovered;
836 }
837
838 let painter = ui.painter();
839
840 painter.set(
841 node.background_shape.take().unwrap(),
842 egui::Shape::rect_filled(
843 node.rect,
844 node.layout_style.corner_rounding,
845 node_background,
846 ),
847 );
848 if node.title_bar_content_rect.height() > 0.0 {
849 painter.set(
850 node.titlebar_shape.take().unwrap(),
851 egui::Shape::rect_filled(
852 node.get_node_title_rect(),
853 node.layout_style.corner_rounding,
854 titlebar_background,
855 ),
856 );
857 }
858 if (self.style.flags & StyleFlags::NodeOutline as usize) != 0 {
859 painter.set(
860 node.outline_shape.take().unwrap(),
861 egui::Shape::rect_stroke(
862 node.rect,
863 node.layout_style.corner_rounding,
864 (node.layout_style.border_thickness, node.color_style.outline),
865 egui::StrokeKind::Inside,
866 ),
867 );
868 }
869
870 for pin_idx in node.pin_indices.clone() {
871 self.draw_pin(pin_idx, ui);
872 }
873
874 if node_hovered && self.left_mouse_clicked && self.interactive_node_index != Some(node_idx)
875 {
876 self.begin_node_selection(node_idx);
877 }
878 }
879
880 fn draw_pin(&mut self, pin_idx: usize, ui: &mut egui::Ui) {
881 let pin = &mut self.pins.pool[pin_idx];
882 let parent_node_rect = self.nodes.pool[pin.parent_node_idx].rect;
883
884 pin.pos = self.style.get_screen_space_pin_coordinates(
885 &parent_node_rect,
886 &pin.attribute_rect,
887 pin.kind,
888 );
889
890 let mut pin_color = pin.color_style.background;
891
892 let pin_hovered = self.hovered_pin_index == Some(pin_idx)
893 && self.click_interaction_type != ClickInteractionType::BoxSelection;
894 let pin_shape = pin.shape;
895 let pin_pos = pin.pos;
896 let pin_shape_gui = pin
897 .shape_gui
898 .take()
899 .expect("Unable to take pin shape. Perhaps your pin id is not unique?");
900
901 if pin_hovered {
902 self.hovered_pin_flags = pin.flags;
903 pin_color = pin.color_style.hovered;
904
905 if self.left_mouse_clicked {
906 self.begin_link_creation(pin_idx);
907 }
908 }
909
910 self.style.draw_pin_shape(pin_pos, pin_shape, pin_color, pin_shape_gui, ui);
911 }
912
913 fn begin_canvas_interaction(&mut self) {
914 let any_ui_element_hovered = self.hovered_node_index.is_some()
915 || self.hovered_link_idx.is_some()
916 || self.hovered_pin_index.is_some();
917
918 let mouse_not_in_canvas = !self.mouse_in_canvas;
919
920 if self.click_interaction_type != ClickInteractionType::None
921 || any_ui_element_hovered
922 || mouse_not_in_canvas
923 {
924 return;
925 }
926
927 if self.alt_mouse_clicked {
928 self.click_interaction_type = ClickInteractionType::Panning;
929 } else {
930 self.click_interaction_type = ClickInteractionType::BoxSelection;
931 self.click_interaction_state.box_selection.min = self.mouse_pos;
932 }
933 }
934
935 fn translate_selected_nodes(&mut self) {
936 if self.left_mouse_dragging {
937 let delta = self.mouse_delta;
938 for idx in self.selected_node_indices.iter() {
939 let node = &mut self.nodes.pool[*idx];
940 if node.draggable {
941 node.origin += delta;
942 }
943 }
944 }
945 }
946
947 fn should_link_snap_to_pin(
948 &self,
949 start_pin: &PinData,
950 hovered_pin_idx: usize,
951 duplicate_link: Option<usize>,
952 ) -> bool {
953 let end_pin = &self.pins.pool[hovered_pin_idx];
954 if start_pin.parent_node_idx == end_pin.parent_node_idx {
955 return false;
956 }
957
958 if start_pin.kind == end_pin.kind {
959 return false;
960 }
961
962 if duplicate_link.is_some_and(|x| Some(x) != self.snap_link_idx) {
963 return false;
964 }
965 true
966 }
967
968 fn box_selector_update_selection(&mut self) -> egui::Rect {
969 let mut box_rect = self.click_interaction_state.box_selection;
970 if box_rect.min.x > box_rect.max.x {
971 std::mem::swap(&mut box_rect.min.x, &mut box_rect.max.x);
972 }
973
974 if box_rect.min.y > box_rect.max.y {
975 std::mem::swap(&mut box_rect.min.y, &mut box_rect.max.y);
976 }
977
978 self.selected_node_indices.clear();
979 for (idx, node) in self.nodes.pool.iter().enumerate() {
980 if self.nodes.in_use[idx] && box_rect.intersects(node.rect) {
981 self.selected_node_indices.push(idx);
982 }
983 }
984
985 self.selected_link_indices.clear();
986 for (idx, link) in self.links.pool.iter().enumerate() {
987 if self.links.in_use[idx] {
988 let pin_start = &self.pins.pool[link.start_pin_index];
989 let pin_end = &self.pins.pool[link.end_pin_index];
990 let node_start_rect = self.nodes.pool[pin_start.parent_node_idx].rect;
991 let node_end_rect = self.nodes.pool[pin_end.parent_node_idx].rect;
992 let start = self.style.get_screen_space_pin_coordinates(
993 &node_start_rect,
994 &pin_start.attribute_rect,
995 pin_start.kind,
996 );
997 let end = self.style.get_screen_space_pin_coordinates(
998 &node_end_rect,
999 &pin_end.attribute_rect,
1000 pin_end.kind,
1001 );
1002
1003 if self.rectangle_overlaps_link(&box_rect, &start, &end, pin_start.kind) {
1004 self.selected_link_indices.push(idx);
1005 }
1006 }
1007 }
1008 box_rect
1009 }
1010
1011 #[inline]
1012 fn rectangle_overlaps_link(
1013 &self,
1014 rect: &egui::Rect,
1015 start: &egui::Pos2,
1016 end: &egui::Pos2,
1017 start_type: AttributeType,
1018 ) -> bool {
1019 let mut lrect = egui::Rect::from_min_max(*start, *end);
1020 if lrect.min.x > lrect.max.x {
1021 std::mem::swap(&mut lrect.min.x, &mut lrect.max.x);
1022 }
1023
1024 if lrect.min.y > lrect.max.y {
1025 std::mem::swap(&mut lrect.min.y, &mut lrect.max.y);
1026 }
1027
1028 if rect.intersects(lrect) {
1029 if rect.contains(*start) || rect.contains(*end) {
1030 return true;
1031 }
1032
1033 let link_data = LinkBezierData::get_link_renderable(
1034 *start,
1035 *end,
1036 start_type,
1037 self.style.link_line_segments_per_length,
1038 );
1039 return link_data.rectangle_overlaps_bezier(rect);
1040 }
1041 false
1042 }
1043
1044 fn click_interaction_update(&mut self, ui: &mut egui::Ui) {
1045 match self.click_interaction_type {
1046 ClickInteractionType::BoxSelection => {
1047 self.click_interaction_state.box_selection.max = self.mouse_pos;
1048 let rect = self.box_selector_update_selection();
1049
1050 let box_selector_color = self.style.colors[ColorStyle::BoxSelector as usize];
1051 let box_selector_outline =
1052 self.style.colors[ColorStyle::BoxSelectorOutline as usize];
1053 ui.painter().rect(
1054 rect,
1055 0.0,
1056 box_selector_color,
1057 (1.0, box_selector_outline),
1058 egui::StrokeKind::Inside,
1059 );
1060
1061 if self.left_mouse_released {
1062 let mut idxs = Vec::with_capacity(self.selected_node_indices.len());
1063 let depth_stack = &mut self.node_depth_order;
1064 let selected_nodes = &self.selected_node_indices;
1065 depth_stack.retain(|x| {
1066 if selected_nodes.contains(x) {
1067 idxs.push(*x);
1068 false
1069 } else {
1070 true
1071 }
1072 });
1073 self.node_depth_order.extend(idxs);
1074 self.click_interaction_type = ClickInteractionType::None;
1075 }
1076 }
1077 ClickInteractionType::Node => {
1078 self.translate_selected_nodes();
1079 if self.left_mouse_released {
1080 self.click_interaction_type = ClickInteractionType::None;
1081 }
1082 }
1083 ClickInteractionType::Link => {
1084 if self.left_mouse_released {
1085 self.click_interaction_type = ClickInteractionType::None;
1086 }
1087 }
1088 ClickInteractionType::LinkCreation => {
1089 let maybe_duplicate_link_idx = self.hovered_pin_index.and_then(|idx| {
1090 self.find_duplicate_link(
1091 self.click_interaction_state.link_creation.start_pin_idx,
1092 idx,
1093 )
1094 });
1095
1096 let should_snap = self.hovered_pin_index.is_some_and(|idx| {
1097 let start_pin =
1098 &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
1099 self.should_link_snap_to_pin(start_pin, idx, maybe_duplicate_link_idx)
1100 });
1101
1102 let snapping_pin_changed = self
1103 .click_interaction_state
1104 .link_creation
1105 .end_pin_index
1106 .is_some_and(|idx| self.hovered_pin_index != Some(idx));
1107
1108 if snapping_pin_changed && self.snap_link_idx.is_some() {
1109 self.begin_link_detach(
1110 self.snap_link_idx.unwrap(),
1111 self.click_interaction_state.link_creation.end_pin_index.unwrap(),
1112 );
1113 }
1114
1115 let start_pin =
1116 &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
1117 let start_pos = self.get_screen_space_pin_coordinates(start_pin);
1118
1119 let end_pos = if should_snap {
1120 self.get_screen_space_pin_coordinates(
1121 &self.pins.pool[self.hovered_pin_index.unwrap()],
1122 )
1123 } else {
1124 self.mouse_pos
1125 };
1126
1127 let link_data = LinkBezierData::get_link_renderable(
1128 start_pos,
1129 end_pos,
1130 start_pin.kind,
1131 self.style.link_line_segments_per_length,
1132 );
1133 ui.painter().add(link_data.draw((
1134 self.style.link_thickness,
1135 self.style.colors[ColorStyle::Link as usize],
1136 )));
1137
1138 let link_creation_on_snap = self.hovered_pin_index.is_some_and(|idx| {
1139 (self.pins.pool[idx].flags & AttributeFlags::EnableLinkCreationOnSnap as usize)
1140 != 0
1141 });
1142
1143 if !should_snap {
1144 self.click_interaction_state.link_creation.end_pin_index.take();
1145 }
1146
1147 let create_link =
1148 should_snap && (self.left_mouse_released || link_creation_on_snap);
1149
1150 if create_link && maybe_duplicate_link_idx.is_none() {
1151 if !self.left_mouse_released
1152 && self.click_interaction_state.link_creation.end_pin_index
1153 == self.hovered_pin_index
1154 {
1155 return;
1156 }
1157 self.element_state_change |= ElementStateChange::LinkCreated as usize;
1158 self.click_interaction_state.link_creation.end_pin_index =
1159 self.hovered_pin_index;
1160 }
1161
1162 if self.left_mouse_released {
1163 self.click_interaction_type = ClickInteractionType::None;
1164 if !create_link {
1165 self.element_state_change |= ElementStateChange::LinkDropped as usize;
1166 }
1167 }
1168 }
1169 ClickInteractionType::Panning => {
1170 if self.alt_mouse_dragging || self.alt_mouse_clicked {
1171 self.panning += self.mouse_delta;
1172 } else {
1173 self.click_interaction_type = ClickInteractionType::None;
1174 }
1175 }
1176 ClickInteractionType::None => (),
1177 }
1178 }
1179
1180 fn begin_link_detach(&mut self, idx: usize, detach_idx: usize) {
1181 self.click_interaction_state.link_creation.end_pin_index.take();
1182 let link = &self.links.pool[idx];
1183 self.click_interaction_state.link_creation.start_pin_idx =
1184 if detach_idx == link.start_pin_index {
1185 link.end_pin_index
1186 } else {
1187 link.start_pin_index
1188 };
1189 self.deleted_link_idx.replace(idx);
1190 }
1191
1192 fn begin_link_interaction(&mut self, idx: usize) {
1193 if self.click_interaction_type == ClickInteractionType::LinkCreation {
1194 if (self.hovered_pin_flags & AttributeFlags::EnableLinkDetachWithDragClick as usize)
1195 != 0
1196 {
1197 self.begin_link_detach(idx, self.hovered_pin_index.unwrap());
1198 self.click_interaction_state.link_creation.link_creation_type =
1199 LinkCreationType::FromDetach;
1200 }
1201 } else if self.link_detatch_with_modifier_click {
1202 let link = &self.links.pool[idx];
1203 let start_pin = &self.pins.pool[link.start_pin_index];
1204 let end_pin = &self.pins.pool[link.end_pin_index];
1205 let dist_to_start = start_pin.pos.distance(self.mouse_pos);
1206 let dist_to_end = end_pin.pos.distance(self.mouse_pos);
1207 let closest_pin_idx = if dist_to_start < dist_to_end {
1208 link.start_pin_index
1209 } else {
1210 link.end_pin_index
1211 };
1212 self.click_interaction_type = ClickInteractionType::LinkCreation;
1213 self.begin_link_detach(idx, closest_pin_idx);
1214 } else {
1215 self.begin_link_selection(idx);
1216 }
1217 }
1218
1219 fn begin_link_creation(&mut self, hovered_pin_idx: usize) {
1220 self.click_interaction_type = ClickInteractionType::LinkCreation;
1221 self.click_interaction_state.link_creation.start_pin_idx = hovered_pin_idx;
1222 self.click_interaction_state.link_creation.end_pin_index.take();
1223 self.click_interaction_state.link_creation.link_creation_type = LinkCreationType::Standard;
1224 self.element_state_change |= ElementStateChange::LinkStarted as usize;
1225 }
1226
1227 fn begin_link_selection(&mut self, idx: usize) {
1228 self.click_interaction_type = ClickInteractionType::Link;
1229 self.selected_node_indices.clear();
1230 self.selected_link_indices.clear();
1231 self.selected_link_indices.push(idx);
1232 }
1233
1234 fn find_duplicate_link(&self, start_pin_idx: usize, end_pin_idx: usize) -> Option<usize> {
1235 let mut test_link = LinkData::new(0);
1236 test_link.start_pin_index = start_pin_idx;
1237 test_link.end_pin_index = end_pin_idx;
1238 for (idx, (link, in_use)) in
1239 self.links.pool.iter().zip(self.links.in_use.iter()).enumerate()
1240 {
1241 if *in_use && *link == test_link {
1242 return Some(idx);
1243 }
1244 }
1245 None
1246 }
1247
1248 fn begin_node_selection(&mut self, idx: usize) {
1249 if self.click_interaction_type != ClickInteractionType::None {
1250 return;
1251 }
1252 self.click_interaction_type = ClickInteractionType::Node;
1253 if !self.selected_node_indices.contains(&idx) {
1254 self.selected_node_indices.clear();
1255 self.selected_link_indices.clear();
1256 self.selected_node_indices.push(idx);
1257
1258 self.node_depth_order.retain(|x| *x != idx);
1259 self.node_depth_order.push(idx);
1260 }
1261 }
1262}
1263
1264#[derive(Debug)]
1265enum ElementStateChange {
1266 None = 0,
1267 LinkStarted = 1 << 0,
1268 LinkDropped = 1 << 1,
1269 LinkCreated = 1 << 2,
1270}
1271
1272#[derive(PartialEq, Debug)]
1273enum ClickInteractionType {
1274 Node,
1275 Link,
1276 LinkCreation,
1277 Panning,
1278 BoxSelection,
1279 None,
1280}
1281
1282#[derive(PartialEq, Debug)]
1283enum LinkCreationType {
1284 Standard,
1285 FromDetach,
1286}
1287
1288#[derive(Derivative, Debug)]
1289#[derivative(Default)]
1290struct ClickInteractionStateLinkCreation {
1291 start_pin_idx: usize,
1292 end_pin_index: Option<usize>,
1293 #[derivative(Default(value = "LinkCreationType::Standard"))]
1294 link_creation_type: LinkCreationType,
1295}
1296
1297#[derive(Derivative, Debug)]
1298#[derivative(Default)]
1299struct ClickInteractionState {
1300 link_creation: ClickInteractionStateLinkCreation,
1301 #[derivative(Default(value = "[[0.0; 2].into(); 2].into()"))]
1302 box_selection: egui::Rect,
1303}
1304
1305#[derive(Debug)]
1306struct ColorStyleElement {
1307 color: egui::Color32,
1308 item: ColorStyle,
1309}
1310
1311impl ColorStyleElement {
1312 fn new(color: egui::Color32, item: ColorStyle) -> Self {
1313 Self { color, item }
1314 }
1315}
1316
1317#[derive(Debug)]
1318struct StyleElement {
1319 item: StyleVar,
1320 value: f32,
1321}
1322
1323impl StyleElement {
1324 fn new(value: f32, item: StyleVar) -> Self {
1325 Self { value, item }
1326 }
1327}
1328
1329#[derive(Derivative, Debug)]
1331#[derivative(Default)]
1332pub struct IO {
1333 #[derivative(Default(value = "Modifiers::None"))]
1335 pub emulate_three_button_mouse: Modifiers,
1336
1337 #[derivative(Default(value = "Modifiers::None"))]
1339 pub link_detatch_with_modifier_click: Modifiers,
1340
1341 #[derivative(Default(value = "Some(egui::PointerButton::Middle)"))]
1343 pub alt_mouse_button: Option<egui::PointerButton>,
1344}
1345
1346#[derive(Debug)]
1348pub enum Modifiers {
1349 Alt,
1350 Crtl,
1351 Shift,
1352 Command,
1353 None,
1354}
1355
1356impl Modifiers {
1357 fn is_active(&self, mods: &egui::Modifiers) -> bool {
1358 match self {
1359 Modifiers::Alt => mods.alt,
1360 Modifiers::Crtl => mods.ctrl,
1361 Modifiers::Shift => mods.shift,
1362 Modifiers::Command => mods.command,
1363 Modifiers::None => false,
1364 }
1365 }
1366}
1367
1368trait Id {
1369 fn id(&self) -> usize;
1370 fn new(id: usize) -> Self;
1371}
1372
1373#[derive(Default, Debug)]
1374struct ObjectPool<T> {
1375 pool: Vec<T>,
1376 in_use: Vec<bool>,
1377 free: Vec<usize>,
1378 map: HashMap<usize, usize>,
1379}
1380
1381impl<T> ObjectPool<T> {
1382 fn find(&self, id: usize) -> Option<usize> {
1383 self.map.get(&id).copied()
1384 }
1385 fn reset(&mut self) {
1386 self.in_use.iter_mut().for_each(|x| *x = false);
1387 }
1388}
1389
1390impl<T: Id> ObjectPool<T> {
1391 fn update(&mut self) {
1392 self.free.clear();
1393 for (i, (in_use, obj)) in self.in_use.iter().zip(self.pool.iter()).enumerate() {
1394 if !*in_use {
1395 self.map.remove(&obj.id());
1396 self.free.push(i);
1397 }
1398 }
1399 }
1400
1401 fn find_or_create_index(&mut self, id: usize) -> usize {
1402 let index = {
1403 if let Some(index) = self.find(id) {
1404 index
1405 } else {
1406 let index = if let Some(index) = self.free.pop() {
1407 self.pool[index] = T::new(id);
1408 index
1409 } else {
1410 self.pool.push(T::new(id));
1411 self.in_use.push(false);
1412 self.pool.len() - 1
1413 };
1414 self.map.insert(id, index);
1415 index
1416 }
1417 };
1418 self.in_use[index] = true;
1419 index
1420 }
1421}
1422
1423impl Context {
1424 fn node_pool_update(&mut self) {
1425 self.nodes.free.clear();
1426 for (i, (in_use, node)) in
1427 self.nodes.in_use.iter_mut().zip(self.nodes.pool.iter_mut()).enumerate()
1428 {
1429 if *in_use {
1430 node.pin_indices.clear();
1431 } else {
1432 if self.nodes.map.contains_key(&node.id) {
1433 self.node_depth_order.retain(|x| *x != i);
1434 }
1435 self.nodes.map.remove(&node.id);
1436 self.nodes.free.push(i);
1437 }
1438 }
1439 }
1440
1441 fn node_pool_find_or_create_index(&mut self, id: usize, origin: Option<egui::Pos2>) -> usize {
1442 let index = {
1443 if let Some(index) = self.nodes.find(id) {
1444 index
1445 } else {
1446 let mut new_node = NodeData::new(id);
1447 if let Some(origin) = origin {
1448 new_node.origin = self.screen_space_to_grid_space(origin);
1449 }
1450 let index = if let Some(index) = self.nodes.free.pop() {
1451 self.nodes.pool[index] = new_node;
1452 index
1453 } else {
1454 self.nodes.pool.push(new_node);
1455 self.nodes.in_use.push(false);
1456 self.nodes.pool.len() - 1
1457 };
1458 self.nodes.map.insert(id, index);
1459 self.node_depth_order.push(index);
1460 index
1461 }
1462 };
1463 self.nodes.in_use[index] = true;
1464 index
1465 }
1466}