//! Tool to create rooms in the shape of generic polygons. use super::Tool; use crate::colours::DEFAULT_COLOURS; use crate::map::Map; use crate::math::{self, PolygonGraph, Vec2}; use crate::transform::Transform; use crate::FLOAT_MARGIN; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; /// The tool itself. pub struct PolygonRoomTool { unfinished_room: Option<(PolygonGraph, Vec2)>, last_mouse_pos_m: Vec2, } impl PolygonRoomTool { /// Create a new polygon room tool. There should be only one instance and it should be created /// in the editor. #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { unfinished_room: None, last_mouse_pos_m: Vec2::new(0., 0.), } } /* Helper function to try and finish the currently drawn polygon. If successful, it will add it * to the map, clear the currently drawn polygon and return bool. Otherwise it will leave the * unfinished polygon as is and return false without pushing anything. */ fn try_push(&mut self, map: &mut Map) -> bool { if self.unfinished_room.is_none() { return false; } match self .unfinished_room .as_ref() .unwrap() .0 .clone() .bounding_polygon(FLOAT_MARGIN) { Some(polygon) => { map.push_room(polygon); self.unfinished_room = None; true } None => false, } } } impl Tool for PolygonRoomTool { fn deactivate(&mut self) { self.unfinished_room = None; } fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2) { // Update the last mouse position that was seen for later use. self.last_mouse_pos_m = *mouse_pos_m; } fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { if let Some((graph, last_node)) = &self.unfinished_room { /* To turn the graph into a polygon, we need a copy, might as well do * it now, so we can add the working corner to it. */ let mut graph = graph.clone(); // Add the current mouse position as the next position if possible. graph.add_edge(&last_node, &self.last_mouse_pos_m); if graph.num_nodes() <= 1 { // Only able to draw a point rld.draw_circle_v( transform.point_m_to_px(&self.last_mouse_pos_m), transform.length_m_to_px(0.1) as f32, DEFAULT_COLOURS.room_selected, ); } else if let Some(polygon) = graph.clone().bounding_polygon(FLOAT_MARGIN) { let triangles = math::triangulate(polygon, FLOAT_MARGIN); for triangle in triangles { let triangle: [Vec2; 3] = triangle.into(); rld.draw_triangle( transform.point_m_to_px(&triangle[0]), transform.point_m_to_px(&triangle[1]), transform.point_m_to_px(&triangle[2]), DEFAULT_COLOURS.room_selected, ) } } else { // For some reason the polygon creation failed. Draw lines for the edges instead. for edge in graph.edge_iter() { rld.draw_line_ex( transform.point_m_to_px(&edge.start), transform.point_m_to_px(&edge.end), transform.length_m_to_px(0.1) as f32, DEFAULT_COLOURS.room_selected, ); } } } } fn place_single(&mut self, map: &mut Map, mouse_pos_m: &Vec2) { if let Some((ref mut graph, ref mut last_placed)) = &mut self.unfinished_room { // If the corner already exists in the polygon, try to finish and push it after adding the // next edge. let try_finish = graph.has_node(&mouse_pos_m); // Add an edge from the last corner to the currently active position if possible. if graph.add_edge(last_placed, &mouse_pos_m) { *last_placed = *mouse_pos_m; } if try_finish { self.try_push(map); } } else { // Start a new unfinished polygon self.unfinished_room = Some((PolygonGraph::new(), *mouse_pos_m)); } } fn finish(&mut self, map: &mut Map) { self.try_push(map); } fn abort(&mut self) { self.unfinished_room = None; } }