use collections::agg_fast_path::{AggFastPath, AggFastPathConfig, Path}; use core::f32; use font::{FontConfig, LineStyle, PositionText, TextLine}; use glow::HasContext; use std::rc::Rc; use tracker::track; use transforms::viewport; use crate::font_manager::{FontSize, FontStyle}; use crate::graphics::{font::Text, *}; use crate::pg::layout_type::ViewPort; use crate::ui::helper_components::colorbar::colorbar; use crate::ui::operation::{self, Operation}; use crate::SETTING; use crate::{ data_loader::Data, errors::*, font_manager::FontManager, ui::typ, utils::{cache::CachedData, resources::ManagedResource}, }; use imgui::VerticalSlider; use ppi::{PPIConfig, PPI}; use threed::Trackball; use super::{Attach, Module, ModuleCursor}; pub struct PPIModule<'b, 'a: 'b> { gl: &'a glow::Context, ppi_program: &'b mut PPI, line_program: &'b mut AggFastPath, text_program: &'b mut Text<'a>, } impl<'b, 'a: 'b> PPIModule<'b, 'a> { pub fn new( gl: &'a glow::Context, ppi: &'b mut PPI, text: &'b mut Text<'a>, line: &'b mut AggFastPath, ) -> Result { let config = PPIConfig::default(); Ok(Self { gl, ppi_program: ppi, text_program: text, line_program: line, }) } fn bind_ppi_pg( &self, attach: &mut Attach, data: &Data, config: &PPIModuleConfig, ) -> Result<()> { let (vbo, ebo, len) = self.ppi_program.bake(data, &config.to_ppi_config())?; attach.bind_data(&vbo, ebo.as_ref(), len); Ok(()) } fn bind_line_pg( &self, attach: &mut Attach, data: &Data, config: &PPIModuleConfig, ) -> Result<()> { let config = config.to_line_config(); // Create the path let mut path = Path::new(true); // Will be changed in the future let outskirt = 10.0; for seg in 0..500 { let angle = 2f32 * f32::consts::PI / 500.0 * seg as f32; let x = (angle.cos() * outskirt) as f32; let y = (angle.sin() * outskirt) as f32; path.push([x, y, 0.0]); } path.finish(); let (vbo, ebo, len) = self.line_program.bake(&vec![path], &config)?; attach.bind_data(&vbo, ebo.as_ref(), len); Ok(()) } fn bind_tick(&self, attach: &mut Attach, data: &Data, config: &PPIModuleConfig) -> Result<()> { let font_style = config.to_font_config(); match font_style { FontConfig::Textline(line_style, font_style) => { let new_text = TextLine::new("ABC", Some(font_style), None); let position_text = PositionText::new(new_text, [0.0, 0.0, 0.0], font::Anchor::BottomCenter); let text_pg_config = config.to_font_config(); let (vbo, ebo, len) = self.text_program.bake(&position_text, &text_pg_config)?; attach.bind_data(&vbo, ebo.as_ref(), len); } } Ok(()) } } impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> { type Cursor = PPIPackage<'a>; type Data = Data; type Operation = Trackball; fn render<'dt>( &mut self, cursor: &mut Self::Cursor, operation: &Operation, viewport: &ViewPort, ) -> Result<()> { // Mount PPI Program self.ppi_program.mount(&self.gl)?; // Deal with the operation operation.attach_with_program(&self.gl, self.ppi_program.program()); // PPI Program let ppi_attach = &mut cursor.ppi_attach; let data = &cursor.ppi_data.borrow(); let config = &mut cursor.ppi_config; // Update the config self.ppi_program .set_config(&self.gl, &config.to_ppi_config())?; // if the layer is changed, we need to rebind the data if config.changed_layer() { self.bind_ppi_pg(ppi_attach, data, config); } // PPI Draw ppi_attach.bind_self(); self.ppi_program.draw(&self.gl, ppi_attach.len())?; ppi_attach.unbind_self(); // Unmount PPI Program self.ppi_program.unmount(&self.gl)?; // Mount Line Program self.line_program.mount(&self.gl)?; // Deal with the operation operation.attach_with_program(&self.gl, self.line_program.program()); // Set the viewport, this is important self.line_program.set_viewport(&self.gl, viewport.size()); // Update the config self.line_program .set_config(&self.gl, &config.to_line_config()); // PPI Tick Draw let attach = &mut cursor.line_attach; attach.bind_self(); self.line_program.draw(&self.gl, attach.len())?; attach.unbind_self(); self.line_program.unmount(&self.gl)?; self.text_program.mount(&self.gl); let tick_attach = &mut cursor.tick_attach; // Deal with the operation operation.attach_with_program(&self.gl, self.text_program.program_mut()); self.text_program.set_viewport(viewport.size()); self.text_program .set_config(&self.gl, &config.to_font_config()); tick_attach.bind_self(); self.text_program.draw(&self.gl, tick_attach.len())?; tick_attach.unbind_self(); config.reset(); Ok(()) } fn load_data<'dt>(&self, data: &CachedData) -> Result { let _data = data.borrow(); // Check if the data is valid if _data.blocks.len() == 0 { return Err(Error::InvalidDataType); } // Init the memory let (vao, vbo, ebo) = self.ppi_program.init(&self.gl); let mut ppi_attach = Attach::new(&self.gl, vao, vbo, ebo, None); // Get the data info let (r, a, t, max_layer, unvalid) = self.ppi_program.data_info(&_data)?; // Find the color map let cmap = SETTING.find(&t); // Check if the color map is valid if cmap.is_none() { return Err(Error::InvalidDataType); } 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, vao, vbo, ebo, None); // Tick Label let (vao, vbo, ebo) = self.text_program.init(&self.gl); let mut tick_attach = Attach::new(&self.gl, vao, vbo, ebo, None); let mut config = PPIModuleConfig::default(); config.rdpi = r; config.adpi = a; config.max_layer = max_layer; config.unvalid_value = unvalid; config.colors = cmap.color()?; config.color_range = cmap.value_range(); // Bind the data self.bind_ppi_pg(&mut ppi_attach, &data.borrow(), &config); self.bind_line_pg(&mut line_attach, &data.borrow(), &config); self.bind_tick(&mut tick_attach, &data.borrow(), &config); Ok(PPIPackage::new( config, ppi_attach, line_attach, tick_attach, data, )) } } pub struct PPIPackage<'gl> { draw_helper: bool, ppi_config: PPIModuleConfig, ppi_attach: Attach<'gl>, line_attach: Attach<'gl>, tick_attach: Attach<'gl>, ppi_data: CachedData, } impl<'gl> PPIPackage<'gl> { fn new( ppi_config: PPIModuleConfig, ppi_attach: Attach<'gl>, line_attach: Attach<'gl>, tick_attach: Attach<'gl>, data: &CachedData, ) -> Self { Self { draw_helper: true, ppi_config, ppi_attach, line_attach, tick_attach, ppi_data: Rc::clone(data), } } } impl<'gl> ModuleCursor for PPIPackage<'gl> { type Module = PPIModule<'gl, 'gl>; type Data = Data; type Config = PPIModuleConfig; fn set_config(&mut self, f: F) where F: FnOnce(&mut Self::Config), { f(&mut self.ppi_config); } fn ui_build(&mut self, ui: &imgui::Ui) -> bool { let mut layer = self.ppi_config.layer; let mut is_three_d = self.ppi_config.is_three_d; ui.text("PPI Data Config"); ui.slider("Layer", 0, self.ppi_config.max_layer - 1, &mut layer); ui.checkbox("three d?", &mut is_three_d); ui.separator(); let mut line_width = self.ppi_config.line_width; let mut line_antialias = self.ppi_config.line_antialias; ui.text("PPI Line Config"); ui.slider("line width", 0.1, 10.0, &mut line_width); ui.slider("line ana", 0.1, 10.0, &mut line_antialias); self.ppi_config.set_layer(layer); self.ppi_config.set_is_three_d(is_three_d); self.ppi_config.set_line_width(line_width); self.ppi_config.set_line_antialias(line_antialias); self.ppi_config.changed_any() } fn helper_layer(&self, canvas: &mut femtovg::Canvas) -> bool { colorbar(canvas, &self.ppi_config.colors, &vec![]); true } } #[track] #[derive(PartialEq)] pub struct PPIModuleConfig { 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 unvalid_value: f32, #[do_not_track] pub max_layer: usize, #[do_not_track] pub rdpi: f32, #[do_not_track] pub adpi: f32, } impl Default for PPIModuleConfig { fn default() -> Self { Self { ticks: true, line_color: [1.0, 1.0, 1.0, 1.0], line_width: 2.0, line_antialias: 2.0, 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, rdpi: 0.0, adpi: 0.0, tracker: 0, } } } impl PPIModuleConfig { fn to_ppi_config(&self) -> PPIConfig { PPIConfig { unvalid_value: self.unvalid_value, color_range: self.color_range, colors: self.colors.clone(), layer: self.layer, rdpi: self.rdpi, adpi: self.adpi, three_d: self.is_three_d, } } 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) } }