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
use crate::AppLike;
use geom::{Circle, Distance, FindClosest, Pt2D, Ring};
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
use widgetry::{Cached, Color, EventCtx, GfxCtx, Key};
pub struct EditPolygon {
points: Vec<Pt2D>,
world: Cached<f64, World<Obj>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum Obj {
Polygon,
Point(usize),
}
impl ObjectID for Obj {}
impl EditPolygon {
pub fn new(mut points: Vec<Pt2D>) -> Self {
if *points.last().unwrap() == points[0] {
points.pop();
}
Self {
points,
world: Cached::new(),
}
}
fn rebuild_world(&mut self, ctx: &mut EventCtx, app: &dyn AppLike) {
let mut world = World::bounded(app.map().get_bounds());
if self.points.len() >= 3 {
let mut pts = self.points.to_vec();
pts.push(pts[0]);
world
.add(Obj::Polygon)
.hitbox(Ring::must_new(pts).into_polygon())
.zorder(0)
.draw_color(Color::BLUE.alpha(0.6))
.hover_alpha(0.3)
.draggable()
.build(ctx);
}
for (idx, pt) in self.points.iter().enumerate() {
world
.add(Obj::Point(idx))
.hitbox(Circle::new(*pt, Distance::meters(10.0) / ctx.canvas.cam_zoom).to_polygon())
.zorder(1)
.draw_color(Color::RED)
.hover_alpha(0.8)
.hotkey(Key::Backspace, "delete")
.draggable()
.build(ctx);
}
world.initialize_hover(ctx);
if let Some(prev) = self.world.value() {
world.rebuilt_during_drag(prev);
}
self.world.set(ctx.canvas.cam_zoom, world);
}
pub fn event(&mut self, ctx: &mut EventCtx, app: &dyn AppLike) {
if self.world.key() != Some(ctx.canvas.cam_zoom) {
self.rebuild_world(ctx, app);
}
match self.world.value_mut().unwrap().event(ctx) {
WorldOutcome::ClickedFreeSpace(pt) => {
let mut closest = FindClosest::new(app.map().get_bounds());
for (idx, pair) in self.points.windows(2).enumerate() {
closest.add(idx + 1, &[pair[0], pair[1]]);
}
if let Some((idx, _)) = closest.closest_pt(pt, Distance::meters(1000.0)) {
self.points.insert(idx, pt);
} else {
self.points.push(pt);
}
self.rebuild_world(ctx, app);
}
WorldOutcome::Dragging {
obj: Obj::Point(idx),
dx,
dy,
..
} => {
self.points[idx] = self.points[idx].offset(dx, dy);
self.rebuild_world(ctx, app);
}
WorldOutcome::Dragging {
obj: Obj::Polygon,
dx,
dy,
..
} => {
for pt in &mut self.points {
*pt = pt.offset(dx, dy);
}
self.rebuild_world(ctx, app);
}
WorldOutcome::Keypress("delete", Obj::Point(idx)) => {
self.points.remove(idx);
self.rebuild_world(ctx, app);
}
_ => {}
}
}
pub fn draw(&self, g: &mut GfxCtx) {
self.world.value().unwrap().draw(g);
}
pub fn get_points(&self) -> &[Pt2D] {
&self.points
}
}