diff options
Diffstat (limited to 'src/gui')
| -rw-r--r-- | src/gui/dimension_indicator.rs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/src/gui/dimension_indicator.rs b/src/gui/dimension_indicator.rs new file mode 100644 index 0000000..cc12f0b --- /dev/null +++ b/src/gui/dimension_indicator.rs @@ -0,0 +1,128 @@ +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<f64>, Vec2<f64>)>, +} + +impl DimensionIndicator { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + length_lines: Vec::new(), + } + } + + pub fn from_corner_points(corner_points: &[Vec2<f64>]) -> 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<f64>]) { + 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, + ); + } + } +} |
