sync
This commit is contained in:
parent
c5a5c1961e
commit
73d53b734c
69
Cargo.lock
generated
69
Cargo.lock
generated
@ -537,6 +537,8 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.8.8",
|
"toml 0.8.8",
|
||||||
"topojson",
|
"topojson",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
"tracker",
|
"tracker",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2368,6 +2370,16 @@ dependencies = [
|
|||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -2624,6 +2636,12 @@ dependencies = [
|
|||||||
"libredox 0.0.2",
|
"libredox 0.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owned_ttf_parser"
|
name = "owned_ttf_parser"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
@ -3439,6 +3457,15 @@ dependencies = [
|
|||||||
"geo-types",
|
"geo-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shared_library"
|
name = "shared_library"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@ -3691,6 +3718,16 @@ dependencies = [
|
|||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -3873,6 +3910,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"nu-ansi-term",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4000,6 +4063,12 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|||||||
@ -83,6 +83,8 @@ smallvec = "1.13.1"
|
|||||||
rayon = "1.8.1"
|
rayon = "1.8.1"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
sorted-vec = "0.8.3"
|
sorted-vec = "0.8.3"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = "0.3.18"
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
offscreen_renderer::OffscreenRenderer,
|
offscreen_renderer::OffscreenRenderer,
|
||||||
render_pipeline::RenderResult,
|
render_pipeline::RenderResult,
|
||||||
utils::{Dispatcher, Pipeline},
|
utils::{data_to_layer, Dispatcher, Pipeline},
|
||||||
},
|
},
|
||||||
plugin_system::init_plugin,
|
plugin_system::init_plugin,
|
||||||
widgets::{
|
widgets::{
|
||||||
@ -15,6 +15,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
PLUGIN_MANAGER,
|
PLUGIN_MANAGER,
|
||||||
};
|
};
|
||||||
|
use adw::prelude::*;
|
||||||
|
use gtk::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::{Borrow, BorrowMut},
|
borrow::{Borrow, BorrowMut},
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
@ -22,10 +24,11 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
control_panel::{ControlPanelInputMsg, ControlPanelModel},
|
control_panel::{ControlPanelInputMsg, ControlPanelModel},
|
||||||
messages::MonitorInputMsg,
|
messages::{MonitorInputMsg, MonitorOutputMsg},
|
||||||
monitor::MonitorModel,
|
monitor::MonitorModel,
|
||||||
setting::SettingModel,
|
setting::SettingModel,
|
||||||
ControlPanelOutputMsg, TimelineMsg,
|
ControlPanelOutputMsg, TimelineMsg,
|
||||||
@ -57,18 +60,29 @@ pub enum AppMsg {
|
|||||||
CloseRequest,
|
CloseRequest,
|
||||||
Close,
|
Close,
|
||||||
OpenDialog,
|
OpenDialog,
|
||||||
|
SwitchTo((String, DateTime<Utc>, Option<Layer>)),
|
||||||
|
RenderLayer((Layer, DateTime<Utc>)),
|
||||||
OpenDialogMulti,
|
OpenDialogMulti,
|
||||||
OpenFile((DateTime<Utc>, Layer)),
|
RenderSuccess,
|
||||||
CheckTo((String, DateTime<Utc>)),
|
|
||||||
}
|
}
|
||||||
|
pub type Buffer = Rc<RefCell<HashMap<String, HashMap<DateTime<Utc>, Option<RenderResult>>>>>;
|
||||||
|
type RcDispatcher = Rc<RefCell<Dispatcher>>;
|
||||||
|
#[tracker::track]
|
||||||
pub struct AppModel {
|
pub struct AppModel {
|
||||||
dispatcher: Arc<Mutex<Dispatcher>>,
|
#[do_not_track]
|
||||||
buffer: Rc<RefCell<HashMap<String, Vec<(DateTime<Utc>, Option<RenderResult>)>>>>,
|
dispatcher: RcDispatcher,
|
||||||
|
#[do_not_track]
|
||||||
|
buffer: Buffer,
|
||||||
|
waiting_for: Option<DateTime<Utc>>,
|
||||||
|
#[do_not_track]
|
||||||
open_dialog: Controller<OpenDialog>,
|
open_dialog: Controller<OpenDialog>,
|
||||||
|
#[do_not_track]
|
||||||
control: Controller<ControlPanelModel>,
|
control: Controller<ControlPanelModel>,
|
||||||
|
#[do_not_track]
|
||||||
target_pipeline: HashMap<String, Pipeline>,
|
target_pipeline: HashMap<String, Pipeline>,
|
||||||
|
#[do_not_track]
|
||||||
render: Controller<MonitorModel>,
|
render: Controller<MonitorModel>,
|
||||||
|
#[do_not_track]
|
||||||
setting: Controller<SettingModel>,
|
setting: Controller<SettingModel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +120,12 @@ impl Component for AppModel {
|
|||||||
set_stack: Some(&view_stack),
|
set_stack: Some(&view_stack),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
#[name="view_stack"]
|
#[name="view_stack"]
|
||||||
adw::ViewStack{
|
adw::ViewStack{
|
||||||
set_hexpand:true,
|
set_hexpand:true,
|
||||||
set_vexpand:true,
|
set_vexpand:true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
connect_close_request[sender] => move |_| {
|
connect_close_request[sender] => move |_| {
|
||||||
sender.input(AppMsg::CloseRequest);
|
sender.input(AppMsg::CloseRequest);
|
||||||
gtk::Inhibit(true)
|
gtk::Inhibit(true)
|
||||||
@ -122,6 +134,34 @@ impl Component for AppModel {
|
|||||||
popover_child = gtk::Spinner {
|
popover_child = gtk::Spinner {
|
||||||
set_spinning: true,
|
set_spinning: true,
|
||||||
},
|
},
|
||||||
|
home_page = gtk::Box{
|
||||||
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
|
set_hexpand:true,
|
||||||
|
set_vexpand:true,
|
||||||
|
gtk::Box{
|
||||||
|
set_orientation: gtk::Orientation::Horizontal,
|
||||||
|
set_margin_top: 2,
|
||||||
|
set_margin_start: 10,
|
||||||
|
set_margin_end: 10,
|
||||||
|
#[name="popover_menu_bar"]
|
||||||
|
gtk::PopoverMenuBar::from_model(Some(&main_menu)){
|
||||||
|
set_hexpand: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
model.control.widget(),
|
||||||
|
#[name="monitor_toast"]
|
||||||
|
adw::ToastOverlay{
|
||||||
|
set_hexpand: true,
|
||||||
|
set_vexpand: true,
|
||||||
|
model.render.widget(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
home_stack_page = view_stack.add_titled(&home_page, Some("home"), "Home") -> adw::ViewStackPage{
|
||||||
|
set_icon_name:Some("home-filled"),
|
||||||
|
},
|
||||||
|
setting_stack_page = view_stack.add_titled(model.setting.widget(), Some("setting"), "Setting") -> adw::ViewStackPage{
|
||||||
|
set_icon_name:Some("settings-filled")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
menu! {
|
menu! {
|
||||||
@ -150,13 +190,17 @@ impl Component for AppModel {
|
|||||||
let control = ControlPanelModel::builder().launch(0).forward(
|
let control = ControlPanelModel::builder().launch(0).forward(
|
||||||
sender.input_sender(),
|
sender.input_sender(),
|
||||||
|msg| match msg {
|
|msg| match msg {
|
||||||
ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::CheckTo((key, time)),
|
ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::SwitchTo((key, time, None)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let render = MonitorModel::builder()
|
let render =
|
||||||
.launch(())
|
MonitorModel::builder()
|
||||||
.forward(sender.input_sender(), |a| AppMsg::Close);
|
.launch(())
|
||||||
|
.forward(sender.input_sender(), |a| match a {
|
||||||
|
MonitorOutputMsg::LayerRenderFinished => AppMsg::RenderSuccess,
|
||||||
|
_ => AppMsg::Close,
|
||||||
|
});
|
||||||
|
|
||||||
let setting = SettingModel::builder()
|
let setting = SettingModel::builder()
|
||||||
.launch(())
|
.launch(())
|
||||||
@ -167,18 +211,19 @@ impl Component for AppModel {
|
|||||||
.launch(OpenDialogSettings::default())
|
.launch(OpenDialogSettings::default())
|
||||||
.forward(sender.input_sender(), |response| match response {
|
.forward(sender.input_sender(), |response| match response {
|
||||||
OpenDialogResponse::Accept(path) => {
|
OpenDialogResponse::Accept(path) => {
|
||||||
let plugin = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap();
|
if let Some((a, b)) = Self::open_file(path) {
|
||||||
let mut result = plugin.load(RStr::from_str(path.to_str().unwrap())).unwrap();
|
AppMsg::SwitchTo((b.name.clone(), a, Some(b)))
|
||||||
let mut block = result.blocks.pop().unwrap();
|
} else {
|
||||||
data_to_grid_layer(block)
|
AppMsg::Close
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OpenDialogResponse::Cancel => AppMsg::Close,
|
_ => AppMsg::Close,
|
||||||
});
|
});
|
||||||
|
|
||||||
let app = relm4::main_application();
|
let app = relm4::main_application();
|
||||||
relm4_icons::initialize_icons();
|
relm4_icons::initialize_icons();
|
||||||
|
|
||||||
let buffer = Rc::new(RefCell::new(HashMap::new()));
|
let buffer: Buffer = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let mut dispatcher = Dispatcher::new(5, 5, chrono::Duration::minutes(1), buffer.clone());
|
let mut dispatcher = Dispatcher::new(5, 5, chrono::Duration::minutes(1), buffer.clone());
|
||||||
let mut path_format = HashMap::new();
|
let mut path_format = HashMap::new();
|
||||||
|
|
||||||
@ -189,47 +234,20 @@ impl Component for AppModel {
|
|||||||
dispatcher.set_path_format(path_format);
|
dispatcher.set_path_format(path_format);
|
||||||
|
|
||||||
let model = AppModel {
|
let model = AppModel {
|
||||||
// time_buffer: HashMap::new(),
|
|
||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
dispatcher: Arc::new(Mutex::new(dispatcher)),
|
dispatcher: Rc::new(RefCell::new(dispatcher)),
|
||||||
|
waiting_for: None,
|
||||||
open_dialog: dialog,
|
open_dialog: dialog,
|
||||||
target_pipeline: HashMap::new(),
|
target_pipeline: HashMap::new(),
|
||||||
control,
|
control,
|
||||||
render,
|
render,
|
||||||
setting,
|
setting,
|
||||||
|
tracker: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let widgets = view_output!();
|
let widgets = view_output!();
|
||||||
let mut group = RelmActionGroup::<FileActionGroup>::new();
|
let mut group = RelmActionGroup::<FileActionGroup>::new();
|
||||||
|
|
||||||
let page_home = gtk::Box::builder()
|
|
||||||
.hexpand(true)
|
|
||||||
.vexpand(true)
|
|
||||||
.orientation(gtk::Orientation::Vertical)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let setting_bar = gtk::Box::builder()
|
|
||||||
.orientation(gtk::Orientation::Horizontal)
|
|
||||||
.margin_top(2)
|
|
||||||
.margin_start(10)
|
|
||||||
.margin_end(10)
|
|
||||||
.build();
|
|
||||||
let popover_menu_bar = gtk::PopoverMenuBar::from_model(Some(&main_menu));
|
|
||||||
popover_menu_bar.set_hexpand(true);
|
|
||||||
setting_bar.append(&popover_menu_bar);
|
|
||||||
page_home.append(&setting_bar);
|
|
||||||
page_home.append(model.control.widget());
|
|
||||||
page_home.append(model.render.widget());
|
|
||||||
|
|
||||||
let view_stack = widgets.view_stack.clone();
|
|
||||||
let page_home = view_stack.add_titled(&page_home, Some("renderer"), "Render");
|
|
||||||
page_home.set_icon_name(Some("home-filled"));
|
|
||||||
|
|
||||||
let page_setting = model.setting.widget();
|
|
||||||
|
|
||||||
let page_setting = view_stack.add_titled(page_setting, Some("setting"), "Setting");
|
|
||||||
page_setting.set_icon_name(Some("settings-filled"));
|
|
||||||
|
|
||||||
app.set_accelerators_for_action::<OpenAction>(&["<primary>O"]);
|
app.set_accelerators_for_action::<OpenAction>(&["<primary>O"]);
|
||||||
let action: RelmAction<OpenAction> = {
|
let action: RelmAction<OpenAction> = {
|
||||||
RelmAction::new_stateless(move |_| {
|
RelmAction::new_stateless(move |_| {
|
||||||
@ -242,121 +260,131 @@ impl Component for AppModel {
|
|||||||
ComponentParts { model, widgets }
|
ComponentParts { model, widgets }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>, root: &Self::Root) {
|
fn update_with_view(
|
||||||
|
&mut self,
|
||||||
|
widgets: &mut Self::Widgets,
|
||||||
|
msg: Self::Input,
|
||||||
|
_sender: ComponentSender<Self>,
|
||||||
|
root: &Self::Root,
|
||||||
|
) {
|
||||||
|
self.reset();
|
||||||
match msg {
|
match msg {
|
||||||
AppMsg::OpenFile((time, layer)) => {
|
AppMsg::SwitchTo((key, datetime, layer)) => {
|
||||||
let layer_name = layer.name.clone();
|
println!("Switch to {}", datetime);
|
||||||
|
self.create_pipeline(key.clone());
|
||||||
|
self.create_buffer(key.clone());
|
||||||
|
|
||||||
// Init Pipeline and buffer by key
|
{
|
||||||
let pipeline = self.create_pipeline(layer_name.as_ref());
|
let mut current_buffer = (*self.buffer).borrow_mut();
|
||||||
let buffer = self.create_buffer(layer_name.clone());
|
let current_buffer = current_buffer.get_mut(key.as_str()).unwrap();
|
||||||
let tasks = self.create_tasks(layer_name, time);
|
|
||||||
|
|
||||||
// Monitor Render Result first and Set the timeline widget's info
|
if let Some(layer) = layer {
|
||||||
self.render.sender().emit(MonitorInputMsg::AddLayer(layer));
|
self.control.emit(ControlPanelInputMsg::Disable);
|
||||||
self.control
|
_sender.input(AppMsg::RenderLayer((layer, datetime)));
|
||||||
.sender()
|
} else {
|
||||||
.emit(ControlPanelInputMsg::Selection(Some(time)));
|
if let Some(v) = current_buffer.get_mut(&datetime) {
|
||||||
self.control
|
// Task already in pipeline
|
||||||
.sender()
|
if v.is_none() {
|
||||||
.emit(ControlPanelInputMsg::TimeLine(TimelineMsg::SetStart(
|
// Still on the way, need to show the progress
|
||||||
time - Duration::minutes(30),
|
self.control.emit(ControlPanelInputMsg::Disable);
|
||||||
)));
|
self.waiting_for = Some(datetime);
|
||||||
|
info!("Task still on the way");
|
||||||
|
let toast =
|
||||||
|
adw::Toast::builder().title("Task still on the way").build();
|
||||||
|
widgets.monitor_toast.add_toast(toast);
|
||||||
|
} else {
|
||||||
|
// Task finished, Need to show the result
|
||||||
|
let v = v.as_ref().unwrap().clone();
|
||||||
|
|
||||||
// Start New Pipeline
|
info!("Task finished");
|
||||||
|
_sender.input(AppMsg::RenderLayer((v.layer, datetime)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Task not in pipeline, Need to open this file
|
||||||
|
let layer = (*self.dispatcher)
|
||||||
|
.borrow()
|
||||||
|
.get_single_path(&key, datetime, true)
|
||||||
|
.map(|p| Self::open_file(p))
|
||||||
|
.flatten()
|
||||||
|
.map(|x| x.1);
|
||||||
|
if layer.is_none() {
|
||||||
|
// TODO: Show Error
|
||||||
|
} else {
|
||||||
|
self.control.emit(ControlPanelInputMsg::Disable);
|
||||||
|
_sender.input(AppMsg::RenderLayer((layer.unwrap(), datetime)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tasks = self.create_tasks(key.clone(), datetime);
|
||||||
|
let pipeline = self.target_pipeline.get_mut(key.as_str()).unwrap();
|
||||||
let worker = Pipeline::run(pipeline);
|
let worker = Pipeline::run(pipeline);
|
||||||
|
let render_sender = self.render.sender();
|
||||||
|
let control_sender = self.control.sender();
|
||||||
let new_sender = _sender.clone();
|
let new_sender = _sender.clone();
|
||||||
|
|
||||||
// Listen to the result, if the result is ok, send it to the buffer
|
if let Some(tasks) = tasks {
|
||||||
let mut thumb_senders: Vec<oneshot::Sender<gtk::gdk::Texture>> = Vec::new();
|
if tasks.len() == 0 {
|
||||||
let mut thumb_recivers = Vec::new();
|
widgets.monitor_toast.add_toast(
|
||||||
let mut current_buffer = (*self.buffer).borrow_mut();
|
adw::Toast::builder()
|
||||||
let mut current_buffer = current_buffer.get_mut(layer_name.as_str()).unwrap();
|
.title(format!("no data found: {}", datetime))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(*self.buffer)
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(key.clone().as_str())
|
||||||
|
.unwrap()
|
||||||
|
.extend(tasks.clone());
|
||||||
|
let (thumb_recivers, listening_func) = self.create_listening_with_thumb(
|
||||||
|
key.clone(),
|
||||||
|
tasks.len(),
|
||||||
|
new_sender.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
for _ in 0..num {
|
control_sender.emit(ControlPanelInputMsg::SetThumb(
|
||||||
let (tx, rx) = oneshot::channel();
|
thumb_recivers
|
||||||
thumb_recivers.push(rx);
|
.into_iter()
|
||||||
thumb_senders.push(tx);
|
.map(|p| (None, Some(p), datetime))
|
||||||
|
.collect(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let listening = self
|
||||||
|
.current_pipeline(key.clone())
|
||||||
|
.listening_one_by_one(listening_func);
|
||||||
|
|
||||||
|
// Spawn the worker and listen to the result
|
||||||
|
new_sender.oneshot_command(async move {
|
||||||
|
worker.await;
|
||||||
|
listening.await;
|
||||||
|
AppCommand::Test
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.control.emit(ControlPanelInputMsg::SetThumb(
|
|
||||||
thumb_recivers
|
|
||||||
.into_iter()
|
|
||||||
.map(|p| (None, Some(p), time))
|
|
||||||
.collect(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut p = Vec::new();
|
|
||||||
for sender in thumb_senders {
|
|
||||||
let new_sender = _sender.clone();
|
|
||||||
let f = move |tx: oneshot::Receiver<Result<RenderResult, RenderError>>| {
|
|
||||||
Box::pin(async move {
|
|
||||||
if let Ok(r) = tx.await {
|
|
||||||
let r = r.unwrap();
|
|
||||||
let tex = (&r)
|
|
||||||
.layer
|
|
||||||
.render_target()
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.thumbnail
|
|
||||||
.clone();
|
|
||||||
sender.send(tex.unwrap()).unwrap();
|
|
||||||
new_sender
|
|
||||||
.command_sender()
|
|
||||||
.emit(AppCommand::TestBuffer((format!("DBZ"), r)));
|
|
||||||
}
|
|
||||||
}) as BoxFuture<'static, ()>
|
|
||||||
};
|
|
||||||
p.push(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
let listening = pipeline.listening_one_by_one(p);
|
|
||||||
// Spawn the worker and listen to the result
|
|
||||||
new_sender.oneshot_command(async move {
|
|
||||||
task::spawn(worker);
|
|
||||||
listening.await;
|
|
||||||
AppCommand::Test
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
AppMsg::CloseRequest => {}
|
AppMsg::CloseRequest => {}
|
||||||
AppMsg::Close => {}
|
AppMsg::Close => {}
|
||||||
AppMsg::OpenDialog => {
|
AppMsg::OpenDialog => {
|
||||||
self.open_dialog.emit(OpenDialogMsg::Open);
|
self.open_dialog.emit(OpenDialogMsg::Open);
|
||||||
}
|
}
|
||||||
AppMsg::CheckTo((key, datetime)) => {
|
AppMsg::RenderLayer((layer, dt)) => {
|
||||||
use std::iter::Iterator;
|
self.render.emit(MonitorInputMsg::AddLayer(layer));
|
||||||
println!("CheckTo");
|
self.control.emit(ControlPanelInputMsg::Selection(Some(dt)));
|
||||||
let buffer = (*self.buffer).borrow();
|
self.control
|
||||||
if let Some(t) = buffer.get(key.as_str()) {
|
.emit(ControlPanelInputMsg::TimeLine(TimelineMsg::SetStart(
|
||||||
let current_pipeline = self.target_pipeline.get_mut(key.as_str()).unwrap();
|
dt - Duration::minutes(30),
|
||||||
current_pipeline.set_current(datetime, true);
|
)));
|
||||||
let worker = Pipeline::run(current_pipeline);
|
}
|
||||||
|
AppMsg::RenderSuccess => {
|
||||||
if let Some(p) = t.iter().position(|(dt, t)| *dt == datetime) {
|
self.control.emit(ControlPanelInputMsg::Enable);
|
||||||
let (_, rs) = t.get(p).unwrap();
|
|
||||||
if let None = rs.as_ref() {
|
|
||||||
//TODO: Add a spinner
|
|
||||||
} else {
|
|
||||||
let r = rs.as_ref().unwrap();
|
|
||||||
self.render.emit(MonitorInputMsg::RemoveLayer(0));
|
|
||||||
self.render.emit(MonitorInputMsg::AddLayer(r.layer.clone()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("Pipeline error");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("Pipeline error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
AppMsg::OpenDialogMulti => {}
|
AppMsg::OpenDialogMulti => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cmd_with_view(
|
fn update_cmd(
|
||||||
&mut self,
|
&mut self,
|
||||||
widgets: &mut Self::Widgets,
|
|
||||||
message: Self::CommandOutput,
|
message: Self::CommandOutput,
|
||||||
sender: ComponentSender<Self>,
|
sender: ComponentSender<Self>,
|
||||||
root: &Self::Root,
|
root: &Self::Root,
|
||||||
@ -367,11 +395,13 @@ impl Component for AppModel {
|
|||||||
let datetime = result.time();
|
let datetime = result.time();
|
||||||
let mut buffer = (*self.buffer).borrow_mut();
|
let mut buffer = (*self.buffer).borrow_mut();
|
||||||
let mut buffer = buffer.get_mut(key.as_str()).unwrap();
|
let mut buffer = buffer.get_mut(key.as_str()).unwrap();
|
||||||
let _position = buffer
|
if let Some(waiting_for) = self.waiting_for {
|
||||||
.iter_mut()
|
if waiting_for == datetime {
|
||||||
.position(|(t, r)| *t == result.time())
|
self.waiting_for = None;
|
||||||
.unwrap();
|
sender.input(AppMsg::RenderLayer((result.layer.clone(), datetime)));
|
||||||
buffer[_position].1 = Some(result);
|
}
|
||||||
|
}
|
||||||
|
buffer.get_mut(&datetime).unwrap().replace(result);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("test");
|
println!("test");
|
||||||
@ -381,17 +411,12 @@ impl Component for AppModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppModel {
|
impl AppModel {
|
||||||
fn create_buffer(
|
fn create_buffer(&mut self, key: impl Borrow<str>) {
|
||||||
&mut self,
|
|
||||||
key: impl Borrow<str>,
|
|
||||||
) -> &mut Vec<(DateTime<Utc>, Option<RenderResult>)> {
|
|
||||||
if !(*self.buffer).borrow().contains_key(key.borrow()) {
|
if !(*self.buffer).borrow().contains_key(key.borrow()) {
|
||||||
(*self.buffer)
|
(*self.buffer)
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(key.borrow().to_string(), Vec::new());
|
.insert(key.borrow().to_string(), HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
&mut (*(*self.buffer).borrow_mut().get_mut(key.borrow()).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_pipeline(&mut self, key: impl Borrow<str>) -> &mut Pipeline {
|
fn create_pipeline(&mut self, key: impl Borrow<str>) -> &mut Pipeline {
|
||||||
@ -410,99 +435,75 @@ impl AppModel {
|
|||||||
time: DateTime<Utc>,
|
time: DateTime<Utc>,
|
||||||
) -> Option<Vec<(DateTime<Utc>, Option<RenderResult>)>> {
|
) -> Option<Vec<(DateTime<Utc>, Option<RenderResult>)>> {
|
||||||
let pipeline = self.target_pipeline.get_mut(key.as_ref()).unwrap();
|
let pipeline = self.target_pipeline.get_mut(key.as_ref()).unwrap();
|
||||||
let time_list = pipeline.set_current(time, true);
|
let time_list = pipeline.set_current(time, true, 3);
|
||||||
let mut buffer = (*self.buffer).borrow_mut();
|
|
||||||
let mut buffer = buffer.get_mut(key.as_ref()).unwrap();
|
|
||||||
time_list.map(|time_list| time_list.into_iter().map(|t| (t, None)).collect())
|
time_list.map(|time_list| time_list.into_iter().map(|t| (t, None)).collect())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! match_in_macro {
|
fn current_pipeline(&mut self, key: impl AsRef<str>) -> &mut Pipeline {
|
||||||
($block:ident,$name:literal, $(($branch:path, $t:ty, $color:expr)),+) => {
|
self.target_pipeline.get_mut(key.as_ref()).unwrap()
|
||||||
{
|
}
|
||||||
match $block.data_type {
|
|
||||||
$(
|
fn create_listening_with_thumb(
|
||||||
$branch => {
|
&self,
|
||||||
let datetime = Utc.timestamp_opt($block.datetime,0).unwrap();
|
key: impl Borrow<str>,
|
||||||
let data: $t = $block.into();
|
num: usize,
|
||||||
let layer = Layer::grid_render_layer(data, format!($name), $color);
|
_sender: ComponentSender<Self>,
|
||||||
AppMsg::OpenFile((datetime ,layer))
|
) -> (
|
||||||
},
|
Vec<oneshot::Receiver<gtk::gdk::Texture>>,
|
||||||
)+
|
Vec<
|
||||||
_ => AppMsg::Close,
|
impl FnOnce(
|
||||||
}
|
oneshot::Receiver<Result<RenderResult, RenderError>>,
|
||||||
|
) -> BoxFuture<'static, ()>
|
||||||
|
+ Send
|
||||||
|
+ 'static
|
||||||
|
+ Sync,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
let mut thumb_senders: Vec<oneshot::Sender<gtk::gdk::Texture>> = Vec::new();
|
||||||
|
let mut thumb_recivers = Vec::new();
|
||||||
|
let mut current_buffer = (*self.buffer).borrow_mut();
|
||||||
|
let mut current_buffer = current_buffer.get_mut(key.borrow()).unwrap();
|
||||||
|
|
||||||
|
for _ in 0..num {
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
thumb_recivers.push(rx);
|
||||||
|
thumb_senders.push(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
let mut p = Vec::new();
|
||||||
}
|
for sender in thumb_senders {
|
||||||
|
let new_sender = _sender.clone();
|
||||||
|
let f = move |tx: oneshot::Receiver<Result<RenderResult, RenderError>>| {
|
||||||
|
Box::pin(async move {
|
||||||
|
if let Ok(r) = tx.await {
|
||||||
|
let r = r.unwrap();
|
||||||
|
let tex = (&r)
|
||||||
|
.layer
|
||||||
|
.render_target()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.thumbnail
|
||||||
|
.clone();
|
||||||
|
sender.send(tex.unwrap()).unwrap();
|
||||||
|
new_sender
|
||||||
|
.command_sender()
|
||||||
|
.emit(AppCommand::TestBuffer((format!("DBZ"), r)));
|
||||||
|
}
|
||||||
|
}) as BoxFuture<'static, ()>
|
||||||
|
};
|
||||||
|
p.push(f);
|
||||||
|
}
|
||||||
|
(thumb_recivers, p)
|
||||||
|
}
|
||||||
|
|
||||||
fn data_to_grid_layer(block: Block) -> AppMsg {
|
fn open_file(path: impl AsRef<std::path::Path>) -> Option<(DateTime<Utc>, Layer)> {
|
||||||
use crate::utils::*;
|
let plugin = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap();
|
||||||
use radarg_plugin_interface::PluginResultType;
|
let mut result = plugin
|
||||||
match block.shape {
|
.load(RStr::from_str(path.as_ref().to_str().unwrap()))
|
||||||
DataShape::Matrix => match_in_macro!(
|
.unwrap();
|
||||||
block,
|
let mut block = result.blocks.pop().unwrap();
|
||||||
"DBZ",
|
data_to_layer(block)
|
||||||
(
|
|
||||||
PluginResultType::DBZ,
|
|
||||||
Radar2d<i8>,
|
|
||||||
create_dbz_boundarynorm()
|
|
||||||
),
|
|
||||||
(PluginResultType::R, Radar2d<i8>, create_dbz_boundarynorm()),
|
|
||||||
(PluginResultType::V, Radar2d<f32>, create_vel_boundarynorm()),
|
|
||||||
(
|
|
||||||
PluginResultType::ZDR,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_zdr_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::PHIDP,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_phidp_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::KDP,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_kdp_boundarynorm()
|
|
||||||
),
|
|
||||||
(PluginResultType::CC, Radar2d<f32>, create_cc_boundarynorm()),
|
|
||||||
(
|
|
||||||
PluginResultType::HCA,
|
|
||||||
Radar2d<i8>,
|
|
||||||
create_cpc_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::QPE,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_vil_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::QPF,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_vil_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::VIL,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_vil_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::OHP,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_vil_boundarynorm()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PluginResultType::THP,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_vil_boundarynorm()
|
|
||||||
),
|
|
||||||
(PluginResultType::ET, Radar2d<f32>, create_et_boundarynorm()),
|
|
||||||
(
|
|
||||||
PluginResultType::EB,
|
|
||||||
Radar2d<f32>,
|
|
||||||
create_hgt_boundarynorm()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_ => AppMsg::Close,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@ use std::path::PathBuf;
|
|||||||
#[tracker::track]
|
#[tracker::track]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ControlPanelModel {
|
pub struct ControlPanelModel {
|
||||||
|
timeline_enabled: bool,
|
||||||
|
enabled: bool,
|
||||||
timeline_start: DateTime<Utc>,
|
timeline_start: DateTime<Utc>,
|
||||||
selection: Option<DateTime<Utc>>,
|
selection: Option<DateTime<Utc>>,
|
||||||
#[tracker::no_eq]
|
#[tracker::no_eq]
|
||||||
@ -71,6 +73,8 @@ impl SimpleComponent for ControlPanelModel {
|
|||||||
set_spacing:10,
|
set_spacing:10,
|
||||||
gtk::Button{
|
gtk::Button{
|
||||||
set_icon_name: "rewind-filled",
|
set_icon_name: "rewind-filled",
|
||||||
|
#[track = "model.changed(ControlPanelModel::enabled())"]
|
||||||
|
set_sensitive: model.enabled,
|
||||||
connect_clicked[sender] => move |_| {
|
connect_clicked[sender] => move |_| {
|
||||||
sender.input(ControlPanelInputMsg::SelectionRewind);
|
sender.input(ControlPanelInputMsg::SelectionRewind);
|
||||||
},
|
},
|
||||||
@ -80,6 +84,8 @@ impl SimpleComponent for ControlPanelModel {
|
|||||||
},
|
},
|
||||||
gtk::Button{
|
gtk::Button{
|
||||||
set_icon_name: "fast-forward-filled",
|
set_icon_name: "fast-forward-filled",
|
||||||
|
#[track = "model.changed(ControlPanelModel::enabled())"]
|
||||||
|
set_sensitive: model.enabled,
|
||||||
connect_clicked[sender] => move |_| {
|
connect_clicked[sender] => move |_| {
|
||||||
sender.input(ControlPanelInputMsg::SelectionFastForward);
|
sender.input(ControlPanelInputMsg::SelectionFastForward);
|
||||||
},
|
},
|
||||||
@ -209,6 +215,8 @@ impl SimpleComponent for ControlPanelModel {
|
|||||||
|
|
||||||
let timeline_start = Utc::now();
|
let timeline_start = Utc::now();
|
||||||
let model = ControlPanelModel {
|
let model = ControlPanelModel {
|
||||||
|
timeline_enabled: true,
|
||||||
|
enabled: true,
|
||||||
selection: None,
|
selection: None,
|
||||||
timeline_start,
|
timeline_start,
|
||||||
list_img_wrapper,
|
list_img_wrapper,
|
||||||
@ -231,6 +239,8 @@ impl SimpleComponent for ControlPanelModel {
|
|||||||
TimelineMsg::SetStart(time) => {
|
TimelineMsg::SetStart(time) => {
|
||||||
self.set_timeline_start(time);
|
self.set_timeline_start(time);
|
||||||
}
|
}
|
||||||
|
TimelineMsg::Disable => {}
|
||||||
|
TimelineMsg::Enable => {}
|
||||||
},
|
},
|
||||||
ControlPanelInputMsg::Selection(selection) => {
|
ControlPanelInputMsg::Selection(selection) => {
|
||||||
self.set_selection(selection);
|
self.set_selection(selection);
|
||||||
@ -264,6 +274,14 @@ impl SimpleComponent for ControlPanelModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ControlPanelInputMsg::SetThumbByDate((time, thumb)) => {}
|
ControlPanelInputMsg::SetThumbByDate((time, thumb)) => {}
|
||||||
|
|
||||||
|
ControlPanelInputMsg::Disable => {
|
||||||
|
self.set_enabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlPanelInputMsg::Enable => {
|
||||||
|
self.set_enabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ pub enum TimelineMsg {
|
|||||||
Rewind(Duration),
|
Rewind(Duration),
|
||||||
FastForward(Duration),
|
FastForward(Duration),
|
||||||
SetStart(DateTime<Utc>),
|
SetStart(DateTime<Utc>),
|
||||||
|
Disable,
|
||||||
|
Enable,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -22,6 +24,8 @@ pub enum ControlPanelInputMsg {
|
|||||||
SetThumbByDate((DateTime<Utc>, Option<gtk::gdk::Texture>)),
|
SetThumbByDate((DateTime<Utc>, Option<gtk::gdk::Texture>)),
|
||||||
SelectionRewind,
|
SelectionRewind,
|
||||||
SelectionFastForward,
|
SelectionFastForward,
|
||||||
|
Disable,
|
||||||
|
Enable,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@ -63,9 +63,13 @@ impl RelmListItem for ImgItem {
|
|||||||
let ltex = self.img.clone();
|
let ltex = self.img.clone();
|
||||||
let new_img = img.clone();
|
let new_img = img.clone();
|
||||||
self.handle = Some(relm4::spawn_local(async move {
|
self.handle = Some(relm4::spawn_local(async move {
|
||||||
let tex = new_rx.await.unwrap();
|
let tex = new_rx.await;
|
||||||
ltex.lock().unwrap().replace(tex);
|
if let Ok(tex) = tex {
|
||||||
new_img.set_paintable(ltex.lock().unwrap().as_ref());
|
ltex.lock().unwrap().replace(tex);
|
||||||
|
new_img.set_paintable(ltex.lock().unwrap().as_ref());
|
||||||
|
} else {
|
||||||
|
eprintln!("Failed to get texture");
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
let ltex = self.img.lock().unwrap();
|
let ltex = self.img.lock().unwrap();
|
||||||
|
|||||||
@ -4,8 +4,8 @@ use crate::widgets::render::Layer;
|
|||||||
|
|
||||||
pub enum MonitorInputMsg {
|
pub enum MonitorInputMsg {
|
||||||
AddLayer(Layer),
|
AddLayer(Layer),
|
||||||
RemoveLayer(usize),
|
RemoveLayer(String),
|
||||||
UpdateLayer((usize, Box<dyn Fn(&mut Layer) + 'static>)),
|
UpdateLayer((String, Box<dyn Fn(&mut Layer) + 'static>)),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ pub enum MonitorOutputMsg {
|
|||||||
LayerAdded(usize),
|
LayerAdded(usize),
|
||||||
LayerRemoved(usize),
|
LayerRemoved(usize),
|
||||||
LayerUpdated(usize),
|
LayerUpdated(usize),
|
||||||
|
LayerRenderFinished,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@ -6,7 +6,10 @@ use crate::{
|
|||||||
widgets::dynamic_col::DynamicCol,
|
widgets::dynamic_col::DynamicCol,
|
||||||
widgets::render::{Layer, Render},
|
widgets::render::{Layer, Render},
|
||||||
};
|
};
|
||||||
use glib::clone;
|
use glib::{clone, PropertyGet};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use super::sidebar::{sidebar::SideBarModel, Msg, SideBarOutputMsg};
|
use super::sidebar::{sidebar::SideBarModel, Msg, SideBarOutputMsg};
|
||||||
@ -24,8 +27,9 @@ pub struct MonitorModel {
|
|||||||
render_range: (f64, f64, f64, f64),
|
render_range: (f64, f64, f64, f64),
|
||||||
sidebar_open: bool,
|
sidebar_open: bool,
|
||||||
sidebar_width: i32,
|
sidebar_width: i32,
|
||||||
|
new_layer: i8,
|
||||||
#[no_eq]
|
#[no_eq]
|
||||||
layers: Vec<Layer>,
|
layers: Rc<RefCell<HashMap<String, Layer>>>,
|
||||||
#[no_eq]
|
#[no_eq]
|
||||||
sidebar: Controller<SideBarModel>,
|
sidebar: Controller<SideBarModel>,
|
||||||
}
|
}
|
||||||
@ -65,12 +69,15 @@ impl Component for MonitorModel {
|
|||||||
gtk::Overlay{
|
gtk::Overlay{
|
||||||
#[wrap(Some)]
|
#[wrap(Some)]
|
||||||
set_child = &Render{
|
set_child = &Render{
|
||||||
#[watch]
|
#[track = "model.changed(MonitorModel::new_layer())"]
|
||||||
set_interior_layers: model.layers.clone(),
|
set_interior_layers: model.layers.borrow().values().cloned().collect(),
|
||||||
#[track = "model.changed(MonitorModel::render_cfg())"]
|
#[track = "model.changed(MonitorModel::render_cfg())"]
|
||||||
set_cfg: model.render_cfg,
|
set_cfg: model.render_cfg,
|
||||||
#[track = "model.changed(MonitorModel::render_range())"]
|
#[track = "model.changed(MonitorModel::render_range())"]
|
||||||
set_view: model.render_range,
|
set_view: model.render_range,
|
||||||
|
connect_render_status_notify[sender] => move |r| {
|
||||||
|
sender.output(MonitorOutputMsg::LayerRenderFinished);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
add_overlay=>k::Button{
|
add_overlay=>k::Button{
|
||||||
set_label:"Add",
|
set_label:"Add",
|
||||||
@ -92,55 +99,45 @@ impl Component for MonitorModel {
|
|||||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
|
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
|
||||||
match message {
|
match message {
|
||||||
MonitorInputMsg::AddLayer(layer) => {
|
MonitorInputMsg::AddLayer(layer) => {
|
||||||
// let mut canvas = OFFSCREEN.lock().unwrap();
|
let need_prepare = { layer.get_prepare().lock().unwrap().is_some() };
|
||||||
if layer.get_imp().is_some() {
|
if need_prepare {
|
||||||
sender.oneshot_command(async move {
|
sender.oneshot_command(async move {
|
||||||
let mut back = OffscreenRenderer::new(3000, 3000).unwrap();
|
let mut back = OffscreenRenderer::new(3000, 3000).unwrap();
|
||||||
let canvas = back.create_canvas();
|
let canvas = back.create_canvas();
|
||||||
// let new_canvas = OffscreenRenderer::new().unwrap();
|
|
||||||
let f = {
|
let f = {
|
||||||
let p = layer.get_prepare();
|
let p = layer.get_prepare();
|
||||||
let mut _p = p.lock().unwrap();
|
let mut _p = p.lock().unwrap();
|
||||||
_p.take()
|
_p.take().unwrap()
|
||||||
};
|
};
|
||||||
let target = if let Some(f) = f {
|
let imp = layer.get_imp().unwrap();
|
||||||
let imp = layer.get_imp().unwrap();
|
let map: Mapper = Mercator::default().into();
|
||||||
|
let cms = CMS::new(map, (3000.0, 3000.0));
|
||||||
let map: Mapper = Mercator::default().into();
|
let canvas = Arc::new(Mutex::new(canvas));
|
||||||
let cms = CMS::new(map, (3000.0, 3000.0));
|
let target = f(imp, canvas, cms);
|
||||||
let canvas = Arc::new(Mutex::new(canvas));
|
layer.set_render_target(target);
|
||||||
let c = f(imp, canvas, cms);
|
|
||||||
Some(c)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(target) = target {
|
|
||||||
layer.set_render_target(target);
|
|
||||||
}
|
|
||||||
MonitorCommand::NewLayer(layer)
|
MonitorCommand::NewLayer(layer)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.layers.push(layer);
|
self.layers
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(layer.name.as_str())
|
||||||
|
.map(|v| *v = layer);
|
||||||
sender
|
sender
|
||||||
.output_sender()
|
.output_sender()
|
||||||
.send(MonitorOutputMsg::LayerAdded(0))
|
.send(MonitorOutputMsg::LayerAdded(0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
self.sidebar.sender().send(Msg::RefreshList);
|
||||||
self.sidebar
|
|
||||||
.sender()
|
|
||||||
.send(Msg::RefreshList(self.layers.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MonitorInputMsg::RemoveLayer(index) => {
|
MonitorInputMsg::RemoveLayer(k) => {
|
||||||
self.layers.remove(index);
|
self.layers.borrow_mut().remove(&k);
|
||||||
sender
|
sender
|
||||||
.output_sender()
|
.output_sender()
|
||||||
.send(MonitorOutputMsg::LayerRemoved(0))
|
.send(MonitorOutputMsg::LayerRemoved(0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
MonitorInputMsg::UpdateLayer((idx, f)) => {
|
MonitorInputMsg::UpdateLayer((k, f)) => {
|
||||||
f(&mut self.layers[idx]);
|
f(&mut (*self.layers.borrow_mut().get_mut(&k).unwrap()));
|
||||||
sender
|
sender
|
||||||
.output_sender()
|
.output_sender()
|
||||||
.send(MonitorOutputMsg::LayerUpdated(0))
|
.send(MonitorOutputMsg::LayerUpdated(0))
|
||||||
@ -155,13 +152,13 @@ impl Component for MonitorModel {
|
|||||||
root: &Self::Root,
|
root: &Self::Root,
|
||||||
sender: ComponentSender<Self>,
|
sender: ComponentSender<Self>,
|
||||||
) -> ComponentParts<Self> {
|
) -> ComponentParts<Self> {
|
||||||
let sidebar: Controller<SideBarModel> =
|
let layers = Rc::new(RefCell::new(HashMap::new()));
|
||||||
SideBarModel::builder()
|
let sidebar: Controller<SideBarModel> = SideBarModel::builder()
|
||||||
.launch(())
|
.launch(layers.clone())
|
||||||
.forward(sender.input_sender(), |msg| match msg {
|
.forward(sender.input_sender(), |msg| match msg {
|
||||||
SideBarOutputMsg::NewLayer(layer) => MonitorInputMsg::AddLayer(layer),
|
SideBarOutputMsg::NewLayer(layer) => MonitorInputMsg::AddLayer(layer),
|
||||||
_ => MonitorInputMsg::None,
|
_ => MonitorInputMsg::None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let render_cfg = RenderConfig {
|
let render_cfg = RenderConfig {
|
||||||
padding: [20.0, 40.0, 20.0, 40.0],
|
padding: [20.0, 40.0, 20.0, 40.0],
|
||||||
@ -169,10 +166,11 @@ impl Component for MonitorModel {
|
|||||||
|
|
||||||
let model = MonitorModel {
|
let model = MonitorModel {
|
||||||
render_range: (4.0, 53.3, 73.3, 135.0),
|
render_range: (4.0, 53.3, 73.3, 135.0),
|
||||||
|
new_layer: 0,
|
||||||
render_cfg,
|
render_cfg,
|
||||||
sidebar_open: true,
|
sidebar_open: true,
|
||||||
sidebar_width: 400,
|
sidebar_width: 400,
|
||||||
layers: vec![],
|
layers: layers,
|
||||||
sidebar,
|
sidebar,
|
||||||
tracker: 0,
|
tracker: 0,
|
||||||
};
|
};
|
||||||
@ -190,12 +188,12 @@ impl Component for MonitorModel {
|
|||||||
self.reset();
|
self.reset();
|
||||||
match msg {
|
match msg {
|
||||||
MonitorCommand::NewLayer(layer) => {
|
MonitorCommand::NewLayer(layer) => {
|
||||||
self.layers.push(layer);
|
// self.layers.push(layer);
|
||||||
|
self.layers.borrow_mut().insert(layer.name.clone(), layer);
|
||||||
self.set_render_range((29.13, 30.16, 119.53, 121.13));
|
self.set_render_range((29.13, 30.16, 119.53, 121.13));
|
||||||
self.sidebar
|
self.sidebar.sender().send(Msg::RefreshList).unwrap();
|
||||||
.sender()
|
let raw_id = self.get_new_layer();
|
||||||
.send(Msg::RefreshList(self.layers.clone()))
|
self.set_new_layer(*raw_id + 1);
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
use abi_stable::type_level::trait_marker::Hash;
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::WidgetExt;
|
use gtk::prelude::WidgetExt;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
@ -17,6 +20,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::bottom_bar::BottomBarModel;
|
use super::bottom_bar::BottomBarModel;
|
||||||
pub struct SideBarModel {
|
pub struct SideBarModel {
|
||||||
|
layers: Rc<RefCell<HashMap<String, Layer>>>,
|
||||||
counter: u8,
|
counter: u8,
|
||||||
list_view_wrapper: TypedListView<LayerItem, gtk::SingleSelection>,
|
list_view_wrapper: TypedListView<LayerItem, gtk::SingleSelection>,
|
||||||
bottom_bar_vec: FactoryVecDeque<BottomBarModel>,
|
bottom_bar_vec: FactoryVecDeque<BottomBarModel>,
|
||||||
@ -24,7 +28,7 @@ pub struct SideBarModel {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
RefreshList(Vec<Layer>),
|
RefreshList,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +39,7 @@ pub enum SideBarOutputMsg {
|
|||||||
|
|
||||||
#[relm4::component(pub)]
|
#[relm4::component(pub)]
|
||||||
impl SimpleComponent for SideBarModel {
|
impl SimpleComponent for SideBarModel {
|
||||||
type Init = ();
|
type Init = Rc<RefCell<HashMap<String, Layer>>>;
|
||||||
type Output = SideBarOutputMsg;
|
type Output = SideBarOutputMsg;
|
||||||
type Input = Msg;
|
type Input = Msg;
|
||||||
|
|
||||||
@ -121,6 +125,7 @@ impl SimpleComponent for SideBarModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let model = SideBarModel {
|
let model = SideBarModel {
|
||||||
|
layers: init,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
list_view_wrapper,
|
list_view_wrapper,
|
||||||
bottom_bar_vec,
|
bottom_bar_vec,
|
||||||
@ -149,13 +154,16 @@ impl SimpleComponent for SideBarModel {
|
|||||||
|
|
||||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||||
match message {
|
match message {
|
||||||
Msg::RefreshList(layers) => {
|
Msg::RefreshList => {
|
||||||
for layer in layers {
|
let mut list = self
|
||||||
self.list_view_wrapper
|
.layers
|
||||||
.append(LayerItem::new(layer.name, true));
|
.borrow()
|
||||||
}
|
.iter()
|
||||||
|
.map(|(k, v)| LayerItem::new(k.clone(), v.visiable))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.list_view_wrapper.clear();
|
||||||
|
self.list_view_wrapper.extend_from_iter(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,12 +189,6 @@ struct Widgets {
|
|||||||
button: gtk::CheckButton,
|
button: gtk::CheckButton,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Widgets {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
dbg!(self.label.label());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RelmListItem for LayerItem {
|
impl RelmListItem for LayerItem {
|
||||||
type Root = gtk::Box;
|
type Root = gtk::Box;
|
||||||
type Widgets = Widgets;
|
type Widgets = Widgets;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
#![feature(step_trait)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
#[macro_use]
|
||||||
mod utils;
|
mod utils;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use gtk::gio;
|
use gtk::gio;
|
||||||
@ -17,6 +19,8 @@ mod plugin_system;
|
|||||||
use components::app::AppModel;
|
use components::app::AppModel;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use tracing::info;
|
||||||
|
use tracing_subscriber;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
const APP_ID: &str = "org.tsuki.radar_g";
|
const APP_ID: &str = "org.tsuki.radar_g";
|
||||||
@ -30,6 +34,7 @@ static PLUGIN_MANAGER: Lazy<PluginManager> = Lazy::new(|| PluginManager::new().u
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Load GL pointers from epoxy (GL context management library used by GTK).
|
// Load GL pointers from epoxy (GL context management library used by GTK).
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
{
|
{
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let library =
|
let library =
|
||||||
@ -50,9 +55,9 @@ fn main() {
|
|||||||
.unwrap_or(ptr::null())
|
.unwrap_or(ptr::null())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let relm = relm4::RelmApp::new(APP_ID);
|
let relm = relm4::RelmApp::new(APP_ID);
|
||||||
initialize_custom_css();
|
initialize_custom_css();
|
||||||
|
info!("Init plugin system");
|
||||||
let pluginmanager = PluginManager::new();
|
let pluginmanager = PluginManager::new();
|
||||||
relm.run::<AppModel>(());
|
relm.run::<AppModel>(());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,9 @@ impl OffscreenRenderer {
|
|||||||
|
|
||||||
let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
|
let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
|
||||||
version: surfman::GLVersion::new(4, 1),
|
version: surfman::GLVersion::new(4, 1),
|
||||||
flags: ContextAttributeFlags::empty(),
|
flags: ContextAttributeFlags::ALPHA
|
||||||
|
.union(ContextAttributeFlags::DEPTH)
|
||||||
|
.union(ContextAttributeFlags::STENCIL),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut context = device.create_context(&descriptor, None)?;
|
let mut context = device.create_context(&descriptor, None)?;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
use super::render_pipeline::RenderResult;
|
use super::render_pipeline::RenderResult;
|
||||||
use super::{offscreen_renderer::OffscreenRenderer, pool::DataPool};
|
use super::{offscreen_renderer::OffscreenRenderer, pool::DataPool};
|
||||||
|
use crate::components::app::Buffer;
|
||||||
use crate::coords::proj::Mercator;
|
use crate::coords::proj::Mercator;
|
||||||
use crate::coords::Mapper;
|
use crate::coords::Mapper;
|
||||||
use crate::widgets::{Render, Target, TargetType, CMS};
|
use crate::widgets::{Render, Target, TargetType, CMS};
|
||||||
use crate::{data::Radar2d, errors::RenderError, widgets::Layer, PLUGIN_MANAGER};
|
use crate::{data::Radar2d, errors::RenderError, widgets::Layer, PLUGIN_MANAGER};
|
||||||
use chrono::{prelude::*, Duration};
|
use chrono::{prelude::*, Duration};
|
||||||
|
use euclid::approxord::max;
|
||||||
use futures::future::*;
|
use futures::future::*;
|
||||||
use radarg_plugin_interface::*;
|
use radarg_plugin_interface::*;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@ -32,10 +34,9 @@ pub fn ck() {
|
|||||||
type RenderR = Result<RenderResult, RenderError>;
|
type RenderR = Result<RenderResult, RenderError>;
|
||||||
|
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
pool: Option<Vec<BoxFuture<'static, ()>>>,
|
pool: Vec<BoxFuture<'static, ()>>,
|
||||||
// switcher: Pool<oneshot::Receiver<i32>>,
|
|
||||||
results: SmallVec<[RenderResult; 20]>,
|
results: SmallVec<[RenderResult; 20]>,
|
||||||
dispatcher: Option<Arc<std::sync::Mutex<Dispatcher>>>,
|
dispatcher: Option<Rc<RefCell<Dispatcher>>>,
|
||||||
handlers: Option<Vec<oneshot::Receiver<RenderR>>>,
|
handlers: Option<Vec<oneshot::Receiver<RenderR>>>,
|
||||||
handler: Option<mpsc::Receiver<RenderR>>,
|
handler: Option<mpsc::Receiver<RenderR>>,
|
||||||
sender: Option<mpsc::Sender<RenderR>>,
|
sender: Option<mpsc::Sender<RenderR>>,
|
||||||
@ -45,8 +46,7 @@ pub struct Pipeline {
|
|||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
pub fn new(len: usize, key: String) -> Self {
|
pub fn new(len: usize, key: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pool: Some(Vec::new()),
|
pool: Vec::new(),
|
||||||
// switcher: Pool::new(len),
|
|
||||||
results: SmallVec::new(),
|
results: SmallVec::new(),
|
||||||
dispatcher: None,
|
dispatcher: None,
|
||||||
handlers: None,
|
handlers: None,
|
||||||
@ -56,7 +56,7 @@ impl Pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_dispatcher(&mut self, dispatcher: Arc<std::sync::Mutex<Dispatcher>>) {
|
pub fn set_dispatcher(&mut self, dispatcher: Rc<RefCell<Dispatcher>>) {
|
||||||
self.dispatcher = Some(dispatcher);
|
self.dispatcher = Some(dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +68,11 @@ impl Pipeline {
|
|||||||
&mut self,
|
&mut self,
|
||||||
current_time: DateTime<Utc>,
|
current_time: DateTime<Utc>,
|
||||||
check_existed: bool,
|
check_existed: bool,
|
||||||
|
max_retry_time: usize,
|
||||||
) -> Option<Vec<DateTime<Utc>>> {
|
) -> Option<Vec<DateTime<Utc>>> {
|
||||||
let dispatcher = self.dispatcher.clone().unwrap();
|
let dispatcher = self.dispatcher.clone().unwrap();
|
||||||
let dispatcher = dispatcher.lock().unwrap();
|
let dispatcher = dispatcher.borrow_mut();
|
||||||
let paths = dispatcher.get_path(&self.key, current_time, check_existed);
|
let paths = dispatcher.get_path(&self.key, current_time, check_existed, max_retry_time);
|
||||||
|
|
||||||
if let Some(paths) = paths {
|
if let Some(paths) = paths {
|
||||||
let mut recvs = Vec::new();
|
let mut recvs = Vec::new();
|
||||||
@ -92,7 +93,8 @@ impl Pipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn work_num(&self) -> usize {
|
pub fn work_num(&self) -> usize {
|
||||||
self.pool.as_ref().unwrap().len()
|
// self.pool.as_ref().unwrap().len()
|
||||||
|
self.pool.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker(
|
fn worker(
|
||||||
@ -149,14 +151,15 @@ impl Pipeline {
|
|||||||
tx.send(data).unwrap();
|
tx.send(data).unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.pool.as_mut().unwrap().push(Box::pin(future));
|
self.pool.push(Box::pin(future));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> BoxFuture<'static, ()> {
|
pub fn run(value: &mut Self) -> BoxFuture<'static, ()> {
|
||||||
let pool = self.get_pool().unwrap();
|
let pool = value.get_pool();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
for f in pool.into_iter() {
|
for f in pool.into_iter() {
|
||||||
f.await;
|
task::spawn(f);
|
||||||
|
// f.await;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -181,13 +184,17 @@ impl Pipeline {
|
|||||||
let mut handler = self.handlers.take().unwrap();
|
let mut handler = self.handlers.take().unwrap();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
for (h, f) in handler.into_iter().zip(f) {
|
for (h, f) in handler.into_iter().zip(f) {
|
||||||
f(h).await;
|
task::spawn(f(h));
|
||||||
|
// f(h).await;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pool(&mut self) -> Option<Vec<BoxFuture<'static, ()>>> {
|
pub fn get_pool(&mut self) -> Vec<BoxFuture<'static, ()>> {
|
||||||
self.pool.take()
|
// self.pool.clone()
|
||||||
|
use std::mem::replace;
|
||||||
|
let pool = replace(&mut self.pool, Vec::new());
|
||||||
|
pool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel_task(&mut self, timestamp: i64) {}
|
pub fn cancel_task(&mut self, timestamp: i64) {}
|
||||||
@ -199,7 +206,7 @@ pub struct Dispatcher {
|
|||||||
fore_len: usize,
|
fore_len: usize,
|
||||||
back_len: usize,
|
back_len: usize,
|
||||||
step: Duration,
|
step: Duration,
|
||||||
registered_buffer: Rc<RefCell<HashMap<String, Vec<(DateTime<Utc>, Option<RenderResult>)>>>>,
|
registered_buffer: Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dispatcher {
|
impl Dispatcher {
|
||||||
@ -207,7 +214,7 @@ impl Dispatcher {
|
|||||||
fore_len: usize,
|
fore_len: usize,
|
||||||
back_len: usize,
|
back_len: usize,
|
||||||
step: Duration,
|
step: Duration,
|
||||||
registered_buffer: Rc<RefCell<HashMap<String, Vec<(DateTime<Utc>, Option<RenderResult>)>>>>,
|
registered_buffer: Buffer,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
datetime: Utc::now(),
|
datetime: Utc::now(),
|
||||||
@ -239,11 +246,47 @@ impl Dispatcher {
|
|||||||
self.back_len = back_len;
|
self.back_len = back_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_single_path(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
current_time: DateTime<Utc>,
|
||||||
|
check_existed: bool,
|
||||||
|
) -> Option<String> {
|
||||||
|
let datetime_format: regex::Regex =
|
||||||
|
Regex::new(r"(?:%[YHMSmd](?:[-/:_]?%[YHMSmd])*)").unwrap();
|
||||||
|
let c = self.path_format.get(name).map(|s| {
|
||||||
|
let path = s.clone();
|
||||||
|
let need_formated = datetime_format.captures_iter(&path).collect::<Vec<_>>();
|
||||||
|
let mut result_path = path.clone();
|
||||||
|
|
||||||
|
for need_format in need_formated.iter() {
|
||||||
|
let fmt = need_format.get(0).unwrap().as_str();
|
||||||
|
let t = current_time.format(fmt).to_string();
|
||||||
|
result_path = result_path.replace(fmt, &t);
|
||||||
|
}
|
||||||
|
result_path
|
||||||
|
});
|
||||||
|
if let Some(c) = c {
|
||||||
|
if check_existed {
|
||||||
|
if std::path::Path::new(&c).exists() {
|
||||||
|
Some(c)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_path(
|
pub fn get_path(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
current_time: DateTime<Utc>,
|
current_time: DateTime<Utc>,
|
||||||
check_existed: bool,
|
check_existed: bool,
|
||||||
|
mut max_retry_time: usize,
|
||||||
) -> Option<Vec<(String, DateTime<Utc>)>> {
|
) -> Option<Vec<(String, DateTime<Utc>)>> {
|
||||||
let datetime_format: regex::Regex =
|
let datetime_format: regex::Regex =
|
||||||
Regex::new(r"(?:%[YHMSmd](?:[-/:_]?%[YHMSmd])*)").unwrap();
|
Regex::new(r"(?:%[YHMSmd](?:[-/:_]?%[YHMSmd])*)").unwrap();
|
||||||
@ -258,10 +301,11 @@ impl Dispatcher {
|
|||||||
while fore > 0 {
|
while fore > 0 {
|
||||||
let mut result_path = path.clone();
|
let mut result_path = path.clone();
|
||||||
let t = current_time - self.step * fore as i32;
|
let t = current_time - self.step * fore as i32;
|
||||||
if buffer.iter().position(|(dt, _)| *dt == t).is_some() {
|
if buffer.get(&t).is_some() {
|
||||||
fore = fore - 1;
|
fore = fore - 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for need_format in need_formated.iter() {
|
for need_format in need_formated.iter() {
|
||||||
let fmt = need_format.get(0).unwrap().as_str();
|
let fmt = need_format.get(0).unwrap().as_str();
|
||||||
let t = t.format(fmt).to_string();
|
let t = t.format(fmt).to_string();
|
||||||
@ -269,7 +313,11 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if check_existed {
|
if check_existed {
|
||||||
|
if max_retry_time == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if !std::path::Path::new(&result_path).exists() {
|
if !std::path::Path::new(&result_path).exists() {
|
||||||
|
max_retry_time = max_retry_time - 1;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
result_paths.push((result_path.clone(), t));
|
result_paths.push((result_path.clone(), t));
|
||||||
@ -283,7 +331,8 @@ impl Dispatcher {
|
|||||||
while back < self.back_len {
|
while back < self.back_len {
|
||||||
let mut result_path = path.clone();
|
let mut result_path = path.clone();
|
||||||
let t = current_time + self.step * back as i32;
|
let t = current_time + self.step * back as i32;
|
||||||
if buffer.iter().position(|(dt, _)| *dt == t).is_some() {
|
|
||||||
|
if buffer.get(&t).is_some() {
|
||||||
back = back + 1;
|
back = back + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -294,7 +343,11 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if check_existed {
|
if check_existed {
|
||||||
|
if max_retry_time == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if !std::path::Path::new(&result_path).exists() {
|
if !std::path::Path::new(&result_path).exists() {
|
||||||
|
max_retry_time = max_retry_time - 1;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
result_paths.push((result_path.clone(), t));
|
result_paths.push((result_path.clone(), t));
|
||||||
@ -313,12 +366,13 @@ impl Dispatcher {
|
|||||||
macro_rules! match_in_macro {
|
macro_rules! match_in_macro {
|
||||||
($block:ident,$name:literal, $(($branch:path, $t:ty, $color:expr)),+) => {
|
($block:ident,$name:literal, $(($branch:path, $t:ty, $color:expr)),+) => {
|
||||||
{
|
{
|
||||||
|
let datetime = Utc.timestamp_opt($block.datetime, 0).unwrap();
|
||||||
match $block.data_type {
|
match $block.data_type {
|
||||||
$(
|
$(
|
||||||
$branch => {
|
$branch => {
|
||||||
let data: $t = $block.into();
|
let data: $t = $block.into();
|
||||||
let layer = Layer::grid_render_layer(data, format!($name), $color);
|
let layer = Layer::grid_render_layer(data, format!($name), $color);
|
||||||
Some((Utc::now() ,layer))
|
Some(( datetime ,layer))
|
||||||
},
|
},
|
||||||
)+
|
)+
|
||||||
_ => None
|
_ => None
|
||||||
@ -328,7 +382,7 @@ macro_rules! match_in_macro {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_to_layer(block: Block) -> Option<(DateTime<Utc>, Layer)> {
|
pub fn data_to_layer(block: Block) -> Option<(DateTime<Utc>, Layer)> {
|
||||||
use crate::utils::*;
|
use crate::utils::*;
|
||||||
use radarg_plugin_interface::PluginResultType;
|
use radarg_plugin_interface::PluginResultType;
|
||||||
match block.shape {
|
match block.shape {
|
||||||
|
|||||||
@ -1,317 +0,0 @@
|
|||||||
use super::exterior::ExteriorWidget;
|
|
||||||
use super::interior::InteriorWidget;
|
|
||||||
use super::{Layer, WindowCoord};
|
|
||||||
use crate::coords::proj::Mercator;
|
|
||||||
use crate::coords::Mapper;
|
|
||||||
use femtovg::{Canvas, Color, FontId, Renderer};
|
|
||||||
use gtk::glib;
|
|
||||||
use gtk::subclass::prelude::*;
|
|
||||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::num::NonZeroU32;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
|
||||||
pub struct RenderConfig {
|
|
||||||
pub padding: [f32; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum RenderMotion {
|
|
||||||
Translate,
|
|
||||||
Scale,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RenderMotion {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct RenderStatus {
|
|
||||||
pub(super) window_size: Option<(i32, i32)>,
|
|
||||||
pub(super) scale_rate: Option<f64>,
|
|
||||||
pub(super) pointer_location: WindowCoord,
|
|
||||||
pub(super) motion: RenderMotion,
|
|
||||||
pub(super) scale: f32,
|
|
||||||
pub(super) translate: Option<(f32, f32)>,
|
|
||||||
pub(super) view_range: Option<((f64, f64), (f64, f64))>,
|
|
||||||
translation: Option<(f64, f64)>,
|
|
||||||
init_translation: (f64, f64),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Render {
|
|
||||||
pub(super) exterior: RefCell<ExteriorWidget>,
|
|
||||||
pub(super) interior: RefCell<InteriorWidget>,
|
|
||||||
pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
|
||||||
pub config: RefCell<RenderConfig>,
|
|
||||||
pub status: RefCell<RenderStatus>,
|
|
||||||
pub mapper: RefCell<Mapper>,
|
|
||||||
pub(super) interior_layers: RefCell<Vec<Layer>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Render {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
exterior: RefCell::new(ExteriorWidget::default()),
|
|
||||||
interior: RefCell::new(InteriorWidget::default()),
|
|
||||||
interior_layers: RefCell::new(Vec::new()),
|
|
||||||
config: RefCell::new(RenderConfig::default()),
|
|
||||||
status: RefCell::new(RenderStatus::default()),
|
|
||||||
mapper: RefCell::new(Mercator::default().into()),
|
|
||||||
canvas: RefCell::new(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[glib::object_subclass]
|
|
||||||
impl ObjectSubclass for Render {
|
|
||||||
const NAME: &'static str = "Render";
|
|
||||||
type Type = super::Render;
|
|
||||||
type ParentType = gtk::GLArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for Render {
|
|
||||||
fn constructed(&self) {
|
|
||||||
self.parent_constructed();
|
|
||||||
let area = self.obj();
|
|
||||||
area.set_has_stencil_buffer(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetImpl for Render {}
|
|
||||||
|
|
||||||
impl GLAreaImpl for Render {
|
|
||||||
fn resize(&self, width: i32, height: i32) {
|
|
||||||
self.status.borrow_mut().window_size = Some((width, height));
|
|
||||||
self.ensure_canvas();
|
|
||||||
let mut canvas = self.canvas.borrow_mut();
|
|
||||||
let canvas = canvas.as_mut().unwrap();
|
|
||||||
canvas.set_size(
|
|
||||||
width as u32,
|
|
||||||
height as u32,
|
|
||||||
self.obj().scale_factor() as f32,
|
|
||||||
);
|
|
||||||
|
|
||||||
let translation = self.status.borrow().translation;
|
|
||||||
let scale_rate = self.status.borrow().scale_rate;
|
|
||||||
let mapper = self.mapper.borrow();
|
|
||||||
|
|
||||||
if let None = translation {
|
|
||||||
let mut status = self.status.borrow_mut();
|
|
||||||
status.translation = Some((mapper.get_bounds().0, mapper.get_bounds().2));
|
|
||||||
status.init_translation = (mapper.get_bounds().0, mapper.get_bounds().2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let None = scale_rate {
|
|
||||||
let scale = (mapper.get_bounds().3 - mapper.get_bounds().2) / canvas.height() as f64;
|
|
||||||
self.status.borrow_mut().scale_rate = Some(scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
|
||||||
self.ensure_canvas();
|
|
||||||
let configs = self.config.borrow();
|
|
||||||
let (w, h) = {
|
|
||||||
let mut canvas = self.canvas.borrow_mut();
|
|
||||||
let canvas = canvas.as_mut().unwrap();
|
|
||||||
|
|
||||||
let dpi = self.obj().scale_factor();
|
|
||||||
let w = canvas.width();
|
|
||||||
let h = canvas.height();
|
|
||||||
|
|
||||||
canvas.clear_rect(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
(w as i32 * dpi) as u32,
|
|
||||||
(h as i32 * dpi) as u32,
|
|
||||||
Color::rgba(0, 0, 0, 255),
|
|
||||||
);
|
|
||||||
(w, h)
|
|
||||||
};
|
|
||||||
|
|
||||||
let render_range = self.status.borrow().view_range.clone();
|
|
||||||
if let Some(((lat1, lat2), (lon1, lon2))) = render_range {
|
|
||||||
let mut status = self.status.borrow_mut();
|
|
||||||
|
|
||||||
let mapper = self.mapper.borrow();
|
|
||||||
|
|
||||||
let (tx, ty) = mapper.map((lon1, lat1)).unwrap();
|
|
||||||
status.translation.replace((tx, ty));
|
|
||||||
|
|
||||||
let (lon1, lat1) = mapper.map((lon1, lat1)).unwrap();
|
|
||||||
let (lon2, lat2) = mapper.map((lon2, lat2)).unwrap();
|
|
||||||
|
|
||||||
let scale = ((lat1 - lat2).abs() / h as f64).max((lon1 - lon2).abs() / w as f64);
|
|
||||||
status.scale_rate.replace(scale);
|
|
||||||
|
|
||||||
status.view_range = None;
|
|
||||||
} else {
|
|
||||||
let mut status = self.status.borrow_mut();
|
|
||||||
match status.motion {
|
|
||||||
RenderMotion::Translate => {
|
|
||||||
if let Some((x, y)) = status.translate {
|
|
||||||
let (ix, iy) = status.init_translation;
|
|
||||||
status.translation = Some((
|
|
||||||
ix + x as f64 * status.scale_rate.unwrap(),
|
|
||||||
iy + y as f64 * status.scale_rate.unwrap(),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
status.init_translation = status.translation.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RenderMotion::Scale => {
|
|
||||||
let scale_rate = status.scale_rate.unwrap();
|
|
||||||
let scale_flag = status.scale as f64;
|
|
||||||
let step = scale_rate * 0.1;
|
|
||||||
|
|
||||||
let (tx, ty) = status.translation.unwrap();
|
|
||||||
let (px, py) = status.pointer_location;
|
|
||||||
|
|
||||||
let scaled = scale_rate + scale_flag * step;
|
|
||||||
status.scale_rate = Some(scaled);
|
|
||||||
let sx = scale_flag * step * px as f64;
|
|
||||||
let sy = scale_flag * step * py as f64;
|
|
||||||
status.translation = Some((tx - sx, ty - sy));
|
|
||||||
status.init_translation = status.translation.unwrap();
|
|
||||||
}
|
|
||||||
RenderMotion::None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let c = &(*self.interior_layers.borrow());
|
|
||||||
self.interior
|
|
||||||
.borrow()
|
|
||||||
.draw(c, &self.obj(), self.status.borrow(), configs);
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut canvas = self.canvas.borrow_mut();
|
|
||||||
let canvas = canvas.as_mut().unwrap();
|
|
||||||
self.exterior.borrow().draw(canvas, &self.obj());
|
|
||||||
canvas.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render {
|
|
||||||
fn ensure_canvas(&self) {
|
|
||||||
use femtovg::{renderer, Canvas};
|
|
||||||
use glow::HasContext;
|
|
||||||
|
|
||||||
if self.canvas.borrow().is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let widget = self.obj();
|
|
||||||
widget.attach_buffers();
|
|
||||||
|
|
||||||
static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
|
|
||||||
|s| epoxy::get_proc_addr(s) as *const _;
|
|
||||||
// SAFETY: Need to get the framebuffer id that gtk expects us to draw into, so femtovg
|
|
||||||
// knows which framebuffer to bind. This is safe as long as we call attach_buffers
|
|
||||||
// beforehand. Also unbind it here just in case, since this can be called outside render.
|
|
||||||
let (mut renderer, fbo) = unsafe {
|
|
||||||
let renderer =
|
|
||||||
renderer::OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
|
|
||||||
let ctx = glow::Context::from_loader_function(LOAD_FN);
|
|
||||||
let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32)
|
|
||||||
.expect("No GTK provided framebuffer binding");
|
|
||||||
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
|
|
||||||
(renderer, glow::NativeFramebuffer(id))
|
|
||||||
};
|
|
||||||
renderer.set_screen_target(Some(fbo));
|
|
||||||
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
|
|
||||||
canvas
|
|
||||||
.add_font_dir(std::path::Path::new("./src/assets"))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.canvas.replace(Some(canvas));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn window_size(&self) -> Option<(i32, i32)> {
|
|
||||||
self.status.borrow().window_size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn view_range(&self) -> Option<((f64, f64), (f64, f64))> {
|
|
||||||
let padding = self.padding();
|
|
||||||
let (w, h) = self.window_size().unwrap();
|
|
||||||
let (w, h) = (
|
|
||||||
w as f32 - padding[1] - padding[3],
|
|
||||||
h as f32 - padding[0] - padding[2],
|
|
||||||
);
|
|
||||||
let (w, h) = (w as f64, h as f64);
|
|
||||||
|
|
||||||
let mapper = self.mapper.borrow();
|
|
||||||
|
|
||||||
let status = self.status.borrow();
|
|
||||||
status.translation.and_then(|(tx, ty)| {
|
|
||||||
status.scale_rate.and_then(|scale| {
|
|
||||||
let (x1, y1) = (tx + padding[3] as f64, ty + padding[2] as f64);
|
|
||||||
let (x2, y2) = (
|
|
||||||
tx + w * scale + padding[3] as f64,
|
|
||||||
ty + h * scale + padding[2] as f64,
|
|
||||||
);
|
|
||||||
let (x1, y1) = mapper.inverse_map((x1, y1)).unwrap();
|
|
||||||
let (x2, y2) = mapper.inverse_map((x2, y2)).unwrap();
|
|
||||||
Some(((x1, x2), (y1, y2)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn inverse_map(&self, loc: (f32, f32)) -> Option<(f64, f64)> {
|
|
||||||
let (x, y) = loc;
|
|
||||||
let status = self.status.borrow();
|
|
||||||
status.translation.and_then(|(tx, ty)| {
|
|
||||||
status.scale_rate.and_then(|scale| {
|
|
||||||
let (x, y) = (x as f64, y as f64);
|
|
||||||
Some((tx + x * scale, (ty + y * scale)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn padding(&self) -> [f32; 4] {
|
|
||||||
self.config.borrow().padding
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
|
||||||
let (x, y) = loc;
|
|
||||||
let (_, h) = self.window_size().unwrap();
|
|
||||||
let status = self.status.borrow();
|
|
||||||
status.translation.and_then(|(tx, ty)| {
|
|
||||||
status.scale_rate.and_then(|scale| {
|
|
||||||
Some((
|
|
||||||
(x - tx as f64) as f32 / scale as f32,
|
|
||||||
h as f32 - (y - ty as f64) as f32 / scale as f32,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn set_translation(&self, translation: (f64, f64)) {
|
|
||||||
let mut status = self.status.borrow_mut();
|
|
||||||
status.translation = Some(translation);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pointer_loc(&self) -> (f32, f32) {
|
|
||||||
let (x, y) = self.status.borrow().pointer_location.clone();
|
|
||||||
let (_, h) = self.window_size().unwrap();
|
|
||||||
(x, h as f32 - y)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn set_view(&self, range: (f64, f64, f64, f64)) {
|
|
||||||
let (lat1, lat2, lon1, lon2) = range;
|
|
||||||
self.status
|
|
||||||
.borrow_mut()
|
|
||||||
.view_range
|
|
||||||
.replace(((lat1, lat2), (lon1, lon2)));
|
|
||||||
// if let Some((w, h)) = self.window_size() {
|
|
||||||
// println!("w:{}, h:{}", w, h);
|
|
||||||
// let scale = ((lat1 - lat2).abs() / h as f64).max((lon1 - lon2).abs() / w as f64);
|
|
||||||
// self.status.borrow_mut().scale_rate.replace(scale);
|
|
||||||
// self.set_translation((lat1, lon1));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
src/utils.rs
10
src/utils.rs
@ -89,6 +89,7 @@ pub fn create_dbz_boundarynorm() -> BoundaryNorm<i8> {
|
|||||||
Color::rgb(139, 0, 255),
|
Color::rgb(139, 0, 255),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +116,7 @@ pub fn create_vel_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(255, 0, 0),
|
Color::rgb(255, 0, 0),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +144,7 @@ pub fn create_phidp_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(193, 0, 0),
|
Color::rgb(193, 0, 0),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +169,7 @@ pub fn create_zdr_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(0, 192, 39),
|
Color::rgb(0, 192, 39),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +196,7 @@ pub fn create_cc_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(193, 0, 0),
|
Color::rgb(193, 0, 0),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +222,7 @@ pub fn create_vil_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(255, 255, 255),
|
Color::rgb(255, 255, 255),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +248,7 @@ pub fn create_hgt_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(255, 255, 255),
|
Color::rgb(255, 255, 255),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,6 +279,7 @@ pub fn create_et_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(139, 0, 0),
|
Color::rgb(139, 0, 0),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,6 +300,7 @@ pub fn create_cpc_boundarynorm() -> BoundaryNorm<i8> {
|
|||||||
Color::rgb(187, 165, 204),
|
Color::rgb(187, 165, 204),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,5 +328,6 @@ pub fn create_kdp_boundarynorm() -> BoundaryNorm<f32> {
|
|||||||
Color::rgb(253, 6, 253),
|
Color::rgb(253, 6, 253),
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
|
-125.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,10 @@ use super::{Layer, WindowCoord};
|
|||||||
use crate::coords::proj::Mercator;
|
use crate::coords::proj::Mercator;
|
||||||
use crate::coords::Mapper;
|
use crate::coords::Mapper;
|
||||||
use femtovg::{Canvas, Color, FontId, Renderer};
|
use femtovg::{Canvas, Color, FontId, Renderer};
|
||||||
use gtk::glib;
|
use gtk::glib::{self, prelude::*, Properties};
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||||
@ -41,7 +41,11 @@ pub struct RenderStatus {
|
|||||||
init_translation: (f64, f64),
|
init_translation: (f64, f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Properties)]
|
||||||
|
#[properties(wrapper_type = super::Render)]
|
||||||
pub struct Render {
|
pub struct Render {
|
||||||
|
#[property(get, set)]
|
||||||
|
render_status: Cell<i64>,
|
||||||
pub(super) exterior: RefCell<ExteriorWidget>,
|
pub(super) exterior: RefCell<ExteriorWidget>,
|
||||||
pub(super) interior: RefCell<InteriorWidget>,
|
pub(super) interior: RefCell<InteriorWidget>,
|
||||||
pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
||||||
@ -54,6 +58,7 @@ pub struct Render {
|
|||||||
impl Default for Render {
|
impl Default for Render {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
render_status: Cell::new(0),
|
||||||
exterior: RefCell::new(ExteriorWidget::default()),
|
exterior: RefCell::new(ExteriorWidget::default()),
|
||||||
interior: RefCell::new(InteriorWidget::default()),
|
interior: RefCell::new(InteriorWidget::default()),
|
||||||
interior_layers: RefCell::new(Vec::new()),
|
interior_layers: RefCell::new(Vec::new()),
|
||||||
@ -78,6 +83,18 @@ impl ObjectImpl for Render {
|
|||||||
let area = self.obj();
|
let area = self.obj();
|
||||||
area.set_has_stencil_buffer(true);
|
area.set_has_stencil_buffer(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
Self::derived_properties()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(&self, _id: usize, _value: &glib::Value, _pspec: &glib::ParamSpec) {
|
||||||
|
Self::derived_set_property(&self, _id, _value, _pspec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property(&self, _id: usize, _pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
|
Self::derived_property(&self, _id, _pspec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetImpl for Render {}
|
impl WidgetImpl for Render {}
|
||||||
@ -139,6 +156,7 @@ impl GLAreaImpl for Render {
|
|||||||
|
|
||||||
let (tx, ty) = mapper.map((lon1, lat1)).unwrap();
|
let (tx, ty) = mapper.map((lon1, lat1)).unwrap();
|
||||||
status.translation.replace((tx, ty));
|
status.translation.replace((tx, ty));
|
||||||
|
status.init_translation = (tx, ty);
|
||||||
|
|
||||||
let (lon1, lat1) = mapper.map((lon1, lat1)).unwrap();
|
let (lon1, lat1) = mapper.map((lon1, lat1)).unwrap();
|
||||||
let (lon2, lat2) = mapper.map((lon2, lat2)).unwrap();
|
let (lon2, lat2) = mapper.map((lon2, lat2)).unwrap();
|
||||||
@ -191,7 +209,6 @@ impl GLAreaImpl for Render {
|
|||||||
self.exterior.borrow().draw(canvas, &self.obj());
|
self.exterior.borrow().draw(canvas, &self.obj());
|
||||||
canvas.flush();
|
canvas.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,11 +324,5 @@ impl Render {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.view_range
|
.view_range
|
||||||
.replace(((lat1, lat2), (lon1, lon2)));
|
.replace(((lat1, lat2), (lon1, lon2)));
|
||||||
// if let Some((w, h)) = self.window_size() {
|
|
||||||
// println!("w:{}, h:{}", w, h);
|
|
||||||
// let scale = ((lat1 - lat2).abs() / h as f64).max((lon1 - lon2).abs() / w as f64);
|
|
||||||
// self.status.borrow_mut().scale_rate.replace(scale);
|
|
||||||
// self.set_translation((lat1, lon1));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ mod imp;
|
|||||||
mod interior;
|
mod interior;
|
||||||
pub mod predefined;
|
pub mod predefined;
|
||||||
mod renders;
|
mod renders;
|
||||||
|
pub mod widget;
|
||||||
pub use self::cms::CMS;
|
pub use self::cms::CMS;
|
||||||
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
|
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
|
||||||
use crate::coords::Mapper;
|
use crate::coords::Mapper;
|
||||||
@ -126,6 +127,7 @@ impl Render {
|
|||||||
pub fn set_interior_layers(&self, layers: Vec<Layer>) {
|
pub fn set_interior_layers(&self, layers: Vec<Layer>) {
|
||||||
self.imp().interior_layers.replace(layers);
|
self.imp().interior_layers.replace(layers);
|
||||||
self.queue_render();
|
self.queue_render();
|
||||||
|
self.set_render_status(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||||
|
|||||||
@ -2,8 +2,18 @@ use std::fmt::Debug;
|
|||||||
|
|
||||||
use femtovg::Color;
|
use femtovg::Color;
|
||||||
use num_traits::NumOps;
|
use num_traits::NumOps;
|
||||||
pub trait ColorMapper<T: NumOps + PartialOrd> : Debug + Send + Sync {
|
pub trait ColorMapper<T: NumOps + PartialOrd>: Debug + Send + Sync {
|
||||||
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color>;
|
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color>;
|
||||||
|
// fn map_min_to_max(&self, min: T, max: T, invalid_value: T, step: usize) -> Vec<femtovg::Color> {
|
||||||
|
// let mut colors = Vec::new();
|
||||||
|
// for i in (min..=max).step_by(step) {
|
||||||
|
// colors.push(self.map_value_to_color(i, invalid_value).unwrap());
|
||||||
|
// }
|
||||||
|
// colors
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn min_max(&self) -> (T, T);
|
||||||
|
fn invalid(&self) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -11,6 +21,7 @@ pub struct BoundaryNorm<T: NumOps + PartialOrd> {
|
|||||||
boundaries: Vec<T>,
|
boundaries: Vec<T>,
|
||||||
extrand: bool,
|
extrand: bool,
|
||||||
colors: Vec<femtovg::Color>,
|
colors: Vec<femtovg::Color>,
|
||||||
|
invalid_value: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BoundaryNorm<i8> {
|
impl Default for BoundaryNorm<i8> {
|
||||||
@ -35,17 +46,24 @@ impl Default for BoundaryNorm<i8> {
|
|||||||
Color::rgb(212, 142, 254),
|
Color::rgb(212, 142, 254),
|
||||||
Color::rgb(170, 36, 250),
|
Color::rgb(170, 36, 250),
|
||||||
],
|
],
|
||||||
|
invalid_value: -125,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: NumOps + PartialOrd> BoundaryNorm<T> {
|
impl<T: NumOps + PartialOrd> BoundaryNorm<T> {
|
||||||
pub fn new(boundaries: Vec<T>, colors: Vec<femtovg::Color>, extrand: bool) -> Self {
|
pub fn new(
|
||||||
|
boundaries: Vec<T>,
|
||||||
|
colors: Vec<femtovg::Color>,
|
||||||
|
extrand: bool,
|
||||||
|
invalid_value: T,
|
||||||
|
) -> Self {
|
||||||
// assert_eq!(boundaries.len(), colors.len() + 1);
|
// assert_eq!(boundaries.len(), colors.len() + 1);
|
||||||
BoundaryNorm {
|
BoundaryNorm {
|
||||||
boundaries,
|
boundaries,
|
||||||
extrand,
|
extrand,
|
||||||
colors,
|
colors,
|
||||||
|
invalid_value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,4 +87,15 @@ impl<T: NumOps + PartialOrd + Debug + Send + Sync> ColorMapper<T> for BoundaryNo
|
|||||||
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
|
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
|
||||||
self.map_value_to_color(value, invalid_value)
|
self.map_value_to_color(value, invalid_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn min_max(&self) -> (T, T) {
|
||||||
|
(
|
||||||
|
*self.boundaries.first().unwrap(),
|
||||||
|
*self.boundaries.last().unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid(&self) -> T {
|
||||||
|
self.invalid_value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
pub mod color_mapper;
|
pub mod color_mapper;
|
||||||
pub mod grid_field_renderer;
|
|
||||||
pub mod gis;
|
pub mod gis;
|
||||||
|
pub mod grid_field_renderer;
|
||||||
pub mod layers;
|
pub mod layers;
|
||||||
|
pub mod widgets;
|
||||||
|
|||||||
51
src/widgets/render/predefined/widgets.rs
Normal file
51
src/widgets/render/predefined/widgets.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use femtovg::{Color, Paint, Path};
|
||||||
|
use std::iter::Step;
|
||||||
|
|
||||||
|
use super::{super::widget::Widget as WidgetTrait, color_mapper::ColorMapper};
|
||||||
|
pub struct ColorBar<V, T>
|
||||||
|
where
|
||||||
|
V: num_traits::NumOps + PartialOrd + Step,
|
||||||
|
T: ColorMapper<V>,
|
||||||
|
{
|
||||||
|
color_mapper: T,
|
||||||
|
padding: [f32; 4],
|
||||||
|
width: Option<f32>,
|
||||||
|
height: Option<f32>,
|
||||||
|
origin: (f32, f32),
|
||||||
|
phantom: std::marker::PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, T> WidgetTrait for ColorBar<V, T>
|
||||||
|
where
|
||||||
|
V: num_traits::NumOps + PartialOrd + Step,
|
||||||
|
T: ColorMapper<V>,
|
||||||
|
{
|
||||||
|
fn render(&self, layer: crate::widgets::Layer, render: crate::widgets::Render) {
|
||||||
|
let (w, h) = render.window_size();
|
||||||
|
let bar_width = self.width.unwrap_or(w as f32) - self.padding[1] - self.padding[3];
|
||||||
|
let bar_height = self.height.unwrap_or(10.0);
|
||||||
|
let (x, y) = self.origin;
|
||||||
|
|
||||||
|
let (l, ll) = self.color_mapper.min_max();
|
||||||
|
let invalid = self.color_mapper.invalid();
|
||||||
|
let colors = self.color_mapper.map_min_to_max(l, ll, invalid, 100);
|
||||||
|
|
||||||
|
let b_w = w as f32 / 100.0;
|
||||||
|
|
||||||
|
let mut canvas = render.get_canvas();
|
||||||
|
|
||||||
|
if let Some(canvas) = canvas.as_mut() {
|
||||||
|
for (i, color) in colors.into_iter().enumerate() {
|
||||||
|
let x = x + i as f32 * b_w;
|
||||||
|
let y = y;
|
||||||
|
let w = b_w;
|
||||||
|
let h = bar_height;
|
||||||
|
|
||||||
|
let mut path = Path::new();
|
||||||
|
path.rect(x, y, w, h);
|
||||||
|
let paint = Paint::color(color);
|
||||||
|
canvas.fill_path(&path, &paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/widgets/render/widget/mod.rs
Normal file
3
src/widgets/render/widget/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod widget;
|
||||||
|
|
||||||
|
pub use widget::Widget;
|
||||||
7
src/widgets/render/widget/widget.rs
Normal file
7
src/widgets/render/widget/widget.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use crate::widgets::{Layer, Render, CMS};
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub trait Widget {
|
||||||
|
fn render(&self, layer: Layer, render: Render);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user