aboutsummaryrefslogtreecommitdiff
path: root/src/dimension_indicator.rs
blob: cc12f0b5743dc568be100a327da3bda62fbdf01a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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,
            );
        }
    }
}