aboutsummaryrefslogtreecommitdiff
path: root/src/tool
diff options
context:
space:
mode:
Diffstat (limited to 'src/tool')
-rw-r--r--src/tool/polygon_room_tool.rs184
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);
}
}