use std::{ops::Deref, sync::Arc};
use ccutils::{log::log_error, sync::ArcRwLock};
use egui::{ColorImage, TextureHandle};
use crate::{Result, map, renderer, styling};
pub struct Renderer
{
vello_renderer: crate::vello::ThreadedRenderer,
cpu_image_sub: crossbeam::channel::Receiver<crate::vello::CpuImage>,
texture: ArcRwLock<Option<TextureHandle>>,
}
impl Renderer
{
pub fn new<TFeature: map::Feature + 'static>(
map: Arc<map::Map<TFeature>>,
style: styling::Style<TFeature>,
) -> Result<Self>
{
let (device, queue) = crate::vello::ThreadedRenderer::new_device()?;
let mut vello_renderer = crate::vello::ThreadedRenderer::new_thread(device, queue, map, style)?;
let cpu_image_sub = vello_renderer.subscribe_to_cpu_image()?;
Ok(Self {
vello_renderer,
cpu_image_sub,
texture: None.into(),
})
}
pub fn update(&mut self) -> Result<()>
{
self.vello_renderer.update()
}
fn resize(&mut self, s: egui::Vec2) -> Result<()>
{
self.vello_renderer.resize(
s.x.ceil() as u32,
s.y.ceil() as u32,
Option::<fn(&crate::vello::Texture)>::None,
)
}
pub fn update_view_controller(&mut self, u: impl Fn(&mut renderer::ViewController))
{
self.vello_renderer.update_view_controller(u)
}
pub fn use_view_controller<T>(&self, u: impl FnOnce(&renderer::ViewController) -> T) -> T
{
self.vello_renderer.use_view_controller(u)
}
}
pub struct RendererWidget<'a>
{
renderer: &'a mut Renderer,
}
impl<'a> RendererWidget<'a>
{
fn new(renderer: &'a mut Renderer) -> Self
{
Self { renderer }
}
}
impl<'a> egui::Widget for RendererWidget<'a>
{
fn ui(self, ui: &mut egui::Ui) -> egui::Response
{
let (id, rect) = ui.allocate_space(ui.available_size());
let response = ui.interact(rect, id, egui::Sense::click_and_drag());
log_error!(self.renderer.resize(rect.size()), "While updating egui");
if let Ok(cpu_image) = self.renderer.cpu_image_sub.try_recv()
{
let mut texture = self.renderer.texture.write().unwrap();
let color_image = ColorImage::from_rgba_unmultiplied(
[cpu_image.width as usize, cpu_image.height as usize],
&cpu_image.data,
);
*texture = Some(ui.ctx().load_texture(
"vello_image",
color_image,
egui::TextureOptions::LINEAR,
));
}
let texture = self.renderer.texture.read().unwrap();
if let Some(texture) = texture.as_ref()
{
ui.painter().image(
texture.id(),
rect,
egui::Rect::from_min_max(egui::pos2(0.0, 0.0), egui::pos2(1.0, 1.0)),
egui::Color32::WHITE,
);
}
response
}
}
pub struct Response<'a>
{
raw_response: egui::Response,
map: &'a mut Renderer,
wheel_delta: Option<egui::Vec2>,
}
impl<'a> Deref for Response<'a>
{
type Target = egui::Response;
fn deref(&self) -> &Self::Target
{
&self.raw_response
}
}
impl<'a> Response<'a>
{
pub fn handle_interractions(&mut self) -> Result<()>
{
let mut should_update = false;
if let Some(wheel) = self.wheel_delta
{
if wheel.y > 0.0
{
self.map.update_view_controller(|vc| vc.zoom(0.75));
}
else
{
self.map.update_view_controller(|vc| vc.zoom(1.0 / 0.75));
}
should_update = true;
}
if self.raw_response.is_pointer_button_down_on()
{
let dm = self.drag_delta();
self.map.update_view_controller(|vc| {
let m = vc.motion_view_to_coord(&(dm.x as f64, dm.y as f64).into());
vc.pan(-m.x, -m.y);
});
should_update = true;
}
if should_update
{
self.map.update()?;
}
Ok(())
}
pub fn latest_pos_map(&self) -> Option<geo::Coord>
{
self.hover_pos().map(|cp| {
self.map.use_view_controller(|vc| {
vc.view_to_coord(
&(
(cp.x - self.rect.left()) as f64,
(cp.y - self.rect.top()) as f64,
)
.into(),
)
})
})
}
}
pub trait Ui
{
fn map_viewer<'a>(&mut self, map: &'a mut Renderer) -> Response<'a>;
}
impl Ui for egui::Ui
{
fn map_viewer<'a>(&mut self, map: &'a mut Renderer) -> Response<'a>
{
use egui::Widget;
let raw_response = RendererWidget::new(map).ui(self);
let wheel_delta = if raw_response.hovered()
{
self.input(|i| {
i.events.iter().find_map(|e| match e
{
egui::Event::MouseWheel {
delta, modifiers, ..
} if modifiers.command_only() => Some(*delta),
_ => None,
})
})
}
else
{
None
};
Response {
raw_response,
wheel_delta,
map,
}
}
}