use super::Tool; use crate::button::Button; use crate::config::{PolygonRoomToolKeybindings, ToolKeybindings}; use crate::dimension_indicator::DimensionIndicator; use crate::grid::{snap_to_grid, SNAP_SIZE}; use crate::map_data::MapData; use crate::math::{self, Polygon, PolygonError, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; use raylib::ffi::Color; use raylib::RaylibHandle; struct UnfinishedPolygon { pub corners: Vec>, pub working_corner: Vec2, } pub struct PolygonRoomTool { keybindings: PolygonRoomToolKeybindings, unfinished_polygon: Option, dimension_indicator: DimensionIndicator, } impl UnfinishedPolygon { // Check the validity of the already completed corners only pub fn check_validity_completed(&self) -> Result<(), PolygonError> { Polygon::check_validity(&self.corners) } // Check the validity of the already completed corners, but with the working corner wedged // between the last and first corner (making it the new last corner). pub fn check_validity_all(&self) -> Result<(), PolygonError> { // TODO: Is this possible without changing the mutability of self and without reallocation? let mut corners = self.corners.clone(); corners.push(self.working_corner); Polygon::check_validity(&corners) } pub fn try_into_completed(&mut self) -> Option> { match self.check_validity_completed() { Ok(()) => Some(Polygon::new_unchecked(self.corners.drain(..).collect())), Err(e) => { warn!("Cannot turn placed corners into a polygon: {}", e); None } } } pub fn try_into_all(&mut self) -> Option> { match self.check_validity_all() { Ok(()) => { self.corners.push(self.working_corner); Some(Polygon::new_unchecked(self.corners.drain(..).collect())) } Err(e) => { warn!( "Cannot turn corners with newest corner into a polygon: {}", e ); None } } } pub fn try_push_working(&mut self) -> Result<(), PolygonError> { assert!(!self.corners.is_empty()); if self.corners.len() == 1 { return if self.corners[0] != self.working_corner { self.corners.push(self.working_corner); Ok(()) } else { Err(PolygonError::VertexDoubling(self.corners[0], 0)) }; } self.check_validity_all()?; self.corners.push(self.working_corner); Ok(()) } } impl PolygonRoomTool { pub fn new(keybindings: PolygonRoomToolKeybindings) -> Self { Self { keybindings, unfinished_polygon: None, dimension_indicator: DimensionIndicator::new(), } } } impl Tool for PolygonRoomTool { fn activate(&mut self) {} fn deactivate(&mut self) { self.unfinished_polygon = None; } fn active_update( &mut self, map: &mut MapData, rl: &RaylibHandle, transform: &Transform, mouse_blocked: bool, ) { let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into()); let snapped_mouse_pos_m = snap_to_grid(mouse_pos_m, SNAP_SIZE); // Update the position of the node that would be placed into the polygon next. if let Some(ref mut polygon) = &mut self.unfinished_polygon { polygon.working_corner = snapped_mouse_pos_m; polygon.corners.push(polygon.working_corner); self.dimension_indicator.update_dimensions(&polygon.corners); polygon.working_corner = polygon.corners.pop().unwrap(); } /* Check if the finishing keybinding has been pressed. If so, try to turn the part of the * polygon that is already completed into a proper polygon and push it into the map data. */ if self.keybindings.finish.is_pressed(rl, mouse_blocked) { if let Some(ref mut polygon) = self.unfinished_polygon { if let Some(polygon) = polygon.try_into_completed() { self.dimension_indicator.clear_dimensions(); map.polygons_mut().push(polygon); self.unfinished_polygon = None; } } } /* Handle placing a new corner of the polygon. If the corner is placed on the first node, * the polygon will be created. */ if self.keybindings.place_node.is_pressed(rl, mouse_blocked) { if let Some(ref mut polygon) = &mut self.unfinished_polygon { if polygon.working_corner == polygon.corners[0] { /* The working corner will be ignored, since it would double the vertex at the * polygon starting position. */ if let Some(polygon) = polygon.try_into_completed() { self.dimension_indicator.clear_dimensions(); map.polygons_mut().push(polygon); self.unfinished_polygon = None; } } else { // Check if we can add the corner to the polygon without ruining it. if let Err(e) = polygon.try_push_working() { error!("Cannot add corner to polygon: {}", e); } } } else { // Start a new unfinished polygon self.unfinished_polygon = Some(UnfinishedPolygon { corners: vec![snapped_mouse_pos_m], working_corner: snapped_mouse_pos_m, }); } } if self.keybindings.abort.is_pressed(rl, false) { self.unfinished_polygon = None; } } fn draw(&self, map: &MapData, rld: &mut RaylibDrawHandle, transform: &Transform) { // TODO: Buffer triangles so the polygons don't always have to be retriangulated. for polygon in map.polygons() { let triangles = math::triangulate(polygon.clone()); 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]), Color { r: 180, g: 180, b: 180, a: 255, }, ) } } if let Some(polygon) = &self.unfinished_polygon { // The first corner is guaranteed to be set, so we can at least draw a line. if polygon.corners.len() == 1 { rld.draw_line_ex( transform.point_m_to_px(&polygon.corners[0]), transform.point_m_to_px(&polygon.working_corner), transform.length_m_to_px(0.1) as f32, Color { r: 150, g: 200, b: 150, a: 255, }, ); } else if polygon.corners.len() == 2 { // We have three valid corners, so we can draw a triangle. rld.draw_triangle( transform.point_m_to_px(&polygon.corners[0]), transform.point_m_to_px(&polygon.corners[1]), transform.point_m_to_px(&polygon.working_corner), Color { r: 150, g: 200, b: 150, a: 255, }, ) } else { // A proper polygon can be drawn. if polygon.check_validity_all().is_ok() { let mut corners = polygon.corners.clone(); corners.push(polygon.working_corner); let polygon = Polygon::new_unchecked(corners); let triangles = math::triangulate(polygon); 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]), Color { r: 150, g: 200, b: 150, a: 255, }, ) } } else if polygon.check_validity_completed().is_ok() { let polygon = Polygon::new_unchecked(polygon.corners.clone()); let triangles = math::triangulate(polygon); 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]), Color { r: 150, g: 200, b: 150, a: 255, }, ) } } } self.dimension_indicator.draw(rld, transform); } } fn activation_key(&self) -> Button { self.keybindings.activation_key() } }