From 0836c004ebdb79c6a9ea81e745a51c265f3701ae Mon Sep 17 00:00:00 2001 From: Tsuki Date: Thu, 5 Sep 2024 16:35:24 +0800 Subject: [PATCH] sync --- gi/src/graphics/collections/agg_fast_path.rs | 21 +- gi/src/graphics/colormap/linear.rs | 3 - gi/src/graphics/font/mod.rs | 19 +- gi/src/graphics/geoquadmesh.rs | 385 +++++++++++++++ gi/src/graphics/mod.rs | 18 +- gi/src/graphics/ppi.rs | 20 +- gi/src/lib.rs | 1 + gi/src/pg/mod.rs | 123 +++-- gi/src/pg/modules/geoquadmesh.rs | 478 +++++++++++++++++++ gi/src/pg/modules/mod.rs | 36 +- gi/src/pg/modules/ppi.rs | 82 ++-- gi/src/shaders/geoquadmesh.rs | 91 ++++ gi/src/shaders/mod.rs | 2 + gi/src/shaders/proj.rs | 45 ++ radar-g/src/components/monitor/messages.rs | 4 +- radar-g/src/components/monitor/monitor.rs | 80 ++-- radar-g/src/widgets/render/imp.rs | 22 +- radar-g/src/widgets/render/mod.rs | 2 +- radarg_core/src/radarg_data/mod.rs | 41 +- 19 files changed, 1329 insertions(+), 144 deletions(-) create mode 100644 gi/src/graphics/geoquadmesh.rs create mode 100644 gi/src/pg/modules/geoquadmesh.rs create mode 100644 gi/src/shaders/geoquadmesh.rs create mode 100644 gi/src/shaders/proj.rs diff --git a/gi/src/graphics/collections/agg_fast_path.rs b/gi/src/graphics/collections/agg_fast_path.rs index c38a466..31503e2 100644 --- a/gi/src/graphics/collections/agg_fast_path.rs +++ b/gi/src/graphics/collections/agg_fast_path.rs @@ -1,12 +1,13 @@ use std::ops::{Deref, DerefMut}; use bytemuck::{Pod, Zeroable}; -use glow::{HasContext, NativeBuffer, NativeVertexArray}; +use glow::{HasContext, NativeBuffer, NativeTexture, NativeVertexArray}; use crate::components::{Program, Shader}; use crate::errors::*; use crate::graphics::ty::Ty; use crate::graphics::{AttaWithBuffer, Graphics}; +use crate::pg::Attach; use crate::shaders::agg_path::{AggPathFragment, AggPathVertex}; use super::Colletion; @@ -129,7 +130,8 @@ impl AttaWithBuffer for AggFastPath { gl: &'gl glow::Context, data: &Self::Data, config: &::Config, - ) -> Result<(Vec, Option>, i32)> { + attach: &mut Attach, + ) -> Result<()> { let points = data.iter().map(|v| &v.points).flatten().collect::>(); let mut lens = Vec::with_capacity(data.len()); @@ -156,10 +158,19 @@ impl AttaWithBuffer for AggFastPath { let len = ebo.len() as i32; - Ok((vbo, Some(ebo), len)) + attach.bind_data(&vbo, Some(&ebo), len, glow::STATIC_DRAW); + Ok(()) } - fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option) { + fn init( + &self, + gl: &glow::Context, + ) -> ( + NativeVertexArray, + NativeBuffer, + Option, + Option, + ) { unsafe { let vao = gl.create_vertex_array().unwrap(); gl.bind_vertex_array(Some(vao)); @@ -183,7 +194,7 @@ impl AttaWithBuffer for AggFastPath { gl.bind_vertex_array(None); gl.bind_buffer(glow::ARRAY_BUFFER, None); - (vao, vbo, Some(ebo)) + (vao, vbo, Some(ebo), None) } } } diff --git a/gi/src/graphics/colormap/linear.rs b/gi/src/graphics/colormap/linear.rs index e3af7a5..415042d 100644 --- a/gi/src/graphics/colormap/linear.rs +++ b/gi/src/graphics/colormap/linear.rs @@ -108,9 +108,6 @@ impl ColorMap for LinearColormap { gl.active_texture(glow::TEXTURE0); gl.bind_texture(glow::TEXTURE_1D, self.texture); let location = program.get_uniform_location(gl, "colormap"); - - let error = gl.get_error(); - gl.uniform_1_i32(location.as_ref(), 0); } diff --git a/gi/src/graphics/font/mod.rs b/gi/src/graphics/font/mod.rs index d44478a..12cb3e9 100644 --- a/gi/src/graphics/font/mod.rs +++ b/gi/src/graphics/font/mod.rs @@ -289,19 +289,29 @@ impl Graphics for Text { impl AttaWithBuffer for Text { type Data = PositionText; + // fn bake<'a, 'gl: 'a>( + // &'a self, + // gl: &'gl glow::Context, + // data: &Self::Data, + // config: &::Config, + // ) -> Result<(Vec, Option>, i32)> { + + // } + fn bake<'a, 'gl: 'a>( &'a self, gl: &'gl glow::Context, data: &Self::Data, config: &::Config, - ) -> Result<(Vec, Option>, i32)> { + attach: &mut crate::pg::Attach, + ) -> Result<()> { let v = data.bake( &self.gl, &mut *self.font_manager.borrow_mut(), &mut *self.cache.borrow_mut(), )?; - - Ok((v.vertex(), None, v.len() as i32)) + attach.bind_data(&v.vertex(), None, v.len() as i32, glow::STATIC_DRAW); + Ok(()) } fn init( @@ -311,6 +321,7 @@ impl AttaWithBuffer for Text { glow::NativeVertexArray, glow::NativeBuffer, Option, + Option, ) { unsafe { let vao = gl.create_vertex_array().unwrap(); @@ -326,7 +337,7 @@ impl AttaWithBuffer for Text { gl.bind_vertex_array(None); gl.bind_buffer(glow::ARRAY_BUFFER, None); - (vao, vbo, None) + (vao, vbo, None, None) } } } diff --git a/gi/src/graphics/geoquadmesh.rs b/gi/src/graphics/geoquadmesh.rs new file mode 100644 index 0000000..6ddbc7e --- /dev/null +++ b/gi/src/graphics/geoquadmesh.rs @@ -0,0 +1,385 @@ +use glow::HasContext; +use radarg_core::{CoordType, ProbeDataType, RadarGridData}; + +use crate::{ + components::{Program, Shader}, + errors::*, + pg::Attach, + shaders::geoquadmesh::{GeoQuadMeshFragment, GeoQuadMeshVertex}, +}; + +use super::{ + colormap::{linear::LinearColormap, ColorMap}, + AttaWithBuffer, Graphics, +}; + +pub struct GeoQuadMesh { + program: Program, + cmap: LinearColormap, +} + +impl GeoQuadMesh { + pub fn new() -> Result { + let vertex = Shader::new(glow::VERTEX_SHADER, GeoQuadMeshVertex::new())?; + let fragment = Shader::new(glow::FRAGMENT_SHADER, GeoQuadMeshFragment::new())?; + let program = Program::new(vertex, fragment, None, "330 core"); + + let mut cmap = LinearColormap::new()?; + Ok(Self { program, cmap }) + } + + fn set_conf(&mut self, gl: &glow::Context, config: &GeoQuadMeshConfig) { + let lon_dpi = config.lon_dpi; + let lat_dpi = config.lat_dpi; + let alt_dpi = config.alt_dpi; + + let location = self.program.get_uniform_location(gl, "data_info"); + + self.cmap + .set_range(config.color_range[0], config.color_range[1]); + + self.cmap.set_colors(config.colors.clone()); + self.cmap.set_unvalid_value(config.unvalid_value); + self.cmap.attach_with_program(gl, &self.program); + let location_r = self.program.get_uniform_location(gl, "R"); + let location_layer = self.program.get_uniform_location(gl, "layer"); + let tex_conf = self.program.get_uniform_location(gl, "tex_conf"); + + unsafe { + gl.uniform_1_f32(location_r.as_ref(), 6378137.0); + gl.uniform_4_f32(location.as_ref(), lon_dpi, lat_dpi, alt_dpi, 0.0); + gl.uniform_1_f32(location_layer.as_ref(), config.layer as f32); + + let lon_s = config.lon_range[0]; + let lon_e = config.lon_range[1]; + let lat_s = config.lat_range[0]; + let lat_e = config.lat_range[1]; + gl.uniform_4_f32(tex_conf.as_ref(), lon_s, lon_e, lat_s, lat_e); + } + } + + pub fn program(&mut self) -> &mut Program { + &mut self.program + } + + pub fn data_info( + &self, + data: &RadarGridData, + ) -> Result<( + Option<[f32; 2]>, + [f32; 2], + [f32; 2], + [f32; 3], + ProbeDataType, + )> { + if data.coord_type().is_none() { + return Err(Error::InvalidDataType(format!("Invalid CoordType"))); + } + match data.coord_type().unwrap() { + CoordType::Cartesian { hgt, lat, lon } => { + let lat_start = *lat.first().unwrap() as f32; + let lat_end = *lat.last().unwrap() as f32; + let lon_start = *lon.first().unwrap() as f32; + let lon_end = *lon.last().unwrap() as f32; + + let lat_dpi = (lat[1] - lat[0]); + let lon_dpi = (lon[1] - lon[0]); + + let (hgt_range, alt_dpi) = if let Some(ref hgt) = hgt { + let hgt_start = *hgt.first().unwrap() as f32; + let hgt_end = *hgt.last().unwrap() as f32; + let alt_dpi = (hgt[1] - hgt[0]); + (Some([hgt_start, hgt_end]), alt_dpi) + } else { + (None, 0.0) + }; + + let data_type = data.data_type(); + Ok(( + hgt_range, + [lat_start, lat_end], + [lon_start, lon_end], + [alt_dpi as f32, lat_dpi as f32, lon_dpi as f32], + data_type, + )) + } + _ => Err(Error::InvalidDataType(format!("Invalid CoordType"))), + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct GeoQuadMeshConfig { + pub unvalid_value: f32, + pub layer: usize, + pub colors: Vec<[u8; 4]>, + pub color_range: [f32; 2], + pub lon_dpi: f32, + pub lat_dpi: f32, + pub alt_dpi: f32, + pub lat_range: [f32; 2], + pub lon_range: [f32; 2], + pub alt_range: [f32; 2], +} + +impl Graphics for GeoQuadMesh { + const id: &'static str = "geoquadmesh"; + type Config = GeoQuadMeshConfig; + + fn compile(&mut self, gl: &glow::Context) -> Result<()> { + self.program.compile(gl) + } + + fn destroy(&mut self, gl: &glow::Context) -> Result<()> { + self.program.destroy(gl); + Ok(()) + } + + fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> { + unsafe { + gl.draw_arrays(glow::TRIANGLE_STRIP_ADJACENCY, 0, count); + } + Ok(()) + } + + fn mount(&self, gl: &glow::Context) -> Result<()> { + unsafe { + gl.use_program(self.program.native_program); + } + + Ok(()) + } + + fn unmount(&self, gl: &glow::Context) -> Result<()> { + unsafe { + gl.use_program(None); + } + + Ok(()) + } + + fn program_mut(&mut self) -> &mut Program { + &mut self.program + } + + fn program_ref(&self) -> &Program { + &self.program + } + + fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> { + self.set_conf(gl, config); + Ok(()) + } +} + +impl AttaWithBuffer for GeoQuadMesh { + type Data = RadarGridData; + + fn bake<'a, 'gl: 'a>( + &'a self, + gl: &'gl glow::Context, + data: &Self::Data, + config: &::Config, + attach: &mut Attach, + ) -> Result<()> { + if let Some(coord_type) = data.coord_type() { + if let CoordType::Cartesian { hgt, lat, lon } = coord_type { + let mut vertices = Vec::with_capacity(12); + let lat_start = *lat.first().unwrap() as f32; + let lat_end = *lat.last().unwrap() as f32; + let lon_start = *lon.first().unwrap() as f32; + let lon_end = *lon.last().unwrap() as f32; + + let start = mercator_project([lon_start, lat_start]); + let end = mercator_project([lon_end, lat_end]); + + let lon_len = (end[0] - start[0]).abs(); + let lat_len = (end[1] - start[1]).abs(); + + let rate = lat_len / lon_len; + let rel_width = 10.0; + let rel_height = rel_width * rate; + + if let Some(ref hgt) = hgt { + let hgt_start = *hgt.first().unwrap() as f32; + let hgt_end = *hgt.last().unwrap() as f32; + + vertices.extend_from_slice(&[ + // Left Bottom + lon_start, + lat_start, + 0.0, + -rel_width / 2.0, + -rel_height / 2.0, + 0.0, + // Right Bottom + lon_end, + lat_start, + 0.0, + rel_width / 2.0, + -rel_height / 2.0, + 0.0, + // Left Top + lon_start, + lat_end, + 0.0, + -rel_width / 2.0, + rel_height / 2.0, + 0.0, + // Right Top + lon_end, + lat_end, + 0.0, + rel_width / 2.0, + rel_height / 2.0, + 0.0, + ]); + } else { + vertices.extend_from_slice(&[ + // Left Bottom + lon_start, + lat_start, + 0.0, + -rel_width / 2.0, + -rel_height / 2.0, + 0.0, + // Right Bottom + lon_end, + lat_start, + 0.0, + rel_width / 2.0, + -rel_height / 2.0, + 0.0, + // Left Top + lon_start, + lat_end, + 0.0, + -rel_width / 2.0, + rel_height / 2.0, + 0.0, + // Right Top + lon_end, + lat_end, + 0.0, + rel_width / 2.0, + rel_height / 2.0, + 0.0, + ]); + } + attach.bind_data(&vertices, None, 4, glow::STATIC_DRAW); + + // Texture + unsafe { + use bytemuck::cast_slice; + gl.bind_texture( + glow::TEXTURE_3D, + (attach.textures.as_ref()).map(|t| t.native()), + ); + + let _data = &data.data; + let shape = _data.shape(); + + let (depth, height, width) = if shape.len() == 2 { + (1i32, shape[0] as i32, shape[1] as i32) + } else { + (shape[0] as i32, shape[1] as i32, shape[2] as i32) + }; + + let pixels: &[u8] = cast_slice(_data.as_slice().unwrap()); + + gl.tex_image_3d( + glow::TEXTURE_3D, + 0, + glow::R8 as i32, + width, + height, + depth, + 0, + glow::RED, + glow::UNSIGNED_BYTE, + Some(pixels), + ); + } + + return Ok(()); + } else { + return Err(Error::InvalidDataType(format!( + "Expect Cartesian, but got {}", + coord_type + ))); + } + } else { + return Err(Error::InvalidDataType("No coord type".to_string())); + } + } + + fn init( + &self, + gl: &glow::Context, + ) -> ( + glow::NativeVertexArray, + glow::NativeBuffer, + Option, + Option, + ) { + unsafe { + let vao = gl.create_vertex_array().unwrap(); + gl.bind_vertex_array(Some(vao)); + + let vbo = gl.create_buffer().unwrap(); + gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo)); + gl.enable_vertex_attrib_array(0); + gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 24, 0); + gl.enable_vertex_attrib_array(1); + gl.vertex_attrib_pointer_f32(1, 3, glow::FLOAT, false, 24, 12); + gl.bind_vertex_array(None); + gl.bind_buffer(glow::ARRAY_BUFFER, None); + + // Create Texture + let texture = gl.create_texture().unwrap(); + gl.bind_texture(glow::TEXTURE_3D, Some(texture)); + + gl.tex_parameter_i32( + glow::TEXTURE_3D, + glow::TEXTURE_MIN_FILTER, + glow::NEAREST as i32, + ); + gl.tex_parameter_i32( + glow::TEXTURE_3D, + glow::TEXTURE_MAG_FILTER, + glow::NEAREST as i32, + ); + gl.tex_parameter_i32( + glow::TEXTURE_3D, + glow::TEXTURE_WRAP_S, + glow::CLAMP_TO_EDGE as i32, + ); + gl.tex_parameter_i32( + glow::TEXTURE_3D, + glow::TEXTURE_WRAP_T, + glow::CLAMP_TO_EDGE as i32, + ); + gl.tex_parameter_i32( + glow::TEXTURE_3D, + glow::TEXTURE_WRAP_R, + glow::CLAMP_TO_EDGE as i32, + ); + + (vao, vbo, None, Some(texture)) + } + } +} + +fn mercator_project(loc: [f32; 2]) -> [f32; 2] { + // 将经纬度从度转换为弧度 + let lon_rad = loc[0].to_radians(); + let lat_rad = loc[1].to_radians(); + + // X轴投影 (线性映射经度) + let x = 6378137.0 * lon_rad; + + // Y轴投影 (非线性映射纬度) + let y = 6378137.0 * (lat_rad / 2.0 + 45.0_f32.to_radians()).tan().ln(); + + return [x, y]; +} diff --git a/gi/src/graphics/mod.rs b/gi/src/graphics/mod.rs index efe6626..064f5c7 100644 --- a/gi/src/graphics/mod.rs +++ b/gi/src/graphics/mod.rs @@ -2,6 +2,7 @@ pub mod collections; pub mod colormap; mod colormesh; pub mod font; +pub mod geoquadmesh; pub mod ppi; pub mod threed; pub mod tools; @@ -13,11 +14,11 @@ use crate::{ components::Program, errors::*, graphics::font::FontConfig, - pg::layout_type::ViewPort, + pg::{layout_type::ViewPort, Attach}, ui::{operation::Projection, typ::CameraOP}, }; -use glow::{HasContext, NativeBuffer, NativeVertexArray}; +use glow::{HasContext, NativeBuffer, NativeTexture, NativeVertexArray}; pub trait Graphics { const id: &'static str; @@ -64,8 +65,17 @@ pub trait AttaWithBuffer: Graphics { gl: &'gl glow::Context, data: &Self::Data, config: &::Config, - ) -> Result<(Vec, Option>, i32)>; - fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option); + attach: &mut Attach, + ) -> Result<()>; + fn init( + &self, + gl: &glow::Context, + ) -> ( + NativeVertexArray, + NativeBuffer, + Option, + Option, + ); } macro_rules! config_for_everyitem { diff --git a/gi/src/graphics/ppi.rs b/gi/src/graphics/ppi.rs index 24a54c4..13da5c7 100644 --- a/gi/src/graphics/ppi.rs +++ b/gi/src/graphics/ppi.rs @@ -186,7 +186,8 @@ impl AttaWithBuffer for PPI { gl: &'gl glow::Context, data: &Self::Data, config: &::Config, - ) -> Result<(Vec, Option>, i32)> { + attach: &mut crate::pg::Attach, + ) -> Result<()> { let layer = config.layer; match data.coord_type().unwrap() { @@ -213,15 +214,24 @@ impl AttaWithBuffer for PPI { } } let len = vertices.len() as i32 / 4; - return Ok((vertices, None, len)); + + attach.bind_data(&vertices, None, len, glow::STATIC_DRAW); + Ok(()) } _ => { return Err(Error::InvalidDataType(format!("Invalid CoordType"))); } } } - - fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option) { + fn init( + &self, + gl: &glow::Context, + ) -> ( + NativeVertexArray, + NativeBuffer, + Option, + Option, + ) { unsafe { let vao = gl.create_vertex_array().unwrap(); gl.bind_vertex_array(Some(vao)); @@ -232,7 +242,7 @@ impl AttaWithBuffer for PPI { gl.bind_vertex_array(None); gl.bind_buffer(glow::ARRAY_BUFFER, None); - (vao, vbo, None) + (vao, vbo, None, None) } } } diff --git a/gi/src/lib.rs b/gi/src/lib.rs index 79fe334..0bd86d2 100644 --- a/gi/src/lib.rs +++ b/gi/src/lib.rs @@ -1,4 +1,5 @@ #![feature(proc_macro_hygiene)] +#![feature(concat_idents)] #![allow(unused)] mod camera; mod components; diff --git a/gi/src/pg/mod.rs b/gi/src/pg/mod.rs index 5747bf6..f62eb7c 100644 --- a/gi/src/pg/mod.rs +++ b/gi/src/pg/mod.rs @@ -3,7 +3,10 @@ use femtovg::renderer::OpenGl; use femtovg::Canvas; use glow::HasContext; use layout_type::ViewPort; -use modules::{PPIModuleConfigComponent, PPIModuleRef}; +use modules::{ + GeoQuadMeshModuleConfigComponent, GeoQuadMeshModuleRef, GeoQuadMeshPackage, + PPIModuleConfigComponent, PPIModuleRef, +}; use radarg_core::{datapool::Value, radarg_data::Data}; use paste::paste; @@ -16,12 +19,13 @@ use relm4::{ }; pub mod layout_type; mod modules; +pub(crate) use modules::Attach; use crate::{ errors::*, font_manager::FontManager, graphics::{ - collections::agg_fast_path::AggFastPath, font::Text, ppi::PPI, + collections::agg_fast_path::AggFastPath, font::Text, geoquadmesh::GeoQuadMesh, ppi::PPI, transforms::plane::PlaneTrans, AttaWithProgram, AttachWithIO, Graphics, }, ui::{operation::Operation, typ::LayoutAttach}, @@ -29,7 +33,7 @@ use crate::{ }; pub use app::{App, Context}; -pub use modules::{Module, ModuleCursor, PPIModule, PPIPackage}; +pub use modules::{GeoQuadMeshModule, Module, ModuleCursor, PPIModule, PPIPackage}; use std::sync::atomic::AtomicUsize; use std::{cell::RefCell, collections::HashMap}; use std::{rc::Rc, sync::Arc}; @@ -44,22 +48,42 @@ pub enum SideBarInputMsg { None, } +macro_rules! program_impl { + ($({$name:ident | $name_ref:ident, $module: ty | $module_ref: ty, ($($m:ident),+)}),+) => { + impl Programs { + $( + pub fn $name(&mut self) -> $module { + <$module>::new(&self.gl, $(&mut self.$m),+) + } + + pub fn $name_ref(&self) -> $module_ref { + <$module_ref>::new(&self.gl, $(&self.$m),+) + } + )+ + + + + } + }; +} + pub struct Programs { gl: GL, _ppi: PPI, _text: Text, _line: AggFastPath, + _geo_quad_mesh: GeoQuadMesh, } impl Programs { fn new(gl: GL) -> Result { let font_manager = FontManager::new()?; - Ok(Self { gl: gl.clone(), _ppi: PPI::new()?, _text: Text::new(gl, font_manager)?, _line: AggFastPath::new()?, + _geo_quad_mesh: GeoQuadMesh::new()?, }) } @@ -67,6 +91,7 @@ impl Programs { self._ppi.program().compile(&self.gl)?; self._line.program().compile(&self.gl)?; self._text.program_mut().compile(&self.gl)?; + self._geo_quad_mesh.program().compile(&self.gl)?; Ok(()) } @@ -74,48 +99,13 @@ impl Programs { self._ppi.destroy(&self.gl)?; self._text.destroy(&self.gl)?; self._line.destroy(&self.gl)?; - Ok(()) - } - - pub fn ppi(&mut self) -> PPIModule { - PPIModule::new(&self.gl, &mut self._ppi, &mut self._text, &mut self._line) - } - - pub fn ppi_ref(&self) -> PPIModuleRef { - PPIModuleRef::new(&self.gl, &self._ppi, &self._text, &self._line) - } - - pub fn supported_modules<'a>( - &mut self, - data: &'a Value, - ) -> HashMap<&'a Arc, Vec> { - let mut result = HashMap::new(); - - for (k, d) in data.iter() { - result.insert(d, vec![ModuleRefs::PPI(self.ppi_ref())]); - } - - result - } - - pub fn draw_modules( - &mut self, - modules: &mut ModulePackage, - operation: &Operation, - viewport: &ViewPort, - ) -> Result<()> { - match &mut modules.modules { - _ModulePackage::PPI(ppi) => { - self.ppi().render(ppi, operation, viewport)?; - } - } - modules.need_update = false; + self._geo_quad_mesh.destroy(&self.gl)?; Ok(()) } } macro_rules! impl_module_package { - ($({$t:ty => $b: tt | $module:tt | $module_ref:tt | $c: ty}),+) => { + ($({$method:ident|$ref_method:ident ,$t:ty => $b: tt | $module:tt | $module_ref:tt | $c: ty}),+) => { pub enum Modules<'b, 'gl: 'b>{ $( @@ -123,9 +113,10 @@ macro_rules! impl_module_package { )+ } + #[derive(Clone,Copy)] pub enum ModuleRefs<'b, 'gl:'b>{ $( - $b($module_ref<'b,'gl>) + $b($module_ref<'b,'gl>), )+ } @@ -134,7 +125,7 @@ macro_rules! impl_module_package { match self { $( Modules::$b(m) => { - let cursor = m.load_data(data.into(), setting)?; + let cursor = m.load_data(data.try_into().unwrap(), setting)?; Ok(cursor.into()) } )+ @@ -241,11 +232,55 @@ macro_rules! impl_module_package { } } + impl Programs { + + pub fn draw_modules( + &mut self, + modules: &mut ModulePackage, + operation: &Operation, + viewport: &ViewPort, + ) -> Result<()> { + match &mut modules.modules { + $( + _ModulePackage::$b(m) => { + self.$method().render(m, operation, viewport)?; + } + )+ + } + modules.need_update = false; + Ok(()) + } + + pub fn supported_modules<'a>(&mut self, data: &'a Value) -> HashMap<&'a Arc, Vec> { + let mut result = HashMap::with_capacity(data.len()); + for (k, d) in data.iter() { + $( + { + if self.$ref_method().supported(d) { + let _ref = self.$ref_method(); + result.entry(d).and_modify(|v:&mut Vec| v.push(ModuleRefs::$b(_ref))).or_insert_with(|| vec![ModuleRefs::$b(_ref)]); + } + } + + )+ + + } + result + } + + } + }; } +program_impl!( + {ppi | ppi_ref, PPIModule | PPIModuleRef, (_ppi, _text, _line)}, + {geo_quad_mesh | geo_quad_mesh_ref, GeoQuadMeshModule | GeoQuadMeshModuleRef, (_geo_quad_mesh,_text, _line)} +); + impl_module_package!( - {PPIPackage => PPI | PPIModule | PPIModuleRef | PPIModuleConfigComponent} + {ppi|ppi_ref,PPIPackage => PPI | PPIModule | PPIModuleRef | PPIModuleConfigComponent}, + {geo_quad_mesh|geo_quad_mesh_ref,GeoQuadMeshPackage => GeoQuadMesh | GeoQuadMeshModule | GeoQuadMeshModuleRef | GeoQuadMeshModuleConfigComponent} ); impl ModulePackage { diff --git a/gi/src/pg/modules/geoquadmesh.rs b/gi/src/pg/modules/geoquadmesh.rs new file mode 100644 index 0000000..b764484 --- /dev/null +++ b/gi/src/pg/modules/geoquadmesh.rs @@ -0,0 +1,478 @@ +use crate::{ + graphics::{ + collections::agg_fast_path::{AggFastPath, AggFastPathConfig, Path}, + font::{Anchor, FontConfig, LineStyle, PositionText, Text, TextLine}, + geoquadmesh::{GeoQuadMesh, GeoQuadMeshConfig}, + ppi::{PPIConfig, PPI}, + transforms::plane::PlaneTrans, + AttaWithBuffer, Graphics, + }, + pg::SideBarInputMsg, + GL, +}; +use radarg_core::{config::Setting, CoordType, Data}; +use relm4::{ + adw::{self, prelude::*}, + gtk::{self, prelude::*}, + ComponentParts, SimpleComponent, +}; + +use core::f32; +use glow::HasContext; +use std::{ + cell::{RefCell, RefMut}, + rc::Rc, + sync::Arc, +}; +use tracker::track; + +use crate::font_manager::{FontSize, FontStyle}; + +use crate::pg::layout_type::ViewPort; +use crate::ui::operation::{self, Operation}; +use crate::{errors::*, font_manager::FontManager, ui::typ, utils::resources::ManagedResource}; + +use radarg_core::radarg_data::RadarGridData; + +use super::{Attach, Module, ModuleCursor}; +pub struct GeoQuadMeshModule<'b, 'gl: 'b> { + gl: &'gl GL, + geo_quad_mesh_program: &'b mut GeoQuadMesh, + line_program: &'b mut AggFastPath, + text_program: &'b mut Text, +} + +#[derive(Copy, Clone)] +pub struct GeoQuadMeshModuleRef<'b, 'gl: 'b> { + gl: &'gl GL, + geo_quad_mesh_program: &'b GeoQuadMesh, + line_program: &'b AggFastPath, + text_program: &'b Text, +} + +impl<'b, 'a: 'b> GeoQuadMeshModuleRef<'b, 'a> { + pub fn new(gl: &'a GL, quad: &'b GeoQuadMesh, text: &'b Text, line: &'b AggFastPath) -> Self { + let config = PPIConfig::default(); + Self { + gl, + geo_quad_mesh_program: quad, + text_program: text, + line_program: line, + } + } + + fn bind_line_pg( + &self, + attach: &mut Attach, + data: &RadarGridData, + config: &GeoQuadMeshModuleConfig, + ) -> Result<()> { + Ok(()) + } + + fn bind_tick( + &self, + attach: &mut Attach, + data: &RadarGridData, + config: &GeoQuadMeshModuleConfig, + ) -> Result<()> { + Ok(()) + } + + pub fn name(&self) -> &'static str { + "GeoQuadMesh" + } + + pub fn supported(&self, data: &Data) -> bool { + let data: std::result::Result<&RadarGridData, radarg_core::errors::DataError> = + data.try_into(); + if let Ok(data) = data { + match data.coord_type() { + Some(CoordType::Cartesian { .. }) => true, + _ => false, + } + } else { + false + } + } +} + +impl<'b, 'a: 'b> GeoQuadMeshModule<'b, 'a> { + pub fn new( + gl: &'a GL, + geo_quad_mesh: &'b mut GeoQuadMesh, + text: &'b mut Text, + line: &'b mut AggFastPath, + ) -> Self { + let config = PPIConfig::default(); + Self { + gl, + geo_quad_mesh_program: geo_quad_mesh, + text_program: text, + line_program: line, + } + } + + fn bind_geo_quad_mesh_pg( + &self, + attach: &mut Attach, + data: &RadarGridData, + config: &GeoQuadMeshModuleConfig, + ) -> Result<()> { + self.geo_quad_mesh_program + .bake(&self.gl, data, &config.to_quad_config(), attach) + } + + fn bind_line_pg( + &self, + attach: &mut Attach, + data: &RadarGridData, + config: &GeoQuadMeshModuleConfig, + ) -> Result<()> { + Ok(()) + } + + fn bind_tick( + &self, + attach: &mut Attach, + data: &RadarGridData, + config: &GeoQuadMeshModuleConfig, + ) -> Result<()> { + Ok(()) + } +} + +impl<'b, 'a: 'b> Module for GeoQuadMeshModule<'b, 'a> { + type Cursor = GeoQuadMeshPackage; + type Data = RadarGridData; + type Operation = PlaneTrans; + + const NAME: &'static str = "GeoQuadMesh"; + + fn render<'dt>( + &mut self, + cursor: &mut Self::Cursor, + operation: &Operation, + viewport: &ViewPort, + ) -> Result<()> { + unsafe { + let err = self.gl.get_error(); + if err != glow::NO_ERROR { + panic!("Error: {}", err); + } + } + // PPI Program + let data = &cursor.data; + let mut config = &mut cursor.ppi_config.borrow_mut(); + // Mount PPI Program + self.geo_quad_mesh_program.mount(&self.gl)?; + // Deal with the operation + operation.attach_with_program(&self.gl, self.geo_quad_mesh_program.program()); + let quad_attach = &mut cursor.quad_attach; + unsafe { + let err = self.gl.get_error(); + if err != glow::NO_ERROR { + panic!("Error: {}", err); + } + } + + // Update the config + self.geo_quad_mesh_program + .set_config(&self.gl, &config.to_quad_config())?; + unsafe { + let err = self.gl.get_error(); + if err != glow::NO_ERROR { + panic!("Error: {}", err); + } + } + + // Quad Draw + quad_attach.bind_self(self.geo_quad_mesh_program.program_ref()); + self.geo_quad_mesh_program + .draw(&self.gl, quad_attach.len())?; + + // quad_attach.unbind_self(); + + // Unmount Program + self.geo_quad_mesh_program.unmount(&self.gl)?; + + config.reset(); + + Ok(()) + } + + fn load_data<'dt>(&self, data: &Arc, setting: &Setting) -> Result { + // Init the memory + let (vao, vbo, ebo, texs) = self.geo_quad_mesh_program.init(&self.gl); + let mut quad_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, texs, None); + + // Get the data info + let (hgt_range, lat_range, lon_range, dpi, t) = + self.geo_quad_mesh_program.data_info(&data)?; + + println!("name: {}", data.info.value_name); + + // Find the color map + let cmap = setting.find(&t); + + // Check if the color map is valid + if cmap.is_none() { + return Err(Error::InvalidDataType(format!( + "{}'s colormap is not found", + data.info.value_name + ))); + } + let cmap = cmap.unwrap(); + + // Init the memory for the line program + let (vao, vbo, ebo, texs) = self.line_program.init(&self.gl); + let mut line_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, texs, None); + + // Tick Label + let (vao, vbo, ebo, texs) = self.text_program.init(&self.gl); + let mut tick_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, texs, None); + + let mut config = GeoQuadMeshModuleConfig::default(); + config.alt_dpi = dpi[0]; + config.lat_dpi = dpi[1]; + config.lon_dpi = dpi[2]; + + config.lat_range = lat_range; + config.lon_range = lon_range; + config.hgt_range = hgt_range.unwrap_or_default(); + config.colors = cmap.color().unwrap(); + config.color_range = cmap.value_range(); + + // Bind the data + self.bind_geo_quad_mesh_pg(&mut quad_attach, data, &config); + + // self.bind_line_pg(&mut line_attach, &data, &config); + // self.bind_tick(&mut tick_attach, &data.borrow(), &config); + + let data = data.clone(); + Ok(Self::Cursor::new( + config, + quad_attach, + line_attach, + tick_attach, + data, + )) + } + fn supported(&self, data: &radarg_core::Data) -> bool { + let data: std::result::Result<&RadarGridData, radarg_core::errors::DataError> = + data.try_into(); + if let Ok(data) = data { + match data.coord_type() { + Some(CoordType::Cartesian { .. }) => true, + _ => false, + } + } else { + false + } + } +} + +#[derive(Debug)] +pub struct GeoQuadMeshPackage { + draw_helper: bool, + ppi_config: Rc>, + quad_attach: Attach, + line_attach: Attach, + tick_attach: Attach, + data: Arc, +} + +impl GeoQuadMeshPackage { + fn new( + ppi_config: GeoQuadMeshModuleConfig, + quad_attach: Attach, + line_attach: Attach, + tick_attach: Attach, + data: Arc, + ) -> Self { + Self { + draw_helper: true, + ppi_config: Rc::new(RefCell::new(ppi_config)), + quad_attach, + line_attach, + tick_attach, + data, + } + } +} + +#[track] +#[derive(PartialEq, Debug, Clone)] +pub struct GeoQuadMeshModuleConfig { + pub ticks: bool, + pub line_color: [f32; 4], + pub line_width: f32, + pub line_antialias: f32, + pub layer: usize, + pub colors: Vec<[u8; 4]>, + pub color_range: [f32; 2], + pub is_three_d: bool, + + pub tick_label_color: [f32; 4], + pub tick_label_size: f32, + #[do_not_track] + pub lat_range: [f32; 2], + + #[do_not_track] + pub lon_range: [f32; 2], + + #[do_not_track] + pub hgt_range: [f32; 2], + + #[do_not_track] + pub unvalid_value: f32, + #[do_not_track] + pub max_layer: usize, + #[do_not_track] + pub lat_dpi: f32, + #[do_not_track] + pub alt_dpi: f32, + #[do_not_track] + pub lon_dpi: f32, +} + +impl Default for GeoQuadMeshModuleConfig { + fn default() -> Self { + Self { + ticks: true, + line_color: [1.0, 1.0, 1.0, 1.0], + line_width: 1.5, + line_antialias: 0.5, + layer: 0, + colors: vec![], + color_range: [0.0, 0.0], + is_three_d: true, + unvalid_value: 0.0, + tick_label_color: [1.0, 1.0, 1.0, 1.0], + tick_label_size: 24.0, + max_layer: 0, + lat_dpi: 0.0, + alt_dpi: 0.0, + lon_dpi: 0.0, + tracker: 0, + lat_range: [0.0, 0.0], + lon_range: [0.0, 0.0], + hgt_range: [0.0, 0.0], + } + } +} + +impl GeoQuadMeshModuleConfig { + fn to_quad_config(&self) -> GeoQuadMeshConfig { + GeoQuadMeshConfig { + unvalid_value: self.unvalid_value, + layer: self.layer, + colors: self.colors.clone(), + color_range: self.color_range, + lon_dpi: self.lon_dpi, + lat_dpi: self.lat_dpi, + alt_dpi: self.alt_dpi, + lat_range: self.lat_range, + lon_range: self.lon_range, + alt_range: self.hgt_range, + } + } + + fn to_line_config(&self) -> AggFastPathConfig { + AggFastPathConfig { + color: self.line_color, + linewidth: self.line_width, + antialias: self.line_antialias, + } + } + + fn to_font_config(&self) -> FontConfig { + let line_style = LineStyle::default(); + let mut font_style = FontStyle::default(); + font_style.size = FontSize::Absolute(self.tick_label_size); + font_style.color = self.tick_label_color; + FontConfig::Textline(line_style, font_style) + } +} + +pub struct GeoQuadMeshModuleConfigComponent { + config: Rc>, +} + +#[derive(Debug)] +pub enum OutputMsg { + Refresh, +} + +#[relm4::component(pub)] +impl SimpleComponent for GeoQuadMeshModuleConfigComponent { + type Widgets = GeoQuadMeshModuleConfigComponentWidgets; + type Init = Rc>; + type Input = (); + type Output = OutputMsg; + + view! { + adw::PreferencesPage { + adw::PreferencesGroup { + set_title:"PPI Config", + set_hexpand:true, + adw::SwitchRow { + set_title:"Ticks", + set_active: init_config.ticks, + connect_active_notify[sender, config_ref] => move |this| { + let active = this.is_active(); + config_ref.borrow_mut().set_ticks(active); + sender.output(OutputMsg::Refresh); + } + }, + + } + + } + } + + fn init( + init: Self::Init, + root: Self::Root, + sender: relm4::ComponentSender, + ) -> relm4::ComponentParts { + let model = GeoQuadMeshModuleConfigComponent { + config: init.clone(), + }; + let init_config = init.borrow().clone(); + let config_ref = model.config.clone(); + + let widgets = view_output!(); + + ComponentParts { model, widgets } + } + + fn update(&mut self, message: Self::Input, sender: relm4::ComponentSender) {} +} + +impl ModuleCursor for GeoQuadMeshPackage { + type Module<'rf, 'gl: 'rf> = GeoQuadMeshModule<'rf, 'gl>; + type Config = GeoQuadMeshModuleConfig; + type Data = RadarGridData; + type Component = GeoQuadMeshModuleConfigComponent; + + type ComponentOutput = OutputMsg; + + fn set_config(&mut self, f: F) + where + F: FnOnce(&mut Self::Config), + { + let mut config = self.ppi_config.borrow_mut(); + f(&mut config); + } + + fn component_config(&self) -> Rc> { + self.ppi_config.clone() + } + + fn component_sender(&self) -> Box crate::pg::SideBarInputMsg> { + Box::new(|c| match c { + OutputMsg::Refresh => SideBarInputMsg::Refresh, + }) + } +} diff --git a/gi/src/pg/modules/mod.rs b/gi/src/pg/modules/mod.rs index 8e3314c..38b15de 100644 --- a/gi/src/pg/modules/mod.rs +++ b/gi/src/pg/modules/mod.rs @@ -1,6 +1,7 @@ use crate::{ + components::Program, graphics::AttachWithIO, - resources::{RcGlRcBuffer, RcGlRcVertexArray}, + resources::{RcGlRcBuffer, RcGlRcResource, RcGlRcVertexArray}, ui::{ operation::{self, Operation}, typ::CameraOP, @@ -9,11 +10,16 @@ use crate::{ GL, }; use femtovg::{renderer::OpenGl, Canvas}; -use glow::{HasContext, NativeBuffer, NativeVertexArray}; -use radarg_core::config::Setting; +use glow::{HasContext, NativeBuffer, NativeTexture, NativeVertexArray}; +use radarg_core::{config::Setting, Data}; use std::{cell::RefCell, path::Component, rc::Rc, sync::Arc}; +mod geoquadmesh; mod ppi; use crate::errors::*; +pub use geoquadmesh::{ + GeoQuadMeshModule, GeoQuadMeshModuleConfig, GeoQuadMeshModuleConfigComponent, + GeoQuadMeshModuleConfigComponentWidgets, GeoQuadMeshModuleRef, GeoQuadMeshPackage, +}; pub use ppi::{ PPIModule, PPIModuleConfigComponent, PPIModuleConfigComponentWidgets, PPIModuleRef, PPIPackage, }; @@ -23,40 +29,54 @@ use relm4::Component as RComponent; use super::{layout_type::ViewPort, SideBarInputMsg}; #[derive(Clone, Debug)] -struct Attach { +pub(crate) struct Attach { gl: GL, pub vao: RcGlRcVertexArray, pub vbo: RcGlRcBuffer, pub ebo: Option, + pub textures: Option>, pub len: i32, } impl Attach { - fn new( + pub fn new( gl: GL, vao: NativeVertexArray, vbo: NativeBuffer, ebo: Option, + texs: Option, len: Option, ) -> Self { let vao = gl.create_resource_rc_with(vao); let vbo = gl.create_resource_rc_with(vbo); let ebo = ebo.map(|ebo| gl.create_resource_rc_with(ebo)); + let textures = texs.map(|texs| gl.create_resource_rc_with(texs)); + Self { gl, vao, vbo, ebo, + textures, len: len.unwrap_or(0), } } - fn bind_self(&self) { + fn bind_self(&self, program: &Program) { self.vao.bind(glow::VERTEX_ARRAY); self.vbo.bind(glow::ARRAY_BUFFER); if let Some(ebo) = self.ebo.as_ref() { ebo.bind(glow::ELEMENT_ARRAY_BUFFER); } + + if let Some(tex) = self.textures.as_ref() { + unsafe { + self.gl.active_texture(glow::TEXTURE0); + self.gl.bind_texture(glow::TEXTURE_3D, Some(tex.native())); + let loc = program.get_uniform_location(&self.gl, "_texture"); + self.gl.uniform_1_i32(loc.as_ref(), 0); + } + } } fn unbind_self(&self) { @@ -67,7 +87,7 @@ impl Attach { } } - fn bind_data(&mut self, vbo: &Vec, ebo: Option<&Vec>, len: i32, usage: u32) { + pub fn bind_data(&mut self, vbo: &Vec, ebo: Option<&Vec>, len: i32, usage: u32) { use bytemuck::cast_slice; self.vbo.bind(glow::ARRAY_BUFFER); unsafe { @@ -108,6 +128,8 @@ pub trait Module: Sized { ) -> Result<()>; fn load_data<'dt>(&self, data: &Arc, setting: &Setting) -> Result; + + fn supported(&self, data: &Data) -> bool; } pub trait ModuleCursor { diff --git a/gi/src/pg/modules/ppi.rs b/gi/src/pg/modules/ppi.rs index 3884e77..a7f8b1e 100644 --- a/gi/src/pg/modules/ppi.rs +++ b/gi/src/pg/modules/ppi.rs @@ -9,7 +9,7 @@ use crate::{ pg::SideBarInputMsg, GL, }; -use radarg_core::config::Setting; +use radarg_core::{config::Setting, CoordType}; use relm4::{ adw::{self, prelude::*}, gtk::{self, prelude::*}, @@ -41,6 +41,7 @@ pub struct PPIModule<'b, 'gl: 'b> { text_program: &'b mut Text, } +#[derive(Clone, Copy)] pub struct PPIModuleRef<'b, 'gl: 'b> { gl: &'gl GL, ppi_program: &'b PPI, @@ -65,10 +66,8 @@ impl<'b, 'a: 'b> PPIModuleRef<'b, 'a> { data: &RadarGridData, config: &PPIModuleConfig, ) -> Result<()> { - let (vbo, ebo, len) = self - .ppi_program - .bake(&self.gl, data, &config.to_ppi_config())?; - attach.bind_data(&vbo, ebo.as_ref(), len, glow::DYNAMIC_DRAW); + self.ppi_program + .bake(&self.gl, data, &config.to_ppi_config(), attach)?; Ok(()) } @@ -126,8 +125,8 @@ impl<'b, 'a: 'b> PPIModuleRef<'b, 'a> { paths.push(path); } - let (vbo, ebo, len) = self.line_program.bake(&self.gl, &paths, &raw_config)?; - attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW); + self.line_program + .bake(&self.gl, &paths, &raw_config, attach)?; Ok(()) } @@ -145,10 +144,9 @@ impl<'b, 'a: 'b> PPIModuleRef<'b, 'a> { let position_text = PositionText::new(new_text, [0.0, 0.0, 0.0], Anchor::BottomCenter); let text_pg_config = config.to_font_config(); - let (vbo, ebo, len) = - self.text_program - .bake(&self.gl, &position_text, &text_pg_config)?; - attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW); + + self.text_program + .bake(&self.gl, &position_text, &text_pg_config, attach)?; } } @@ -158,6 +156,19 @@ impl<'b, 'a: 'b> PPIModuleRef<'b, 'a> { pub fn name(&self) -> &'static str { "PPI" } + + pub fn supported(&self, data: &radarg_core::Data) -> bool { + let data: std::result::Result<&RadarGridData, radarg_core::errors::DataError> = + data.try_into(); + if let Ok(data) = data { + match data.coord_type() { + Some(CoordType::Polar { .. }) => true, + _ => false, + } + } else { + false + } + } } impl<'b, 'a: 'b> PPIModule<'b, 'a> { @@ -182,11 +193,8 @@ impl<'b, 'a: 'b> PPIModule<'b, 'a> { data: &RadarGridData, config: &PPIModuleConfig, ) -> Result<()> { - let (vbo, ebo, len) = self - .ppi_program - .bake(&self.gl, data, &config.to_ppi_config())?; - attach.bind_data(&vbo, ebo.as_ref(), len, glow::DYNAMIC_DRAW); - Ok(()) + self.ppi_program + .bake(&self.gl, data, &config.to_ppi_config(), attach) } fn bind_line_pg( @@ -243,8 +251,8 @@ impl<'b, 'a: 'b> PPIModule<'b, 'a> { paths.push(path); } - let (vbo, ebo, len) = self.line_program.bake(&self.gl, &paths, &raw_config)?; - attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW); + self.line_program + .bake(&self.gl, &paths, &raw_config, attach)?; Ok(()) } @@ -262,10 +270,9 @@ impl<'b, 'a: 'b> PPIModule<'b, 'a> { let position_text = PositionText::new(new_text, [0.0, 0.0, 0.0], Anchor::BottomCenter); let text_pg_config = config.to_font_config(); - let (vbo, ebo, len) = - self.text_program - .bake(&self.gl, &position_text, &text_pg_config)?; - attach.bind_data(&vbo, ebo.as_ref(), len, glow::STATIC_DRAW); + + self.text_program + .bake(&self.gl, &position_text, &text_pg_config, attach)?; } } @@ -307,7 +314,7 @@ impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { } // PPI Draw - ppi_attach.bind_self(); + ppi_attach.bind_self(&self.ppi_program.program_ref()); self.ppi_program.draw(&self.gl, ppi_attach.len())?; ppi_attach.unbind_self(); @@ -336,7 +343,7 @@ impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { // PPI Tick Draw let attach = &mut cursor.line_attach; - attach.bind_self(); + attach.bind_self(self.line_program.program_ref()); self.line_program.draw(&self.gl, attach.len())?; attach.unbind_self(); @@ -351,7 +358,7 @@ impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { self.text_program .set_config(&self.gl, &config.to_font_config()); - tick_attach.bind_self(); + tick_attach.bind_self(self.text_program.program_ref()); self.text_program.draw(&self.gl, tick_attach.len())?; tick_attach.unbind_self(); @@ -362,8 +369,8 @@ impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { fn load_data<'dt>(&self, data: &Arc, setting: &Setting) -> Result { // Init the memory - let (vao, vbo, ebo) = self.ppi_program.init(&self.gl); - let mut ppi_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, None); + let (vao, vbo, ebo, texs) = self.ppi_program.init(&self.gl); + let mut ppi_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, texs, None); // Get the data info let (r, a, t, max_layer, unvalid) = self.ppi_program.data_info(&data)?; @@ -383,12 +390,12 @@ impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { let cmap = cmap.unwrap(); // Init the memory for the line program - let (vao, vbo, ebo) = self.line_program.init(&self.gl); - let mut line_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, None); + let (vao, vbo, ebo, texs) = self.line_program.init(&self.gl); + let mut line_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, texs, None); // Tick Label - let (vao, vbo, ebo) = self.text_program.init(&self.gl); - let mut tick_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, None); + let (vao, vbo, ebo, texs) = self.text_program.init(&self.gl); + let mut tick_attach = Attach::new(self.gl.clone(), vao, vbo, ebo, texs, None); let mut config = PPIModuleConfig::default(); config.rdpi = r; @@ -412,6 +419,19 @@ impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { data, )) } + + fn supported(&self, data: &radarg_core::Data) -> bool { + let data: std::result::Result<&RadarGridData, radarg_core::errors::DataError> = + data.try_into(); + if let Ok(data) = data { + match data.coord_type() { + Some(CoordType::Polar { .. }) => true, + _ => false, + } + } else { + false + } + } } #[derive(Debug)] diff --git a/gi/src/shaders/geoquadmesh.rs b/gi/src/shaders/geoquadmesh.rs new file mode 100644 index 0000000..c16a775 --- /dev/null +++ b/gi/src/shaders/geoquadmesh.rs @@ -0,0 +1,91 @@ +use super::colormap::ColorMap; +use super::trackball::Trackball; +use super::CodePiece; +use crate::impl_code_piece; +use glsl::syntax::{ShaderStage, TranslationUnit}; +use glsl::transpiler::glsl::show_translation_unit; +use glsl_quasiquote::glsl; + +use super::proj::Mercator; + +pub struct GeoQuadMeshVertex(pub ShaderStage); +pub struct GeoQuadMeshGeom(pub ShaderStage); +pub struct GeoQuadMeshFragment(pub ShaderStage); + +impl GeoQuadMeshVertex { + pub fn new() -> Self { + let mut mercator = Mercator::new().0; + let mut trackball = Trackball::new().0; + let raw = glsl! { + // Position (x, y, z) --- x, y are longitude and latitude, z is the Altitude. + layout(location = 0) in vec3 in_position; + layout(location = 1) in vec3 relative_position; + + out vec2 projected_meter; + + void main() { + vec2 projected = mercatorProject(in_position.xy); + projected_meter = projected; + gl_Position = transform(position(relative_position)); + } + + }; + + mercator.extend(trackball); + mercator.extend(raw); + Self(mercator) + } +} + +impl GeoQuadMeshFragment { + pub fn new() -> Self { + let mut mercator = Mercator::new().0; + + let color_map = ColorMap::new().0; + + let raw = glsl! { + // lon_s,lon_e, lat_s, lat_e + uniform vec4 tex_conf; + + // lon_reso, lat_reso, alt_reso + uniform vec4 data_info; + + // Current layer + uniform float layer; + + out vec4 fragColor; + in vec2 projected_meter; + + // data array + uniform sampler3D _texture; + + void main() { + + vec2 lon_lat = mercatorInverse(projected_meter); + + vec3 tex_coord = vec3(lon_lat.x / tex_conf.z, lon_lat.y / tex_conf.w, layer / tex_conf.x); + + float value = texture(_texture, tex_coord).r; + + vec4 color = linear_colormap(value); + + fragColor = vec4(1.0,1.0,1.0,1.0); + + // if (color.a < 0.1) { + // discard; + // } else { + // fragColor = color; + // } + + } + }; + + mercator.extend(color_map); + mercator.extend(raw); + + Self(mercator) + } +} + +impl_code_piece!(GeoQuadMeshVertex, 0); +impl_code_piece!(GeoQuadMeshFragment, 0); diff --git a/gi/src/shaders/mod.rs b/gi/src/shaders/mod.rs index 84ca983..74a9b07 100644 --- a/gi/src/shaders/mod.rs +++ b/gi/src/shaders/mod.rs @@ -1,9 +1,11 @@ pub mod agg_path; pub mod colormap; pub mod font; +pub mod geoquadmesh; pub mod math; pub mod polar; pub mod ppi; +pub mod proj; pub mod trackball; use glsl::{ syntax::{ShaderStage, TranslationUnit}, diff --git a/gi/src/shaders/proj.rs b/gi/src/shaders/proj.rs new file mode 100644 index 0000000..fbd943e --- /dev/null +++ b/gi/src/shaders/proj.rs @@ -0,0 +1,45 @@ +use super::CodePiece; +use crate::impl_code_piece; +use glsl::syntax::ShaderStage; +use glsl::syntax::TranslationUnit; +use glsl::transpiler::glsl::show_translation_unit; +use glsl_quasiquote::glsl; + +pub struct Mercator(pub ShaderStage); + +impl Mercator { + pub fn new() -> Self { + let raw = glsl! { + + uniform float R; + + vec2 mercatorProject(vec2 loc) { + // 将经纬度从度转换为弧度 + float lonRad = radians(loc.x); + float latRad = radians(loc.y); + + // X轴投影 (线性映射经度) + float x = R * lonRad; + + // Y轴投影 (非线性映射纬度) + float y = R * log(tan(latRad / 2.0 + radians(45.0))); + + return vec2(x, y); + } + + vec2 mercatorInverse(vec2 loc) { + // 计算经度 (lon),线性反映射 + float lon = degrees(loc.x / R); // 将弧度转换为度数 + + // 计算纬度 (lat),非线性反映射 + float lat = degrees(2.0 * atan(exp(loc.y / R)) - radians(90.0)); + + return vec2(lon, lat); // 返回经纬度 (lon, lat) + } + }; + + Self(raw) + } +} + +impl_code_piece!(Mercator, 0); diff --git a/radar-g/src/components/monitor/messages.rs b/radar-g/src/components/monitor/messages.rs index 5f3ff41..e066e5d 100644 --- a/radar-g/src/components/monitor/messages.rs +++ b/radar-g/src/components/monitor/messages.rs @@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc}; pub enum MonitorInputMsg { PushData(Value), - Draw(Arc), + Draw(&'static str, Arc), Prepare(Vec>), KeyPress(u32), KeyRelease(u32), @@ -18,7 +18,7 @@ impl Debug for MonitorInputMsg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { MonitorInputMsg::QueueDraw => write!(f, "MonitorInputMsg::QueueDraw"), - MonitorInputMsg::Draw(data) => { + MonitorInputMsg::Draw(module, data) => { write!(f, "MonitorInputMsg::Draw({:?})", data) } MonitorInputMsg::Prepare(data) => { diff --git a/radar-g/src/components/monitor/monitor.rs b/radar-g/src/components/monitor/monitor.rs index 5bf8a43..ccf3a36 100644 --- a/radar-g/src/components/monitor/monitor.rs +++ b/radar-g/src/components/monitor/monitor.rs @@ -9,7 +9,7 @@ use crate::{ }; use geo::k_nearest_concave_hull; use gi::graphics::transforms::plane::PlaneTrans; -use gi::pg::{Module, ModulePackage}; +use gi::pg::{GeoQuadMeshModule, Module, ModulePackage, PPIModule}; use gi::ui::operation::Operation; use gtk::glib::clone; @@ -29,6 +29,43 @@ use slippy_map_tiles::Tile; use tokio::task; use tracing::instrument::WithSubscriber; +macro_rules! impl_draw { + ($slf:ident,$module_name:ident,$widgets:ident,$data:ident, $sender:ident, $({$module:ty | $method:ident }),+) => { + $widgets.renderer.get_gi(|gi| { + let data: &Data = &*$data; + + match $module_name { + $( + <$module>::NAME => { + match gi.program().$method().load_data(data.try_into().unwrap(), &SETTING) { + Ok(package) => { + let package = Rc::new(RefCell::new(package.into())); + $slf.update_module_packages(|p| { + p.clear(); + p.push((data.description.clone(), package)); + }); + $sender.output(MonitorOutputMsg::Attached($slf.module_packages.clone())); + } + Err(e) => { + error!("Error loading data: {:?}", e); + $sender.output(MonitorOutputMsg::Alert(e.to_string(), format!("Close"))); + } + } + } + )+ + _ => { + error!("Unsupported module: {}", $module_name); + $sender.output(MonitorOutputMsg::Alert( + format!("Unsupported module: {}", $module_name), + format!("Close"), + )); + } + } + $sender.input(MonitorInputMsg::QueueDraw); + }); + }; +} + #[derive(Debug)] pub enum MonitorCommand { LoadedTile, @@ -116,7 +153,10 @@ impl Component for MonitorModel { } if result.len() == 1 && result.values().next().unwrap().len() == 1 { - sender.input(MonitorInputMsg::Draw(data.values().next().unwrap().clone())); + sender.input(MonitorInputMsg::Draw( + result.values().next().unwrap().first().unwrap().module_name, + data.values().next().unwrap().clone(), + )); } else { let new_sender = sender.clone(); let dialog = Dialog::builder().launch(result).forward( @@ -155,18 +195,15 @@ impl Component for MonitorModel { widgets.renderer.queue_render(); } - // MonitorInputMsg::KeyPress(key) => { - // widgets.renderer.set_key_pressed(key); - // } - - // MonitorInputMsg::KeyRelease(key) => { - // widgets.renderer.set_key_released(key); - // } MonitorInputMsg::Prepare(datas) => widgets.renderer.get_gi(|gi| { let mut packages: Vec<(String, Rc>)> = vec![]; for data in datas { let data = &*data; - match gi.program().ppi().load_data(data.into(), &SETTING) { + match gi + .program() + .ppi() + .load_data(data.try_into().unwrap(), &SETTING) + { Ok(package) => { let package = Rc::new(RefCell::new(package.into())); packages.push((data.description.clone(), package)); @@ -187,25 +224,10 @@ impl Component for MonitorModel { sender.output(MonitorOutputMsg::Attached(self.module_packages.clone())); sender.output(MonitorOutputMsg::DialogClose); }), - MonitorInputMsg::Draw(ref data) => { - // widgets.renderer.get_gi(|gi| { - // let data: &Data = &*data; - - // match gi.program().ppi().load_data(data.into(), &SETTING) { - // Ok(package) => { - // let package = Rc::new(RefCell::new(package.into())); - // self.set_module_packages(vec![package.clone()]); - // sender.output(MonitorOutputMsg::Attached(package)); - // } - // Err(e) => { - // error!("Error loading data: {:?}", e); - // sender.output(MonitorOutputMsg::Alert(e.to_string(), format!("Close"))); - // } - // } - - // sender.input(MonitorInputMsg::QueueDraw); - // }); - // sender.output(MonitorOutputMsg::DialogClose); + MonitorInputMsg::Draw(module, ref data) => { + impl_draw!(self, module, widgets, data, sender, { PPIModule | ppi }, { + GeoQuadMeshModule | geo_quad_mesh + }); } MonitorInputMsg::KeyPress(key) => { widgets.renderer.set_key_pressed(key); diff --git a/radar-g/src/widgets/render/imp.rs b/radar-g/src/widgets/render/imp.rs index 2642991..416373f 100644 --- a/radar-g/src/widgets/render/imp.rs +++ b/radar-g/src/widgets/render/imp.rs @@ -129,7 +129,6 @@ impl ObjectImpl for Render { impl WidgetImpl for Render { fn realize(&self) { self.parent_realize(); - self.ensure_canvas(); } fn unrealize(&self) { self.obj().make_current(); @@ -158,6 +157,7 @@ impl GLAreaImpl for Render { } fn render(&self, context: >k::gdk::GLContext) -> glib::Propagation { + self.ensure_canvas(); let viewport = self.viewport.borrow(); let viewport = viewport.as_ref().unwrap(); @@ -172,9 +172,22 @@ impl GLAreaImpl for Render { } if let Some(gi) = gi.as_mut() { + let gl = &gi.context.gl; + unsafe { + let err = gl.get_error(); + if err != glow::NO_ERROR { + panic!("GL Error: {:?}", err); + } + } let mut operation = self.opeartion.borrow_mut(); let viewport = self.viewport.borrow(); operation.deal_io(&viewport.as_ref().unwrap(), &self.io.borrow()); + unsafe { + let err = gl.get_error(); + if err != glow::NO_ERROR { + panic!("GL Error: {:?}", err); + } + } gi.render( &mut *self.modules.borrow_mut(), @@ -183,7 +196,7 @@ impl GLAreaImpl for Render { ); } - viewport.unbind(); + // viewport.unbind(); self.io.borrow_mut().reset(); @@ -195,10 +208,11 @@ impl Render { fn ensure_canvas(&self) { use gi::{App as GI, Helper, GL}; let widget = self.obj(); - widget.make_current(); - widget.attach_buffers(); + if self.gi.borrow().is_none() { info!("Creating canvas"); + widget.make_current(); + widget.attach_buffers(); let (mut gi, viewport) = unsafe { static LOAD_FN: fn(&str) -> *const std::ffi::c_void = |s| epoxy::get_proc_addr(s) as *const _; diff --git a/radar-g/src/widgets/render/mod.rs b/radar-g/src/widgets/render/mod.rs index 31821f2..e552f1e 100644 --- a/radar-g/src/widgets/render/mod.rs +++ b/radar-g/src/widgets/render/mod.rs @@ -195,6 +195,6 @@ impl Render { pub fn draw(&self, packages: &Vec>>) { let mut modules = self.imp().modules.borrow_mut(); *(&mut *modules) = packages.clone(); - self.queue_draw(); + self.queue_render(); } } diff --git a/radarg_core/src/radarg_data/mod.rs b/radarg_core/src/radarg_data/mod.rs index dd064f2..fce6fb9 100644 --- a/radarg_core/src/radarg_data/mod.rs +++ b/radarg_core/src/radarg_data/mod.rs @@ -142,6 +142,15 @@ pub enum CoordType<'a> { }, } +impl Display for CoordType<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CoordType::Cartesian { .. } => write!(f, "Cartesian"), + CoordType::Polar { .. } => write!(f, "Polar"), + } + } +} + impl Display for RadarGridData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "shape: {:?}, dimension_names: {:?}, fill_value: {}, datetime: {:?}, value_description: {:?}, value_name: {}, maybe_probe_data_type: {:?}, dimension_description: {}", @@ -300,11 +309,33 @@ impl From for Data { // } // } -impl<'a> From<&'a Data> for &'a Arc { - fn from(data: &'a Data) -> Self { - match &data._data { - _Data::RadarGridData(v) => v, - _ => panic!("Unsupported data type"), +// impl<'a> From<&'a Data> for &'a Arc { +// fn from(data: &'a Data) -> Self { +// match &data._data { +// _Data::RadarGridData(v) => v, +// _ => panic!("Unsupported data type"), +// } +// } +// } + +impl<'a> TryFrom<&'a Data> for &'a Arc { + type Error = crate::errors::DataError; + + fn try_from(value: &'a Data) -> Result { + match &value._data { + _Data::RadarGridData(v) => Ok(&v), + _ => Err(crate::errors::DataError::FormatError), + } + } +} + +impl<'a> TryFrom<&'a Data> for &'a RadarGridData { + type Error = crate::errors::DataError; + + fn try_from(value: &'a Data) -> Result { + match &value._data { + _Data::RadarGridData(v) => Ok(&*v), + _ => Err(crate::errors::DataError::FormatError), } } }