render in img

This commit is contained in:
Tsuki 2023-08-17 20:10:05 +08:00
parent 5df83c4398
commit 4ec48fc21c
26 changed files with 421 additions and 290 deletions

View File

@ -2,6 +2,6 @@ fn main() {
glib_build_tools::compile_resources( glib_build_tools::compile_resources(
&["src/resources"], &["src/resources"],
"src/resources/resources.gresource.xml", "src/resources/resources.gresource.xml",
"monitor.gresource", "p.gresource",
); );
} }

BIN
src/assets/Roboto-Bold.ttf Normal file

Binary file not shown.

BIN
src/assets/Roboto-Light.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/entypo.ttf Normal file

Binary file not shown.

View File

@ -1,21 +1,20 @@
use super::{proj::ProjectionS, Range};
use geo_types::{coord, Coord as GCoord, LineString}; use geo_types::{coord, Coord as GCoord, LineString};
use proj::{Proj, ProjError}; use proj::{Proj, ProjError};
use std::ops::Range; use std::ops;
use super::proj::ProjectionS;
pub struct Mapper { pub struct Mapper {
proj: Proj, proj: Proj,
range: (Range<f64>, Range<f64>), pub range: (Range, Range),
bounds: (f64, f64, f64, f64), bounds: (f64, f64, f64, f64),
} }
impl From<Proj> for Mapper { impl From<Proj> for Mapper {
fn from(proj: Proj) -> Self { fn from(proj: Proj) -> Self {
let default_range: (Range<f64>, Range<f64>) = (-180.0..180.0, -90.0..90.0); let default_range: (Range, Range) = ((-180.0..180.0).into(), (-90.0..90.0).into());
let bounds = Self::bound(&proj, default_range.clone()).unwrap(); let bounds = Self::bound(&proj, default_range.clone()).unwrap();
Self { Self {
proj: proj, proj: proj,
range: default_range, range: (default_range.0.into(), default_range.1.into()),
bounds, bounds,
} }
} }
@ -29,9 +28,13 @@ impl<C: ProjectionS> From<C> for Mapper {
} }
impl Mapper { impl Mapper {
pub fn new(proj: Proj, lon_range: Range<f64>, lat_range: Range<f64>) -> Self { pub fn new(
let bounds = Self::bound(&proj, (lon_range.clone(), lat_range.clone())).unwrap(); proj: Proj,
let range = (lon_range, lat_range); lon_range: std::ops::Range<f64>,
lat_range: std::ops::Range<f64>,
) -> Self {
let bounds = Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
let range = (lon_range.into(), lat_range.into());
Self { Self {
proj: proj, proj: proj,
range, range,
@ -50,22 +53,19 @@ impl Mapper {
Ok((x, y)) Ok((x, y))
} }
pub fn set_lon_range(&mut self, range: Range<f64>) { pub fn set_lon_range(&mut self, range: std::ops::Range<f64>) {
self.range.0 = range; self.range.0 = range.into();
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap(); self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
} }
pub fn set_lat_range(&mut self, range: Range<f64>) { pub fn set_lat_range(&mut self, range: std::ops::Range<f64>) {
self.range.1 = range; self.range.1 = range.into();
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap(); self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
} }
fn bound( fn bound(proj: &Proj, range: (Range, Range)) -> Result<(f64, f64, f64, f64), ProjError> {
proj: &Proj, let left_bottom = proj.convert((range.0 .0.to_radians(), range.1 .0.to_radians()))?;
range: (Range<f64>, Range<f64>), let right_top = proj.convert((range.0 .1.to_radians(), range.1 .1.to_radians()))?;
) -> Result<(f64, f64, f64, f64), ProjError> {
let left_bottom = proj.convert((range.0.start.to_radians(), range.1.start.to_radians()))?;
let right_top = proj.convert((range.0.end.to_radians(), range.1.end.to_radians()))?;
Ok((left_bottom.0, right_top.0, left_bottom.1, right_top.1)) Ok((left_bottom.0, right_top.0, left_bottom.1, right_top.1))
} }
@ -81,7 +81,7 @@ impl Mapper {
let delta2 = 0.5; let delta2 = 0.5;
let depth = 16; let depth = 16;
let mut res: Vec<GCoord> = Vec::new(); let mut res: Vec<GCoord> = Vec::new();
res.push(l.start); res.push(p.start);
self.resample_line_to( self.resample_line_to(
start_projected, start_projected,
end_projected, end_projected,
@ -94,7 +94,7 @@ impl Mapper {
depth, depth,
&mut res, &mut res,
)?; )?;
res.push(l.end); res.push(p.end);
result.extend(res); result.extend(res);
} }

View File

@ -5,7 +5,7 @@ pub mod proj;
pub mod wgs84; pub mod wgs84;
pub use mapper::Mapper; pub use mapper::Mapper;
pub use wgs84::LatLonCoord; // pub use wgs84::LatLonCoord;
pub type ScreenCoord = (f64, f64); pub type ScreenCoord = (f64, f64);
type Lat = f64; type Lat = f64;
@ -114,6 +114,14 @@ impl<T: AsPrimitive<f64> + Num> From<(T, T)> for Range {
} }
} }
impl<T: Num + AsPrimitive<f64>> From<std::ops::Range<T>> for Range {
fn from(value: std::ops::Range<T>) -> Self {
let value = (value.start.as_(), value.end.as_());
let (_min, _max) = (value.0.min(value.1), value.0.max(value.1));
Self(_min, _max)
}
}
// impl<T, Raw> RadarData2d<T, Raw> // impl<T, Raw> RadarData2d<T, Raw>
// where // where
// T: Num + Clone + PartialEq + PartialOrd, // T: Num + Clone + PartialEq + PartialOrd,

View File

@ -8,10 +8,6 @@ use geo_macros::Prj;
pub struct Mercator { pub struct Mercator {
/// The central longitude of the projection. /// The central longitude of the projection.
pub central_lon: f64, pub central_lon: f64,
/// The minimum latitude of the projection.
pub min_latitude: f64,
/// The maximum latitude of the projection.
pub max_latitude: f64,
/// The false easting of the projection. /// The false easting of the projection.
pub false_easting: f64, pub false_easting: f64,
/// The false northing of the projection. /// The false northing of the projection.
@ -32,8 +28,6 @@ impl Mercator {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
central_lon: 0.0, central_lon: 0.0,
min_latitude: -82.0,
max_latitude: 82.0,
false_easting: 0.0, false_easting: 0.0,
false_northing: 0.0, false_northing: 0.0,
latitude_true_scale: 0.0, latitude_true_scale: 0.0,
@ -61,20 +55,4 @@ impl ProjectionS for Mercator {
_proj_string _proj_string
} }
fn logic_range(&self, lon_range: Option<Range>, lat_range: Option<Range>) -> (Range, Range) {
let lon_range = lon_range.unwrap_or(Range {
0: -180f64,
1: 180f64,
});
let lat_range = lat_range.unwrap_or(Range {
0: self.min_latitude,
1: self.max_latitude,
});
let lat_range = Range {
0: lat_range.0.max(self.min_latitude),
1: lat_range.1.min(self.max_latitude),
};
(lon_range, lat_range)
}
} }

View File

@ -18,18 +18,6 @@ pub enum Projs {
pub trait ProjectionS { pub trait ProjectionS {
/// Returns a proj-string of the projection. /// Returns a proj-string of the projection.
fn build(&self) -> String; fn build(&self) -> String;
/// Returns the logical range of the projection.
/// In common, different projections have different logical ranges.
/// # Arguments
///
/// * `lon_range` - An optional longitude range.
/// * `lat_range` - An optional latitude range.
///
/// # Returns
///
/// A tuple containing the longitude and latitude ranges.
fn logic_range(&self, lon_range: Option<Range>, lat_range: Option<Range>) -> (Range, Range);
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
@ -38,42 +26,42 @@ pub(super) enum ProjError {
ProjError(#[from] proj::ProjError), ProjError(#[from] proj::ProjError),
} }
pub(super) struct PCS<T: ProjectionS> { // pub(super) struct PCS<T: ProjectionS> {
pub lon_range: Range, // pub lon_range: Range,
pub lat_range: Range, // pub lat_range: Range,
pub proj_param: T, // pub proj_param: T,
// pub proj_target: proj5::CoordinateBuf, // // pub proj_target: proj5::CoordinateBuf,
pub transformer: Proj, // pub transformer: Proj,
} // }
impl<T: ProjectionS> PCS<T> { // impl<T: ProjectionS> PCS<T> {
pub(super) fn new(proj_param: T, lon_range: Option<Range>, lat_range: Option<Range>) -> Self { // pub(super) fn new(proj_param: T, lon_range: Option<Range>, lat_range: Option<Range>) -> Self {
let (lon_range, lat_range) = proj_param.logic_range(lon_range, lat_range); // let (lon_range, lat_range) = proj_param.logic_range(lon_range, lat_range);
Self { // Self {
lon_range, // lon_range,
lat_range, // lat_range,
transformer: Proj::new(proj_param.build().as_str()).unwrap(), // transformer: Proj::new(proj_param.build().as_str()).unwrap(),
proj_param: proj_param, // proj_param: proj_param,
} // }
} // }
pub fn bbox(&self) -> Result<(Range, Range), ProjError> { // pub fn bbox(&self) -> Result<(Range, Range), ProjError> {
let _proj_transformer = &self.transformer; // let _proj_transformer = &self.transformer;
let lb = (self.lon_range.0.to_radians(), self.lat_range.0.to_radians()); // let lb = (self.lon_range.0.to_radians(), self.lat_range.0.to_radians());
let rt = (self.lon_range.1.to_radians(), self.lat_range.1.to_radians()); // let rt = (self.lon_range.1.to_radians(), self.lat_range.1.to_radians());
let bl = _proj_transformer.convert(lb)?; // let bl = _proj_transformer.convert(lb)?;
let rt = _proj_transformer.convert(rt)?; // let rt = _proj_transformer.convert(rt)?;
Ok((Range::from((bl.0, rt.0)), Range::from((bl.1, rt.1)))) // Ok((Range::from((bl.0, rt.0)), Range::from((bl.1, rt.1))))
} // }
pub fn map(&self, lon_lat: (Lat, Lon)) -> (f64, f64) { // pub fn map(&self, lon_lat: (Lat, Lon)) -> (f64, f64) {
let _proj_transformer = &self.transformer; // let _proj_transformer = &self.transformer;
let _lon_lat = _proj_transformer // let _lon_lat = _proj_transformer
.convert((lon_lat.0.to_radians(), lon_lat.1.to_radians())) // .convert((lon_lat.0.to_radians(), lon_lat.1.to_radians()))
.unwrap(); // .unwrap();
_lon_lat // _lon_lat
} // }
} // }

View File

@ -1,4 +1,4 @@
use super::proj::{ProjectionS, PCS}; // use super::proj::{ProjectionS, PCS};
use super::Coord; use super::Coord;
use super::{Lat, Lon, Range}; use super::{Lat, Lon, Range};
use proj::ProjError; use proj::ProjError;
@ -13,76 +13,76 @@ pub enum CoordError {
}, },
} }
pub struct LatLonCoord<T: ProjectionS> { // pub struct LatLonCoord<T: ProjectionS> {
actual: (Range, Range),
logical: (Range, Range),
pcs: PCS<T>,
}
impl<T: ProjectionS> LatLonCoord<T> {
/// Creates a new `LatLonCoord` instance.
///
/// # Arguments
///
/// * `lon` - An optional longitude range.
/// * `lat` - An optional latitude range.
/// * `actual` - A tuple containing the actual ranges.
/// * `project` - A projection.
///
/// # Returns
///
/// A new `LatLonCoord` instance.
pub fn new(
lon: Option<Range>,
lat: Option<Range>,
actual: ((i32, i32), (i32, i32)),
// actual: (Range, Range), // actual: (Range, Range),
project: T, // logical: (Range, Range),
) -> Self { // pcs: PCS<T>,
let pcs = PCS::new(project, lon, lat); // }
let _box = pcs.bbox().unwrap();
Self {
actual: (Range::from(actual.0), Range::from(actual.1)),
pcs: pcs,
logical: _box,
}
}
pub fn set_actual(&mut self, actual: ((i32, i32), (i32, i32))) { // impl<T: ProjectionS> LatLonCoord<T> {
self.actual = (Range::from(actual.0), Range::from(actual.1)); // /// Creates a new `LatLonCoord` instance.
} // ///
// /// # Arguments
// ///
// /// * `lon` - An optional longitude range.
// /// * `lat` - An optional latitude range.
// /// * `actual` - A tuple containing the actual ranges.
// /// * `project` - A projection.
// ///
// /// # Returns
// ///
// /// A new `LatLonCoord` instance.
// pub fn new(
// lon: Option<Range>,
// lat: Option<Range>,
// actual: ((i32, i32), (i32, i32)),
// // actual: (Range, Range),
// project: T,
// ) -> Self {
// let pcs = PCS::new(project, lon, lat);
// let _box = pcs.bbox().unwrap();
// Self {
// actual: (Range::from(actual.0), Range::from(actual.1)),
// pcs: pcs,
// logical: _box,
// }
// }
pub fn lon_range(&self) -> Range { // pub fn set_actual(&mut self, actual: ((i32, i32), (i32, i32))) {
self.pcs.lon_range // self.actual = (Range::from(actual.0), Range::from(actual.1));
} // }
pub fn lat_range(&self) -> Range { // pub fn lon_range(&self) -> Range {
self.pcs.lat_range // self.pcs.lon_range
} // }
}
impl<T: ProjectionS> Coord<f64> for LatLonCoord<T> { // pub fn lat_range(&self) -> Range {
fn map(&self, axis_1: f64, axis_2: f64) -> super::ScreenCoord { // self.pcs.lat_range
let point = self.pcs.map((axis_1, axis_2)); // }
let logical_dim1_span = self.logical.0 .1 - self.logical.0 .0; // }
let dim1_rate = (point.0 - self.logical.0 .0) / logical_dim1_span; // impl<T: ProjectionS> Coord<f64> for LatLonCoord<T> {
let logical_dim2_span = self.logical.1 .1 - self.logical.1 .0; // fn map(&self, axis_1: f64, axis_2: f64) -> super::ScreenCoord {
let dim2_rate = (point.1 - self.logical.1 .0) / logical_dim2_span; // let point = self.pcs.map((axis_1, axis_2));
// let logical_dim1_span = self.logical.0 .1 - self.logical.0 .0;
( // let dim1_rate = (point.0 - self.logical.0 .0) / logical_dim1_span;
(dim1_rate * (self.actual.0 .1 - self.actual.0 .0) as f64) + self.actual.0 .0, // let logical_dim2_span = self.logical.1 .1 - self.logical.1 .0;
(dim2_rate * (self.actual.1 .1 - self.actual.1 .0) as f64) + self.actual.1 .0, // let dim2_rate = (point.1 - self.logical.1 .0) / logical_dim2_span;
)
}
fn dim1_range(&self) -> (f64, f64) { // (
let v = self.lon_range(); // (dim1_rate * (self.actual.0 .1 - self.actual.0 .0) as f64) + self.actual.0 .0,
(v.0, v.1) // (dim2_rate * (self.actual.1 .1 - self.actual.1 .0) as f64) + self.actual.1 .0,
} // )
// }
fn dim2_range(&self) -> (f64, f64) { // fn dim1_range(&self) -> (f64, f64) {
let v = self.lat_range(); // let v = self.lon_range();
(v.0, v.1) // (v.0, v.1)
} // }
}
// fn dim2_range(&self) -> (f64, f64) {
// let v = self.lat_range();
// (v.0, v.1)
// }
// }

View File

@ -1,6 +1,7 @@
use coords::proj::Mercator; use coords::proj::Mercator;
use coords::Mapper; use coords::Mapper;
use data::{Npz, Radar2d}; use data::{Npz, Radar2d};
use femtovg::{Color, Paint};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{gio, glib, Application, ApplicationWindow}; use gtk::{gio, glib, Application, ApplicationWindow};
use std::ptr; use std::ptr;
@ -10,7 +11,6 @@ mod errors;
mod monitor; mod monitor;
mod pipeline; mod pipeline;
mod render; mod render;
mod tree;
mod window; mod window;
use monitor::Monitor; use monitor::Monitor;
use render::{BackgroundConfig, BackgroundWidget, ForegroundConfig, ForegroundWidget, Render}; use render::{BackgroundConfig, BackgroundWidget, ForegroundConfig, ForegroundWidget, Render};
@ -29,8 +29,11 @@ fn main() -> glib::ExitCode {
.or_else(|_| libloading::os::windows::Library::open_already_loaded("epoxy-0.dll")) .or_else(|_| libloading::os::windows::Library::open_already_loaded("epoxy-0.dll"))
.unwrap(); .unwrap();
gio::resources_register_include!("p.gresource")
.expect("Failed to register resources.");
epoxy::load_with(|name| { epoxy::load_with(|name| {
unsafe { library.get::<_>(name.as_bytes()) } unsafe { library.get::<>(name.as_bytes()) }
.map(|symbol| *symbol) .map(|symbol| *symbol)
.unwrap_or(ptr::null()) .unwrap_or(ptr::null())
}); });
@ -52,16 +55,20 @@ fn build_ui(app: &Application) {
.title("My GTK App") .title("My GTK App")
.build(); .build();
let mut background_config = BackgroundConfig::new(); let mut background_config = BackgroundConfig::new()
.change_painter(Paint::color(Color::white()))
.change_show_lat_lines(true)
.change_show_lon_lines(true);
let mut foreground_config = ForegroundConfig::new(); let mut foreground_config = ForegroundConfig::new();
let background_widget = BackgroundWidget::new(background_config); let background_widget = BackgroundWidget::new(background_config);
let foreground_widget = ForegroundWidget::new(foreground_config); let foreground_widget = ForegroundWidget::new(foreground_config);
let render = Render::new(background_widget, foreground_widget); let render = Render::new(background_widget, foreground_widget);
let path = "/Users/ruomu/projects/cinrad_g/test2.npz"; let path = "/Users/tsuki/projects/radar-g/test2.npz";
let data = Radar2d::<i8>::load(path, Npz).unwrap(); let data = Radar2d::<i8>::load(path, Npz).unwrap();
let projection = Mercator::new(); let projection = Mercator::new();
let mut mapper: Mapper = projection.into(); let mut mapper: Mapper = projection.into();
mapper.set_lat_range(29.960..30.764); mapper.set_lat_range(29.960..30.764);
mapper.set_lon_range(120.038..120.965); mapper.set_lon_range(120.038..120.965);

View File

@ -1,9 +1,10 @@
use anyhow::{Ok, Result}; use anyhow::{Ok, Result};
use femtovg; use femtovg::{self};
use geo_types::{line_string, LineString}; use geo_types::{line_string, LineString};
use image::RgbImage; use image::RgbImage;
use ndarray::Array2; use ndarray::parallel::prelude::*;
use num_traits::Num; use ndarray::{Array2, ArrayView2};
use num_traits::{Num, AsPrimitive, FromPrimitive};
use crate::{ use crate::{
coords::Mapper, coords::Mapper,
@ -18,22 +19,34 @@ impl Color {
} }
} }
impl From<femtovg::Color> for Color {
fn from(value: femtovg::Color) -> Self {
Self(value)
}
}
pub trait Pipeline<T> { pub trait Pipeline<T> {
type Output; type Output;
fn run(&self, input: T) -> Result<Self::Output>; fn run(&self, input: T) -> Result<Self::Output>;
} }
pub struct ProjPipe { pub struct ProjPipe<'a> {
pub mapper: Mapper, pub mapper: &'a Mapper,
} }
impl<T, Raw> Pipeline<RadarData2d<T, Raw>> for ProjPipe impl<'a> ProjPipe<'a> {
pub fn new(mapper: &'a Mapper) -> Self {
Self { mapper }
}
}
impl<'a, 'b: 'a, T, Raw> Pipeline<&'b RadarData2d<T, Raw>> for ProjPipe<'a>
where where
T: Num + Clone + PartialEq + PartialOrd, T: Num + Clone + PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone, Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
{ {
type Output = Array2<LineString>; type Output = Array2<LineString>;
fn run(&self, input: RadarData2d<T, Raw>) -> Result<Self::Output> { fn run(&self, input: &'b RadarData2d<T, Raw>) -> Result<Self::Output> {
let dim1 = input.dim1.view(); let dim1 = input.dim1.view();
let dim2 = input.dim2.view(); let dim2 = input.dim2.view();
@ -92,3 +105,26 @@ impl<T: Num + PartialOrd> ShadePipe<T> {
&self.colors[left] &self.colors[left]
} }
} }
impl<'a, T, Raw> Pipeline<&'a RadarData2d<T, Raw>> for ShadePipe<T>
where
T: Num + PartialEq + PartialOrd + Clone + FromPrimitive,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
{
type Output = Array2<Option<femtovg::Color>>;
fn run(&self, input: &'a RadarData2d<T, Raw>) -> Result<Array2<Option<femtovg::Color>>> {
let data = input.data.view();
let result = data.mapv(|v| {
if T::from_i8(-125).unwrap() == v {
None
} else {
let color = self.get_color(v);
Some(color.0)
}
});
Ok(result)
}
}

View File

@ -9,8 +9,8 @@ use std::cell::RefCell;
pub struct BackgroundConfig { pub struct BackgroundConfig {
pub show_lat_lines: bool, pub show_lat_lines: bool,
pub show_lon_lines: bool, pub show_lon_lines: bool,
pub lat_lines: Vec<Vec<WindowCoord>>, pub lat_lines: Vec<f64>,
pub lon_lines: Vec<Vec<WindowCoord>>, pub lon_lines: Vec<f64>,
pub painter: Paint, pub painter: Paint,
} }

View File

@ -1,9 +1,9 @@
mod imp; mod imp;
use crate::render::WindowCoord; use crate::coords::{Mapper, Range};
use femtovg::{renderer::OpenGl, Canvas, Path}; use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
use geo_types::{line_string, LineString, Point};
use glib::subclass::types::ObjectSubclassIsExt; use glib::subclass::types::ObjectSubclassIsExt;
use gtk::{ffi::gtk_widget_get_width, glib, graphene::Rect, prelude::SnapshotExtManual}; use std::cell::Ref;
use std::ops::Range;
pub use self::imp::BackgroundConfig; pub use self::imp::BackgroundConfig;
@ -25,36 +25,122 @@ impl BackgroundWidget {
this this
} }
fn mesh_lines(&self, canvas: &mut Canvas<OpenGl>) { fn draw_lines<V: IntoIterator<Item = LineString<f32>>>(
let imp = self.imp(); &self,
let line_painter = &imp.config.borrow().painter; canvas: &mut Canvas<OpenGl>,
if imp.config.borrow().show_lat_lines { line_painter: &Paint,
for lat_line in imp.config.borrow().lat_lines.iter() { p: V,
) {
p.into_iter().for_each(|line| {
let mut path = Path::new(); let mut path = Path::new();
lat_line.iter().for_each(|v| { let points: Vec<Point<f32>> = line.points().collect();
let (x, y) = *v; path.move_to(points[0].x() as f32, points[0].y() as f32);
path.move_to(x, y); points[1..].into_iter().for_each(|p| {
path.line_to(p.x() as f32, p.y() as f32);
}); });
canvas.stroke_path(&mut path, line_painter); canvas.stroke_path(&mut path, line_painter);
} });
}
if imp.config.borrow().show_lon_lines {}
} }
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, scale: f32, dpi: i32) { fn mesh_lines(
let canvas_widht = canvas.width(); &self,
canvas: &mut Canvas<OpenGl>,
canvas_width: f32,
canvas_height: f32,
mapper: Ref<'_, Mapper>,
bound: (Range, Range),
) {
let imp = self.imp();
let line_painter = &imp.config.borrow().painter;
let (left, right) = (bound.0 .0, bound.0 .1);
let (bottom, top) = (bound.1 .0, bound.1 .1);
let config = imp.config.borrow();
if config.show_lat_lines {
let r = config.lat_lines.iter().map(|lat| {
let line = LineString::new(vec![(left, *lat).into(), (right, *lat).into()]);
let result = mapper.map_line(&line).unwrap();
LineString::new(
result
.points()
.map(|p| (p.x() as f32 * canvas_width, p.y() as f32 * canvas_height).into())
.collect(),
)
});
self.draw_lines(canvas, line_painter, r);
}
if config.show_lat_lines {
config.lat_lines.iter().for_each(|lat| {
let mut paint = Paint::color(Color::white());
paint.set_font_size(35.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,
);
});
}
if config.show_lon_lines {
config.lon_lines.iter().for_each(|lon| {
let mut paint = Paint::color(Color::white());
paint.set_font_size(35.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,
);
});
}
if imp.config.borrow().show_lon_lines {
let r = config.lon_lines.iter().map(|lon| {
let line = LineString::new(vec![(*lon, bottom).into(), (*lon, top).into()]);
let result = mapper.map_line(&line).unwrap();
LineString::new(
result
.points()
.map(|p| (p.x() as f32 * canvas_width, p.y() as f32 * canvas_height).into())
.collect(),
)
});
self.draw_lines(canvas, line_painter, r);
}
}
pub fn draw(
&self,
canvas: &mut Canvas<OpenGl>,
scale: f32,
dpi: i32,
mapper: Ref<'_, Mapper>,
bound: (Range, Range),
) {
let canvas_width = canvas.width();
let canvas_height = canvas.height(); let canvas_height = canvas.height();
let config = self.imp().config.borrow(); let config = self.imp().config.borrow();
self.mesh_lines(canvas); self.mesh_lines(canvas, canvas_width, canvas_height, mapper, bound);
} }
pub fn set_lat_lines(&self, lat_lines: Vec<Vec<WindowCoord>>) { pub fn set_lat_lines(&self, lat_lines: Vec<f64>) {
let imp = self.imp(); let imp = self.imp();
imp.config.borrow_mut().lat_lines = lat_lines; imp.config.borrow_mut().lat_lines = lat_lines;
} }
pub fn set_lon_lines(&self, lon_lines: Vec<Vec<WindowCoord>>) { pub fn set_lon_lines(&self, lon_lines: Vec<f64>) {
let imp = self.imp(); let imp = self.imp();
imp.config.borrow_mut().lon_lines = lon_lines; imp.config.borrow_mut().lon_lines = lon_lines;
} }

View File

@ -1,5 +1,5 @@
use crate::render::{imp, WindowCoord}; use crate::render::{imp, WindowCoord};
use femtovg::{ImageId, Paint}; use femtovg::{ImageId, Paint, Color};
use geo_macros::Prj; use geo_macros::Prj;
use geo_types::LineString; use geo_types::LineString;
use gtk::glib; use gtk::glib;
@ -29,6 +29,7 @@ pub struct ForegroundWidget {
pub(super) dim2: RefCell<Option<Array2<f64>>>, pub(super) dim2: RefCell<Option<Array2<f64>>>,
pub(super) image: RefCell<Option<ImageId>>, pub(super) image: RefCell<Option<ImageId>>,
pub(super) data: RefCell<Option<Array2<LineString>>>, pub(super) data: RefCell<Option<Array2<LineString>>>,
pub(super) color: RefCell<Option<Array2<Option<Color>>>>,
} }
#[glib::object_subclass] #[glib::object_subclass]

View File

@ -1,9 +1,8 @@
mod imp; mod imp;
use crate::coords::Mapper; use crate::coords::Mapper;
use crate::tree::get;
use femtovg::{renderer::OpenGl, Canvas, Path}; use femtovg::{renderer::OpenGl, Canvas, Path};
use femtovg::{Color, FontId, ImageFlags, Paint}; use femtovg::{Color, FontId, ImageFlags, ImageId, Paint};
use geo_types::LineString; use geo_types::{LineString, Point};
use glib::subclass::types::ObjectSubclassIsExt; use glib::subclass::types::ObjectSubclassIsExt;
use gtk::glib; use gtk::glib;
use ndarray::{s, Array2, Axis, Zip}; use ndarray::{s, Array2, Axis, Zip};
@ -34,12 +33,12 @@ impl ForegroundWidget {
let canvas_height = canvas.height(); let canvas_height = canvas.height();
let config = self.imp().config.borrow(); let config = self.imp().config.borrow();
println!("Resize: {} {}", canvas.width(), canvas.height()); let colors = self.imp().color.borrow();
let data = self.imp().data.borrow();
let mut img = self.imp().image.borrow_mut(); if self.imp().image.borrow().is_none() {
println!("rebuild image");
if img.is_none() { let img_id = canvas
let mut img_id = canvas
.create_image_empty( .create_image_empty(
canvas_width as usize, canvas_width as usize,
canvas_height as usize, canvas_height as usize,
@ -47,44 +46,75 @@ impl ForegroundWidget {
ImageFlags::empty(), ImageFlags::empty(),
) )
.unwrap(); .unwrap();
canvas.save(); if let Ok(v) = canvas.image_size(img_id) {
canvas.reset(); println!("create image: {:?}", v);
if let Ok(size) = canvas.image_size(img_id) { canvas.set_render_target(femtovg::RenderTarget::Image(img_id));
// canvas.set_render_target(femtovg::RenderTarget::Image(img_id)); let colors = self.imp().color.borrow();
canvas.clear_rect(0, 0, size.0 as u32, size.1 as u32, Color::rgb(255, 255, 0)); let data = self.imp().data.borrow();
if colors.is_some() && data.is_some() {
img.replace(img_id); for (i, c) in colors
.as_ref()
.unwrap()
.t()
.iter()
.zip(data.as_ref().unwrap().iter())
{
if i.is_none() {
continue;
} }
let pts = c.points().collect::<Vec<Point>>();
canvas.restore(); let first_point = pts[0];
canvas.reset();
}
let img = canvas
.load_image_file("test.png", ImageFlags::NEAREST)
.unwrap();
let mut path = Path::new(); let mut path = Path::new();
path.move_to(
path.rect(0.0, 0.0, canvas_width, canvas_height); first_point.x() as f32 * v.0 as f32 + 0.8,
canvas.fill_path( first_point.y() as f32 * v.1 as f32 + 0.8,
&path,
&Paint::image(img, 0.0, 0.0, canvas_width, canvas_height, 0.0, 1.0),
); );
canvas.flush(); pts[1..].into_iter().for_each(|p| {
path.line_to(p.x() as f32 * v.0 as f32, p.y() as f32 * v.1 as f32)
});
let c = i.unwrap();
canvas.fill_path(&path, &Paint::color(c));
}
} }
pub(super) fn set_dims(&mut self, dims: (Array2<f64>, Array2<f64>)) { self.imp().image.replace(Some(img_id));
}
}
canvas.set_render_target(femtovg::RenderTarget::Screen);
let mut path = Path::new();
path.rect(0.0, 0.0, canvas_width, canvas_height);
canvas.fill_path(
&path,
&Paint::image(
self.imp().image.borrow().as_ref().unwrap().to_owned(),
0.0,
0.0,
canvas_width,
canvas_height,
0.0,
1.0,
),
);
}
pub(super) fn set_dims(&self, dims: (Array2<f64>, Array2<f64>)) {
self.imp().dim1.replace(Some(dims.0)); self.imp().dim1.replace(Some(dims.0));
self.imp().dim2.replace(Some(dims.1)); self.imp().dim2.replace(Some(dims.1));
} }
// pub(super) fn set_image(&mut self, image: Vec<u8>) { pub(super) fn set_image(&self, image: ImageId) {
// self.imp().image.replace(Some(image)); self.imp().image.replace(Some(image));
// } }
pub(super) fn set_data(&mut self, data: Array2<LineString>) { pub fn set_colors(&self, colors: Array2<Option<Color>>) {
self.imp().color.replace(Some(colors));
}
pub fn set_data(&self, data: Array2<LineString>) {
self.imp().data.replace(Some(data)); self.imp().data.replace(Some(data));
} }
} }

