add renderer

This commit is contained in:
Tsuki 2023-08-24 00:14:50 +08:00
parent 42e001d00d
commit 9a929b3493
6 changed files with 300 additions and 55 deletions

View File

@ -1,3 +1,5 @@
use crate::render::WindowCoord;
use super::{proj::ProjectionS, Range};
use geo_types::{coord, Coord as GCoord, LineString};
use proj::{Proj, ProjError};
@ -7,6 +9,9 @@ pub struct Mapper {
proj: Proj,
pub range: (Range, Range),
bounds: (f64, f64, f64, f64),
scale: f32,
translate: (f32, f32),
focus_point: Option<(f64, f64)>,
}
impl From<Proj> for Mapper {
fn from(proj: Proj) -> Self {
@ -16,6 +21,9 @@ impl From<Proj> for Mapper {
proj: proj,
range: (default_range.0.into(), default_range.1.into()),
bounds,
scale: 1.0,
translate: (0.0, 0.0),
focus_point: None,
}
}
}
@ -32,34 +40,92 @@ impl Mapper {
proj: Proj,
lon_range: std::ops::Range<f64>,
lat_range: std::ops::Range<f64>,
scale: f32,
translate: (f32, f32),
) -> Self {
let bounds = Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
let bounds =
Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
let range = (lon_range.into(), lat_range.into());
Self {
proj: proj,
range,
bounds,
scale,
translate,
focus_point: None,
}
}
pub fn fore_map(&self, coord: WindowCoord) -> Result<(f64, f64), ProjError> {
let (t1, t2) = if let Some(_) = self.focus_point {
let (a, b) = self.translate;
(a * (self.scale - 1.0), b * (self.scale - 1.0))
} else {
self.translate
};
let (x, y) = coord;
let c = (x as f64 + t1 as f64) / self.scale as f64 * (self.bounds.1 - self.bounds.0)
+ self.bounds.0;
let d = (y as f64 + t2 as f64) / self.scale as f64 * (self.bounds.3 - self.bounds.2)
+ self.bounds.2;
let (lon, lat) = self.proj.project((c, d), true)?;
Ok((lon.to_degrees(), lat.to_degrees()))
}
pub fn set_scale(&mut self, scale: f32) {
self.scale = scale;
}
pub fn set_translate(&mut self, translate: (f32, f32)) {
self.translate = translate;
}
pub fn set_focus_point(&mut self, point: (f64, f64)) {
self.focus_point = Some(point);
let (rx, ry) = self.map(point).unwrap();
self.translate = (rx as f32, ry as f32);
}
pub fn point_in_bound(&self, point: (f64, f64)) -> bool {
let (x, y) = point;
let (lon_range, lat_range) = self.range;
return (x <= lon_range.1 && x >= lon_range.0) && (y <= lat_range.1 && y >= lat_range.0);
}
pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> {
let mut point = point;
if !self.point_in_bound(point) {
point = (
point.0.clamp(self.range.0 .0, self.range.0 .1),
point.1.clamp(self.range.1 .0, self.range.1 .1),
);
}
let (p1, p2) = self
.proj
.convert((point.0.to_radians(), point.1.to_radians()))?;
let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
let (t1, t2) = if let Some(_) = self.focus_point {
let (a, b) = self.translate;
(a * (self.scale - 1.0), b * (self.scale - 1.0))
} else {
self.translate
};
let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0) * self.scale as f64
- t1 as f64 * self.scale as f64;
let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2) * self.scale as f64
- t2 as f64 * self.scale as f64;
Ok((x, y))
}
pub fn set_lon_range(&mut self, range: std::ops::Range<f64>)->&mut Self {
pub fn set_lon_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
self.range.0 = range.into();
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
self
}
pub fn set_lat_range(&mut self, range: std::ops::Range<f64>)->&mut Self {
pub fn set_lat_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
self.range.1 = range.into();
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
self

View File

@ -1,14 +1,14 @@
mod imp;
use crate::data::RadarData2d;
use crate::render::Render;
use adw::prelude::{GLAreaExt, GestureDragExt};
use glib::clone;
use glib::subclass::prelude::*;
use gtk::glib;
use gtk::traits::WidgetExt;
use gtk::{EventControllerScrollFlags, Inhibit};
use num_traits::{AsPrimitive, FromPrimitive, Num};
use proj::{Proj, ProjError};
use std::borrow::Borrow;
use proj::ProjError;
use std::fmt::Debug;
glib::wrapper! {
@ -22,32 +22,50 @@ impl Monitor {
let pointer_location_detecture = gtk::EventControllerMotion::new();
pointer_location_detecture.connect_motion(
clone!(@weak this as s => move |_context, x, y| {
s.imp().renderer.borrow().change_cfg(
|cfg| cfg.pointer_location = (x as f32, y as f32)
s.imp().renderer.borrow_mut().change_pointer(x as f32, y as f32);
}
),
);
}),
);
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
scale_detecture.connect_scroll(clone!(
@weak this as s => @default-panic,move |_context, _x, y| {
let renderer = s.imp().borrow().renderer.borrow();
@weak this as s, @weak render as r => @default-panic,move |_context, _x, y| {
let renderer = s.imp().renderer.borrow();
renderer.change_cfg(|cfg| {
let current_scale = cfg.scale;
cfg.scale = (current_scale + y as f32 / 100.0).max(1.0).min(5.0);
let scale = (current_scale - y as f32 / 10.0).max(1.0).min(5.0);
cfg.scale = scale;
r.imp().mapper.borrow_mut().set_scale(scale);
});
r.calc_translate();
Inhibit(false)
}
));
let drag_detecture = gtk::GestureDrag::new();
drag_detecture.connect_drag_update(clone!(
@weak render as r => move |this, _, _| {
let (ox, oy) = this.offset().unwrap_or((0.0,0.0));
r.change_cfg(|cfg|{
cfg.updated_translate = ( - ox as f32, -oy as f32);
} );
}));
drag_detecture.connect_drag_end(clone!(
@weak render as r => move |this,_,_|{
let t = r.imp().translate();
r.change_cfg(|cfg| {
cfg.translate = t ;
cfg.updated_translate = (0.0,0.0);
})
}
));
render.add_controller(pointer_location_detecture);
render.add_controller(scale_detecture);
render.add_controller(drag_detecture);
render.set_hexpand(true);
render.set_vexpand(true);
render.set_parent(&this);
this.imp().renderer.replace(render);
this
}

View File

@ -1,6 +1,7 @@
use crate::render::{imp, WindowCoord};
use femtovg::Paint;
use geo_macros::Prj;
use geo_types::MultiPolygon;
use gtk::glib;
use gtk::subclass::prelude::*;
use std::cell::RefCell;
@ -23,6 +24,7 @@ impl BackgroundConfig {
#[derive(Default)]
pub struct BackgroundWidget {
pub(super) config: RefCell<BackgroundConfig>,
pub(super) shp: RefCell<Option<Vec<MultiPolygon>>>
}
#[glib::object_subclass]

View File

@ -3,6 +3,7 @@ use crate::coords::{Mapper, Range};
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
use geo_types::{line_string, LineString, Point};
use glib::subclass::types::ObjectSubclassIsExt;
use shapefile;
use std::cell::Ref;
pub use self::imp::BackgroundConfig;
@ -21,6 +22,17 @@ impl BackgroundWidget {
pub fn new(config: BackgroundConfig) -> Self {
let this: Self = glib::Object::new();
let imp = this.imp();
let polygons = shapefile::read_as::<_, shapefile::Polygon, shapefile::dbase::Record>(
"/Users/tsuki/Downloads/ne_110m_admin_0_countries/ne_110m_admin_0_countries.shp",
)
.unwrap();
let polys: Vec<geo_types::MultiPolygon<f64>> = polygons
.into_iter()
.map(|(polygon, _)| polygon.into())
.collect();
imp.shp.replace(Some(polys));
imp.config.replace(config);
this
}
@ -45,9 +57,11 @@ impl BackgroundWidget {
fn mesh_lines(
&self,
canvas: &mut Canvas<OpenGl>,
scale: f32,
translate: (f32, f32),
canvas_width: f32,
canvas_height: f32,
mapper: Ref<'_, Mapper>,
mapper: &Ref<'_, Mapper>,
bound: (Range, Range),
) {
let imp = self.imp();
@ -63,7 +77,13 @@ impl BackgroundWidget {
LineString::new(
result
.points()
.map(|p| (p.x() as f32 * canvas_width, p.y() as f32 * canvas_height).into())
.map(|p| {
(
p.x() as f32 * canvas_width - translate.0,
(1.0 - p.y() as f32) * canvas_height - translate.1,
)
.into()
})
.collect(),
)
});
@ -76,14 +96,16 @@ impl BackgroundWidget {
let mut paint = Paint::color(Color::white());
paint.set_font_size(25.0);
paint.set_line_width(1.0);
let text_location = mapper.map((left, *lat)).unwrap();
let _ = canvas.stroke_text(
text_location.0 as f32 * canvas_width,
text_location.1 as f32 * canvas_height,
format!("{:.2} N", lat),
&paint,
let (x, y) = self.map_to_screen(
mapper,
scale,
translate,
canvas_height,
canvas_width,
(left, *lat),
);
let _ = canvas.stroke_text(x, y, format!("{:.2} N", lat), &paint);
});
}
@ -92,14 +114,15 @@ impl BackgroundWidget {
let mut paint = Paint::color(Color::white());
paint.set_font_size(25.0);
paint.set_line_width(1.0);
let text_location = mapper.map((*lon, top + 0.1)).unwrap();
let _ = canvas.stroke_text(
text_location.0 as f32 * canvas_width,
text_location.1 as f32 * canvas_height,
format!("{:.2}", lon),
&paint,
let (x, y) = self.map_to_screen(
mapper,
scale,
translate,
canvas_height,
canvas_width,
(*lon + 0.5, bottom + 0.1),
);
let _ = canvas.stroke_text(x, y, format!("{:.2}", lon), &paint);
});
}
@ -110,7 +133,13 @@ impl BackgroundWidget {
LineString::new(
result
.points()
.map(|p| (p.x() as f32 * canvas_width, p.y() as f32 * canvas_height).into())
.map(|p| {
(
p.x() as f32 * canvas_width - translate.0,
(1.0 - p.y()) as f32 * canvas_height - translate.1,
)
.into()
})
.collect(),
)
});
@ -124,25 +153,100 @@ impl BackgroundWidget {
canvas: &mut Canvas<OpenGl>,
scale: f32,
dpi: i32,
translate: (f32, f32),
mapper: Ref<'_, Mapper>,
bound: (Range, Range),
) {
let canvas_width = canvas.width();
let canvas_height = canvas.height();
let config = self.imp().config.borrow();
self.mesh_lines(
canvas,
scale,
translate,
canvas_width,
canvas_height,
&mapper,
bound,
);
self.shp(
mapper,
canvas,
translate,
scale,
canvas_width,
canvas_height,
);
}
self.mesh_lines(canvas, canvas_width, canvas_height, mapper, bound);
fn shp(
&self,
mapper: Ref<'_, Mapper>,
canvas: &mut Canvas<OpenGl>,
translate: (f32, f32),
scale: f32,
canvas_width: f32,
canvas_height: f32,
) {
if let Some(s) = self.imp().shp.borrow().as_ref() {
for p in s.iter() {
for poly in p.iter() {
let mut points = poly.exterior().points();
let first_point = points.next().unwrap();
let mut path = Path::new();
let first = self.map_to_screen(
&mapper,
scale,
translate,
canvas_height,
canvas_width,
(first_point.x(), first_point.y()),
);
path.move_to(first.0, first.1);
for p in points {
let (nx, ny) = self.map_to_screen(
&mapper,
scale,
translate,
canvas_height,
canvas_width,
(p.x(), p.y()),
);
path.line_to(nx, ny);
}
canvas.stroke_path(&path, &Paint::color(Color::white()));
}
}
}
}
#[inline]
fn map_to_screen(
&self,
mapper: &Ref<'_, Mapper>,
scale: f32,
translate: (f32, f32),
canvas_height: f32,
canvas_width: f32,
point: (f64, f64),
) -> (f32, f32) {
let (x, y) = mapper.map(point).unwrap();
(
x as f32 * canvas_width - translate.0,
(1.0 - y as f32) * canvas_height - translate.1,
)
}
pub fn set_lat_lines(&self, lat_lines: Vec<f64>) {
println!("set lat lines {:?}", lat_lines);
let imp = self.imp();
imp.config.borrow_mut().lat_lines = lat_lines;
}
pub fn set_lon_lines(&self, lon_lines: Vec<f64>) {
println!("set lon lines {:?}", lon_lines);
let imp = self.imp();
imp.config.borrow_mut().lon_lines = lon_lines;
}

View File

@ -3,8 +3,9 @@ use super::foreground::ForegroundWidget;
use super::WindowCoord;
use crate::coords::proj::Mercator;
use crate::coords::Mapper;
use femtovg::{Color, Paint, Path, FontId};
use femtovg::{Color, FontId, Paint, Path};
use gtk::glib;
use gtk::prelude::WidgetExtManual;
use gtk::subclass::prelude::*;
use gtk::traits::{GLAreaExt, WidgetExt};
use ndarray::Array2;
@ -17,7 +18,9 @@ pub struct RenderConfig {
pub dim2: Option<Array2<f64>>,
pub scale: f32,
pub pointer_location: WindowCoord,
pub transform: WindowCoord,
pub pointer_lon_lat: (f64, f64),
pub translate: WindowCoord,
pub updated_translate: WindowCoord,
}
struct Fonts {
@ -31,9 +34,7 @@ pub struct Render {
pub(super) foreground: RefCell<ForegroundWidget>,
pub config: RefCell<RenderConfig>,
pub mapper: RefCell<Mapper>,
canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
canvas_width: RefCell<f32>,
canvas_height: RefCell<f32>,
pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
}
impl Default for Render {
@ -44,8 +45,6 @@ impl Default for Render {
config: RefCell::new(RenderConfig::default()),
mapper: RefCell::new(Mercator::new().into()),
canvas: RefCell::new(None),
canvas_height: RefCell::new(0.0),
canvas_width: RefCell::new(0.0),
}
}
}
@ -63,6 +62,11 @@ impl ObjectImpl for Render {
self.parent_constructed();
let area = self.obj();
area.set_has_stencil_buffer(true);
area.add_tick_callback(|area, _| {
area.queue_render();
glib::Continue(true)
});
}
}
@ -90,7 +94,6 @@ impl GLAreaImpl for Render {
let w = canvas.width();
let h = canvas.height();
let configs = self.config.borrow();
canvas.clear_rect(
0,
0,
@ -101,7 +104,6 @@ impl GLAreaImpl for Render {
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));
@ -110,7 +112,7 @@ impl GLAreaImpl for Render {
let background_widget = self.background.borrow_mut();
background_widget.set_lon_lines(lon_range.key_points(20));
}
let translate = self.translate();
self.foreground
.borrow()
.draw(canvas, configs.scale, dpi, self.mapper.borrow());
@ -119,12 +121,11 @@ impl GLAreaImpl for Render {
canvas,
configs.scale,
dpi,
translate,
self.mapper.borrow(),
(lon_range, lat_range),
);
canvas.flush();
true
}
}
@ -157,7 +158,17 @@ impl Render {
};
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();
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);
}
}

