497 lines
14 KiB
Rust
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,
|
|
})
|
|
}
|
|
}
|