diff --git a/Cargo.toml b/Cargo.toml index ce0d050..ef5c70d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,21 +2,21 @@ name = "topola" version = "0.1.0" edition = "2021" -default-run = "topola-gui" +default-run = "topola-egui" [lib] name = "topola" [[bin]] -name = "topola-gui" -required-features = ["gui"] +name = "topola-egui" +required-features = ["egui"] [[bin]] name = "topola-sdl2-demo" required-features = ["sdl2"] [features] -gui = ["dep:egui", "dep:eframe"] +egui = ["dep:egui", "dep:eframe"] sdl2 = ["dep:sdl2"] [dependencies] diff --git a/src/bin/topola-gui/app.rs b/src/bin/topola-egui/app.rs similarity index 74% rename from src/bin/topola-gui/app.rs rename to src/bin/topola-egui/app.rs index 6dcecd9..51ed5cc 100644 --- a/src/bin/topola-gui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -1,7 +1,7 @@ /// Deserialize/Serialize is needed to persist app state between restarts. #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] -pub struct TemplateApp { +pub struct App { // Example stuff: label: String, @@ -9,7 +9,7 @@ pub struct TemplateApp { value: f32, } -impl Default for TemplateApp { +impl Default for App { fn default() -> Self { Self { // Example stuff: @@ -19,7 +19,7 @@ impl Default for TemplateApp { } } -impl TemplateApp { +impl App { /// Called once on start. pub fn new(cc: &eframe::CreationContext<'_>) -> Self { // Load previous app state if one exists. @@ -31,7 +31,7 @@ impl TemplateApp { } } -impl eframe::App for TemplateApp { +impl eframe::App for App { /// Called to save state before shutdown. fn save(&mut self, storage: &mut dyn eframe::Storage) { eframe::set_value(storage, eframe::APP_KEY, self); @@ -55,6 +55,14 @@ impl eframe::App for TemplateApp { }); }); - egui::CentralPanel::default().show(ctx, |ui| {}); + egui::CentralPanel::default().show(ctx, |ui| { + egui::Frame::canvas(ui.style()).show(ui, |ui| { + let (rect, response) = + ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag()); + let stroke = egui::Stroke::new(10.0, egui::Color32::from_white_alpha(255)); + ui.painter() + .line_segment([[10.0, 10.0].into(), [30.0, 30.0].into()], stroke); + }) + }); } } diff --git a/src/bin/topola-gui/main.rs b/src/bin/topola-egui/main.rs similarity index 91% rename from src/bin/topola-gui/main.rs rename to src/bin/topola-egui/main.rs index a5119af..6452fd2 100644 --- a/src/bin/topola-gui/main.rs +++ b/src/bin/topola-egui/main.rs @@ -1,7 +1,8 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release mod app; -use app::TemplateApp; +mod painter; +use app::App; // Build to native. #[cfg(not(target_arch = "wasm32"))] @@ -15,9 +16,9 @@ fn main() -> eframe::Result<()> { ..Default::default() }; eframe::run_native( - "eframe template", + "topola-egui", native_options, - Box::new(|cc| Box::new(TemplateApp::new(cc))), + Box::new(|cc| Box::new(App::new(cc))), ) } diff --git a/src/bin/topola-egui/painter.rs b/src/bin/topola-egui/painter.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/bin/topola-egui/painter.rs @@ -0,0 +1 @@ + diff --git a/src/bin/topola-sdl2-demo/main.rs b/src/bin/topola-sdl2-demo/main.rs index b262369..746fbf6 100644 --- a/src/bin/topola-sdl2-demo/main.rs +++ b/src/bin/topola-sdl2-demo/main.rs @@ -1,5 +1,7 @@ extern crate sdl2; +mod painter; + macro_rules! dbg_dot { ($graph:expr) => { use petgraph::dot::Dot; @@ -8,6 +10,7 @@ macro_rules! dbg_dot { } use geo::point; +use painter::Painter; use petgraph::visit::{EdgeRef, IntoEdgeReferences}; use topola::draw::DrawException; use topola::layout::connectivity::BandIndex; @@ -665,6 +668,7 @@ fn render_times( let window_size = window.size(); let mut canvas = Canvas::new(vec2f(window_size.0 as f32, window_size.1 as f32)) .get_context_2d(font_context.clone()); + let mut painter = Painter::new(&mut canvas); let layout = match router_or_layout { RouterOrLayout::Router(ref mut router) => { @@ -701,17 +705,17 @@ fn render_times( }; let shape = node.primitive(layout).shape(); - render_shape(&mut canvas, &shape, color); + painter.paint_shape(&shape, color); } for ghost in ghosts { - render_shape(&mut canvas, &ghost, ColorU::new(75, 75, 150, 255)); + painter.paint_shape(&ghost, ColorU::new(75, 75, 150, 255)); } if let Some(ref mesh) = maybe_mesh { for edge in mesh.edge_references() { - let start_point = edge.source().primitive(layout).shape().center(); - let end_point = edge.target().primitive(layout).shape().center(); + let to = edge.source().primitive(layout).shape().center(); + let from = edge.target().primitive(layout).shape().center(); let color = 'blk: { if let (Some(source_pos), Some(target_pos)) = ( @@ -726,12 +730,7 @@ fn render_times( ColorU::new(125, 125, 125, 255) }; - let mut path = Path2D::new(); - path.move_to(vec2f(start_point.x() as f32, start_point.y() as f32)); - path.line_to(vec2f(end_point.x() as f32, end_point.y() as f32)); - canvas.set_stroke_style(color); - canvas.set_line_width(1.0); - canvas.stroke_path(path); + painter.paint_edge(from, to, color); } } //}); @@ -752,54 +751,3 @@ fn render_times( ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)); } } - -fn render_shape(canvas: &mut CanvasRenderingContext2D, shape: &Shape, color: ColorU) { - canvas.set_stroke_style(color); - canvas.set_fill_style(color); - - match shape { - Shape::Dot(dot) => { - let mut path = Path2D::new(); - path.ellipse( - vec2f(dot.c.pos.x() as f32, dot.c.pos.y() as f32), - dot.c.r as f32, - 0.0, - 0.0, - std::f32::consts::TAU, - ); - canvas.fill_path(path, FillRule::Winding); - } - Shape::Seg(seg) => { - let mut path = Path2D::new(); - path.move_to(vec2f(seg.from.x() as f32, seg.from.y() as f32)); - path.line_to(vec2f(seg.to.x() as f32, seg.to.y() as f32)); - canvas.set_line_width(seg.width as f32); - canvas.stroke_path(path); - } - Shape::Bend(bend) => { - let delta1 = bend.from - bend.c.pos; - let delta2 = bend.to - bend.c.pos; - - let angle1 = delta1.y().atan2(delta1.x()); - let angle2 = delta2.y().atan2(delta2.x()); - - let mut path = Path2D::new(); - path.arc( - vec2f(bend.c.pos.x() as f32, bend.c.pos.y() as f32), - bend.circle().r as f32, - angle1 as f32, - angle2 as f32, - ArcDirection::CW, - ); - canvas.set_line_width(bend.width as f32); - canvas.stroke_path(path); - } - } - let envelope = ShapeTrait::envelope(shape, 0.0); - // XXX: points represented as arrays can't be conveniently converted to vector types - let topleft = vec2f(envelope.lower()[0] as f32, envelope.lower()[1] as f32); - let bottomright = vec2f(envelope.upper()[0] as f32, envelope.upper()[1] as f32); - canvas.set_line_width(1.0); - canvas.set_stroke_style(ColorU::new(100, 100, 100, 255)); - canvas.stroke_rect(RectF::new(topleft, bottomright - topleft)); -} diff --git a/src/bin/topola-sdl2-demo/painter.rs b/src/bin/topola-sdl2-demo/painter.rs new file mode 100644 index 0000000..ba21614 --- /dev/null +++ b/src/bin/topola-sdl2-demo/painter.rs @@ -0,0 +1,78 @@ +use geo::Point; +use pathfinder_canvas::{ + vec2f, ArcDirection, Canvas, CanvasRenderingContext2D, ColorU, FillRule, Path2D, RectF, +}; +use topola::layout::geometry::shape::{Shape, ShapeTrait}; + +pub struct Painter<'a> { + canvas: &'a mut CanvasRenderingContext2D, +} + +impl<'a> Painter<'a> { + pub fn new(canvas: &'a mut CanvasRenderingContext2D) -> Self { + Self { canvas } + } + + pub fn paint_shape(&mut self, shape: &Shape, color: ColorU) { + self.canvas.set_stroke_style(color); + self.canvas.set_fill_style(color); + + match shape { + Shape::Dot(dot) => { + let mut path = Path2D::new(); + path.ellipse( + vec2f(dot.c.pos.x() as f32, dot.c.pos.y() as f32), + dot.c.r as f32, + 0.0, + 0.0, + std::f32::consts::TAU, + ); + self.canvas.fill_path(path, FillRule::Winding); + } + Shape::Seg(seg) => { + let mut path = Path2D::new(); + path.move_to(vec2f(seg.from.x() as f32, seg.from.y() as f32)); + path.line_to(vec2f(seg.to.x() as f32, seg.to.y() as f32)); + self.canvas.set_line_width(seg.width as f32); + self.canvas.stroke_path(path); + } + Shape::Bend(bend) => { + let delta1 = bend.from - bend.c.pos; + let delta2 = bend.to - bend.c.pos; + + let angle1 = delta1.y().atan2(delta1.x()); + let angle2 = delta2.y().atan2(delta2.x()); + + let mut path = Path2D::new(); + path.arc( + vec2f(bend.c.pos.x() as f32, bend.c.pos.y() as f32), + bend.circle().r as f32, + angle1 as f32, + angle2 as f32, + ArcDirection::CW, + ); + self.canvas.set_line_width(bend.width as f32); + self.canvas.stroke_path(path); + } + } + + let envelope = ShapeTrait::envelope(shape, 0.0); + // XXX: points represented as arrays can't be conveniently converted to vector types + let topleft = vec2f(envelope.lower()[0] as f32, envelope.lower()[1] as f32); + let bottomright = vec2f(envelope.upper()[0] as f32, envelope.upper()[1] as f32); + self.canvas.set_line_width(1.0); + self.canvas + .set_stroke_style(ColorU::new(100, 100, 100, 255)); + self.canvas + .stroke_rect(RectF::new(topleft, bottomright - topleft)); + } + + pub fn paint_edge(&mut self, from: Point, to: Point, color: ColorU) { + let mut path = Path2D::new(); + path.move_to(vec2f(from.x() as f32, from.y() as f32)); + path.line_to(vec2f(to.x() as f32, to.y() as f32)); + self.canvas.set_stroke_style(color); + self.canvas.set_line_width(1.0); + self.canvas.stroke_path(path); + } +}