1use crate::geo::LonLat;
4
5#[derive(Debug, Clone, PartialEq)]
7pub enum Shape {
8 Marker(LonLat),
9 Polyline(Vec<LonLat>),
10 Polygon(Vec<LonLat>),
11 Circle { center: LonLat, radius_m: f64 },
12 Rectangle { sw: LonLat, ne: LonLat },
13}
14
15impl Shape {
16 pub fn vertex_count(&self) -> usize {
18 match self {
19 Shape::Marker(_) => 1,
20 Shape::Polyline(v) | Shape::Polygon(v) => v.len(),
21 Shape::Circle { .. } => 1,
22 Shape::Rectangle { .. } => 2,
23 }
24 }
25}
26
27#[derive(Debug, Default)]
29pub struct DrawSession {
30 shapes: Vec<Shape>,
31 undo_stack: Vec<DrawOp>,
32 redo_stack: Vec<DrawOp>,
33}
34
35#[derive(Debug, Clone)]
36enum DrawOp {
37 Add { index: usize },
38 Remove { index: usize, shape: Shape },
39 Clear { shapes: Vec<Shape> },
40}
41
42impl DrawSession {
43 pub fn new() -> Self {
44 DrawSession::default()
45 }
46
47 pub fn shapes(&self) -> &[Shape] {
48 &self.shapes
49 }
50
51 pub fn add(&mut self, shape: Shape) {
53 let index = self.shapes.len();
54 self.shapes.push(shape);
55 self.undo_stack.push(DrawOp::Add { index });
56 self.redo_stack.clear();
57 }
58
59 pub fn remove(&mut self, index: usize) -> bool {
61 if index >= self.shapes.len() {
62 return false;
63 }
64 let shape = self.shapes.remove(index);
65 self.undo_stack.push(DrawOp::Remove { index, shape });
66 self.redo_stack.clear();
67 true
68 }
69
70 pub fn clear(&mut self) {
72 if self.shapes.is_empty() {
73 return;
74 }
75 let shapes = std::mem::take(&mut self.shapes);
76 self.undo_stack.push(DrawOp::Clear { shapes });
77 self.redo_stack.clear();
78 }
79
80 pub fn can_undo(&self) -> bool {
81 !self.undo_stack.is_empty()
82 }
83 pub fn can_redo(&self) -> bool {
84 !self.redo_stack.is_empty()
85 }
86
87 pub fn undo(&mut self) -> bool {
89 let op = match self.undo_stack.pop() {
90 Some(op) => op,
91 None => return false,
92 };
93 match op {
94 DrawOp::Add { index } => {
95 if index < self.shapes.len() {
96 let shape = self.shapes.remove(index);
97 self.redo_stack.push(DrawOp::Remove { index, shape });
98 }
99 }
100 DrawOp::Remove { index, shape } => {
101 let idx = index.min(self.shapes.len());
102 self.shapes.insert(idx, shape);
103 self.redo_stack.push(DrawOp::Add { index: idx });
104 }
105 DrawOp::Clear { shapes } => {
106 let current = std::mem::replace(&mut self.shapes, shapes);
107 self.redo_stack.push(DrawOp::Clear { shapes: current });
108 }
109 }
110 true
111 }
112
113 pub fn redo(&mut self) -> bool {
115 let op = match self.redo_stack.pop() {
116 Some(op) => op,
117 None => return false,
118 };
119 match op {
124 DrawOp::Remove { index, shape } => {
125 let idx = index.min(self.shapes.len());
126 self.shapes.insert(idx, shape);
127 self.undo_stack.push(DrawOp::Add { index: idx });
128 }
129 DrawOp::Add { index } => {
130 if index < self.shapes.len() {
131 let shape = self.shapes.remove(index);
132 self.undo_stack.push(DrawOp::Remove { index, shape });
133 }
134 }
135 DrawOp::Clear { .. } => {
136 let shapes = std::mem::take(&mut self.shapes);
137 self.undo_stack.push(DrawOp::Clear { shapes });
138 }
139 }
140 true
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 fn marker() -> Shape {
149 Shape::Marker(LonLat::new(105.85, 21.02))
150 }
151
152 #[test]
153 fn add_undo_redo() {
154 let mut s = DrawSession::new();
155 s.add(marker());
156 assert_eq!(s.shapes().len(), 1);
157 assert!(s.undo());
158 assert_eq!(s.shapes().len(), 0);
159 assert!(s.redo());
160 assert_eq!(s.shapes().len(), 1);
161 assert_eq!(s.shapes()[0], marker());
162 }
163
164 #[test]
165 fn remove_undo_restores_position() {
166 let mut s = DrawSession::new();
167 s.add(marker());
168 s.add(Shape::Marker(LonLat::new(106.0, 21.0)));
169 assert!(s.remove(0));
170 assert_eq!(s.shapes().len(), 1);
171 assert!(s.undo());
172 assert_eq!(s.shapes().len(), 2);
173 assert_eq!(s.shapes()[0], marker());
174 }
175
176 #[test]
177 fn clear_undo_restores_all() {
178 let mut s = DrawSession::new();
179 s.add(marker());
180 s.add(marker());
181 s.clear();
182 assert!(s.shapes().is_empty());
183 assert!(s.undo());
184 assert_eq!(s.shapes().len(), 2);
185 }
186
187 #[test]
188 fn new_action_clears_redo() {
189 let mut s = DrawSession::new();
190 s.add(marker());
191 s.undo();
192 assert!(s.can_redo());
193 s.add(marker());
194 assert!(!s.can_redo());
195 }
196
197 #[test]
198 fn undo_empty_returns_false() {
199 let mut s = DrawSession::new();
200 assert!(!s.undo());
201 assert!(!s.redo());
202 assert!(!s.remove(5));
203 }
204}