View File

@ -11,6 +11,7 @@ pub use self::background::{BackgroundConfig, BackgroundWidget};
pub use self::foreground::{ForegroundConfig, ForegroundWidget};
use self::imp::RenderConfig;
use crate::data::DownSampleMeth;
use adw::prelude::WidgetExt;
use femtovg::Color;
pub use glib::subclass::prelude::*;
use image::RgbImage;
@ -27,12 +28,20 @@ glib::wrapper! {
impl Default for Render {
fn default() -> Self {
Self::new(BackgroundWidget::default(), ForegroundWidget::default(),None)
Self::new(
BackgroundWidget::default(),
ForegroundWidget::default(),
None,
)
}
}
impl Render {
pub fn new(background: BackgroundWidget, foreground: ForegroundWidget,mapper: Option<Mapper>) -> Self {
pub fn new(
background: BackgroundWidget,
foreground: ForegroundWidget,
mapper: Option<Mapper>,
) -> Self {
let this: Self = glib::Object::new();
this.imp().background.replace(background);
this.imp().foreground.replace(foreground);
@ -54,6 +63,41 @@ impl Render {
f(&mut cfg);
}
pub fn change_pointer(&mut self, x: f32, y: f32) {
if let Some(canvas) = self.imp().canvas.borrow().as_ref() {
let dpi = self.scale_factor();
let cw = canvas.width();
let ch = canvas.height();
let (tx, ty) = self.imp().translate();
println!("tx,ty: {:?}", (tx, ty));
println!("x :{} , y: {}, x + tx :{}", x, y, x + tx);
let (lon, lat) = self
.imp()
.mapper
.borrow()
.fore_map((
(x * dpi as f32+ tx) / cw,
(1.0 - (y * dpi as f32+ ty) / ch ),
))
.unwrap();
println!("lon,lat : {:?}", (lon, lat));
let mut cfg = self.imp().config.borrow_mut();
cfg.pointer_lon_lat = (lon, lat);
cfg.pointer_location = (x, y);
}
}
pub fn calc_translate(&self) {
let ll = self.imp().config.borrow().pointer_lon_lat;
let scale = self.imp().config.borrow().scale;
let (rx, ry) = self.imp().mapper.borrow().map((ll.0, ll.1)).unwrap();
self.imp()
.mapper
.borrow_mut()
.set_translate((rx as f32 * (scale - 1.0), ry as f32 * (scale - 1.0)));
}
pub fn set_mapper(&self, mapper: Mapper) {
self.imp().mapper.replace(mapper);
}
@ -104,7 +148,7 @@ impl Render {
let rainbow = pjp.run(&data).unwrap();
self.imp().foreground.borrow_mut().set_data(rainbow);
let pbow:Array2<Option<Color>> = rrp.run(&data).unwrap();
let pbow: Array2<Option<Color>> = rrp.run(&data).unwrap();
self.imp().foreground.borrow_mut().set_colors(pbow);
Ok(())
}