Add ramen_egui crate
This commit is contained in:
parent
1b3a991144
commit
36edf051ff
|
@ -1,7 +1,8 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"editor",
|
||||
"ramen"
|
||||
"ramen",
|
||||
"ramen_egui"
|
||||
]
|
||||
|
||||
[package]
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "ramen_egui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
egui = "0.18"
|
||||
glam = "0.20"
|
||||
ramen = { path = "../ramen" }
|
||||
|
||||
[dev-dependencies]
|
||||
eframe = "0.18"
|
|
@ -0,0 +1,52 @@
|
|||
use eframe::egui;
|
||||
use ramen::node::{AtomOp, Node};
|
||||
use ramen::Graph;
|
||||
|
||||
fn main() {
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
eframe::run_native(
|
||||
"Ramen example",
|
||||
native_options,
|
||||
Box::new(|cc| Box::new(Application::new(cc))),
|
||||
);
|
||||
}
|
||||
|
||||
struct Application {
|
||||
graph: Graph,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||
cc.egui_ctx.set_visuals(egui::Visuals::dark());
|
||||
|
||||
let scale = 250.0;
|
||||
let pos = |x, y| glam::Vec2::new(x, y) * scale;
|
||||
|
||||
let mut graph = Graph::new();
|
||||
let nodes = &mut graph.nodes;
|
||||
let n0 = nodes.insert(Node::literal(1.0).at(pos(1.0, 1.0)));
|
||||
let n1 = nodes.insert(Node::literal(2.0).at(pos(1.0, 2.0)));
|
||||
let n2 = nodes.insert(Node::op(AtomOp::Add, n0, n1).at(pos(2.0, 1.5)));
|
||||
let n3 = nodes.insert(Node::literal(4.0).at(pos(2.0, 2.5)));
|
||||
let n4 = nodes.insert(Node::op(AtomOp::Div, n2, n3).at(pos(3.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)));
|
||||
|
||||
Self { graph }
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for Application {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
egui::TopBottomPanel::top("menu_panel").show(ctx, |ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
egui::widgets::global_dark_light_mode_switch(ui);
|
||||
});
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Hello world!");
|
||||
ramen_egui::draw_graph(ui, &self.graph);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
use egui::{Color32, Frame, Stroke};
|
||||
use glam::Vec2;
|
||||
use ramen::node::{Edge, SlotIndex};
|
||||
use ramen::Graph;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub enum PointerTarget {
|
||||
Grid,
|
||||
Node(usize),
|
||||
Input(SlotIndex),
|
||||
Output(SlotIndex),
|
||||
Edge(Edge),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EdgeLayout {
|
||||
pub points: [Vec2; 4],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GraphStyle {
|
||||
pub grid_spacing: f32,
|
||||
pub grid_stroke: Stroke,
|
||||
pub node_frame: Frame,
|
||||
pub edge_stroke: Stroke,
|
||||
pub edge_control_offset: f32,
|
||||
pub slot_radius: f32,
|
||||
pub slot_spacing: f32,
|
||||
pub slot_fill: Color32,
|
||||
pub slot_stroke: Stroke,
|
||||
}
|
||||
|
||||
impl GraphStyle {
|
||||
pub fn from_ui_style(style: &egui::Style) -> Self {
|
||||
let node_alpha = 192;
|
||||
|
||||
let node_bg = if style.visuals.dark_mode {
|
||||
Color32::from_black_alpha(node_alpha)
|
||||
} else {
|
||||
Color32::from_white_alpha(node_alpha)
|
||||
};
|
||||
|
||||
Self {
|
||||
grid_spacing: 25.0,
|
||||
grid_stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
||||
node_frame: Frame::window(style).fill(node_bg),
|
||||
edge_stroke: style.visuals.widgets.active.fg_stroke,
|
||||
edge_control_offset: 200.0,
|
||||
slot_radius: 5.0,
|
||||
slot_spacing: 20.0,
|
||||
slot_fill: style.visuals.widgets.noninteractive.bg_fill,
|
||||
slot_stroke: style.visuals.widgets.noninteractive.bg_stroke,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct GraphLayout {
|
||||
pub node_rects: HashMap<usize, egui::Rect>,
|
||||
pub input_positions: HashMap<SlotIndex, Vec2>,
|
||||
pub output_positions: HashMap<SlotIndex, Vec2>,
|
||||
pub edges: HashMap<Edge, EdgeLayout>,
|
||||
}
|
||||
|
||||
impl GraphLayout {
|
||||
pub fn layout(style: &GraphStyle, graph: &Graph) -> Self {
|
||||
let mut layout = Self::default();
|
||||
let mut edges = Vec::new();
|
||||
|
||||
for (node_index, node) in graph.nodes.iter() {
|
||||
let node_pos = egui::pos2(node.pos.x, node.pos.y);
|
||||
let node_size = egui::vec2(150., 150.);
|
||||
let node_rect = egui::Rect::from_min_size(node_pos, node_size);
|
||||
layout.node_rects.insert(node_index, node_rect);
|
||||
|
||||
let slot_top = node_pos.y + style.slot_spacing;
|
||||
let slot_left = node_rect.left();
|
||||
let slot_right = node_rect.right();
|
||||
|
||||
for (slot_index, input) in node.inputs.iter().enumerate() {
|
||||
let slot_y = slot_index as f32 * style.slot_spacing + slot_top;
|
||||
let position = glam::Vec2::new(slot_left, slot_y);
|
||||
|
||||
let slot_index = SlotIndex {
|
||||
node: node_index,
|
||||
slot: slot_index,
|
||||
};
|
||||
|
||||
layout.input_positions.insert(slot_index, position);
|
||||
|
||||
if let Some(input) = input {
|
||||
edges.push(Edge {
|
||||
input: *input,
|
||||
output: slot_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
layout.output_positions.insert(
|
||||
SlotIndex {
|
||||
node: node_index,
|
||||
slot: 0,
|
||||
},
|
||||
glam::Vec2::new(slot_right, slot_top),
|
||||
);
|
||||
}
|
||||
|
||||
for edge in edges.into_iter() {
|
||||
let input = *layout.input_positions.get(&edge.output).unwrap();
|
||||
let output = *layout.output_positions.get(&edge.input).unwrap();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_graph(ui: &mut egui::Ui, graph: &Graph) {
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
let painter = ui.painter_at(rect);
|
||||
|
||||
let style = ui.style();
|
||||
let style = GraphStyle::from_ui_style(style);
|
||||
let layout = GraphLayout::layout(&style, graph);
|
||||
|
||||
let pointer_pos = ui.input().pointer.interact_pos();
|
||||
let mut pointer_target: Option<PointerTarget> = None;
|
||||
|
||||
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() {
|
||||
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);
|
||||
}
|
||||
|
||||
for (_edge, edge_layout) in layout.edges.iter() {
|
||||
let points = edge_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);
|
||||
}
|
||||
|
||||
for (_node_index, node_rect) in layout.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(&layout.input_positions);
|
||||
draw_slots(&layout.output_positions);
|
||||
|
||||
println!("{:#?}", pointer_target);
|
||||
}
|
Loading…
Reference in New Issue