diff options
Diffstat (limited to 'src/tool')
| -rw-r--r-- | src/tool/polygon_room_tool.rs | 184 |
1 files changed, 149 insertions, 35 deletions
diff --git a/src/tool/polygon_room_tool.rs b/src/tool/polygon_room_tool.rs index b37774b..58f326f 100644 --- a/src/tool/polygon_room_tool.rs +++ b/src/tool/polygon_room_tool.rs @@ -4,18 +4,85 @@ 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, Vec2}; +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<Vec2<f32>>, + pub working_corner: Vec2<f32>, +} + pub struct PolygonRoomTool { keybindings: PolygonRoomToolKeybindings, - unfinished_polygon: Option<Vec<Vec2<f32>>>, + unfinished_polygon: Option<UnfinishedPolygon>, dimension_indicator: DimensionIndicator, } +impl UnfinishedPolygon { + // Check the validity of the already completed corners only + pub fn check_validity_completed(&self) -> Result<(), PolygonError<f32>> { + 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<f32>> { + // TODO: Is this possible without changing the mutability of self and without reallocation? + let mut corners = self.corners.clone(); + corners.push(self.working_corner); + let res = Polygon::check_validity(&corners); + + res + } + + pub fn try_into_completed(&mut self) -> Option<Polygon<f32>> { + 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<Polygon<f32>> { + 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<f32>> { + assert!(self.corners.len() >= 1); + + 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 { @@ -42,40 +109,55 @@ impl Tool for PolygonRoomTool { ) { 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 corners) = &mut self.unfinished_polygon { - let last_element = corners.len() - 1; - corners[last_element] = snapped_mouse_pos_m; - self.dimension_indicator.update_dimensions(&corners); + 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(); } - if self.keybindings.finish.is_pressed(rl, mouse_blocked) - && self.unfinished_polygon.is_some() - { - // Make sure the polygon is at least a triangle, so it can be drawn. - if self.unfinished_polygon.as_ref().unwrap().len() >= 3 { - let polygon = Polygon::new(self.unfinished_polygon.take().unwrap()); - self.dimension_indicator.clear_dimensions(); - map.polygons_mut().push(polygon); + /* 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 corners) = self.unfinished_polygon.as_mut() { - if snapped_mouse_pos_m == corners[0] { - // Make sure the polygon is at least a triangle, so it can be drawn. - if corners.len() >= 3 { - // The last corner is redundant. - corners.pop(); - let polygon = Polygon::new(self.unfinished_polygon.take().unwrap()); + 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 { - corners.push(snapped_mouse_pos_m); + // 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 { - self.unfinished_polygon = Some(vec![snapped_mouse_pos_m, snapped_mouse_pos_m]); + // Start a new unfinished polygon + self.unfinished_polygon = Some(UnfinishedPolygon { + corners: vec![snapped_mouse_pos_m], + working_corner: snapped_mouse_pos_m, + }); } } @@ -104,15 +186,12 @@ impl Tool for PolygonRoomTool { } } - // Draw the current polygon - if let Some(corners) = &self.unfinished_polygon { - let mut corners = corners.clone(); - corners.dedup(); - match corners.len() { - 0 | 1 => {} - 2 => rld.draw_line_ex( - transform.point_m_to_px(corners[0]), - transform.point_m_to_px(corners[1]), + 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), Color { r: 150, @@ -120,9 +199,43 @@ impl Tool for PolygonRoomTool { b: 150, a: 255, }, - ), - _ => { - let polygon = Polygon::new(corners); + ); + } 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<f32>; 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<f32>; 3] = triangle.into(); @@ -140,6 +253,7 @@ impl Tool for PolygonRoomTool { } } } + self.dimension_indicator.draw(rld, transform); } } |
