-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.rs
More file actions
307 lines (259 loc) · 10.4 KB
/
app.rs
File metadata and controls
307 lines (259 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
use std::{borrow::Cow, mem};
use crate::{
cache::Cache,
gui::{
dock_state::{DockState, TabType},
node_graph::{NodeGraphEditState, NodeGraphEditor},
},
node_graph::NodeId,
pipeline::{self, nodes},
view::{
execution::executor::ViewsExecutor,
views,
views_manager::{DataViewsManager, DataViewsManagerBuilder},
DataViewsState,
},
};
pub struct IVOCTApp {
/// High level pipeline description.
pipeline: pipeline::Pipeline,
/// Editing specific information (Where are the nodes placed).
pipeline_edit_state: NodeGraphEditState,
/// System responsible for executing the pipeline, described py [pipeline].
pipeline_executor: pipeline::PipelineExecutor,
/// High level description of data views (Views that show data from the pipeline).
data_views_state: DataViewsState,
/// System responsible for modifying [data_views_state], hooking them into
/// the pipeline and creating tabs in the UI for them.
data_views_manager: DataViewsManager,
/// System responsible for execution of the data views.
data_views_executor: ViewsExecutor,
/// States of all tabs in the UI.
dock_state: DockState,
/// Key-value cache to reduce redundancy (Mostly when uploading large
/// resources to the GPU, for example when multiple views show partly the
/// same data).
cache: Cache,
/// The node that got double clicked by the User.
interacted_node: Option<NodeId>,
/// Whether and the pipeline to load (JSON). Set in [pipeline_menu_bar],
/// used in [update].
load_pipeline: Option<Cow<'static, str>>,
}
impl IVOCTApp {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// Whether and the pipeline the user had open in the last session (JSON)
let pipeline_json = cc.storage.unwrap().get_string("user_pipeline");
let pipeline_json: Cow<_> = match pipeline_json {
Some(json) => json.into(),
// Use phantom1_1_3 by default
None => pipeline::presets::PHANTOM_1_1_3.into(),
};
let (pipeline, state) = Self::load_pipeline(&pipeline_json);
IVOCTApp {
pipeline,
pipeline_edit_state: state,
pipeline_executor: pipeline::PipelineExecutor::new(),
data_views_state: DataViewsState::new(),
data_views_manager: DataViewsManagerBuilder::new(
&cc.wgpu_render_state.as_ref().unwrap(),
)
// Add all available views, so the DataViewsManager can create them
.with_view::<views::data_vector::View>()
.with_view::<views::m_scan::View>()
.with_view::<views::mesh::View>()
.build(),
data_views_executor: ViewsExecutor::new(),
dock_state: DockState::new(),
cache: Cache::new(),
interacted_node: None,
load_pipeline: None,
}
}
fn load_pipeline(pipeline_json: &str) -> (pipeline::Pipeline, NodeGraphEditState) {
let (mut pipeline, state) = serde_json::from_str(pipeline_json).unwrap_or_else(|e| {
eprintln!("Error loading pipeline: {}", e);
(pipeline::Pipeline::new(), NodeGraphEditState::new())
});
// Clear all paths that do not exist
for (_, node) in &mut pipeline.nodes {
if let Some(node) = node
.as_any_mut()
.downcast_mut::<nodes::binary_input::Node>()
{
if !node.path.exists() {
node.path = "".into();
}
} else if let Some(node) = node.as_any_mut().downcast_mut::<nodes::output::Node>() {
if !node.path.exists() {
node.path = "".into();
}
}
}
(pipeline, state)
}
fn set_pipeline(&mut self, pipeline: pipeline::Pipeline, state: NodeGraphEditState) {
self.pipeline = pipeline;
self.pipeline_edit_state = state;
self.pipeline_executor.clear();
self.data_views_state.clear();
self.dock_state.close_all_views();
}
}
// MARK: impl App
impl eframe::App for IVOCTApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
self.interacted_node = None;
// Satisfy Borrow Checker: Move dock_state onto the current stack frame
let mut dock_state = mem::replace(&mut self.dock_state, DockState::new());
// Render all tabs
egui_dock::DockArea::new(&mut dock_state)
.style(egui_dock::Style::from_egui(ctx.style().as_ref()))
.show(ctx, self);
// Move dock_state back into the app struct
self.dock_state = dock_state;
// Update data view high level description (Create new, reconnect, or
// delete)
self.data_views_manager.update(
&mut self.data_views_state,
&mut self.pipeline,
&mut self.dock_state,
self.interacted_node,
&self.cache,
frame.wgpu_render_state().unwrap(),
ctx.input(|i| i.modifiers.ctrl),
);
// Merge differences between high level pipeline description and
// execution system
self.pipeline_executor.update(&mut self.pipeline);
// Same for data views. They might connect into the pipeline_executor
self.data_views_executor
.update(&mut self.data_views_state, &self.pipeline_executor);
// User requested to load new pipeline in this frame
if let Some(json) = self.load_pipeline.take() {
let (pipeline, state) = Self::load_pipeline(&json);
self.set_pipeline(pipeline, state);
}
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {
// Called in regular intervals
println!("Saving");
let pipeline = serde_json::to_string(&(&self.pipeline, &self.pipeline_edit_state)).unwrap();
storage.set_string("user_pipeline", pipeline)
}
}
// MARK: impl TabViewer
impl egui_dock::TabViewer for IVOCTApp {
type Tab = TabType;
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
match tab {
TabType::Pipeline => "Pipeline".into(),
TabType::DataView(view_id) => {
format!("Data View {:?}", Into::<usize>::into(*view_id)).into()
}
}
}
fn force_close(&mut self, tab: &mut Self::Tab) -> bool {
// Close data views that are either non-existent anymore or have no
// inputs
matches!(
tab,
TabType::DataView(view_id) if self
.data_views_state
.get(*view_id)
.map_or(true, |v| v.inputs().is_empty())
)
}
fn closeable(&mut self, tab: &mut Self::Tab) -> bool {
match tab {
TabType::Pipeline => false,
_ => true,
}
}
fn scroll_bars(&self, _tab: &Self::Tab) -> [bool; 2] {
[false, false]
}
fn id(&mut self, tab: &mut Self::Tab) -> egui::Id {
match tab {
TabType::Pipeline => egui::Id::new("Pipeline"),
TabType::DataView(view_id) => egui::Id::new(view_id).with("DataView"),
}
}
fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
match tab {
TabType::Pipeline => {
self.pipeline_menu_bar(ui);
let _response =
NodeGraphEditor::new(&mut self.pipeline, &mut self.pipeline_edit_state)
.show(ui);
// User double clicked a node
if let Some(interacted_node) = _response.activated {
self.interacted_node = Some(interacted_node);
}
}
TabType::DataView(view_id) => {
if let Some(view) = self.data_views_state.get_mut(*view_id) {
view.ui(ui);
} else {
ui.label(format!(
"Data View {:?} does not exist, You can close this tab.",
Into::<usize>::into(*view_id)
));
}
}
}
}
}
// MARK: Pipeline Menu Bar
impl IVOCTApp {
fn pipeline_menu_bar(&mut self, ui: &mut egui::Ui) {
egui::menu::bar(ui, |ui| {
ui.menu_button("File", |ui| {
if ui.button("Open").clicked() {
let file = native_dialog::FileDialog::new()
.add_filter("JSON", &["json"])
.set_title("Open Pipeline")
.show_open_single_file();
if let Ok(Some(file)) = file {
match std::fs::read_to_string(file) {
Ok(json) => self.load_pipeline = Some(json.into()),
Err(e) => eprintln!("Error loading pipeline: {}", e),
}
}
ui.close_menu();
}
ui.menu_button("Presets", |ui| {
if ui.button("Phantom 1.1.3").clicked() {
self.load_pipeline = Some(pipeline::presets::PHANTOM_1_1_3.into());
ui.close_menu();
}
if ui.button("Phantom 1.2.4").clicked() {
self.load_pipeline = Some(pipeline::presets::PHANTOM_1_2_4.into());
ui.close_menu();
}
if ui.button("Clinic").clicked() {
self.load_pipeline = Some(pipeline::presets::CLINIC.into());
ui.close_menu();
}
});
if ui.button("Save").clicked() {
let file = native_dialog::FileDialog::new()
.add_filter("JSON", &["json"])
.set_title("Save Pipeline")
.show_save_single_file();
if let Ok(Some(file)) = file {
let serialized = serde_json::to_string_pretty(&(
&self.pipeline,
&self.pipeline_edit_state,
))
.unwrap();
if let Err(e) = std::fs::write(file, serialized) {
eprintln!("Error saving pipeline: {}", e);
}
}
ui.close_menu();
}
});
});
}
}