radar-g/src/widgets/render/predefined/grid_field_renderer.rs
2024-03-05 20:39:32 +08:00

234 lines
7.0 KiB
Rust

use super::color_mapper::{BoundaryNorm, ColorMapper};
use crate::pipeline::element::{Target, TargetType};
use femtovg::ImageInfo;
use femtovg::{
renderer::OpenGl, Canvas, ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget,
};
use geo_types::LineString;
use gl::types::GLvoid;
use image::{imageops::resize, ImageBuffer, Rgba};
use ndarray::ArrayView2;
use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps};
use std::{fmt::Debug, io::Cursor, marker::PhantomData};
use super::super::renders::DataRenderer;
use super::super::{LayerImpl, Render};
use crate::{data::Radar2d, utils::meshgrid};
use crate::coords::cms::CMS;
#[derive(Debug)]
pub struct GridFieldRenderer<CMAP, T>
where
T: NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
CMAP: ColorMapper<T>,
{
cmap: CMAP,
value_phantom: PhantomData<T>,
}
impl<T: NumOps + PartialOrd + Copy + FromPrimitive + AsPrimitive<f64>, CMAP: ColorMapper<T>>
GridFieldRenderer<CMAP, T>
{
pub fn new(cmap: CMAP) -> Self {
Self {
cmap,
value_phantom: PhantomData,
}
}
fn draw_2d(
&self,
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
cms: &CMS,
data: ArrayView2<T>,
dims: (ArrayView2<f64>, ArrayView2<f64>),
window_size: (f32, f32),
fill_value: T,
) {
let shape = data.shape();
let (rows, cols) = (shape[0], shape[1]);
let (dim1, dim2) = dims;
let d1_s = dim1[[0, 0]];
let d1_e = dim1[[0, cols - 1]];
let d2_s = dim2[[0, 0]];
let d2_e = dim2[[rows - 1, 0]];
for r in 0..rows - 1 {
for c in 0..cols - 1 {
let lb_lat = dim2[[r, c]];
let lb_lon = dim1[[r, c]];
let rt_lat = dim2[[r + 1, c + 1]];
let rt_lon = dim1[[r + 1, c + 1]];
let cell: LineString = vec![
(lb_lon, lb_lat),
(rt_lon + 0.001, lb_lat),
(rt_lon + 0.001, rt_lat),
(lb_lon, rt_lat + 0.001),
(lb_lon, lb_lat + 0.001),
]
.into();
let v = &data[[r, c]];
let mapped_color = self.cmap.map_value_to_color(*v, fill_value);
if None == mapped_color {
continue;
}
let mapped_ring = cms.ring_map(&cell).unwrap();
let mut path = Path::new();
let mut points = mapped_ring.points();
let first_point = points.next().unwrap();
path.move_to(first_point.x(), first_point.y());
for point in points {
path.line_to(point.x(), point.y());
}
path.close();
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
}
}
}
}
impl<T, CMAP> DataRenderer for GridFieldRenderer<CMAP, T>
where
T: Num + NumOps + PartialOrd + Copy + Clone + FromPrimitive + AsPrimitive<f64>,
CMAP: ColorMapper<T>,
{
type Data = Radar2d<T>;
fn render(
&self,
canvas: &mut Canvas<OpenGl>,
mut cms: &CMS,
data: &Self::Data,
size: (f32, f32),
) -> Target {
let (w, h) = size;
let new_img = canvas
.create_image_empty(w as usize, h as usize, Rgba8, ImageFlags::empty())
.expect("Can't Create Image");
canvas.image_size(new_img).unwrap();
canvas.set_render_target(RenderTarget::Image(new_img));
let _data = data.data.view();
let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view());
let lat_start = data.dim2.view().first().unwrap().clone();
let lat_end = data.dim2.view().last().unwrap().clone();
let lon_start = data.dim1.view().first().unwrap().clone();
let lon_end = data.dim1.view().last().unwrap().clone();
cms.set_lat_range(lat_start..lat_end);
cms.set_lon_range(lon_start..lon_end);
self.draw_2d(
canvas,
&cms,
_data,
(_dim1.view(), _dim2.view()),
(w, h),
data.fill_value,
);
canvas.flush();
let mut pixels: Vec<u8> = vec![0; w as usize * h as usize * 4];
unsafe {
gl::ReadPixels(
0,
0,
w as i32,
h as i32,
gl::RGBA,
gl::UNSIGNED_BYTE,
pixels.as_mut_ptr() as *mut GLvoid,
);
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
}
let img: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::from_raw(w as u32, h as u32, pixels)
.expect("Failed to create ImageBuffer");
let thumbnail = resize(&img, 500, 500, image::imageops::FilterType::Lanczos3);
let mut thumb_buffer = Cursor::new(Vec::new());
img.write_to(&mut thumb_buffer, image::ImageOutputFormat::Png)
.expect("Failed to write PNG buffer");
let thumb_data = thumb_buffer.into_inner();
let thumbnail_tex =
gtk::gdk::Texture::from_bytes(&gtk::glib::Bytes::from(&thumb_data)).unwrap();
// 将 ImageBuffer 编码为 PNG
let mut png_buffer = Cursor::new(Vec::new());
img.write_to(&mut png_buffer, image::ImageOutputFormat::Bmp)
.expect("Failed to write PNG buffer");
let png_data = png_buffer.into_inner();
let d1_start = (data.dim1.view()).first().unwrap().clone();
let d1_end = (data.dim1.view()).last().unwrap().clone();
let d2_start = data.dim2.view().first().unwrap().clone();
let d2_end = data.dim2.view().last().unwrap().clone();
Target::new(
TargetType::Mem(png_data),
w,
h,
((d1_start, d1_end).into(), (d2_start, d2_end).into()),
Some(thumbnail_tex),
)
}
}
#[derive(Debug)]
pub struct GridLayerImpl<CMAP, T>
where
T: Num + NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64> + Send + Sync + Debug,
CMAP: ColorMapper<T>,
{
renderer: GridFieldRenderer<CMAP, T>,
data: Radar2d<T>,
}
pub type DbzGridLayerImpl = GridLayerImpl<BoundaryNorm<i8>, i8>;
impl<CMAP, T> GridLayerImpl<CMAP, T>
where
T: Num + NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64> + Send + Sync + Debug,
CMAP: ColorMapper<T>,
{
pub fn new(renderer: GridFieldRenderer<CMAP, T>, data: Radar2d<T>) -> Self {
Self { renderer, data }
}
}
impl<CMAP, T> LayerImpl for GridLayerImpl<CMAP, T>
where
T: Num
+ NumOps
+ PartialOrd
+ Copy
+ Clone
+ Debug
+ Send
+ Sync
+ FromPrimitive
+ AsPrimitive<f64>,
CMAP: ColorMapper<T> + Debug,
{
fn draw(
&self,
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
cms: &CMS,
) -> Option<Target> {
let result = self
.renderer
.render(canvas, cms, &self.data, (3000.0, 3000.0));
return Some(result);
}
}