Compare commits
3 Commits
36edf051ff
...
51546fc2a2
Author | SHA1 | Date |
---|---|---|
mars | 51546fc2a2 | |
mars | 1803d784f9 | |
mars | be6693b20a |
|
@ -126,6 +126,7 @@ impl Command for DeleteNode {
|
||||||
pub struct MoveNode {
|
pub struct MoveNode {
|
||||||
pub target: usize,
|
pub target: usize,
|
||||||
pub to: glam::Vec2,
|
pub to: glam::Vec2,
|
||||||
|
pub relative: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for MoveNode {
|
impl Command for MoveNode {
|
||||||
|
@ -135,6 +136,7 @@ impl Command for MoveNode {
|
||||||
let undo = Self {
|
let undo = Self {
|
||||||
target: self.target,
|
target: self.target,
|
||||||
to: target.pos,
|
to: target.pos,
|
||||||
|
relative: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Box::new(undo))
|
Ok(Box::new(undo))
|
||||||
|
@ -142,7 +144,13 @@ impl Command for MoveNode {
|
||||||
|
|
||||||
fn apply(&self, graph: &mut Graph) -> GraphResult<()> {
|
fn apply(&self, graph: &mut Graph) -> GraphResult<()> {
|
||||||
let target = graph.get_node_mut(self.target)?;
|
let target = graph.get_node_mut(self.target)?;
|
||||||
target.pos = self.to;
|
|
||||||
|
if self.relative {
|
||||||
|
target.pos += self.to;
|
||||||
|
} else {
|
||||||
|
target.pos = self.to;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use ramen::node::{AtomOp, Node};
|
use ramen::node::{AtomOp, Node};
|
||||||
use ramen::Graph;
|
use ramen::Graph;
|
||||||
|
use ramen_egui::NodeEditor;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let native_options = eframe::NativeOptions::default();
|
let native_options = eframe::NativeOptions::default();
|
||||||
|
@ -12,7 +13,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Application {
|
struct Application {
|
||||||
graph: Graph,
|
editor: NodeEditor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
|
@ -32,7 +33,9 @@ impl Application {
|
||||||
let n5 = nodes.insert(Node::op(AtomOp::Mul, n4, n4).at(pos(4.0, 2.0)));
|
let n5 = nodes.insert(Node::op(AtomOp::Mul, n4, n4).at(pos(4.0, 2.0)));
|
||||||
let _n6 = nodes.insert(Node::op(AtomOp::Add, n5, n0).at(pos(3.0, 1.0)));
|
let _n6 = nodes.insert(Node::op(AtomOp::Add, n5, n0).at(pos(3.0, 1.0)));
|
||||||
|
|
||||||
Self { graph }
|
Self {
|
||||||
|
editor: NodeEditor::from_graph(graph),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ impl eframe::App for Application {
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.heading("Hello world!");
|
ui.heading("Hello world!");
|
||||||
ramen_egui::draw_graph(ui, &self.graph);
|
self.editor.show(ui);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,132 @@ use ramen::node::{Edge, SlotIndex};
|
||||||
use ramen::Graph;
|
use ramen::Graph;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct NodeEditor {
|
||||||
|
graph: Graph,
|
||||||
|
pointer_state: PointerState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeEditor {
|
||||||
|
pub fn from_graph(graph: Graph) -> Self {
|
||||||
|
Self {
|
||||||
|
graph,
|
||||||
|
pointer_state: PointerState::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&mut self, ui: &mut egui::Ui) {
|
||||||
|
let style = ui.style();
|
||||||
|
let style = GraphStyle::from_ui_style(style);
|
||||||
|
let layout = GraphLayout::layout(&style, &self.pointer_state, &self.graph);
|
||||||
|
|
||||||
|
let rect = ui.available_rect_before_wrap();
|
||||||
|
let sense = egui::Sense::click_and_drag();
|
||||||
|
let response = ui.allocate_rect(rect, sense);
|
||||||
|
|
||||||
|
let pointer_pos = response.interact_pointer_pos();
|
||||||
|
let pointer_target = pointer_pos.map(|pos| layout.pointer_target(pos));
|
||||||
|
let painter = ui.painter_at(rect);
|
||||||
|
layout.paint(rect, &painter);
|
||||||
|
|
||||||
|
if let Some(pointer_pos) = pointer_pos {
|
||||||
|
self.pointer_state.position = pointer_pos;
|
||||||
|
self.pointer_state.delta = response.drag_delta();
|
||||||
|
|
||||||
|
if let PointerAction::Idle(idle) = &mut self.pointer_state.action {
|
||||||
|
*idle = pointer_target.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.drag_started() && response.dragged_by(egui::PointerButton::Primary) {
|
||||||
|
let pointer_target = pointer_target.expect("Drag started but no target");
|
||||||
|
self.pointer_state.start = self.pointer_state.position;
|
||||||
|
|
||||||
|
let next_state = match pointer_target {
|
||||||
|
PointerTarget::Grid => None, // TODO grid navigation
|
||||||
|
PointerTarget::Node(idx) => Some(PointerAction::MovingNode(idx)),
|
||||||
|
PointerTarget::Input(idx) => Some(PointerAction::SettingInput(idx)),
|
||||||
|
PointerTarget::Output(idx) => Some(PointerAction::SettingOutput(idx)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(next_state) = next_state {
|
||||||
|
self.pointer_state.action = next_state;
|
||||||
|
}
|
||||||
|
} else if response.drag_released() {
|
||||||
|
use ramen::command::*;
|
||||||
|
|
||||||
|
let cmd: Option<DynCommand> = match self.pointer_state.action {
|
||||||
|
PointerAction::Idle(_) => None,
|
||||||
|
PointerAction::MovingNode(idx) => {
|
||||||
|
let delta = self.pointer_state.position - self.pointer_state.start;
|
||||||
|
Some(Box::new(MoveNode {
|
||||||
|
target: idx,
|
||||||
|
to: Vec2::new(delta.x, delta.y),
|
||||||
|
relative: true,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
PointerAction::SettingInput(output) => {
|
||||||
|
if let Some(PointerTarget::Output(input)) = pointer_target {
|
||||||
|
Some(Box::new(SetEdge {
|
||||||
|
edge: Edge { input, output },
|
||||||
|
}))
|
||||||
|
} else if self.graph.nodes[output.node].inputs[output.slot].is_some() {
|
||||||
|
Some(Box::new(DeleteEdge { input: output }))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PointerAction::SettingOutput(input) => {
|
||||||
|
if let Some(PointerTarget::Input(output)) = pointer_target {
|
||||||
|
Some(Box::new(SetEdge {
|
||||||
|
edge: Edge { input, output },
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(cmd) = cmd {
|
||||||
|
cmd.apply(&mut self.graph).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pointer_state.action = PointerAction::Idle(None); // TODO idle pointer targeting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PointerState {
|
||||||
|
pub start: egui::Pos2,
|
||||||
|
pub position: egui::Pos2,
|
||||||
|
pub delta: egui::Vec2,
|
||||||
|
pub action: PointerAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
start: egui::Pos2::ZERO,
|
||||||
|
position: egui::Pos2::ZERO,
|
||||||
|
delta: egui::Vec2::ZERO,
|
||||||
|
action: PointerAction::Idle(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
|
pub enum PointerAction {
|
||||||
|
Idle(Option<PointerTarget>),
|
||||||
|
MovingNode(usize),
|
||||||
|
SettingInput(SlotIndex),
|
||||||
|
SettingOutput(SlotIndex),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
pub enum PointerTarget {
|
pub enum PointerTarget {
|
||||||
Grid,
|
Grid,
|
||||||
Node(usize),
|
Node(usize),
|
||||||
Input(SlotIndex),
|
Input(SlotIndex),
|
||||||
Output(SlotIndex),
|
Output(SlotIndex),
|
||||||
Edge(Edge),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -18,7 +137,7 @@ pub struct EdgeLayout {
|
||||||
pub points: [Vec2; 4],
|
pub points: [Vec2; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GraphStyle {
|
pub struct GraphStyle {
|
||||||
pub grid_spacing: f32,
|
pub grid_spacing: f32,
|
||||||
pub grid_stroke: Stroke,
|
pub grid_stroke: Stroke,
|
||||||
|
@ -34,7 +153,7 @@ pub struct GraphStyle {
|
||||||
impl GraphStyle {
|
impl GraphStyle {
|
||||||
pub fn from_ui_style(style: &egui::Style) -> Self {
|
pub fn from_ui_style(style: &egui::Style) -> Self {
|
||||||
let node_alpha = 192;
|
let node_alpha = 192;
|
||||||
|
|
||||||
let node_bg = if style.visuals.dark_mode {
|
let node_bg = if style.visuals.dark_mode {
|
||||||
Color32::from_black_alpha(node_alpha)
|
Color32::from_black_alpha(node_alpha)
|
||||||
} else {
|
} else {
|
||||||
|
@ -47,29 +166,44 @@ impl GraphStyle {
|
||||||
node_frame: Frame::window(style).fill(node_bg),
|
node_frame: Frame::window(style).fill(node_bg),
|
||||||
edge_stroke: style.visuals.widgets.active.fg_stroke,
|
edge_stroke: style.visuals.widgets.active.fg_stroke,
|
||||||
edge_control_offset: 200.0,
|
edge_control_offset: 200.0,
|
||||||
slot_radius: 5.0,
|
slot_radius: 7.0,
|
||||||
slot_spacing: 20.0,
|
slot_spacing: 40.0,
|
||||||
slot_fill: style.visuals.widgets.noninteractive.bg_fill,
|
slot_fill: style.visuals.widgets.noninteractive.bg_fill,
|
||||||
slot_stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
slot_stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub struct GraphLayout {
|
pub struct GraphLayout {
|
||||||
|
pub style: GraphStyle,
|
||||||
pub node_rects: HashMap<usize, egui::Rect>,
|
pub node_rects: HashMap<usize, egui::Rect>,
|
||||||
pub input_positions: HashMap<SlotIndex, Vec2>,
|
pub input_positions: HashMap<SlotIndex, Vec2>,
|
||||||
pub output_positions: HashMap<SlotIndex, Vec2>,
|
pub output_positions: HashMap<SlotIndex, Vec2>,
|
||||||
pub edges: HashMap<Edge, EdgeLayout>,
|
pub edges: HashMap<Edge, EdgeLayout>,
|
||||||
|
pub active_edge: Option<(Vec2, Vec2)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphLayout {
|
impl GraphLayout {
|
||||||
pub fn layout(style: &GraphStyle, graph: &Graph) -> Self {
|
pub fn layout(style: &GraphStyle, pointer: &PointerState, graph: &Graph) -> Self {
|
||||||
let mut layout = Self::default();
|
let mut layout = Self {
|
||||||
|
style: style.clone(),
|
||||||
|
node_rects: Default::default(),
|
||||||
|
input_positions: Default::default(),
|
||||||
|
output_positions: Default::default(),
|
||||||
|
edges: Default::default(),
|
||||||
|
active_edge: None,
|
||||||
|
};
|
||||||
|
|
||||||
let mut edges = Vec::new();
|
let mut edges = Vec::new();
|
||||||
|
|
||||||
for (node_index, node) in graph.nodes.iter() {
|
for (node_index, node) in graph.nodes.iter() {
|
||||||
let node_pos = egui::pos2(node.pos.x, node.pos.y);
|
let mut node_pos = egui::pos2(node.pos.x, node.pos.y);
|
||||||
|
|
||||||
|
if pointer.action == PointerAction::MovingNode(node_index) {
|
||||||
|
node_pos += pointer.position - pointer.start;
|
||||||
|
}
|
||||||
|
|
||||||
let node_size = egui::vec2(150., 150.);
|
let node_size = egui::vec2(150., 150.);
|
||||||
let node_rect = egui::Rect::from_min_size(node_pos, node_size);
|
let node_rect = egui::Rect::from_min_size(node_pos, node_size);
|
||||||
layout.node_rects.insert(node_index, node_rect);
|
layout.node_rects.insert(node_index, node_rect);
|
||||||
|
@ -89,7 +223,10 @@ impl GraphLayout {
|
||||||
|
|
||||||
layout.input_positions.insert(slot_index, position);
|
layout.input_positions.insert(slot_index, position);
|
||||||
|
|
||||||
if let Some(input) = input {
|
if pointer.action == PointerAction::SettingInput(slot_index) {
|
||||||
|
let pointer = Vec2::new(pointer.position.x, pointer.position.y);
|
||||||
|
layout.active_edge = Some((position, pointer));
|
||||||
|
} else if let Some(input) = input {
|
||||||
edges.push(Edge {
|
edges.push(Edge {
|
||||||
input: *input,
|
input: *input,
|
||||||
output: slot_index,
|
output: slot_index,
|
||||||
|
@ -97,102 +234,144 @@ impl GraphLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.output_positions.insert(
|
for slot_index in 0..1 {
|
||||||
SlotIndex {
|
let slot_y = slot_index as f32 * style.slot_spacing + slot_top;
|
||||||
|
let position = glam::Vec2::new(slot_right, slot_y);
|
||||||
|
|
||||||
|
let slot_index = SlotIndex {
|
||||||
node: node_index,
|
node: node_index,
|
||||||
slot: 0,
|
slot: slot_index,
|
||||||
},
|
};
|
||||||
glam::Vec2::new(slot_right, slot_top),
|
|
||||||
);
|
layout.output_positions.insert(slot_index, position);
|
||||||
|
|
||||||
|
if pointer.action == PointerAction::SettingOutput(slot_index) {
|
||||||
|
let pointer = Vec2::new(pointer.position.x, pointer.position.y);
|
||||||
|
layout.active_edge = Some((pointer, position));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for edge in edges.into_iter() {
|
for edge in edges.into_iter() {
|
||||||
let input = *layout.input_positions.get(&edge.output).unwrap();
|
let input = *layout.input_positions.get(&edge.output).unwrap();
|
||||||
let output = *layout.output_positions.get(&edge.input).unwrap();
|
let output = *layout.output_positions.get(&edge.input).unwrap();
|
||||||
|
let edge_layout = layout.layout_edge(input, output);
|
||||||
let control_offset = Vec2::new(style.edge_control_offset, 0.0);
|
|
||||||
let mut input_control = input - control_offset;
|
|
||||||
let mut output_control = output + control_offset;
|
|
||||||
|
|
||||||
if output_control.x > input_control.x && output.x < input.x {
|
|
||||||
let mid = (output_control.x + input_control.x) / 2.0;
|
|
||||||
output_control.x = mid;
|
|
||||||
input_control.x = mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
let edge_layout = EdgeLayout {
|
|
||||||
points: [input, input_control, output_control, output],
|
|
||||||
};
|
|
||||||
|
|
||||||
layout.edges.insert(edge, edge_layout);
|
layout.edges.insert(edge, edge_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
layout
|
layout
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_graph(ui: &mut egui::Ui, graph: &Graph) {
|
pub fn layout_edge(&self, input: Vec2, output: Vec2) -> EdgeLayout {
|
||||||
let rect = ui.available_rect_before_wrap();
|
let control_offset = Vec2::new(self.style.edge_control_offset, 0.0);
|
||||||
let painter = ui.painter_at(rect);
|
let mut input_control = input - control_offset;
|
||||||
|
let mut output_control = output + control_offset;
|
||||||
|
|
||||||
let style = ui.style();
|
if output_control.x > input_control.x && output.x < input.x {
|
||||||
let style = GraphStyle::from_ui_style(style);
|
let mid = (output_control.x + input_control.x) / 2.0;
|
||||||
let layout = GraphLayout::layout(&style, graph);
|
output_control.x = mid;
|
||||||
|
input_control.x = mid;
|
||||||
|
}
|
||||||
|
|
||||||
let pointer_pos = ui.input().pointer.interact_pos();
|
EdgeLayout {
|
||||||
let mut pointer_target: Option<PointerTarget> = None;
|
points: [input, input_control, output_control, output],
|
||||||
|
}
|
||||||
let grid_spacing = style.grid_spacing;
|
|
||||||
let grid_pos = rect.left_top().to_vec2() / egui::vec2(grid_spacing, grid_spacing);
|
|
||||||
let mut grid_pos = grid_pos.floor() * grid_spacing;
|
|
||||||
|
|
||||||
while grid_pos.x < rect.right() {
|
|
||||||
grid_pos.x += grid_spacing;
|
|
||||||
let start = egui::pos2(grid_pos.x, rect.top());
|
|
||||||
let end = egui::pos2(grid_pos.x, rect.bottom());
|
|
||||||
painter.line_segment([start, end], style.grid_stroke);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while grid_pos.y < rect.bottom() {
|
pub fn pointer_target(&self, pointer: egui::Pos2) -> PointerTarget {
|
||||||
grid_pos.y += grid_spacing;
|
let test_slots = |positions: &HashMap<SlotIndex, Vec2>| -> Option<SlotIndex> {
|
||||||
let start = egui::pos2(rect.left(), grid_pos.y);
|
for (index, position) in positions.iter() {
|
||||||
let end = egui::pos2(rect.right(), grid_pos.y);
|
let dx = pointer.x - position.x;
|
||||||
painter.line_segment([start, end], style.grid_stroke);
|
let dy = pointer.y - position.y;
|
||||||
}
|
let d2 = (dx * dx) + (dy * dy);
|
||||||
|
let slot_radius2 = self.style.slot_radius.powi(2);
|
||||||
|
|
||||||
for (_edge, edge_layout) in layout.edges.iter() {
|
if d2 < slot_radius2 {
|
||||||
let points = edge_layout.points.map(|p| p.to_array().into());
|
return Some(*index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let shape = egui::epaint::CubicBezierShape {
|
None
|
||||||
points,
|
|
||||||
closed: false,
|
|
||||||
fill: egui::Color32::TRANSPARENT,
|
|
||||||
stroke: style.edge_stroke,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
painter.add(shape);
|
if let Some(index) = test_slots(&self.input_positions) {
|
||||||
|
return PointerTarget::Input(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(index) = test_slots(&self.output_positions) {
|
||||||
|
return PointerTarget::Output(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (node_index, node_rect) in self.node_rects.iter() {
|
||||||
|
if node_rect.contains(pointer) {
|
||||||
|
return PointerTarget::Node(*node_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerTarget::Grid
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_node_index, node_rect) in layout.node_rects.iter() {
|
pub fn paint(&self, rect: egui::Rect, painter: &egui::Painter) {
|
||||||
let shape = style.node_frame.paint(*node_rect);
|
let style = &self.style;
|
||||||
painter.add(shape);
|
let grid_spacing = style.grid_spacing;
|
||||||
}
|
let grid_pos = rect.left_top().to_vec2() / egui::vec2(grid_spacing, grid_spacing);
|
||||||
|
let mut grid_pos = grid_pos.floor() * grid_spacing;
|
||||||
|
|
||||||
let draw_slots = |positions: &HashMap<SlotIndex, Vec2>| {
|
while grid_pos.x < rect.right() {
|
||||||
for (_index, position) in positions.iter() {
|
grid_pos.x += grid_spacing;
|
||||||
let shape = egui::epaint::CircleShape {
|
let start = egui::pos2(grid_pos.x, rect.top());
|
||||||
center: egui::pos2(position.x, position.y),
|
let end = egui::pos2(grid_pos.x, rect.bottom());
|
||||||
radius: style.slot_radius,
|
painter.line_segment([start, end], style.grid_stroke);
|
||||||
fill: style.slot_fill,
|
}
|
||||||
stroke: style.slot_stroke,
|
|
||||||
|
while grid_pos.y < rect.bottom() {
|
||||||
|
grid_pos.y += grid_spacing;
|
||||||
|
let start = egui::pos2(rect.left(), grid_pos.y);
|
||||||
|
let end = egui::pos2(rect.right(), grid_pos.y);
|
||||||
|
painter.line_segment([start, end], style.grid_stroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
let paint_edge = |layout: &EdgeLayout| {
|
||||||
|
let points = layout.points.map(|p| p.to_array().into());
|
||||||
|
|
||||||
|
let shape = egui::epaint::CubicBezierShape {
|
||||||
|
points,
|
||||||
|
closed: false,
|
||||||
|
fill: egui::Color32::TRANSPARENT,
|
||||||
|
stroke: style.edge_stroke,
|
||||||
};
|
};
|
||||||
|
|
||||||
painter.add(shape);
|
painter.add(shape);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (_edge, edge_layout) in self.edges.iter() {
|
||||||
|
paint_edge(edge_layout);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
draw_slots(&layout.input_positions);
|
if let Some(active_edge) = self.active_edge {
|
||||||
draw_slots(&layout.output_positions);
|
let layout = self.layout_edge(active_edge.0, active_edge.1);
|
||||||
|
paint_edge(&layout);
|
||||||
|
}
|
||||||
|
|
||||||
println!("{:#?}", pointer_target);
|
for (_node_index, node_rect) in self.node_rects.iter() {
|
||||||
|
let shape = style.node_frame.paint(*node_rect);
|
||||||
|
painter.add(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
let draw_slots = |positions: &HashMap<SlotIndex, Vec2>| {
|
||||||
|
for (_index, position) in positions.iter() {
|
||||||
|
let shape = egui::epaint::CircleShape {
|
||||||
|
center: egui::pos2(position.x, position.y),
|
||||||
|
radius: style.slot_radius,
|
||||||
|
fill: style.slot_fill,
|
||||||
|
stroke: style.slot_stroke,
|
||||||
|
};
|
||||||
|
|
||||||
|
painter.add(shape);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_slots(&self.input_positions);
|
||||||
|
draw_slots(&self.output_positions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue