Add ramen_egui crate

This commit is contained in:
mars 2022-09-19 07:13:47 -06:00
parent 1b3a991144
commit 36edf051ff
4 changed files with 264 additions and 1 deletions

View File

@ -1,7 +1,8 @@
[workspace]
members = [
"editor",
"ramen"
"ramen",
"ramen_egui"
]
[package]

12
ramen_egui/Cargo.toml Normal file
View File

@ -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"

View File

@ -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);
});
}
}

198
ramen_egui/src/lib.rs Normal file
View File

@ -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);
}