1use crate::camera_projection::CameraProjection;
50use crate::layer::{Layer, LayerId, LayerKind};
51use crate::layers::image_overlay_layer::ImageOverlayData;
52use rustial_math::GeoCoord;
53use std::any::Any;
54use std::sync::Arc;
55
56#[derive(Debug, Clone)]
62pub struct FrameData {
63 pub width: u32,
65 pub height: u32,
67 pub data: Vec<u8>,
69}
70
71pub trait FrameProvider: Send + Sync {
82 fn next_frame(&mut self) -> Option<FrameData>;
87
88 fn is_animating(&self) -> bool {
95 true
96 }
97}
98
99pub struct CallbackFrameProvider<F> {
103 callback: F,
104 animating: bool,
105}
106
107impl<F: FnMut() -> Option<FrameData> + Send + Sync> CallbackFrameProvider<F> {
108 pub fn new(callback: F) -> Self {
110 Self {
111 callback,
112 animating: true,
113 }
114 }
115
116 pub fn set_animating(&mut self, animating: bool) {
118 self.animating = animating;
119 }
120}
121
122impl<F: FnMut() -> Option<FrameData> + Send + Sync> FrameProvider for CallbackFrameProvider<F> {
123 fn next_frame(&mut self) -> Option<FrameData> {
124 (self.callback)()
125 }
126
127 fn is_animating(&self) -> bool {
128 self.animating
129 }
130}
131
132pub type FrameProviderFactory = Arc<dyn Fn() -> Box<dyn FrameProvider> + Send + Sync>;
138
139pub struct DynamicImageOverlayLayer {
157 id: LayerId,
158 name: String,
159 visible: bool,
160 opacity: f32,
161 coordinates: [GeoCoord; 4],
163 width: u32,
165 height: u32,
166 data: Arc<Vec<u8>>,
168 generation: u64,
170 has_frame: bool,
172 provider: Box<dyn FrameProvider>,
174}
175
176impl std::fmt::Debug for DynamicImageOverlayLayer {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 f.debug_struct("DynamicImageOverlayLayer")
179 .field("id", &self.id)
180 .field("name", &self.name)
181 .field("visible", &self.visible)
182 .field("opacity", &self.opacity)
183 .field("width", &self.width)
184 .field("height", &self.height)
185 .field("has_frame", &self.has_frame)
186 .field("generation", &self.generation)
187 .field("animating", &self.provider.is_animating())
188 .finish()
189 }
190}
191
192impl DynamicImageOverlayLayer {
193 pub fn new(
197 name: impl Into<String>,
198 coordinates: [GeoCoord; 4],
199 provider: Box<dyn FrameProvider>,
200 ) -> Self {
201 Self {
202 id: LayerId::next(),
203 name: name.into(),
204 visible: true,
205 opacity: 1.0,
206 coordinates,
207 width: 0,
208 height: 0,
209 data: Arc::new(Vec::new()),
210 generation: 0,
211 has_frame: false,
212 provider,
213 }
214 }
215
216 #[inline]
218 pub fn coordinates(&self) -> &[GeoCoord; 4] {
219 &self.coordinates
220 }
221
222 pub fn set_coordinates(&mut self, coordinates: [GeoCoord; 4]) {
224 self.coordinates = coordinates;
225 self.generation = self.generation.wrapping_add(1);
226 }
227
228 #[inline]
230 pub fn generation(&self) -> u64 {
231 self.generation
232 }
233
234 #[inline]
236 pub fn dimensions(&self) -> (u32, u32) {
237 (self.width, self.height)
238 }
239
240 #[inline]
242 pub fn has_frame(&self) -> bool {
243 self.has_frame
244 }
245
246 pub fn poll_frame(&mut self) -> bool {
251 if !self.provider.is_animating() {
252 return false;
253 }
254 if let Some(frame) = self.provider.next_frame() {
255 debug_assert_eq!(
256 frame.data.len(),
257 (frame.width * frame.height * 4) as usize,
258 "FrameData RGBA8 length must equal width * height * 4"
259 );
260 self.width = frame.width;
261 self.height = frame.height;
262 self.data = Arc::new(frame.data);
263 self.generation = self.generation.wrapping_add(1);
264 self.has_frame = true;
265 true
266 } else {
267 false
268 }
269 }
270
271 pub fn to_overlay_data(&self, projection: CameraProjection) -> Option<ImageOverlayData> {
276 if !self.has_frame || self.width == 0 || self.height == 0 {
277 return None;
278 }
279 let corners = [
280 project_corner(&self.coordinates[0], projection),
281 project_corner(&self.coordinates[1], projection),
282 project_corner(&self.coordinates[2], projection),
283 project_corner(&self.coordinates[3], projection),
284 ];
285 Some(ImageOverlayData {
286 layer_id: self.id,
287 corners,
288 width: self.width,
289 height: self.height,
290 data: Arc::clone(&self.data),
291 opacity: self.opacity,
292 })
293 }
294
295 pub fn provider(&self) -> &dyn FrameProvider {
297 &*self.provider
298 }
299
300 pub fn provider_mut(&mut self) -> &mut dyn FrameProvider {
302 &mut *self.provider
303 }
304}
305
306fn project_corner(coord: &GeoCoord, projection: CameraProjection) -> [f64; 3] {
307 let w = projection.project(coord);
308 [w.position.x, w.position.y, w.position.z]
309}
310
311impl Layer for DynamicImageOverlayLayer {
316 fn id(&self) -> LayerId {
317 self.id
318 }
319
320 fn name(&self) -> &str {
321 &self.name
322 }
323
324 fn kind(&self) -> LayerKind {
325 LayerKind::Custom
326 }
327
328 fn visible(&self) -> bool {
329 self.visible
330 }
331
332 fn set_visible(&mut self, visible: bool) {
333 self.visible = visible;
334 }
335
336 fn opacity(&self) -> f32 {
337 self.opacity
338 }
339
340 fn set_opacity(&mut self, opacity: f32) {
341 self.opacity = opacity.clamp(0.0, 1.0);
342 }
343
344 fn as_any(&self) -> &dyn Any {
345 self
346 }
347
348 fn as_any_mut(&mut self) -> &mut dyn Any {
349 self
350 }
351}
352
353#[cfg(test)]
358mod tests {
359 use super::*;
360
361 struct CountingProvider {
362 frame_count: u32,
363 width: u32,
364 height: u32,
365 }
366
367 impl CountingProvider {
368 fn new(width: u32, height: u32) -> Self {
369 Self {
370 frame_count: 0,
371 width,
372 height,
373 }
374 }
375 }
376
377 impl FrameProvider for CountingProvider {
378 fn next_frame(&mut self) -> Option<FrameData> {
379 self.frame_count += 1;
380 let fill = (self.frame_count % 256) as u8;
382 Some(FrameData {
383 width: self.width,
384 height: self.height,
385 data: vec![fill; (self.width * self.height * 4) as usize],
386 })
387 }
388 }
389
390 struct PausableProvider {
391 animating: bool,
392 }
393
394 impl FrameProvider for PausableProvider {
395 fn next_frame(&mut self) -> Option<FrameData> {
396 Some(FrameData {
397 width: 2,
398 height: 2,
399 data: vec![128; 16],
400 })
401 }
402
403 fn is_animating(&self) -> bool {
404 self.animating
405 }
406 }
407
408 struct OneShotProvider {
409 sent: bool,
410 }
411
412 impl FrameProvider for OneShotProvider {
413 fn next_frame(&mut self) -> Option<FrameData> {
414 if self.sent {
415 None
416 } else {
417 self.sent = true;
418 Some(FrameData {
419 width: 4,
420 height: 4,
421 data: vec![255; 64],
422 })
423 }
424 }
425
426 fn is_animating(&self) -> bool {
427 !self.sent
428 }
429 }
430
431 fn sample_corners() -> [GeoCoord; 4] {
432 [
433 GeoCoord::from_lat_lon(40.0, -74.0),
434 GeoCoord::from_lat_lon(40.0, -73.0),
435 GeoCoord::from_lat_lon(39.0, -73.0),
436 GeoCoord::from_lat_lon(39.0, -74.0),
437 ]
438 }
439
440 #[test]
441 fn new_layer_starts_without_frame() {
442 let layer = DynamicImageOverlayLayer::new(
443 "test",
444 sample_corners(),
445 Box::new(CountingProvider::new(8, 8)),
446 );
447 assert!(!layer.has_frame());
448 assert_eq!(layer.dimensions(), (0, 0));
449 assert_eq!(layer.generation(), 0);
450 }
451
452 #[test]
453 fn poll_frame_receives_first_frame() {
454 let mut layer = DynamicImageOverlayLayer::new(
455 "test",
456 sample_corners(),
457 Box::new(CountingProvider::new(8, 8)),
458 );
459 assert!(layer.poll_frame());
460 assert!(layer.has_frame());
461 assert_eq!(layer.dimensions(), (8, 8));
462 assert_eq!(layer.generation(), 1);
463 }
464
465 #[test]
466 fn consecutive_polls_bump_generation() {
467 let mut layer = DynamicImageOverlayLayer::new(
468 "test",
469 sample_corners(),
470 Box::new(CountingProvider::new(4, 4)),
471 );
472 layer.poll_frame();
473 layer.poll_frame();
474 layer.poll_frame();
475 assert_eq!(layer.generation(), 3);
476 }
477
478 #[test]
479 fn paused_provider_skips_poll() {
480 let mut layer = DynamicImageOverlayLayer::new(
481 "test",
482 sample_corners(),
483 Box::new(PausableProvider { animating: false }),
484 );
485 assert!(!layer.poll_frame());
486 assert!(!layer.has_frame());
487 assert_eq!(layer.generation(), 0);
488 }
489
490 #[test]
491 fn one_shot_provider_stops_after_first() {
492 let mut layer = DynamicImageOverlayLayer::new(
493 "test",
494 sample_corners(),
495 Box::new(OneShotProvider { sent: false }),
496 );
497 assert!(layer.poll_frame());
498 assert_eq!(layer.generation(), 1);
499 assert!(!layer.poll_frame());
501 assert_eq!(layer.generation(), 1);
502 }
503
504 #[test]
505 fn to_overlay_data_returns_none_without_frame() {
506 let layer = DynamicImageOverlayLayer::new(
507 "test",
508 sample_corners(),
509 Box::new(CountingProvider::new(4, 4)),
510 );
511 assert!(layer
512 .to_overlay_data(CameraProjection::WebMercator)
513 .is_none());
514 }
515
516 #[test]
517 fn to_overlay_data_returns_some_after_poll() {
518 let mut layer = DynamicImageOverlayLayer::new(
519 "test",
520 sample_corners(),
521 Box::new(CountingProvider::new(4, 4)),
522 );
523 layer.poll_frame();
524 let data = layer.to_overlay_data(CameraProjection::WebMercator);
525 assert!(data.is_some());
526 let data = data.unwrap();
527 assert_eq!(data.width, 4);
528 assert_eq!(data.height, 4);
529 assert_eq!(data.data.len(), 64);
530 }
531
532 #[test]
533 fn set_coordinates_bumps_generation() {
534 let mut layer = DynamicImageOverlayLayer::new(
535 "test",
536 sample_corners(),
537 Box::new(CountingProvider::new(4, 4)),
538 );
539 let g0 = layer.generation();
540 layer.set_coordinates([
541 GeoCoord::from_lat_lon(50.0, -75.0),
542 GeoCoord::from_lat_lon(50.0, -74.0),
543 GeoCoord::from_lat_lon(49.0, -74.0),
544 GeoCoord::from_lat_lon(49.0, -75.0),
545 ]);
546 assert_eq!(layer.generation(), g0 + 1);
547 }
548
549 #[test]
550 fn opacity_clamps_to_valid_range() {
551 let mut layer = DynamicImageOverlayLayer::new(
552 "test",
553 sample_corners(),
554 Box::new(CountingProvider::new(4, 4)),
555 );
556 layer.set_opacity(2.0);
557 assert_eq!(layer.opacity(), 1.0);
558 layer.set_opacity(-0.5);
559 assert_eq!(layer.opacity(), 0.0);
560 }
561
562 #[test]
563 fn callback_frame_provider_works() {
564 let counter = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
565 let counter_clone = counter.clone();
566 let provider = CallbackFrameProvider::new(move || {
567 let n = counter_clone.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
568 Some(FrameData {
569 width: 2,
570 height: 2,
571 data: vec![(n % 256) as u8; 16],
572 })
573 });
574 let mut layer = DynamicImageOverlayLayer::new("test", sample_corners(), Box::new(provider));
575 layer.poll_frame();
576 assert!(layer.has_frame());
577 assert_eq!(counter.load(std::sync::atomic::Ordering::Relaxed), 1);
578 layer.poll_frame();
579 assert_eq!(counter.load(std::sync::atomic::Ordering::Relaxed), 2);
580 }
581}