use crate::math::Vec2; use crate::transform::Transform; use raylib::drawing::RaylibDraw; use raylib::ffi::Color; pub struct DimensionIndicator { /// The lines that are used to draw the Dimension Indicator. For a rectangle for instance these /// would be two. One for width and one for height. length_lines: Vec<(Vec2, Vec2)>, } impl DimensionIndicator { #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { length_lines: Vec::new(), } } pub fn from_corner_points(corner_points: &[Vec2]) -> Self { let mut this = Self::new(); this.update_dimensions(corner_points); this } pub fn clear_dimensions(&mut self) { self.length_lines.clear(); } /// Update the dimensions by analysing a given set of points and adjusting the internal /// (measured) dimensions. pub fn update_dimensions(&mut self, corner_points: &[Vec2]) { if corner_points.len() < 2 { warn!("Cannot discern dimensions when not at least two points are given. The dimensions were not updated."); return; } // Discern the bounding box for the given corner points. let mut min = corner_points[0]; let mut max = corner_points[0]; for point in corner_points.iter().skip(1) { if point.x < min.x { min.x = point.x; } if point.x > max.x { max.x = point.x; } if point.y < min.y { min.y = point.y; } if point.y > max.y { max.y = point.y; } } // For now, only use the width and height vectors. // TODO: Change to a more sophisticated approach. self.length_lines.clear(); // Horizontal dimensions, left to right. self.length_lines .push((Vec2::new(min.x, max.y), Vec2::new(max.x, max.y))); // Vertical dimensions, bottom to top. self.length_lines .push((Vec2::new(max.x, max.y), Vec2::new(max.x, min.y))); } pub fn draw(&self, rld: &mut impl RaylibDraw, transform: &Transform) { // Draw all the dimension lines. for (start, end) in &self.length_lines { // Don't draw anything if the length is zero. if start == end { continue; } /* Get the vector that is perpendicular and points right/down from the line, assuming * the lines prefer left as start over right and bottom over top. */ let line_normal = { // Start with the direction of the line vector. let dir = *start - *end; // Calculate perpendicular vec and normalise. dir.rotated_90_clockwise() / dir.length() }; // To not have the line directly in the rect, move start and end outside a bit. let start_px = transform.point_m_to_px(*start) + line_normal * 10.; let end_px = transform.point_m_to_px(*end) + line_normal * 10.; /* Draw the indicator line, with stubs at both ends. */ let line_colour = Color { r: 200, g: 200, b: 200, a: 255, }; // First the two stubs. rld.draw_line_ex( start_px - line_normal * 5., start_px + line_normal * 5., 2., line_colour, ); rld.draw_line_ex( end_px - line_normal * 5., end_px + line_normal * 5., 2., line_colour, ); // Then the actual indicator line. rld.draw_line_ex(start_px, end_px, 2., line_colour); /* Draw the indicator text showing how long this line is in meters. * It should be placed in the middle of the line, but not into the line directly, so it * will be moved out by the normal. */ let text_pos = transform.point_m_to_px((*end + *start) / 2.) + line_normal * 20.; rld.draw_text( &format!("{}m", &(*end - *start).length()), text_pos.x as i32, text_pos.y as i32, 20, line_colour, ); } } }