riCOM_cpp
This repository contains the C++ implementation of the riCOM (Real Time Centre Of Mass) algorithm for 4D Scanning electron microscopy.
Loading...
Searching...
No Matches
RunGUI.cpp
Go to the documentation of this file.
1/* Copyright (C) 2021 Thomas Friedrich, Chu-Ping Yu,
2 * University of Antwerp - All Rights Reserved.
3 * You may use, distribute and modify
4 * this code under the terms of the GPL3 license.
5 * You should have received a copy of the GPL3 license with
6 * this file. If not, please visit:
7 * https://www.gnu.org/licenses/gpl-3.0.en.html
8 *
9 * Authors:
10 * Thomas Friedrich <thomas.friedrich@uantwerpen.be>
11 * Chu-Ping Yu <chu-ping.yu@uantwerpen.be>
12 */
13
14#ifdef _MSC_VER
15#define _CRT_SECURE_NO_DEPRECATE
16#define _CRT_SECURE_NO_WARNINGS
17#pragma warning(disable : 4067)
18#pragma warning(disable : 4333)
19#pragma warning(disable : 4312)
20#endif
21
22#if defined(__GNUC__)
23#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
24#pragma GCC diagnostic ignored "-Wformat-security"
25#endif
26
27#if defined(__clang__)
28#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
29#pragma GCC diagnostic ignored "-Wformat-security"
30#endif
31
32#include <string>
33#include <thread>
34#include <iostream>
35#include <complex>
36#include <map>
37
38#include "imgui.h"
39#include "imgui_impl_sdl.h"
40#include "imgui_impl_opengl3.h"
41#include "imgui_stdlib.h"
42#include "imgui_internal.h"
43
44#include "imfilebrowser.h"
45#define MINI_CASE_SENSITIVE
46#include "ini.h"
47#include "ImGuiINI.hpp"
48
49#include "Ricom.h"
50#include "fonts.h"
51#include "Camera.h"
52#include "GuiUtils.h"
53#include "ImGuiImageWindow.h"
54
55// Macros
56#define GENERIC_WINDOW(name) (generic_windows_f.find(name)->second)
57#define GENERIC_WINDOW_C(name) (generic_windows_c.find(name)->second)
58
59// Forward Declarations
60template <typename T>
61inline void update_views(std::map<std::string, ImGuiImageWindow<T>> &generic_windows_f, Ricom *ricom, bool b_restarted, bool trigger, bool b_redraw);
62inline void bind_tex(SDL_Surface *srf, GLuint tex_id);
63
65// GUI implementation //
67
68int run_gui(Ricom *ricom, CAMERA::Default_configurations &hardware_configurations)
69{
70 std::thread run_thread;
71 std::thread py_thread;
72 if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
73 {
74 printf("Error: %s\n", SDL_GetError());
75 return -1;
76 }
77
78 // Decide GL+GLSL versions
79#if defined(IMGUI_IMPL_OPENGL_ES2)
80 // GL ES 2.0 + GLSL 100
81 const char *glsl_version = "#version 100";
82 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
83 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
85 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
86#elif defined(__APPLE__)
87 // GL 3.2 Core + GLSL 150
88 const char *glsl_version = "#version 150";
89 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
90 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
91 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
93#else
94 // GL 3.0 + GLSL 130
95 const char *glsl_version = "#version 130";
96 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
97 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
98 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
99 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
100#endif
101
102 // Create window with graphics context
103 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
104 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
105 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
106 SDL_Window *window = SDL_CreateWindow("riCOM", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1200, 800, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
107 if (window == NULL)
108 {
109 std::cout << "Window could not be created! SDL Error: " << SDL_GetError() << std::endl;
110 }
111 SDL_GLContext gl_context = SDL_GL_CreateContext(window);
112 SDL_GL_MakeCurrent(window, gl_context);
113 SDL_GL_SetSwapInterval(1); // Enable vsync
114
115 // Setup Dear ImGui context
116 IMGUI_CHECKVERSION();
117 ImGui::CreateContext();
118 ImGuiIO &io = ImGui::GetIO();
119 (void)io;
120
121 mINI::INIFile ini_file("imgui.ini");
122 mINI::INIStructure ini_cfg;
123 ini_file.read(ini_cfg);
124
125 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
126 // Setup Platform/Renderer backends
127 ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
128 ImGui_ImplOpenGL3_Init(glsl_version);
129
130 io.Fonts->AddFontFromMemoryCompressedTTF(KarlaRegular_compressed_data, KarlaRegular_compressed_size, 14.0f);
131 io.Fonts->AddFontFromMemoryCompressedTTF(RobotoMedium_compressed_data, RobotoMedium_compressed_size, 14.0f);
132 io.Fonts->AddFontFromMemoryCompressedTTF(CousineRegular_compressed_data, CousineRegular_compressed_size, 14.0f);
133 io.Fonts->AddFontFromMemoryCompressedTTF(DroidSans_compressed_data, DroidSans_compressed_size, 14.0f);
134 io.Fonts->AddFontFromMemoryCompressedTTF(ProggyClean_compressed_data, ProggyClean_compressed_size, 14.0f);
135 io.Fonts->AddFontFromMemoryCompressedTTF(ProggyTiny_compressed_data, ProggyTiny_compressed_size, 14.0f);
136
137 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
138 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
139 // Multi-Viewport not yet supported with SDL2
140 // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;// Enable Multi-Viewport / Platform Windows
141
142 ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
143 ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
144 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
145 ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white
146
147 ImVec2 menu_bar_size;
148 ImVec2 control_menu_size(200, 800);
149
150 const size_t n_textures = 16;
151 GLuint uiTextureIDs[n_textures];
152 glGenTextures(n_textures, uiTextureIDs);
153
154 for (size_t i = 0; i < n_textures; i++)
155 {
156 glBindTexture(GL_TEXTURE_2D, uiTextureIDs[i]);
157 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
158 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
159 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
160 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
161 }
162
163 // create a file browser instances
164 ImGui::FileBrowser openFileDialog;
165 openFileDialog.SetTitle("Open .mib or .t3p file");
166 openFileDialog.SetTypeFilters({".mib", ".t3p"});
167 ImGui::FileBrowser saveFileDialog(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir);
168 saveFileDialog.SetTitle("Save image as .png");
169 ImGui::FileBrowser saveDataDialog(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir);
170 saveDataDialog.SetTitle("Save COM data in binary format");
171 std::string filename = "";
172
173 // Main loop conditional flags
174 bool b_done = false;
175 bool b_merlin_live_menu = true;
176 bool b_acq_open = false;
177 bool b_started = false;
178 bool b_file_selected = false;
179 bool b_restarted = false;
180 bool b_show_kernel = false;
181 bool b_show_frequencies = true;
182
183 // Merlin Settings (to send to the Camera)
184 struct MerlinSettings merlin_settings;
185
186 // INI file settings to restore previous session settings
187 // Appearance
188 int font_index = 0;
189 int style_index = 0;
190 ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "CBED Colormap", ricom->cbed_cmap);
191 ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "Font", font_index);
192 ImGuiINI::set_font(font_index);
193 ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "Style", style_index);
194 ImGuiINI::set_style(style_index);
195 ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "Show Kernel", b_show_kernel);
196 ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "Show Frequencies", b_show_frequencies);
197 // Hardware Settings
198 std::string python_path;
199#ifdef _WIN32
200 python_path = "py";
201#else
202 python_path = "python3";
203#endif
204 ImGuiINI::check_ini_setting(ini_cfg, "Hardware", "Threads", ricom->n_threads);
205 ImGuiINI::check_ini_setting(ini_cfg, "Hardware", "Queue Size", ricom->queue_size);
206 ImGuiINI::check_ini_setting(ini_cfg, "Hardware", "Image Refresh Interval [ms]", ricom->redraw_interval);
207 // Merlin Settings
208 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "Live Interface Menu", b_merlin_live_menu);
209 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "nx", hardware_configurations[CAMERA::MERLIN].nx_cam);
210 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "ny", hardware_configurations[CAMERA::MERLIN].ny_cam);
211 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "com_port", merlin_settings.com_port);
212 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "data_port", ricom->socket.port);
213 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "ip", ricom->socket.ip);
214 ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "python_path", python_path);
215 // Timepix Settings
216 ImGuiINI::check_ini_setting(ini_cfg, "Timepix", "nx", hardware_configurations[CAMERA::MERLIN].nx_cam);
217 ImGuiINI::check_ini_setting(ini_cfg, "Timepix", "ny", hardware_configurations[CAMERA::MERLIN].ny_cam);
218
219 const char *cmaps[] = {"Parula", "Heat", "Jet", "Turbo", "Hot", "Gray", "Magma", "Inferno", "Plasma", "Viridis", "Cividis", "Github", "HSV"};
220 bool b_redraw = false;
221 bool b_trigger_update = false;
222 float menu_split_v = control_menu_size.y * 0.8f;
223
224 // Initialize Frequency approximation (for plotting only)
225 ricom->kernel.approximate_frequencies((size_t)ricom->nx);
226
227 // Initialize all possible Windows, so everything is on the stack
228 bool show_com_x = false;
229 bool show_com_y = false;
230 bool ricom_fft = false;
231 bool vstem_fft = false;
232 bool e_field_fft = false;
234 std::map<std::string, ImGuiImageWindow<float>> generic_windows_f;
235 std::map<std::string, ImGuiImageWindow<std::complex<float>>> generic_windows_c;
236 generic_windows_f.emplace("RICOM", ImGuiImageWindow<float>("RICOM", &uiTextureIDs[1], true, 9, common_flags | GIM_Flags::FftButton));
237 generic_windows_f.emplace("RICOM-FFT", ImGuiImageWindow<float>("RICOM-FFT", &uiTextureIDs[2], false, 4, common_flags, &ricom_fft));
238 GENERIC_WINDOW("RICOM").fft_window = &GENERIC_WINDOW("RICOM-FFT");
239
240 generic_windows_f.emplace("vSTEM", ImGuiImageWindow<float>("vSTEM", &uiTextureIDs[3], true, 9, common_flags | GIM_Flags::FftButton, &ricom->b_vSTEM));
241 generic_windows_f.emplace("vSTEM-FFT", ImGuiImageWindow<float>("vSTEM-FFT", &uiTextureIDs[4], false, 4, common_flags, &vstem_fft));
242 GENERIC_WINDOW("vSTEM").fft_window = &GENERIC_WINDOW("vSTEM-FFT");
243
244 generic_windows_f.emplace("COM-X", ImGuiImageWindow<float>("RICOM-COMX", &uiTextureIDs[5], true, 9, common_flags, &show_com_x));
245 generic_windows_f.emplace("COM-Y", ImGuiImageWindow<float>("RICOM-COMY", &uiTextureIDs[6], true, 9, common_flags, &show_com_y));
246
247 generic_windows_c.emplace("E-FIELD", ImGuiImageWindow<std::complex<float>>("E-Field", &uiTextureIDs[7], true, 12, common_flags | GIM_Flags::FftButton, &ricom->b_e_mag));
248 generic_windows_f.emplace("E-Field-FFT", ImGuiImageWindow<float>("E-Field-FFT", &uiTextureIDs[8], false, 4, common_flags, &e_field_fft));
249 GENERIC_WINDOW_C("E-FIELD").fft_window = &GENERIC_WINDOW("E-Field-FFT");
250
251 // Initial draw of the Kernel surfaces
252 ricom->kernel.draw_surfaces();
253 bind_tex(ricom->kernel.srf_kx, uiTextureIDs[9]);
254 bind_tex(ricom->kernel.srf_ky, uiTextureIDs[10]);
255
256 ImGuiID dock_id = 3775;
257 Main_Dock main_dock(dock_id);
258
259 // Main loop
260 while (!b_done)
261 {
262 // Poll and handle events (inputs, window resize, etc.)
263 SDL_Event event;
264 while (SDL_PollEvent(&event))
265 {
266 ImGui_ImplSDL2_ProcessEvent(&event);
267 if (event.type == SDL_QUIT)
268 b_done = true;
269 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
270 b_done = true;
271 }
272
273 // Start the Dear ImGui frame
274 ImGui_ImplOpenGL3_NewFrame();
275 ImGui_ImplSDL2_NewFrame();
276 ImGui::NewFrame();
277
278 // Menu
279 if (ImGui::BeginMainMenuBar())
280 {
281 if (ImGui::BeginMenu("Appearance"))
282 {
283 ImGui::Combo("CBED Colormap", &ricom->cbed_cmap, cmaps, IM_ARRAYSIZE(cmaps));
284 ImGuiINI::ShowFontSelector("Font", font_index, ini_cfg);
285 ImGuiINI::ShowStyleSelector("Style", style_index, ini_cfg);
286 ImGui::Checkbox("Show Kernel", &b_show_kernel);
287 ImGui::Checkbox("Show Frequencies", &b_show_frequencies);
288 ImGui::EndMenu();
289 }
290 if (ImGui::BeginMenu("Hardware Settings"))
291 {
292 ImGui::Text("Plotting");
293 if (ImGui::DragInt("Image Refresh Interval [ms]", &ricom->redraw_interval, 20, 10, 1000))
294 {
295 ini_cfg["Hardware"]["Image Refresh Interval [ms]"] = std::to_string(ricom->redraw_interval);
296 }
297 ImGui::Separator();
298
299 ImGui::Text("Multithreading");
300 if (ImGui::DragInt("Threads", &ricom->n_threads, 1, 1, *&ricom->n_threads_max))
301 {
302 ini_cfg["Hardware"]["Threads"] = std::to_string(ricom->n_threads);
303 }
304 if (ImGui::DragInt("Queue Size", &ricom->queue_size, 1, 1, 256))
305 {
306 ini_cfg["Hardware"]["Queue Size"] = std::to_string(ricom->queue_size);
307 }
308 ImGui::Separator();
309
310 ImGui::Text("Merlin Camera");
311 if (ImGui::Checkbox("Live Interface Menu", &b_merlin_live_menu))
312 {
313 ini_cfg["Merlin"]["Live Interface Menu"] = std::to_string(b_merlin_live_menu);
314 }
315 if (ImGui::DragInt("nx Merlin", &hardware_configurations[CAMERA::MERLIN].nx_cam, 1, 1, 2048))
316 {
317 ini_cfg["Merlin"]["nx"] = std::to_string(hardware_configurations[CAMERA::MERLIN].nx_cam);
318 }
319 if (ImGui::DragInt("ny Merlin", &hardware_configurations[CAMERA::MERLIN].ny_cam, 1, 1, 2048))
320 {
321 ini_cfg["Merlin"]["ny"] = std::to_string(hardware_configurations[CAMERA::MERLIN].ny_cam);
322 }
323 if (ImGui::InputText("IP", &ricom->socket.ip))
324 {
325 ini_cfg["Merlin"]["ip"] = ricom->socket.ip;
326 }
327 if (ImGui::InputInt("COM-Port", &merlin_settings.com_port, 8))
328 {
329 ini_cfg["Merlin"]["com_port"] = std::to_string(ricom->socket.port);
330 }
331 if (ImGui::InputInt("Data-Port", &ricom->socket.port, 8))
332 {
333 ini_cfg["Merlin"]["data_port"] = std::to_string(ricom->socket.port);
334 }
335 if (ImGui::InputText("python path", &python_path))
336 {
337 ini_cfg["Merlin"]["python path"] = python_path;
338 }
339 ImGui::Separator();
340
341 ImGui::Text("Timepix Camera");
342 // ImGui::Checkbox("Live Interface Menu", &b_timepix_live_menu);
343 if (ImGui::DragInt("nx Timepix", &hardware_configurations[CAMERA::TIMEPIX].nx_cam, 1, 1, 2048))
344 {
345 ini_cfg["Timepix"]["nx"] = std::to_string(hardware_configurations[CAMERA::TIMEPIX].nx_cam);
346 }
347 if (ImGui::DragInt("ny Timepix", &hardware_configurations[CAMERA::TIMEPIX].ny_cam, 1, 1, 2048))
348 {
349 ini_cfg["Timepix"]["ny"] = std::to_string(hardware_configurations[CAMERA::TIMEPIX].ny_cam);
350 }
351 ImGui::EndMenu();
352 }
353 if (ImGui::BeginMenu("Additional Imaging Modes"))
354 {
355 if (ImGui::Checkbox("Show CoM-X", &show_com_x))
356 {
357 if (show_com_x)
358 {
359 GENERIC_WINDOW("COM-X").set_data(ricom->nx, ricom->ny, &ricom->com_map_x);
360 }
361 else
362 {
363 *GENERIC_WINDOW("COM-X").pb_open = false;
364 }
365 }
366 if (ImGui::Checkbox("Show CoM-Y", &show_com_y))
367 {
368 if (show_com_y)
369 {
370 GENERIC_WINDOW("COM-Y").set_data(ricom->nx, ricom->ny, &ricom->com_map_y);
371 }
372 else
373 {
374 *GENERIC_WINDOW("COM-Y").pb_open = false;
375 }
376 }
377 if (ImGui::Checkbox("Show E-Field", &ricom->b_e_mag))
378 {
379 if (ricom->b_e_mag)
380 {
381 GENERIC_WINDOW_C("E-FIELD").set_data(ricom->nx, ricom->ny, &ricom->e_field_data);
382 }
383 else
384 {
385 *GENERIC_WINDOW_C("E-FIELD").pb_open = false;
386 }
387 }
388 if (ImGui::Checkbox("View vSTEM Image", &ricom->b_vSTEM))
389 {
390 if (ricom->b_vSTEM)
391 {
392 GENERIC_WINDOW("vSTEM").set_data(ricom->nx, ricom->ny, &ricom->stem_data);
393 }
394 else
395 {
396 *GENERIC_WINDOW("vSTEM").pb_open = false;
397 }
398 }
399 ImGui::EndMenu();
400 }
401 menu_bar_size = ImGui::GetWindowSize();
402 ImGui::EndMainMenuBar();
403 }
404
405 const ImGuiViewport *viewport = ImGui::GetMainViewport();
406 if (ricom->p_prog_mon != nullptr)
407 {
408 if (ricom->p_prog_mon->report_set_public)
409 {
410 b_redraw = true;
411 ricom->p_prog_mon->report_set_public = false;
412 }
413 }
414 else
415 {
416 b_redraw = true;
417 }
418
419 ImVec2 pos = viewport->Pos;
420 pos[1] += menu_bar_size.y;
421 ImGui::SetNextWindowPos(pos);
422
423 control_menu_size.y = viewport->Size.y - menu_bar_size.y;
424 ImGui::SetNextWindowSize(control_menu_size);
425 ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1));
426
427 ImGui::Begin("Navigation", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar);
428
429 bool b_nx_changed = false;
430 ImGui::BeginChild("Controls", ImVec2(0, menu_split_v), false);
431 if (ImGui::CollapsingHeader("General Settings", ImGuiTreeNodeFlags_DefaultOpen))
432 {
433 ImGui::Text("Scan Area");
434 b_nx_changed = ImGui::DragInt("nx", &ricom->nx, 1, 1, SDL_MAX_SINT32);
435 ImGui::DragInt("ny", &ricom->ny, 1, 1, SDL_MAX_SINT32);
436 ImGui::DragInt("Repetitions", &ricom->rep, 1, 1, SDL_MAX_SINT32);
437 ImGui::BeginGroup();
438 ImGui::DragInt("skip row", &ricom->skip_row, 1, 0, SDL_MAX_SINT32);
439 ImGui::DragInt("skip img", &ricom->skip_img, 1, 0, SDL_MAX_SINT32);
440 ImGui::EndGroup();
441 if (ImGui::IsItemHovered())
442 {
443 ImGui::SetTooltip("Adjustments for Flyback time:\n skip row: skip n frames after each scan line\n skip img: skip n frames after each scan (when using repetitions)");
444 }
445
446 ImGui::Text("CBED Centre");
447 int *max_nx = (std::max)(&ricom->camera.nx_cam, &ricom->camera.ny_cam);
448 bool offset_changed = ImGui::DragFloat2("Centre", &ricom->offset[0], 0.1f, 0.0, (float)*max_nx);
449 if (offset_changed)
450 {
451 ricom->b_recompute_detector = true;
452 ricom->b_recompute_kernel = true;
453 }
454 ImGui::Checkbox("Auto Centering", &ricom->update_offset);
455 }
456
457 if (ImGui::CollapsingHeader("RICOM Settings", ImGuiTreeNodeFlags_DefaultOpen))
458 {
459 static int filter_max = 8;
460 static bool init_kernel_img = true;
461 bool kernel_changed = ImGui::DragInt("Kernel Size", &ricom->kernel.kernel_size, 1, 1, 300);
462 if (kernel_changed)
463 filter_max = ceil(sqrt(pow(ricom->kernel.kernel_size, 2) * 2));
464 bool rot_changed = ImGui::SliderFloat("Rotation", &ricom->kernel.rotation, 0.0f, 360.0f, "%.1f deg");
465 bool filter_changed = ImGui::Checkbox("Use filter?", &ricom->kernel.b_filter);
466 bool filter_changed2 = ImGui::DragInt2("low / high", &ricom->kernel.kernel_filter_frequency[0], 1, 0, filter_max);
467 if (init_kernel_img || rot_changed || kernel_changed || filter_changed || filter_changed2)
468 {
469 init_kernel_img = false;
470 if (ricom->b_busy)
471 {
472 ricom->b_recompute_kernel = true;
473 }
474 else
475 {
476 ricom->kernel.compute_kernel();
477 }
478 GENERIC_WINDOW("RICOM").reset_min_max();
479 if (b_show_kernel)
480 {
481 ricom->kernel.draw_surfaces();
482 bind_tex(ricom->kernel.srf_kx, uiTextureIDs[9]);
483 bind_tex(ricom->kernel.srf_ky, uiTextureIDs[10]);
484 }
485 }
486 if (kernel_changed || b_nx_changed)
487 {
488 ricom->kernel.approximate_frequencies((size_t)ricom->nx);
489 }
490 if (b_show_kernel)
491 {
492 float sxk = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y * 3) / 2;
493 ImGui::Image((void *)(intptr_t)uiTextureIDs[9], ImVec2(sxk, sxk), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f));
494 ImGui::SameLine();
495 ImGui::Image((void *)(intptr_t)uiTextureIDs[10], ImVec2(sxk, sxk), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f));
496 }
497 if (b_show_frequencies)
498 {
499 ImGui::PlotLines("Frequencies", ricom->kernel.f_approx.data(), ricom->kernel.f_approx.size(), 0, NULL, 0.0f, 1.0f, ImVec2(0, 50));
500 }
501 }
502 if (ricom->b_vSTEM)
503 {
504 if (ImGui::CollapsingHeader("vSTEM Settings", ImGuiTreeNodeFlags_DefaultOpen))
505 {
506 bool inner_changed = ImGui::SliderFloat("Inner Radius", &ricom->detector.radius[0], 0.0f, 182.0f, "%.1f px");
507 bool outer_changed = ImGui::SliderFloat("Outer Radius", &ricom->detector.radius[1], 0.0f, 182.0f, "%.1f px");
508 if (inner_changed || outer_changed)
509 {
510 ricom->b_recompute_detector = true;
511 GENERIC_WINDOW("vSTEM").reset_min_max();
512 }
513 }
514 }
515
516 if (b_merlin_live_menu)
517 {
518 if (ImGui::CollapsingHeader("Merlin Live Mode", ImGuiTreeNodeFlags_DefaultOpen))
519 {
520
521 ImGui::InputInt("hvbias", &merlin_settings.hvbias, 1, 10);
522 ImGui::InputInt("threshold0", &merlin_settings.threshold0, 1, 8);
523 ImGui::InputInt("threshold1", &merlin_settings.threshold1, 1, 8);
524 ImGui::InputFloat("dwell time (us)", &merlin_settings.dwell_time, 0.01, 0.1);
525 ImGui::Checkbox("trigger", &merlin_settings.trigger);
526 ImGui::SameLine();
527 ImGui::Checkbox("headless", &merlin_settings.headless);
528 ImGui::Checkbox("continuousrw", &merlin_settings.continuousrw);
529 ImGui::SameLine();
530 ImGui::Checkbox("raw", &merlin_settings.raw);
531
532 if (merlin_settings.raw)
533 {
534 ImGui::Text("Depth");
535 ImGui::SameLine();
536 ImGui::BeginGroup();
537 ImGui::RadioButton("1", &ricom->camera.depth, 1);
538 ImGui::SameLine();
539 ImGui::RadioButton("6", &ricom->camera.depth, 6);
540 ImGui::SameLine();
541 ImGui::RadioButton("12", &ricom->camera.depth, 12);
542 ImGui::EndGroup();
543 }
544
545 ImGui::Checkbox("save file?", &merlin_settings.save);
546
547 if (ricom->socket.b_connected)
548 {
549 b_started = true;
550 b_restarted = true;
551 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected");
552 if (ricom->socket.connection_information.size() > 0)
553 {
554 ImGui::SameLine();
555 if (ImGui::Button("Show Acqusition Info", ImVec2(-1.0f, 0.0f)))
556 {
557 b_acq_open = true;
558 }
559 }
560 }
561 else
562 {
563 ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Not Connected");
564 }
565
566 if (ImGui::Button("Start Acquisition", ImVec2(-1.0f, 0.0f)))
567 {
568
569 run_thread = std::thread(RICOM::run_ricom, ricom, RICOM::TCP);
570 std::this_thread::sleep_for(std::chrono::milliseconds(500));
571 py_thread = std::thread(RICOM::run_connection_script, ricom, &merlin_settings, python_path);
572 run_thread.detach();
573 py_thread.detach();
574 GENERIC_WINDOW("RICOM").set_data(ricom->nx, ricom->ny, &ricom->ricom_data);
575 }
576 }
577 }
578
579 if (ImGui::CollapsingHeader("File reconstruction", ImGuiTreeNodeFlags_DefaultOpen))
580 {
581
582 if (ImGui::Button("Open File", ImVec2(-1.0f, 0.0f)))
583 {
584 openFileDialog.Open();
585 }
586
587 openFileDialog.Display();
588 if (openFileDialog.HasSelected())
589 {
590 filename = openFileDialog.GetSelected().string();
591 b_file_selected = true;
592 openFileDialog.ClearSelected();
593 ricom->camera = hardware_configurations[ricom->select_mode_by_file(filename.c_str())];
594 }
595 if (b_file_selected)
596 {
597 ImGui::Text("File: %s", filename.c_str());
598
599 if (ricom->camera.model == CAMERA::MERLIN)
600 {
601 ImGui::BeginGroup();
602 ImGui::Text("Depth");
603 ImGui::RadioButton("1", &ricom->camera.depth, 1);
604 ImGui::SameLine();
605 ImGui::RadioButton("6", &ricom->camera.depth, 6);
606 ImGui::SameLine();
607 ImGui::RadioButton("12", &ricom->camera.depth, 12);
608 ImGui::EndGroup();
609 if (ImGui::IsItemHovered())
610 {
611 ImGui::SetTooltip("Only applicable for handling recorded files, \n recorded in raw mode.");
612 }
613 }
614 if (ricom->camera.model == CAMERA::TIMEPIX)
615 {
616 ImGui::DragInt("dwell time", &ricom->camera.dwell_time, 1, 1);
617 }
618
619 if (ImGui::Button("Run File", ImVec2(-1.0f, 0.0f)))
620 {
621 run_thread = std::thread(RICOM::run_ricom, ricom, RICOM::FILE);
622 b_started = true;
623 b_restarted = true;
624 run_thread.detach();
625 GENERIC_WINDOW("RICOM").set_data(ricom->nx, ricom->ny, &ricom->ricom_data);
626 }
627 }
628 }
629 ImGui::EndChild();
630
631 float panel_h_min = control_menu_size.y * 0.4;
632 float panel_h_max = control_menu_size.y - 32;
633 v_splitter(5, menu_split_v, panel_h_min, panel_h_max, pos.y + ImGui::GetStyle().ItemSpacing.y * 3);
634
635 ImGui::BeginChild("Progress", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar);
636 ImGui::ProgressBar(ricom->fr_count / (ricom->fr_total), ImVec2(-1.0f, 0.0f));
637 ImGui::Text("Speed: %.2f kHz", ricom->fr_freq);
638 if (ImGui::Button("Quit", ImVec2(-1.0f, 0.0f)))
639 {
640 ricom->rc_quit = true;
641 b_redraw = true;
642 }
643
644 ImGui::Text("COM= %.2f, %.2f", ricom->com_public[0], ricom->com_public[1]);
645
646 // CBED Plot Area/DockSpace
647 ImGui::DockSpace(1319);
648 static auto first_time = true;
649 if (first_time)
650 {
651 first_time = false;
652 ImGui::DockBuilderRemoveNode(1319); // clear any previous layout
653 ImGui::DockBuilderAddNode(1319, ImGuiDockNodeFlags_DockSpace);
654 ImGui::DockBuilderDockWindow("CBED", 1319);
655 ImGui::DockBuilderFinish(1319);
656 }
657 ImGui::EndChild();
658 control_menu_size = ImGui::GetWindowSize();
659 ImGui::End();
660
661 main_dock.render(ImVec2(control_menu_size.x, pos.y), ImVec2(viewport->Size.x - control_menu_size.x, viewport->Size.y - menu_bar_size.y));
662
663 ImGui::Begin("CBED", nullptr, ImGuiWindowFlags_NoScrollbar);
664 ImGui::Checkbox("Plot CBED", &ricom->b_plot_cbed);
665 if (ricom->b_plot_cbed)
666 {
667 ImGui::SameLine();
668 ImGui::SetNextItemWidth(80);
669 ImGui::InputInt("Frames", &ricom->cbed_cmap_frames);
670 if (ImGui::IsItemHovered())
671 {
672 ImGui::SetTooltip("Number of frames to integrate for CBED image");
673 }
674 }
675 if (ricom->cbed_cmap_frames < 1)
676 ricom->cbed_cmap_frames = 1;
677 ImVec2 rem_space = ImGui::GetContentRegionAvail();
678 float tex_wh = (std::min)(rem_space.x, rem_space.y);
679 ImVec2 p = ImGui::GetCursorScreenPos();
680
681 float com_rel_x = p.x + tex_wh * (ricom->com_public[0] / ricom->camera.nx_cam);
682 float com_rel_y = p.y + tex_wh * (ricom->com_public[1] / ricom->camera.ny_cam);
683
684 float centre_x = p.x + tex_wh * (ricom->offset[0] / ricom->camera.nx_cam);
685 float centre_y = p.y + tex_wh * (ricom->offset[1] / ricom->camera.ny_cam);
686
687 com_rel_x = (std::max)(p.x, com_rel_x);
688 com_rel_y = (std::max)(p.y, com_rel_y);
689 com_rel_x = (std::min)(p.x + tex_wh, com_rel_x);
690 com_rel_y = (std::min)(p.y + tex_wh, com_rel_y);
691
692 float cross_width = tex_wh / 15.0f;
693 if (b_redraw)
694 {
695 if (ricom->srf_cbed != NULL)
696 {
697 bind_tex(ricom->srf_cbed, uiTextureIDs[0]);
698 }
699 }
700 ImGui::Image((ImTextureID)uiTextureIDs[0], ImVec2(tex_wh, tex_wh), uv_min, uv_max, tint_col, border_col);
701 ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * 0.03, IM_COL32(255, 255, 255, 255), 256);
702 ImGui::GetWindowDrawList()->AddLine(ImVec2(com_rel_x - cross_width, com_rel_y), ImVec2(com_rel_x + cross_width, com_rel_y), IM_COL32(255, 0, 0, 255), 1.5f);
703 ImGui::GetWindowDrawList()->AddLine(ImVec2(com_rel_x, com_rel_y - cross_width), ImVec2(com_rel_x, com_rel_y + cross_width), IM_COL32(255, 0, 0, 255), 1.5f);
704 if (ricom->b_vSTEM)
705 {
706 ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * (ricom->detector.radius[0] / ricom->camera.nx_cam), IM_COL32(255, 50, 0, 255), 256);
707 ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * (ricom->detector.radius[1] / ricom->camera.nx_cam), IM_COL32(255, 150, 0, 255), 256);
708 }
709 ImGui::End();
710
711 if (b_acq_open)
712 {
713 ImVec2 pos_t = viewport->Pos;
714 pos_t[0] += control_menu_size[0] + 128;
715 pos_t[1] += menu_bar_size[1] + 128;
716 ImGui::SetNextWindowPos(pos_t, ImGuiCond_FirstUseEver);
717 ImVec2 size = {200, 400};
718 ImGui::SetNextWindowSize(size, ImGuiCond_FirstUseEver);
719
720 ImGui::Begin("Acquisition Header", &b_acq_open);
721 ImGui::BeginChild("Scrolling");
722 ImGui::TextWrapped(ricom->socket.connection_information.data());
723 ImGui::EndChild();
724 ImGui::End();
725 }
726
727 // Render all generic Image Windows
728 bool trigger = (b_trigger_update != ricom->b_busy) && (ricom->b_busy == false);
729 bool redraw_tick = (ricom->b_busy && b_redraw && b_started);
730 update_views(generic_windows_f, ricom, b_restarted, trigger, redraw_tick);
731 update_views(generic_windows_c, ricom, b_restarted, trigger, redraw_tick);
732
733 b_restarted = false;
734 b_trigger_update = ricom->b_busy;
735
736 // Rendering
737 ImGui::Render();
738 glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
739 glClear(GL_COLOR_BUFFER_BIT);
740 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
741
742 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
743 {
744 SDL_Window *backup_current_window = SDL_GL_GetCurrentWindow();
745 SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
746 ImGui::UpdatePlatformWindows();
747 ImGui::RenderPlatformWindowsDefault();
748 SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
749 }
750
751 SDL_GL_SwapWindow(window);
752
753 b_redraw = false;
754 }
755
756 if (run_thread.joinable())
757 run_thread.join();
758 if (py_thread.joinable())
759 py_thread.join();
760
761 ImGui_ImplOpenGL3_Shutdown();
762 ImGui_ImplSDL2_Shutdown();
763 ImGui::DestroyContext();
764
765 SDL_GL_DeleteContext(gl_context);
766 SDL_DestroyWindow(window);
767 SDL_Quit();
768
769 ini_file.write(ini_cfg, true);
770 return 0;
771}
772
773template <typename T>
774void update_views(std::map<std::string, ImGuiImageWindow<T>> &generic_windows_f, Ricom *ricom, bool b_restarted, bool trigger, bool b_redraw)
775{
776 for (auto &wnd : generic_windows_f)
777 {
778 if (wnd.first == "RICOM")
779 {
780 if (b_restarted)
781 {
782 wnd.second.set_nx_ny(ricom->nx, ricom->ny);
783 }
784 wnd.second.render_window(b_redraw, ricom->fr_count, ricom->kernel.kernel_size, trigger);
785 }
786 else
787 {
788 if (*wnd.second.pb_open)
789 {
790 if (b_restarted)
791 {
792 wnd.second.set_nx_ny(ricom->nx, ricom->ny);
793 }
794 wnd.second.render_window(b_redraw, ricom->fr_count, trigger);
795 }
796 }
797 }
798}
799
800void bind_tex(SDL_Surface *srf, GLuint tex_id)
801{
802 glBindTexture(GL_TEXTURE_2D, (tex_id));
803 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, srf->w, srf->h, 0,
804 GL_BGRA, GL_UNSIGNED_BYTE, srf->pixels);
805}
void v_splitter(float thickness, float &size0, const float &min_h, const float &max_h, const float &offset)
Definition GuiUtils.cpp:41
GIM_Flags
@ ColormapSelector
void bind_tex(SDL_Surface *srf, GLuint tex_id)
Definition RunGUI.cpp:800
#define GENERIC_WINDOW_C(name)
Definition RunGUI.cpp:57
int run_gui(Ricom *ricom, CAMERA::Default_configurations &hardware_configurations)
Definition RunGUI.cpp:68
#define GENERIC_WINDOW(name)
Definition RunGUI.cpp:56
void update_views(std::map< std::string, ImGuiImageWindow< T > > &generic_windows_f, Ricom *ricom, bool b_restarted, bool trigger, bool b_redraw)
Definition RunGUI.cpp:774
Camera_model model
Definition Camera.h:43
void render(ImVec2 pos, ImVec2 size)
Definition GuiUtils.cpp:73
std::atomic< bool > report_set_public
std::array< float, 2 > radius
Definition Ricom.h:128
SDL_Surface * srf_kx
Definition Ricom.h:66
void compute_kernel()
Definition Ricom.cpp:21
void draw_surfaces()
Definition Ricom.cpp:128
bool b_filter
Definition Ricom.h:57
std::vector< float > f_approx
Definition Ricom.h:65
void approximate_frequencies(size_t n_im)
Definition Ricom.cpp:111
float rotation
Definition Ricom.h:61
SDL_Surface * srf_ky
Definition Ricom.h:67
std::array< int, 2 > kernel_filter_frequency
Definition Ricom.h:58
int kernel_size
Definition Ricom.h:56
Definition Ricom.h:152
int skip_img
Definition Ricom.h:250
std::array< float, 2 > com_public
Definition Ricom.h:236
SocketConnector socket
Definition Ricom.h:216
int redraw_interval
Definition Ricom.h:221
bool b_busy
Definition Ricom.h:224
int cbed_cmap
Definition Ricom.h:269
bool b_recompute_detector
Definition Ricom.h:231
int n_threads
Definition Ricom.h:253
std::vector< float > stem_data
Definition Ricom.h:240
int rep
Definition Ricom.h:247
bool update_offset
Definition Ricom.h:225
std::vector< float > ricom_data
Definition Ricom.h:239
int n_threads_max
Definition Ricom.h:254
float fr_count
Definition Ricom.h:257
int cbed_cmap_frames
Definition Ricom.h:229
std::vector< std::complex< float > > e_field_data
Definition Ricom.h:241
int nx
Definition Ricom.h:244
Ricom_detector detector
Definition Ricom.h:233
bool rc_quit
Definition Ricom.h:262
int queue_size
Definition Ricom.h:255
bool b_e_mag
Definition Ricom.h:227
SDL_Surface * srf_cbed
Definition Ricom.h:268
int fr_total
Definition Ricom.h:248
std::vector< float > com_map_x
Definition Ricom.h:237
ProgressMonitor * p_prog_mon
Definition Ricom.h:223
enum CAMERA::Camera_model select_mode_by_file(const char *filename)
Definition Ricom.cpp:1070
bool b_plot_cbed
Definition Ricom.h:228
float fr_freq
Definition Ricom.h:256
bool b_recompute_kernel
Definition Ricom.h:232
std::vector< float > com_map_y
Definition Ricom.h:238
CAMERA::Camera_BASE camera
Definition Ricom.h:218
int skip_row
Definition Ricom.h:249
bool b_vSTEM
Definition Ricom.h:226
int ny
Definition Ricom.h:245
std::array< float, 2 > offset
Definition Ricom.h:235
Ricom_kernel kernel
Definition Ricom.h:234
std::string connection_information
@ TIMEPIX
Definition Camera.h:30
@ MERLIN
Definition Camera.h:29
void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, char *value)
Definition ImGuiINI.hpp:116
bool ShowFontSelector(const char *label, int &selectedFont, mINI::INIStructure &ini_cfg)
Definition ImGuiINI.hpp:57
bool ShowStyleSelector(const char *label, int &style_idx, mINI::INIStructure &ini_cfg)
Definition ImGuiINI.hpp:37
void set_font(int font_idx)
Definition ImGuiINI.hpp:81
void set_style(int style_idx)
Definition ImGuiINI.hpp:21
void run_connection_script(Ricom *r, MerlinSettings *merlin, const std::string &python_path)
Definition Ricom.cpp:1104
@ TCP
Definition Ricom.h:145
@ FILE
Definition Ricom.h:144
void run_ricom(Ricom *r, RICOM::modes mode)
Definition Ricom.cpp:1089