widgetry/tools/
lasso.rs

1use geom::{Distance, PolyLine, Polygon, Pt2D, Ring};
2
3use crate::{Color, EventCtx, GfxCtx};
4
5// TODO This is horrifically slow / memory inefficient. Reference implementation somewhere?
6
7/// Draw freehand polygons
8pub struct Lasso {
9    points: Vec<Pt2D>,
10    polygon: Option<Polygon>,
11    threshold: Distance,
12}
13
14impl Lasso {
15    /// How far do points need to be spaced apart to add?
16    pub fn new(threshold: Distance) -> Self {
17        Self {
18            points: Vec::new(),
19            polygon: None,
20            threshold,
21        }
22    }
23
24    /// When this returns a polygon, the interaction is finished
25    pub fn event(&mut self, ctx: &mut EventCtx) -> Option<Polygon> {
26        if self.points.is_empty() {
27            if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
28                if ctx.input.left_mouse_button_pressed() {
29                    self.points.push(pt);
30                }
31            }
32            return None;
33        }
34
35        if ctx.input.left_mouse_button_released() {
36            return self.polygon.take();
37        }
38
39        if ctx.redo_mouseover() {
40            if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
41                if self.points.last().as_ref().unwrap().dist_to(pt) > self.threshold {
42                    self.points.push(pt);
43
44                    // TODO It's better if the user doesn't close the polygon themselves. When they
45                    // try to, usually the result is the smaller polygon chunk
46                    let mut copy = self.points.clone();
47                    copy.push(copy[0]);
48                    self.polygon = Ring::new(copy).ok().map(|ring| ring.into_polygon());
49                }
50            }
51        }
52        None
53    }
54
55    pub fn draw(&self, g: &mut GfxCtx) {
56        if let Ok(pl) = PolyLine::new(self.points.clone()) {
57            g.draw_polygon(
58                Color::RED.alpha(0.8),
59                pl.make_polygons(Distance::meters(5.0) / g.canvas.cam_zoom),
60            );
61        }
62        if let Some(ref polygon) = self.polygon {
63            g.draw_polygon(Color::RED.alpha(0.5), polygon.clone());
64        }
65    }
66}
67
68/// Draw freehand PolyLine
69pub struct PolyLineLasso {
70    pl: Option<PolyLine>,
71}
72
73impl PolyLineLasso {
74    pub fn new() -> Self {
75        Self { pl: None }
76    }
77
78    /// When this returns a polyline, the interaction is finished
79    pub fn event(&mut self, ctx: &mut EventCtx) -> Option<PolyLine> {
80        if self.pl.is_none() {
81            if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
82                if ctx.input.left_mouse_button_pressed() {
83                    self.pl = Some(PolyLine::must_new(vec![pt, pt.offset(0.1, 0.1)]));
84                }
85            }
86            return None;
87        }
88
89        if ctx.input.left_mouse_button_released() {
90            return self.pl.take();
91        }
92
93        if ctx.redo_mouseover() {
94            if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
95                let pl = self.pl.take().unwrap();
96                self.pl = Some(pl.optionally_push(pt));
97            }
98        }
99        None
100    }
101
102    pub fn draw(&self, g: &mut GfxCtx) {
103        if let Some(ref pl) = self.pl {
104            g.draw_polygon(
105                Color::RED.alpha(0.8),
106                pl.make_polygons(Distance::meters(5.0) / g.canvas.cam_zoom),
107            );
108        }
109    }
110}