radar-g/src/render/imp.rs
2023-09-20 19:54:38 +08:00

181 lines
5.5 KiB
Rust

use super::background::BackgroundWidget;
use super::exterior::ExteriorWidget;
use super::foreground::ForegroundWidget;
use super::WindowCoord;
use crate::coords::proj::Mercator;
use crate::coords::Mapper;
use femtovg::{Color, FontId, Paint, Path, Transform2D};
use gtk::glib;
use gtk::prelude::WidgetExtManual;
use gtk::subclass::prelude::*;
use gtk::traits::{GLAreaExt, WidgetExt};
use ndarray::Array2;
use std::cell::RefCell;
use std::num::NonZeroU32;
#[derive(Debug, Default, Clone)]
pub struct RenderConfig {
pub dim1: Option<Array2<f64>>,
pub dim2: Option<Array2<f64>>,
pub scale: f32,
pub pointer_location: WindowCoord,
pub pointer_lon_lat: (f64, f64),
pub translate: WindowCoord,
pub updated_translate: WindowCoord,
}
struct Fonts {
sans: FontId,
bold: FontId,
light: FontId,
}
pub struct Render {
pub(super) background: RefCell<BackgroundWidget>,
pub(super) foreground: RefCell<ForegroundWidget>,
pub(super) exterior: RefCell<ExteriorWidget>,
pub config: RefCell<RenderConfig>,
pub mapper: RefCell<Mapper>,
pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
}
impl Default for Render {
fn default() -> Self {
Self {
background: RefCell::new(BackgroundWidget::default()),
foreground: RefCell::new(ForegroundWidget::default()),
exterior: RefCell::new(ExteriorWidget::default()),
config: RefCell::new(RenderConfig::default()),
mapper: RefCell::new(Mercator::new().into()),
canvas: RefCell::new(None),
}
}
}
#[glib::object_subclass]
impl ObjectSubclass for Render {
const NAME: &'static str = "MyRender";
type Type = super::Render;
type ParentType = gtk::GLArea;
}
// Trait shared by all GObjects
impl ObjectImpl for Render {
fn constructed(&self) {
self.parent_constructed();
let area = self.obj();
area.set_has_stencil_buffer(true);
// area.add_tick_callback(|area, _| {
// area.queue_render();
// glib::Continue(true)
// });
}
}
// Trait shared by all widgets
impl WidgetImpl for Render {}
impl GLAreaImpl for Render {
fn resize(&self, width: i32, height: i32) {
self.ensure_canvas();
let mut canvas = self.canvas.borrow_mut();
let canvas = canvas.as_mut().unwrap();
canvas.set_size(
width as u32,
height as u32,
self.obj().scale_factor() as f32,
);
}
fn render(&self, context: &gtk::gdk::GLContext) -> bool {
self.ensure_canvas();
let mut canvas = self.canvas.borrow_mut();
let canvas = canvas.as_mut().unwrap();
let dpi = self.obj().scale_factor();
let w = canvas.width();
let h = canvas.height();
let configs = self.config.borrow();
canvas.clear_rect(
0,
0,
(w as i32 * dpi) as u32,
(h as i32 * dpi) as u32,
Color::rgba(0, 0, 0, 255),
);
let mapper = self.mapper.borrow();
let (lon_range, lat_range) = mapper.range.clone();
// {
// let background_widget = self.background.borrow_mut();
// background_widget.set_lat_lines(lat_range.key_points(9));
// }
// {
// let background_widget = self.background.borrow_mut();
// background_widget.set_lon_lines(lon_range.key_points(20));
// }
let translate = self.translate();
self.exterior.borrow().draw(canvas,self.mapper.borrow());
// self.foreground
// .borrow()
// .draw(canvas, configs.scale, dpi, self.mapper.borrow());
// self.background.borrow().draw(
// canvas,
// configs.scale,
// dpi,
// translate,
// self.mapper.borrow(),
// (lon_range, lat_range),
// );
canvas.flush();
true
}
}
impl Render {
fn ensure_canvas(&self) {
use femtovg::{renderer, Canvas};
use glow::HasContext;
if self.canvas.borrow().is_some() {
return;
}
let widget = self.obj();
widget.attach_buffers();
static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
|s| epoxy::get_proc_addr(s) as *const _;
// SAFETY: Need to get the framebuffer id that gtk expects us to draw into, so femtovg
// knows which framebuffer to bind. This is safe as long as we call attach_buffers
// beforehand. Also unbind it here just in case, since this can be called outside render.
let (mut renderer, fbo) = unsafe {
let renderer =
renderer::OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
let ctx = glow::Context::from_loader_function(LOAD_FN);
let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32)
.expect("No GTK provided framebuffer binding");
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
(renderer, glow::NativeFramebuffer(id))
};
renderer.set_screen_target(Some(fbo));
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
canvas
.add_font_dir("/Users/tsuki/projects/radar-g/src/assets")
.unwrap();
self.canvas.replace(Some(canvas));
}
pub fn translate(&self) -> WindowCoord {
let cfg = self.config.borrow();
let (rx, ry) = cfg.translate;
let (ux, uy) = cfg.updated_translate;
return (rx + ux, ry + uy);
}
}