diff --git a/board-builder-impl-egui/src/main.rs b/board-builder-impl-egui/src/main.rs index c928a1c..7225d64 100644 --- a/board-builder-impl-egui/src/main.rs +++ b/board-builder-impl-egui/src/main.rs @@ -5,7 +5,7 @@ use image::DynamicImage; use rfd::FileDialog; -use board_builder::{ Board, CoordTransformer, read_board_from_file }; +use board_builder::{ Board, CoordTransformer, read_board_from_file, write_board_to_file }; use std::path::Path; @@ -23,104 +23,106 @@ struct BoardBuilderApp { impl eframe::App for BoardBuilderApp { fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { - TopBottomPanel::top("menubar").show(ctx, |ui| { - menu::bar(ui, |ui| { - fn choose_file() { - FileDialog::new().pick_file(); - } - ui.menu_button("File", |ui| { - ui.button("New"); - if ui.button("Open...").clicked() { - if let Some(board_file) = FileDialog::new().pick_file() { - match read_board_from_file(&board_file) { - Ok((board, image)) => { - self.board = board; - match image { - None => { self.image = None; self.texture = None } - Some(image) => self.load_image(ctx, image), - } - } - Err(_) => panic!("Could not open file!"), - } - } - } - if ui.button("Save As...").clicked() { - choose_file(); - } - if ui.button("Open Image...").clicked() { - let image_file = FileDialog::new() - .add_filter("Image", &["png", "jpg", "jpeg", "gif", "webp", "bmp", "tiff"]) - .pick_file(); - if let Some(image_file) = image_file { - self.load_image_file(ctx, &image_file); - } - } - }); - ui.menu_button("Edit", |ui| { - ui.button("Edit Nodes"); - ui.button("Edit Edges"); - ui.button("Edit Labels..."); - }) - }); - }); - CentralPanel::default().show(ctx, |ui| { - if let Some(texture) = self.texture.as_ref() { - let size = ui.available_size(); - let (response, painter) = ui.allocate_painter(size, Sense::click()); - let image = widgets::Image::new(texture, size); - image.paint_at(ui, response.rect); - let view = View(response.rect); - self.draw_board(&painter, view); - } - }); + TopBottomPanel::top("menubar").show(ctx, |ui| { + menu::bar(ui, |ui| { + fn choose_file() { + FileDialog::new().pick_file(); + } + ui.menu_button("File", |ui| { + ui.button("New"); + if ui.button("Open...").clicked() { + if let Some(board_file) = FileDialog::new().pick_file() { + match read_board_from_file(&board_file) { + Ok((board, image)) => { + self.board = board; + match image { + None => { self.image = None; self.texture = None } + Some(image) => self.load_image(ctx, image), + } + } + Err(_) => panic!("Could not open file!"), + } + } + } + if ui.button("Save As...").clicked() { + if let Some(board_file) = FileDialog::new().save_file() { + write_board_to_file(&self.board, self.image.as_ref(), &board_file); + } + } + if ui.button("Open Image...").clicked() { + let image_file = FileDialog::new() + .add_filter("Image", &["png", "jpg", "jpeg", "gif", "webp", "bmp", "tiff"]) + .pick_file(); + if let Some(image_file) = image_file { + self.load_image_file(ctx, &image_file); + } + } + }); + ui.menu_button("Edit", |ui| { + ui.button("Edit Nodes"); + ui.button("Edit Edges"); + ui.button("Edit Labels..."); + }) + }); + }); + CentralPanel::default().show(ctx, |ui| { + if let Some(texture) = self.texture.as_ref() { + let size = ui.available_size(); + let (response, painter) = ui.allocate_painter(size, Sense::click()); + let image = widgets::Image::new(texture, size); + image.paint_at(ui, response.rect); + let view = View(response.rect); + self.draw_board(&painter, view); + } + }); } } impl BoardBuilderApp { fn new(cc: &eframe::CreationContext<'_>) -> Self { - let mut style = (*cc.egui_ctx.style()).clone(); - let mut button = style::TextStyle::Button.resolve(&style); - button.size = 20.0; - style.text_styles.insert(style::TextStyle::Button, button); - cc.egui_ctx.set_style(style); - - - BoardBuilderApp::default() + let mut style = (*cc.egui_ctx.style()).clone(); + let mut button = style::TextStyle::Button.resolve(&style); + button.size = 20.0; + style.text_styles.insert(style::TextStyle::Button, button); + cc.egui_ctx.set_style(style); + + + BoardBuilderApp::default() } fn load_image_file(&mut self, ctx: &Context, image_file: &Path) -> Result<(), image::ImageError> { - let image = image::io::Reader::open(image_file)?.decode()?; - self.load_image(ctx, image); - Ok(()) + let image = image::io::Reader::open(image_file)?.decode()?; + self.load_image(ctx, image); + Ok(()) } - + fn load_image(&mut self, ctx: &Context, image: DynamicImage) { - let egui_image = egui::ColorImage::from_rgba_unmultiplied( - [image.width() as _, image.height() as _], - image.to_rgba8().as_flat_samples().as_slice(), - ); - - self.image = Some(image); - self.texture = Some(ctx.load_texture("board-image", egui_image)); + let egui_image = egui::ColorImage::from_rgba_unmultiplied( + [image.width() as _, image.height() as _], + image.to_rgba8().as_flat_samples().as_slice(), + ); + + self.image = Some(image); + self.texture = Some(ctx.load_texture("board-image", egui_image)); } fn draw_board(&self, painter: &Painter, view: View) { - for node in self.board.nodes.values() { - painter.text( - view.from_coords(node.x, node.y), - Align2::CENTER_CENTER, - &node.name, - FontId::proportional(16.0), - Color32::BLACK, - ); - let stroke = Stroke { width: 1.0, color: Color32::BLACK }; - for edge in &node.edges { - let other_node = &self.board.nodes[edge]; - painter.line_segment( - [view.from_coords(node.x, node.y), view.from_coords(other_node.x, other_node.y)], - stroke, - ); - } - } + for node in self.board.nodes.values() { + painter.text( + view.from_coords(node.x, node.y), + Align2::CENTER_CENTER, + &node.name, + FontId::proportional(16.0), + Color32::BLACK, + ); + let stroke = Stroke { width: 1.0, color: Color32::BLACK }; + for edge in &node.edges { + let other_node = &self.board.nodes[edge]; + painter.line_segment( + [view.from_coords(node.x, node.y), view.from_coords(other_node.x, other_node.y)], + stroke, + ); + } + } } } diff --git a/board.zip b/board.zip index e42c4e2..0e7ee8a 100644 Binary files a/board.zip and b/board.zip differ diff --git a/src/lib.rs b/src/lib.rs index a06f82b..9359387 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,56 +19,56 @@ pub struct Board { } impl Board { pub fn new() -> Self { - let nodes = HashMap::new(); - let labels = HashMap::new(); - Board { nodes, labels } + let nodes = HashMap::new(); + let labels = HashMap::new(); + Board { nodes, labels } } pub fn add_node(&mut self, x: f32, y: f32, name: String) { - self.nodes.insert(self.next_id(), Node { - x, - y, - name, - edges: HashSet::new(), - labels: HashMap::new(), - }); + self.nodes.insert(self.next_id(), Node { + x, + y, + name, + edges: HashSet::new(), + labels: HashMap::new(), + }); } pub fn remove_node(&mut self, id: usize) { - // We remove this node from the graph, then drop it from each - // other nodes edge. - self.nodes.remove(&id); - for node in self.nodes.values_mut() { - node.edges.remove(&id); - } + // We remove this node from the graph, then drop it from each + // other nodes edge. + self.nodes.remove(&id); + for node in self.nodes.values_mut() { + node.edges.remove(&id); + } } pub fn nearest_node(&self, x: f32, y: f32) -> Option { - let f = |n: &Node| dist_sq((n.x, n.y), (x, y)); - let mut iter = self.nodes.iter(); - if let Some((id, node)) = iter.next() { - let mut min = *id; - let mut min_val = f(node); - for (id, node) in iter { - let val = f(node); - if val < min_val { - min = *id; - min_val = val; - } - } - Some(min) - } else { - None - } + let f = |n: &Node| dist_sq((n.x, n.y), (x, y)); + let mut iter = self.nodes.iter(); + if let Some((id, node)) = iter.next() { + let mut min = *id; + let mut min_val = f(node); + for (id, node) in iter { + let val = f(node); + if val < min_val { + min = *id; + min_val = val; + } + } + Some(min) + } else { + None + } } fn next_id(&self) -> usize { - for i in 0 .. { - if !self.nodes.contains_key(&i) { - return i; - } - } - unreachable!(); + for i in 0 .. { + if !self.nodes.contains_key(&i) { + return i; + } + } + unreachable!(); } } @@ -92,10 +92,10 @@ pub fn write_board_to_file(board: &Board, image: Option<&DynamicImage>, path: &P ar.start_file("graph.json", options)?; ar.write_all(&serde_json::to_vec(board)?)?; if let Some(image) = image { - ar.start_file("image.png", options)?; - let data = encode_png(image); - ar.write_all(&data)?; - ar.flush()?; + ar.start_file("image.png", options)?; + let data = encode_png(image); + ar.write_all(&data)?; + ar.flush()?; } ar.finish()?; Ok(()) @@ -109,7 +109,7 @@ pub fn read_board_from_file(path: &Path) -> io::Result<(Board, Option + From<(f32, f32)>> { fn origin(&self) -> I; fn extremes(&self) -> I; fn to_coords(&self, pos: I) -> (f32, f32) { - let (sx, sy) = self.origin().into(); - let (ex, ey) = self.extremes().into(); - let (x, y) = pos.into(); - ( - inv_lerp(sx, ex, x), - inv_lerp(sy, ey, y), - ) + let (sx, sy) = self.origin().into(); + let (ex, ey) = self.extremes().into(); + let (x, y) = pos.into(); + ( + inv_lerp(sx, ex, x), + inv_lerp(sy, ey, y), + ) } fn from_coords(&self, x: f32, y: f32) -> I { - let (sx, sy) = self.origin().into(); - let (ex, ey) = self.extremes().into(); - (lerp(sx, ex, x), lerp(sy, ey, y)).into() + let (sx, sy) = self.origin().into(); + let (ex, ey) = self.extremes().into(); + (lerp(sx, ex, x), lerp(sy, ey, y)).into() } }