1use egui::{Painter, Pos2, Response};
4use std::any::Any;
5
6use crate::projection::MapProjection;
7
8#[cfg(feature = "geojson")]
10pub mod geojson;
11
12#[cfg(feature = "drawing-layer")]
14pub mod drawing;
15
16#[cfg(feature = "text-layer")]
18pub mod text;
19
20#[cfg(feature = "area-layer")]
22pub mod area;
23
24#[cfg(feature = "tile-layer")]
26pub mod tile;
27
28pub(crate) mod serde_color32 {
30 use egui::Color32;
31 use serde::{self, Deserialize, Deserializer, Serializer};
32
33 pub fn serialize<S>(color: &Color32, serializer: S) -> Result<S::Ok, S::Error>
35 where
36 S: Serializer,
37 {
38 serializer.serialize_str(&color.to_hex())
39 }
40
41 pub fn deserialize<'de, D>(deserializer: D) -> Result<Color32, D::Error>
43 where
44 D: Deserializer<'de>,
45 {
46 let s = <String as Deserialize>::deserialize(deserializer)?;
47 Color32::from_hex(&s).map_err(|err| serde::de::Error::custom(format!("{:?}", err)))
48 }
49}
50
51pub(crate) mod serde_stroke {
53 use crate::layers::serde_color32;
54 use egui::{Color32, Stroke};
55 use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
56
57 #[derive(Serialize, Deserialize)]
58 struct StrokeHelper {
59 width: f32,
60 #[serde(with = "serde_color32")]
61 color: Color32,
62 }
63
64 pub fn serialize<S>(stroke: &Stroke, serializer: S) -> Result<S::Ok, S::Error>
65 where
66 S: Serializer,
67 {
68 let helper = StrokeHelper {
69 width: stroke.width,
70 color: stroke.color,
71 };
72 helper.serialize(serializer)
73 }
74
75 pub fn deserialize<'de, D>(deserializer: D) -> Result<Stroke, D::Error>
76 where
77 D: Deserializer<'de>,
78 {
79 let helper = StrokeHelper::deserialize(deserializer)?;
80 Ok(Stroke {
81 width: helper.width,
82 color: helper.color,
83 })
84 }
85}
86
87pub trait Layer: Any {
89 fn handle_input(&mut self, response: &Response, projection: &MapProjection) -> bool;
92
93 fn draw(&self, painter: &Painter, projection: &MapProjection);
95
96 fn as_any(&self) -> &dyn Any;
98
99 fn as_any_mut(&mut self) -> &mut dyn Any;
101}
102
103pub(crate) fn dist_sq_to_segment(p: Pos2, a: Pos2, b: Pos2) -> f32 {
105 let ab = b - a;
106 let ap = p - a;
107 let l2 = ab.length_sq();
108
109 if l2 == 0.0 {
110 return ap.length_sq();
112 }
113
114 let t = (ap.dot(ab) / l2).clamp(0.0, 1.0);
117
118 let closest_point = a + t * ab;
120
121 p.distance_sq(closest_point)
122}
123
124pub(crate) fn projection_factor(p: Pos2, a: Pos2, b: Pos2) -> f32 {
127 let ab = b - a;
128 let ap = p - a;
129 let l2 = ab.length_sq();
130
131 if l2 == 0.0 {
132 return 0.0;
133 }
134
135 (ap.dot(ab) / l2).clamp(0.0, 1.0)
137}
138
139pub(crate) fn segments_intersect(p1: Pos2, q1: Pos2, p2: Pos2, q2: Pos2) -> bool {
141 fn orientation(p: Pos2, q: Pos2, r: Pos2) -> i8 {
142 let val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
143 if val.abs() < 1e-6 {
144 0 } else if val > 0.0 {
146 1 } else {
148 -1 }
150 }
151
152 let o1 = orientation(p1, q1, p2);
153 let o2 = orientation(p1, q1, q2);
154 let o3 = orientation(p2, q2, p1);
155 let o4 = orientation(p2, q2, q1);
156
157 if o1 != o2 && o3 != o4 {
159 return true;
160 }
161
162 false
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use egui::pos2;
171
172 const EPSILON: f32 = 1e-6;
173
174 #[test]
175 fn test_dist_sq_to_segment() {
176 let a = pos2(0.0, 0.0);
177 let b = pos2(10.0, 0.0);
178
179 let p1 = pos2(5.0, 0.0);
181 assert!((dist_sq_to_segment(p1, a, b) - 0.0).abs() < EPSILON);
182
183 let p2 = pos2(5.0, 5.0);
185 assert!((dist_sq_to_segment(p2, a, b) - 25.0).abs() < EPSILON); let p3 = pos2(-5.0, 5.0);
189 assert!((dist_sq_to_segment(p3, a, b) - 50.0).abs() < EPSILON); let p4 = pos2(15.0, 5.0);
193 assert!((dist_sq_to_segment(p4, a, b) - 50.0).abs() < EPSILON); let c = pos2(5.0, 5.0);
197 let p5 = pos2(10.0, 10.0);
198 assert!((dist_sq_to_segment(p5, c, c) - 50.0).abs() < EPSILON); }
200
201 #[test]
202 fn test_projection_factor() {
203 let a = pos2(0.0, 0.0);
204 let b = pos2(10.0, 0.0);
205
206 assert!((projection_factor(a, a, b) - 0.0).abs() < EPSILON);
208
209 assert!((projection_factor(b, a, b) - 1.0).abs() < EPSILON);
211
212 let p1 = pos2(5.0, 0.0);
214 assert!((projection_factor(p1, a, b) - 0.5).abs() < EPSILON);
215
216 let p2 = pos2(5.0, 5.0);
218 assert!((projection_factor(p2, a, b) - 0.5).abs() < EPSILON);
219
220 let p3 = pos2(-5.0, 5.0);
222 assert!((projection_factor(p3, a, b) - 0.0).abs() < EPSILON);
223
224 let p4 = pos2(15.0, 5.0);
226 assert!((projection_factor(p4, a, b) - 1.0).abs() < EPSILON);
227
228 let c = pos2(5.0, 5.0);
230 let p5 = pos2(10.0, 10.0);
231 assert!((projection_factor(p5, c, c) - 0.0).abs() < EPSILON);
232 }
233
234 #[test]
235 fn test_segments_intersect() {
236 let p1 = pos2(0.0, 0.0);
238 let q1 = pos2(10.0, 10.0);
239 let p2 = pos2(0.0, 10.0);
240 let q2 = pos2(10.0, 0.0);
241 assert!(segments_intersect(p1, q1, p2, q2), "General intersection");
242
243 let p1 = pos2(0.0, 0.0);
245 let q1 = pos2(10.0, 0.0);
246 let p2 = pos2(0.0, 5.0);
247 let q2 = pos2(10.0, 5.0);
248 assert!(
249 !segments_intersect(p1, q1, p2, q2),
250 "Parallel, no intersection"
251 );
252
253 let p1 = pos2(0.0, 0.0);
255 let q1 = pos2(1.0, 1.0);
256 let p2 = pos2(2.0, 2.0);
257 let q2 = pos2(3.0, 0.0);
258 assert!(
259 !segments_intersect(p1, q1, p2, q2),
260 "Not parallel, no intersection"
261 );
262
263 let p1 = pos2(0.0, 5.0);
265 let q1 = pos2(10.0, 5.0);
266 let p2 = pos2(5.0, 0.0);
267 let q2 = pos2(5.0, 5.0);
268 assert!(segments_intersect(p1, q1, p2, q2), "T-junction");
269
270 let p1 = pos2(0.0, 0.0);
272 let q1 = pos2(5.0, 5.0);
273 let p2 = pos2(5.0, 5.0);
274 let q2 = pos2(10.0, 0.0);
275 assert!(segments_intersect(p1, q1, p2, q2), "Meeting at an endpoint");
276
277 let p1 = pos2(0.0, 0.0);
279 let q1 = pos2(10.0, 0.0);
280 let p2 = pos2(5.0, 0.0);
281 let q2 = pos2(15.0, 0.0);
282 assert!(
283 !segments_intersect(p1, q1, p2, q2),
284 "Collinear, overlapping"
285 );
286
287 let p1 = pos2(0.0, 0.0);
289 let q1 = pos2(10.0, 0.0);
290 let p2 = pos2(11.0, 0.0);
291 let q2 = pos2(20.0, 0.0);
292 assert!(
293 !segments_intersect(p1, q1, p2, q2),
294 "Collinear, non-overlapping"
295 );
296
297 let p1 = pos2(0.0, 0.0);
299 let q1 = pos2(10.0, 0.0);
300 let p2 = pos2(2.0, 0.0);
301 let q2 = pos2(8.0, 0.0);
302 assert!(!segments_intersect(p1, q1, p2, q2), "Collinear, contained");
303
304 let p1 = pos2(0.0, 0.0);
306 let q1 = pos2(10.0, 0.0);
307 let p2 = pos2(5.0, 0.0);
308 let q2 = pos2(5.0, 0.0);
309 assert!(!segments_intersect(p1, q1, p2, q2), "Point on segment");
310 }
311}