View File

@ -3,7 +3,7 @@ use super::foreground::ForegroundWidget;
use super::WindowCoord; use super::WindowCoord;
use crate::coords::proj::Mercator; use crate::coords::proj::Mercator;
use crate::coords::Mapper; use crate::coords::Mapper;
use femtovg::{Color, Paint, Path}; use femtovg::{Color, Paint, Path, FontId};
use gtk::glib; use gtk::glib;
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use gtk::traits::{GLAreaExt, WidgetExt}; use gtk::traits::{GLAreaExt, WidgetExt};
@ -20,6 +20,12 @@ pub struct RenderConfig {
pub transform: WindowCoord, pub transform: WindowCoord,
} }
struct Fonts {
sans: FontId,
bold: FontId,
light: FontId,
}
pub struct Render { pub struct Render {
pub(super) background: RefCell<BackgroundWidget>, pub(super) background: RefCell<BackgroundWidget>,
pub(super) foreground: RefCell<ForegroundWidget>, pub(super) foreground: RefCell<ForegroundWidget>,
@ -81,24 +87,42 @@ impl GLAreaImpl for Render {
let canvas = canvas.as_mut().unwrap(); let canvas = canvas.as_mut().unwrap();
let dpi = self.obj().scale_factor(); let dpi = self.obj().scale_factor();
let w = self.obj().width(); let w = canvas.width();
let h = self.obj().width(); let h = canvas.height();
let configs = self.config.borrow(); let configs = self.config.borrow();
canvas.clear_rect( canvas.clear_rect(
0, 0,
0, 0,
(w * dpi) as u32, (w as i32 * dpi) as u32,
(h * dpi) as u32, (h as i32 * dpi) as u32,
Color::rgba(0, 0, 0, 255), Color::rgba(0, 0, 0, 255),
); );
// self.background.borrow().draw(canvas, configs.scale, dpi); 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(5));
}
{
let background_widget = self.background.borrow_mut();
background_widget.set_lon_lines(lon_range.key_points(10));
}
self.foreground self.foreground
.borrow() .borrow()
.draw(canvas, configs.scale, dpi, self.mapper.borrow()); .draw(canvas, configs.scale, dpi, self.mapper.borrow());
self.background.borrow().draw(
canvas,
configs.scale,
dpi,
self.mapper.borrow(),
(lon_range, lat_range),
);
canvas.flush(); canvas.flush();
true true
@ -113,6 +137,7 @@ impl Render {
if self.canvas.borrow().is_some() { if self.canvas.borrow().is_some() {
return; return;
} }
let widget = self.obj(); let widget = self.obj();
widget.attach_buffers(); widget.attach_buffers();
@ -131,7 +156,8 @@ impl Render {
(renderer, glow::NativeFramebuffer(id)) (renderer, glow::NativeFramebuffer(id))
}; };
renderer.set_screen_target(Some(fbo)); renderer.set_screen_target(Some(fbo));
let canvas = Canvas::new(renderer).expect("Cannot create canvas"); 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)); self.canvas.replace(Some(canvas));
} }
} }

View File

@ -1,15 +1,17 @@
mod background; mod background;
mod foreground; mod foreground;
mod imp; mod imp;
use std::fmt::Debug;
use crate::coords::Mapper; use crate::coords::Mapper;
use crate::data::{MultiDimensionData, RadarData2d}; use crate::data::{MultiDimensionData, RadarData2d};
use crate::pipeline::ProjPipe;
use crate::pipeline::{Pipeline, ShadePipe};
use std::fmt::Debug;
pub use self::background::{BackgroundConfig, BackgroundWidget}; pub use self::background::{BackgroundConfig, BackgroundWidget};
pub use self::foreground::{ForegroundConfig, ForegroundWidget}; pub use self::foreground::{ForegroundConfig, ForegroundWidget};
use self::imp::RenderConfig; use self::imp::RenderConfig;
use crate::data::DownSampleMeth; use crate::data::DownSampleMeth;
use femtovg::Color;
pub use glib::subclass::prelude::*; pub use glib::subclass::prelude::*;
use image::RgbImage; use image::RgbImage;
use ndarray::{self, s, Array2, Axis, Dimension, Ix2, Zip}; use ndarray::{self, s, Array2, Axis, Dimension, Ix2, Zip};
@ -70,45 +72,37 @@ impl Render {
{ {
assert!(data.dim1.shape().len() == data.dim2.shape().len()); assert!(data.dim1.shape().len() == data.dim2.shape().len());
let levels: Vec<T> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65]
.into_iter()
.map(|b| T::from_i8(b).unwrap())
.collect();
let colors = vec![
Color::rgb(0, 172, 164),
Color::rgb(192, 192, 254),
Color::rgb(122, 114, 238),
Color::rgb(30, 38, 208),
Color::rgb(166, 252, 168),
Color::rgb(0, 234, 0),
Color::rgb(16, 146, 26),
Color::rgb(252, 244, 100),
Color::rgb(200, 200, 2),
Color::rgb(140, 140, 0),
Color::rgb(254, 172, 172),
Color::rgb(254, 100, 92),
Color::rgb(238, 2, 48),
Color::rgb(212, 142, 254),
Color::rgb(170, 36, 250),
];
let mapper = self.imp().mapper.borrow(); let mapper = self.imp().mapper.borrow();
// data.downsample((801 * 2 / 3, 947 * 2 / 3), DownSampleMeth::VAR);
let pjp = ProjPipe::new(&mapper);
let rrp = ShadePipe::new(levels, colors.into_iter().map(|v| v.into()).collect());
let rainbow = pjp.run(&data).unwrap();
self.imp().foreground.borrow_mut().set_data(rainbow);
let pbow:Array2<Option<Color>> = rrp.run(&data).unwrap();
self.imp().foreground.borrow_mut().set_colors(pbow);
Ok(()) Ok(())
} }
} }
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
// let colors = vec![
// Color::rgb(0, 172, 164),
// Color::rgb(192, 192, 254),
// Color::rgb(122, 114, 238),
// Color::rgb(30, 38, 208),
// Color::rgb(166, 252, 168),
// Color::rgb(0, 234, 0),
// Color::rgb(16, 146, 26),
// Color::rgb(252, 244, 100),
// Color::rgb(200, 200, 2),
// Color::rgb(140, 140, 0),
// Color::rgb(254, 172, 172),
// Color::rgb(254, 100, 92),
// Color::rgb(238, 2, 48),
// Color::rgb(212, 142, 254),
// Color::rgb(170, 36, 250),
// ];
// let c = d.map(|v| {
// let c = get(&levels, &colors, *v);
// image::Rgb([
// (c.r * 255.0) as u8,
// (c.g * 255.0) as u8,
// (c.b * 255.0) as u8,
// ])
// });
// let mut img = RgbImage::from_fn(927, 801, |x, y| c[[y as usize, x as usize]]);
// img.save("test.png").unwrap();
// self.imp()
// .foreground
// .borrow_mut()
// .set_dims((meshed.dim1, meshed.dim2));

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/resources/entypo.ttf Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<gresources> <gresources>
<gresource prefix="/org/cinrad_g/"> <gresource prefix="/org/cinrad_g/">
<file compressed="true" preprocess="xml-stripblanks">monitor.ui</file> <file alias="bold.ttf">Roboto-Bold.ttf</file>
</gresource> </gresource>
</gresources> </gresources>

View File

@ -1,23 +0,0 @@
use femtovg::Color;
use num_traits::Num;
pub fn get<T, V: Copy>(levels: &Vec<T>, colors: &Vec<V>, v: T) -> V
where
T: Num + PartialOrd + Copy,
{
let len = levels.len();
let mut left = 0;
let mut right = len - 1;
while left < right - 1 {
let middle = (right + left) / 2;
if v > levels[middle] {
left = middle;
} else {
right = middle;
}
}
colors[left]
}