radar-g/gi/src/pg/modules/ppi.rs
2024-08-28 19:25:11 +08:00

497 lines
14 KiB
Rust

use crate::{
graphics::{
collections::agg_fast_path::{AggFastPath, AggFastPathConfig, Path},
font::{Anchor, FontConfig, LineStyle, PositionText, Text, TextLine},
ppi::{PPIConfig, PPI},
transforms::plane::PlaneTrans,
AttaWithBuffer, Graphics,
},
pg::SideBarInputMsg,
GL,
};
use radarg_core::config::Setting;
use relm4::{
adw::{self, prelude::*},
gtk::{self, prelude::*},
ComponentParts, SimpleComponent,
};
use core::f32;
use glow::HasContext;
use std::{
cell::{RefCell, RefMut},
rc::Rc,
};
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 PPIModule<'b, 'gl: 'b> {
gl: &'gl GL,
ppi_program: &'b mut PPI,
line_program: &'b mut AggFastPath,
text_program: &'b mut Text,
}
impl<'b, 'a: 'b> PPIModule<'b, 'a> {
pub fn new(
gl: &'a GL,
ppi: &'b mut PPI,
text: &'b mut Text,
line: &'b mut AggFastPath,
) -> Self {
let config = PPIConfig::default();
Self {
gl,
ppi_program: ppi,
text_program: text,
line_program: line,
}
}
fn bind_ppi_pg(
&self,
attach: &mut Attach,
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(())
}
fn bind_line_pg(
&self,
attach: &mut Attach,
data: &RadarGridData,
config: &PPIModuleConfig,
) -> Result<()> {
let raw_config = config.to_line_config();
// Will be changed in the future
let outskirt = 10.0;
let mut paths = vec![];
let range_line_num = config.range_line_num;
let r = outskirt / range_line_num as f32;
let seg_num = 200;
let angle = 2f32 * f32::consts::PI / seg_num as f32;
for range in 1..=range_line_num {
// Create the path
let mut path = Path::new(true);
let r = r * range as f32;
// Draw the circle
for seg in 0..seg_num {
let angle = angle * seg as f32;
let x = (angle.cos() * r) as f32;
let y = (angle.sin() * r) as f32;
path.push([x, y, 0.01]);
}
path.finish();
paths.push(path);
}
let ath_lin_num = config.ath_line_num;
let a = 2.0 * f32::consts::PI / ath_lin_num as f32;
for _a in 0..=ath_lin_num {
let mut path = Path::new(false);
let x = (a * _a as f32).cos() * outskirt;
let y = (a * _a as f32).sin() * outskirt;
path.push([0.0, 0.0, 0.01]);
path.push([x, y, 0.01]);
path.finish();
paths.push(path);
}
if config.vertical_axis {
let mut path = Path::new(false);
path.push([0.0, 0.0, 0.0]);
path.push([0.0, 0.0, outskirt]);
path.finish();
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);
Ok(())
}
fn bind_tick(
&self,
attach: &mut Attach,
data: &RadarGridData,
config: &PPIModuleConfig,
) -> Result<()> {
let font_style = config.to_font_config();
match font_style {
FontConfig::Textline(line_style, font_style) => {
let new_text = TextLine::new("Hello,World", Some(font_style), None);
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);
}
}
Ok(())
}
}
impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> {
type Cursor = PPIPackage;
type Data = RadarGridData;
type Operation = PlaneTrans;
fn render<'dt>(
&mut self,
cursor: &mut Self::Cursor,
operation: &Operation<Self::Operation>,
viewport: &ViewPort,
) -> Result<()> {
// PPI Program
let data = &cursor.data;
let mut config = &mut cursor.ppi_config.borrow_mut();
// Mount PPI Program
self.ppi_program.mount(&self.gl)?;
// Deal with the operation
operation.attach_with_program(&self.gl, self.ppi_program.program());
let ppi_attach = &mut cursor.ppi_attach;
// 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)?;
if config.changed_range_line_num()
|| config.changed_ath_line_num()
|| config.changed_vertical_axis()
{
self.bind_line_pg(&mut cursor.line_attach, data, config)?;
}
// 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(&self.gl, 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: &Rc<Self::Data>, setting: &Setting) -> Result<Self::Cursor> {
// 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);
// 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.clone(), vao, vbo, ebo, 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 mut config = PPIModuleConfig::default();
config.rdpi = r;
config.adpi = a;
config.max_layer = max_layer;
config.unvalid_value = unvalid;
config.colors = cmap.color().unwrap();
config.color_range = cmap.value_range();
// Bind the data
self.bind_ppi_pg(&mut ppi_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(PPIPackage::new(
config,
ppi_attach,
line_attach,
tick_attach,
data,
))
}
}
#[derive(Debug)]
pub struct PPIPackage {
draw_helper: bool,
ppi_config: Rc<RefCell<PPIModuleConfig>>,
ppi_attach: Attach,
line_attach: Attach,
tick_attach: Attach,
data: Rc<RadarGridData>,
}
impl PPIPackage {
fn new(
ppi_config: PPIModuleConfig,
ppi_attach: Attach,
line_attach: Attach,
tick_attach: Attach,
data: Rc<RadarGridData>,
) -> Self {
Self {
draw_helper: true,
ppi_config: Rc::new(RefCell::new(ppi_config)),
ppi_attach,
line_attach,
tick_attach,
data,
}
}
}
#[track]
#[derive(PartialEq, Debug, Clone)]
pub struct PPIModuleConfig {
pub ticks: bool,
pub line_color: [f32; 4],
pub line_width: f32,
pub line_antialias: f32,
pub range_line_num: usize,
pub ath_line_num: usize,
pub vertical_axis: bool,
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: 1.5,
line_antialias: 0.5,
layer: 0,
vertical_axis: false,
range_line_num: 5,
ath_line_num: 6,
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)
}
}
pub struct PPIModuleConfigComponent {
config: Rc<RefCell<PPIModuleConfig>>,
}
#[derive(Debug)]
pub enum OutputMsg {
Refresh,
}
#[relm4::component(pub)]
impl SimpleComponent for PPIModuleConfigComponent {
type Widgets = PPIModuleConfigComponentWidgets;
type Init = Rc<RefCell<PPIModuleConfig>>;
type Input = ();
type Output = OutputMsg;
view! {
adw::PreferencesGroup {
set_title:"PPI Config",
set_hexpand:true,
set_vexpand: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);
}
},
adw::SpinRow {
set_title: "Layer",
set_value: init_config.layer as f64,
set_range: (0.0, init_config.max_layer as f64),
set_digits: 0,
set_numeric: true,
set_climb_rate: 1.0,
connect_value_notify[sender, config_ref] => move |this| {
let layer = this.value() as usize;
config_ref.borrow_mut().set_layer(layer);
}
},
gtk::Button {
set_label: "Refresh",
connect_clicked[sender] => move |_| {
sender.output(OutputMsg::Refresh);
}
}
}
}
fn init(
init: Self::Init,
root: Self::Root,
sender: relm4::ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = PPIModuleConfigComponent {
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<Self>) {}
}
impl ModuleCursor for PPIPackage {
type Module<'rf, 'gl: 'rf> = PPIModule<'rf, 'gl>;
type Config = PPIModuleConfig;
type Data = RadarGridData;
type Component = PPIModuleConfigComponent;
type ComponentOutput = OutputMsg;
fn set_config<F>(&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<RefCell<Self::Config>> {
self.ppi_config.clone()
}
fn component_sender(&self) -> Box<dyn Fn(Self::ComponentOutput) -> crate::pg::SideBarInputMsg> {
Box::new(|c| match c {
OutputMsg::Refresh => SideBarInputMsg::Refresh,
})
}
}