riCOM_cpp
This repository contains the C++ implementation of the riCOM (Real Time Centre Of Mass) algorithm for 4D Scanning electron microscopy.
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
60 template <typename T>
61 inline void update_views(std::map<std::string, ImGuiImageWindow<T>> &generic_windows_f, Ricom *ricom, bool b_restarted, bool trigger, bool b_redraw);
62 inline void bind_tex(SDL_Surface *srf, GLuint tex_id);
63 
65 // GUI implementation //
67 
68 int 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 
181  // Merlin Settings (to send to the Camera)
182  struct MerlinSettings merlin_settings;
183 
184  // INI file settings to restore previous session settings
185  // Appearance
186  int font_index = 0;
187  int style_index = 0;
188  ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "CBED Colormap", ricom->cbed_cmap);
189  ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "Font", font_index);
190  ImGuiINI::set_font(font_index);
191  ImGuiINI::check_ini_setting(ini_cfg, "Appearance", "Style", style_index);
192  ImGuiINI::set_style(style_index);
193  // Hardware Settings
194  std::string python_path;
195 #ifdef _WIN32
196  python_path = "py";
197 #else
198  python_path = "python3";
199 #endif
200  ImGuiINI::check_ini_setting(ini_cfg, "Hardware", "Threads", ricom->n_threads);
201  ImGuiINI::check_ini_setting(ini_cfg, "Hardware", "Queue Size", ricom->queue_size);
202  ImGuiINI::check_ini_setting(ini_cfg, "Hardware", "Image Refresh Interval [ms]", ricom->redraw_interval);
203  // Merlin Settings
204  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "Live Interface Menu", b_merlin_live_menu);
205  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "nx", hardware_configurations[CAMERA::MERLIN].nx_cam);
206  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "ny", hardware_configurations[CAMERA::MERLIN].ny_cam);
207  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "com_port", merlin_settings.com_port);
208  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "data_port", ricom->socket.port);
209  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "ip", ricom->socket.ip);
210  ImGuiINI::check_ini_setting(ini_cfg, "Merlin", "python_path", python_path);
211  // Timepix Settings
212  ImGuiINI::check_ini_setting(ini_cfg, "Timepix", "nx", hardware_configurations[CAMERA::MERLIN].nx_cam);
213  ImGuiINI::check_ini_setting(ini_cfg, "Timepix", "ny", hardware_configurations[CAMERA::MERLIN].ny_cam);
214 
215  const char *cmaps[] = {"Parula", "Heat", "Jet", "Turbo", "Hot", "Gray", "Magma", "Inferno", "Plasma", "Viridis", "Cividis", "Github", "HSV"};
216  bool b_redraw = false;
217  bool b_trigger_update = false;
218  float menu_split_v = control_menu_size.y * 0.8f;
219 
220  // Initialize Frequency approximation (for plotting only)
221  ricom->kernel.approximate_frequencies((size_t)ricom->nx);
222 
223  // Initialize all possible Windows, so everything is on the stack
224  bool show_com_x = false;
225  bool show_com_y = false;
226  bool ricom_fft = false;
227  bool vstem_fft = false;
228  bool e_field_fft = false;
230  std::map<std::string, ImGuiImageWindow<float>> generic_windows_f;
231  std::map<std::string, ImGuiImageWindow<std::complex<float>>> generic_windows_c;
232  generic_windows_f.emplace("RICOM", ImGuiImageWindow<float>("RICOM", &uiTextureIDs[1], true, 9, common_flags | GIM_Flags::FftButton));
233  generic_windows_f.emplace("RICOM-FFT", ImGuiImageWindow<float>("RICOM-FFT", &uiTextureIDs[2], false, 4, common_flags, &ricom_fft));
234  GENERIC_WINDOW("RICOM").fft_window = &GENERIC_WINDOW("RICOM-FFT");
235 
236  generic_windows_f.emplace("vSTEM", ImGuiImageWindow<float>("vSTEM", &uiTextureIDs[3], true, 9, common_flags | GIM_Flags::FftButton, &ricom->b_vSTEM));
237  generic_windows_f.emplace("vSTEM-FFT", ImGuiImageWindow<float>("vSTEM-FFT", &uiTextureIDs[4], false, 4, common_flags, &vstem_fft));
238  GENERIC_WINDOW("vSTEM").fft_window = &GENERIC_WINDOW("vSTEM-FFT");
239 
240  generic_windows_f.emplace("COM-X", ImGuiImageWindow<float>("RICOM-COMX", &uiTextureIDs[5], true, 9, common_flags, &show_com_x));
241  generic_windows_f.emplace("COM-Y", ImGuiImageWindow<float>("RICOM-COMY", &uiTextureIDs[6], true, 9, common_flags, &show_com_y));
242 
243  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));
244  generic_windows_f.emplace("E-Field-FFT", ImGuiImageWindow<float>("E-Field-FFT", &uiTextureIDs[8], false, 4, common_flags, &e_field_fft));
245  GENERIC_WINDOW_C("E-FIELD").fft_window = &GENERIC_WINDOW("E-Field-FFT");
246 
247  ricom->kernel.draw_surfaces();
248  bind_tex(ricom->kernel.srf_kx, uiTextureIDs[9]);
249  bind_tex(ricom->kernel.srf_ky, uiTextureIDs[10]);
250 
251  ImGuiID dock_id = 3775;
252  Main_Dock main_dock(dock_id);
253 
254  // Main loop
255  while (!b_done)
256  {
257  // Poll and handle events (inputs, window resize, etc.)
258  SDL_Event event;
259  while (SDL_PollEvent(&event))
260  {
261  ImGui_ImplSDL2_ProcessEvent(&event);
262  if (event.type == SDL_QUIT)
263  b_done = true;
264  if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
265  b_done = true;
266  }
267 
268  // Start the Dear ImGui frame
269  ImGui_ImplOpenGL3_NewFrame();
270  ImGui_ImplSDL2_NewFrame();
271  ImGui::NewFrame();
272 
273  // Menu
274  if (ImGui::BeginMainMenuBar())
275  {
276  if (ImGui::BeginMenu("Appearance"))
277  {
278  ImGui::Combo("CBED Colormap", &ricom->cbed_cmap, cmaps, IM_ARRAYSIZE(cmaps));
279  ImGuiINI::ShowFontSelector("Font", font_index, ini_cfg);
280  ImGuiINI::ShowStyleSelector("Style", style_index, ini_cfg);
281  ImGui::EndMenu();
282  }
283  if (ImGui::BeginMenu("Hardware Settings"))
284  {
285  ImGui::Text("Plotting");
286  if (ImGui::DragInt("Image Refresh Interval [ms]", &ricom->redraw_interval, 20, 10, 1000))
287  {
288  ini_cfg["Hardware"]["Image Refresh Interval [ms]"] = std::to_string(ricom->redraw_interval);
289  }
290  ImGui::Separator();
291 
292  ImGui::Text("Multithreading");
293  if (ImGui::DragInt("Threads", &ricom->n_threads, 1, 1, *&ricom->n_threads_max))
294  {
295  ini_cfg["Hardware"]["Threads"] = std::to_string(ricom->n_threads);
296  }
297  if (ImGui::DragInt("Queue Size", &ricom->queue_size, 1, 1, 256))
298  {
299  ini_cfg["Hardware"]["Queue Size"] = std::to_string(ricom->queue_size);
300  }
301  ImGui::Separator();
302 
303  ImGui::Text("Merlin Camera");
304  if (ImGui::Checkbox("Live Interface Menu", &b_merlin_live_menu))
305  {
306  ini_cfg["Merlin"]["Live Interface Menu"] = std::to_string(b_merlin_live_menu);
307  }
308  if (ImGui::DragInt("nx Merlin", &hardware_configurations[CAMERA::MERLIN].nx_cam, 1, 1, 2048))
309  {
310  ini_cfg["Merlin"]["nx"] = std::to_string(hardware_configurations[CAMERA::MERLIN].nx_cam);
311  }
312  if (ImGui::DragInt("ny Merlin", &hardware_configurations[CAMERA::MERLIN].ny_cam, 1, 1, 2048))
313  {
314  ini_cfg["Merlin"]["ny"] = std::to_string(hardware_configurations[CAMERA::MERLIN].ny_cam);
315  }
316  if (ImGui::InputText("IP", &ricom->socket.ip))
317  {
318  ini_cfg["Merlin"]["ip"] = ricom->socket.ip;
319  }
320  if (ImGui::InputInt("COM-Port", &merlin_settings.com_port, 8))
321  {
322  ini_cfg["Merlin"]["com_port"] = std::to_string(ricom->socket.port);
323  }
324  if (ImGui::InputInt("Data-Port", &ricom->socket.port, 8))
325  {
326  ini_cfg["Merlin"]["data_port"] = std::to_string(ricom->socket.port);
327  }
328  if (ImGui::InputText("python path", &python_path))
329  {
330  ini_cfg["Merlin"]["python path"] = python_path;
331  }
332  ImGui::Separator();
333 
334  ImGui::Text("Timepix Camera");
335  // ImGui::Checkbox("Live Interface Menu", &b_timepix_live_menu);
336  if (ImGui::DragInt("nx Timepix", &hardware_configurations[CAMERA::TIMEPIX].nx_cam, 1, 1, 2048))
337  {
338  ini_cfg["Timepix"]["nx"] = std::to_string(hardware_configurations[CAMERA::TIMEPIX].nx_cam);
339  }
340  if (ImGui::DragInt("ny Timepix", &hardware_configurations[CAMERA::TIMEPIX].ny_cam, 1, 1, 2048))
341  {
342  ini_cfg["Timepix"]["ny"] = std::to_string(hardware_configurations[CAMERA::TIMEPIX].ny_cam);
343  }
344  ImGui::EndMenu();
345  }
346  if (ImGui::BeginMenu("Additional Imaging Modes"))
347  {
348  if (ImGui::Checkbox("Show CoM-X", &show_com_x))
349  {
350  if (show_com_x)
351  {
352  GENERIC_WINDOW("COM-X").set_data(ricom->nx, ricom->ny, &ricom->com_map_x);
353  }
354  else
355  {
356  *GENERIC_WINDOW("COM-X").pb_open = false;
357  }
358  }
359  if (ImGui::Checkbox("Show CoM-Y", &show_com_y))
360  {
361  if (show_com_y)
362  {
363  GENERIC_WINDOW("COM-Y").set_data(ricom->nx, ricom->ny, &ricom->com_map_y);
364  }
365  else
366  {
367  *GENERIC_WINDOW("COM-Y").pb_open = false;
368  }
369  }
370  if (ImGui::Checkbox("Show E-Field", &ricom->b_e_mag))
371  {
372  if (ricom->b_e_mag)
373  {
374  GENERIC_WINDOW_C("E-FIELD").set_data(ricom->nx, ricom->ny, &ricom->e_field_data);
375  }
376  else
377  {
378  *GENERIC_WINDOW_C("E-FIELD").pb_open = false;
379  }
380  }
381  if (ImGui::Checkbox("View vSTEM Image", &ricom->b_vSTEM))
382  {
383  if (ricom->b_vSTEM)
384  {
385  GENERIC_WINDOW("vSTEM").set_data(ricom->nx, ricom->ny, &ricom->stem_data);
386  }
387  else
388  {
389  *GENERIC_WINDOW("vSTEM").pb_open = false;
390  }
391  }
392  ImGui::EndMenu();
393  }
394  menu_bar_size = ImGui::GetWindowSize();
395  ImGui::EndMainMenuBar();
396  }
397 
398  const ImGuiViewport *viewport = ImGui::GetMainViewport();
399  if (ricom->p_prog_mon != nullptr)
400  {
401  if (ricom->p_prog_mon->report_set_public)
402  {
403  b_redraw = true;
404  ricom->p_prog_mon->report_set_public = false;
405  }
406  }
407  else
408  {
409  b_redraw = true;
410  }
411 
412  ImVec2 pos = viewport->Pos;
413  pos[1] += menu_bar_size.y;
414  ImGui::SetNextWindowPos(pos);
415 
416  control_menu_size.y = viewport->Size.y - menu_bar_size.y;
417  ImGui::SetNextWindowSize(control_menu_size);
418  ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1));
419 
420  ImGui::Begin("Navigation", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar);
421 
422  bool b_nx_changed = false;
423  ImGui::BeginChild("Controls", ImVec2(0, menu_split_v), false);
424  if (ImGui::CollapsingHeader("General Settings", ImGuiTreeNodeFlags_DefaultOpen))
425  {
426  ImGui::Text("Scan Area");
427  b_nx_changed = ImGui::DragInt("nx", &ricom->nx, 1, 1, SDL_MAX_SINT32);
428  ImGui::DragInt("ny", &ricom->ny, 1, 1, SDL_MAX_SINT32);
429  ImGui::DragInt("Repetitions", &ricom->rep, 1, 1, SDL_MAX_SINT32);
430  ImGui::BeginGroup();
431  ImGui::DragInt("skip row", &ricom->skip_row, 1, 0, SDL_MAX_SINT32);
432  ImGui::DragInt("skip img", &ricom->skip_img, 1, 0, SDL_MAX_SINT32);
433  ImGui::EndGroup();
434  if (ImGui::IsItemHovered())
435  {
436  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)");
437  }
438 
439  ImGui::Text("CBED Centre");
440  int *max_nx = (std::max)(&ricom->camera.nx_cam, &ricom->camera.ny_cam);
441  bool offset_changed = ImGui::DragFloat2("Centre", &ricom->offset[0], 0.1f, 0.0, (float)*max_nx);
442  if (offset_changed)
443  {
444  ricom->b_recompute_detector = true;
445  ricom->b_recompute_kernel = true;
446  }
447  ImGui::Checkbox("Auto Centering", &ricom->update_offset);
448  }
449 
450  if (ImGui::CollapsingHeader("RICOM Settings", ImGuiTreeNodeFlags_DefaultOpen))
451  {
452  static int filter_max = 8;
453  static bool init_kernel_img = true;
454  bool kernel_changed = ImGui::DragInt("Kernel Size", &ricom->kernel.kernel_size, 1, 1, 300);
455  if (kernel_changed)
456  filter_max = ceil(sqrt(pow(ricom->kernel.kernel_size, 2) * 2));
457  bool rot_changed = ImGui::SliderFloat("Rotation", &ricom->kernel.rotation, 0.0f, 360.0f, "%.1f deg");
458  bool filter_changed = ImGui::Checkbox("Use filter?", &ricom->kernel.b_filter);
459  bool filter_changed2 = ImGui::DragInt2("low / high", &ricom->kernel.kernel_filter_frequency[0], 1, 0, filter_max);
460  if (init_kernel_img || rot_changed || kernel_changed || filter_changed || filter_changed2)
461  {
462  init_kernel_img = false;
463  if (ricom->b_busy)
464  {
465  ricom->b_recompute_kernel = true;
466  }
467  else
468  {
469  ricom->kernel.compute_kernel();
470  }
471  GENERIC_WINDOW("RICOM").reset_min_max();
472  ricom->kernel.draw_surfaces();
473  bind_tex(ricom->kernel.srf_kx, uiTextureIDs[9]);
474  bind_tex(ricom->kernel.srf_ky, uiTextureIDs[10]);
475  }
476  if (kernel_changed || b_nx_changed)
477  {
478  ricom->kernel.approximate_frequencies((size_t)ricom->nx);
479  }
480  float sxk = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y * 3) / 2;
481  ImGui::Image((void *)(intptr_t)uiTextureIDs[9], ImVec2(sxk, sxk), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f));
482  ImGui::SameLine();
483  ImGui::Image((void *)(intptr_t)uiTextureIDs[10], ImVec2(sxk, sxk), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f));
484  ImGui::PlotLines("Frequencies", ricom->kernel.f_approx.data(), ricom->kernel.f_approx.size(), 0, NULL, 0.0f, 1.0f, ImVec2(0, 50));
485  }
486  if (ricom->b_vSTEM)
487  {
488  if (ImGui::CollapsingHeader("vSTEM Settings", ImGuiTreeNodeFlags_DefaultOpen))
489  {
490  bool inner_changed = ImGui::SliderFloat("Inner Radius", &ricom->detector.radius[0], 0.0f, 182.0f, "%.1f px");
491  bool outer_changed = ImGui::SliderFloat("Outer Radius", &ricom->detector.radius[1], 0.0f, 182.0f, "%.1f px");
492  if (inner_changed || outer_changed)
493  {
494  ricom->b_recompute_detector = true;
495  GENERIC_WINDOW("vSTEM").reset_min_max();
496  }
497  }
498  }
499 
500  if (b_merlin_live_menu)
501  {
502  if (ImGui::CollapsingHeader("Merlin Live Mode", ImGuiTreeNodeFlags_DefaultOpen))
503  {
504 
505  ImGui::InputInt("hvbias", &merlin_settings.hvbias, 1, 10);
506  ImGui::InputInt("threshold0", &merlin_settings.threshold0, 1, 8);
507  ImGui::InputInt("threshold1", &merlin_settings.threshold1, 1, 8);
508  ImGui::InputFloat("dwell time (us)", &merlin_settings.dwell_time, 0.01, 0.1);
509  ImGui::Checkbox("trigger", &merlin_settings.trigger);
510  ImGui::SameLine();
511  ImGui::Checkbox("headless", &merlin_settings.headless);
512  ImGui::Checkbox("continuousrw", &merlin_settings.continuousrw);
513  ImGui::SameLine();
514  ImGui::Checkbox("raw", &merlin_settings.raw);
515 
516  if (merlin_settings.raw)
517  {
518  ImGui::Text("Depth");
519  ImGui::SameLine();
520  ImGui::BeginGroup();
521  ImGui::RadioButton("1", &ricom->camera.depth, 1);
522  ImGui::SameLine();
523  ImGui::RadioButton("6", &ricom->camera.depth, 6);
524  ImGui::SameLine();
525  ImGui::RadioButton("12", &ricom->camera.depth, 12);
526  ImGui::EndGroup();
527  }
528 
529  ImGui::Checkbox("save file?", &merlin_settings.save);
530 
531  if (ricom->socket.b_connected)
532  {
533  b_started = true;
534  b_restarted = true;
535  ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected");
536  if (ricom->socket.connection_information.size() > 0)
537  {
538  ImGui::SameLine();
539  if (ImGui::Button("Show Acqusition Info", ImVec2(-1.0f, 0.0f)))
540  {
541  b_acq_open = true;
542  }
543  }
544  }
545  else
546  {
547  ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Not Connected");
548  }
549 
550  if (ImGui::Button("Start Acquisition", ImVec2(-1.0f, 0.0f)))
551  {
552 
553  run_thread = std::thread(RICOM::run_ricom, ricom, RICOM::TCP);
554  std::this_thread::sleep_for(std::chrono::milliseconds(500));
555  py_thread = std::thread(RICOM::run_connection_script, ricom, &merlin_settings, python_path);
556  run_thread.detach();
557  py_thread.detach();
558  GENERIC_WINDOW("RICOM").set_data(ricom->nx, ricom->ny, &ricom->ricom_data);
559  }
560  }
561  }
562 
563  if (ImGui::CollapsingHeader("File reconstruction", ImGuiTreeNodeFlags_DefaultOpen))
564  {
565 
566  if (ImGui::Button("Open File", ImVec2(-1.0f, 0.0f)))
567  {
568  openFileDialog.Open();
569  }
570 
571  openFileDialog.Display();
572  if (openFileDialog.HasSelected())
573  {
574  filename = openFileDialog.GetSelected().string();
575  b_file_selected = true;
576  openFileDialog.ClearSelected();
577  ricom->camera = hardware_configurations[ricom->select_mode_by_file(filename.c_str())];
578  }
579  if (b_file_selected)
580  {
581  ImGui::Text("File: %s", filename.c_str());
582 
583  if (ricom->camera.model == CAMERA::MERLIN)
584  {
585  ImGui::BeginGroup();
586  ImGui::Text("Depth");
587  ImGui::RadioButton("1", &ricom->camera.depth, 1);
588  ImGui::SameLine();
589  ImGui::RadioButton("6", &ricom->camera.depth, 6);
590  ImGui::SameLine();
591  ImGui::RadioButton("12", &ricom->camera.depth, 12);
592  ImGui::EndGroup();
593  if (ImGui::IsItemHovered())
594  {
595  ImGui::SetTooltip("Only applicable for handling recorded files, \n recorded in raw mode.");
596  }
597  }
598  if (ricom->camera.model == CAMERA::TIMEPIX)
599  {
600  ImGui::DragInt("dwell time", &ricom->camera.dwell_time, 1, 1);
601  }
602 
603  if (ImGui::Button("Run File", ImVec2(-1.0f, 0.0f)))
604  {
605  run_thread = std::thread(RICOM::run_ricom, ricom, RICOM::FILE);
606  b_started = true;
607  b_restarted = true;
608  run_thread.detach();
609  GENERIC_WINDOW("RICOM").set_data(ricom->nx, ricom->ny, &ricom->ricom_data);
610  }
611  }
612  }
613  ImGui::EndChild();
614 
615  float panel_h_min = control_menu_size.y * 0.4;
616  float panel_h_max = control_menu_size.y - 32;
617  v_splitter(5, menu_split_v, panel_h_min, panel_h_max, pos.y + ImGui::GetStyle().ItemSpacing.y * 3);
618 
619  ImGui::BeginChild("Progress", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar);
620  ImGui::ProgressBar(ricom->fr_count / (ricom->fr_total), ImVec2(-1.0f, 0.0f));
621  ImGui::Text("Speed: %.2f kHz", ricom->fr_freq);
622  if (ImGui::Button("Quit", ImVec2(-1.0f, 0.0f)))
623  {
624  ricom->rc_quit = true;
625  b_redraw = true;
626  }
627 
628  ImGui::Text("COM= %.2f, %.2f", ricom->com_public[0], ricom->com_public[1]);
629 
630  // CBED Plot Area/DockSpace
631  ImGui::DockSpace(1319);
632  static auto first_time = true;
633  if (first_time)
634  {
635  first_time = false;
636  ImGui::DockBuilderRemoveNode(1319); // clear any previous layout
637  ImGui::DockBuilderAddNode(1319, ImGuiDockNodeFlags_DockSpace);
638  ImGui::DockBuilderDockWindow("CBED", 1319);
639  ImGui::DockBuilderFinish(1319);
640  }
641  ImGui::EndChild();
642  control_menu_size = ImGui::GetWindowSize();
643  ImGui::End();
644 
645  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));
646 
647  ImGui::Begin("CBED", nullptr, ImGuiWindowFlags_NoScrollbar);
648  ImGui::Checkbox("Plot CBED", &ricom->b_plot_cbed);
649  ImVec2 rem_space = ImGui::GetContentRegionAvail();
650  float tex_wh = (std::min)(rem_space.x, rem_space.y);
651  ImVec2 p = ImGui::GetCursorScreenPos();
652 
653  float com_rel_x = p.x + tex_wh * (ricom->com_public[0] / ricom->camera.nx_cam);
654  float com_rel_y = p.y + tex_wh * (ricom->com_public[1] / ricom->camera.ny_cam);
655 
656  float centre_x = p.x + tex_wh * (ricom->offset[0] / ricom->camera.nx_cam);
657  float centre_y = p.y + tex_wh * (ricom->offset[1] / ricom->camera.ny_cam);
658 
659  com_rel_x = (std::max)(p.x, com_rel_x);
660  com_rel_y = (std::max)(p.y, com_rel_y);
661  com_rel_x = (std::min)(p.x + tex_wh, com_rel_x);
662  com_rel_y = (std::min)(p.y + tex_wh, com_rel_y);
663 
664  float cross_width = tex_wh / 15.0f;
665  if (b_redraw)
666  {
667  if (ricom->srf_cbed != NULL)
668  {
669  bind_tex(ricom->srf_cbed, uiTextureIDs[0]);
670  }
671  }
672  ImGui::Image((ImTextureID)uiTextureIDs[0], ImVec2(tex_wh, tex_wh), uv_min, uv_max, tint_col, border_col);
673  ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * 0.03, IM_COL32(255, 255, 255, 255), 256);
674  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);
675  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);
676  if (ricom->b_vSTEM)
677  {
678  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);
679  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);
680  }
681  ImGui::End();
682 
683  if (b_acq_open)
684  {
685  ImVec2 pos_t = viewport->Pos;
686  pos_t[0] += control_menu_size[0] + 128;
687  pos_t[1] += menu_bar_size[1] + 128;
688  ImGui::SetNextWindowPos(pos_t, ImGuiCond_FirstUseEver);
689  ImVec2 size = {200, 400};
690  ImGui::SetNextWindowSize(size, ImGuiCond_FirstUseEver);
691 
692  ImGui::Begin("Acquisition Header", &b_acq_open);
693  ImGui::BeginChild("Scrolling");
694  ImGui::TextWrapped(ricom->socket.connection_information.data());
695  ImGui::EndChild();
696  ImGui::End();
697  }
698 
699  // Render all generic Image Windows
700  bool trigger = (b_trigger_update != ricom->b_busy) && (ricom->b_busy == false);
701  bool redraw_tick = (ricom->b_busy && b_redraw && b_started);
702  update_views(generic_windows_f, ricom, b_restarted, trigger, redraw_tick);
703  update_views(generic_windows_c, ricom, b_restarted, trigger, redraw_tick);
704 
705  b_restarted = false;
706  b_trigger_update = ricom->b_busy;
707 
708  // Rendering
709  ImGui::Render();
710  glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
711  glClear(GL_COLOR_BUFFER_BIT);
712  ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
713 
714  if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
715  {
716  SDL_Window *backup_current_window = SDL_GL_GetCurrentWindow();
717  SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
718  ImGui::UpdatePlatformWindows();
719  ImGui::RenderPlatformWindowsDefault();
720  SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
721  }
722 
723  SDL_GL_SwapWindow(window);
724 
725  b_redraw = false;
726  }
727 
728  if (run_thread.joinable())
729  run_thread.join();
730  if (py_thread.joinable())
731  py_thread.join();
732 
733  ImGui_ImplOpenGL3_Shutdown();
734  ImGui_ImplSDL2_Shutdown();
735  ImGui::DestroyContext();
736 
737  SDL_GL_DeleteContext(gl_context);
738  SDL_DestroyWindow(window);
739  SDL_Quit();
740 
741  ini_file.write(ini_cfg, true);
742  return 0;
743 }
744 
745 template <typename T>
746 void update_views(std::map<std::string, ImGuiImageWindow<T>> &generic_windows_f, Ricom *ricom, bool b_restarted, bool trigger, bool b_redraw)
747 {
748  for (auto &wnd : generic_windows_f)
749  {
750  if (wnd.first == "RICOM")
751  {
752  if (b_restarted)
753  {
754  wnd.second.set_nx_ny(ricom->nx, ricom->ny);
755  }
756  wnd.second.render_window(b_redraw, ricom->fr_count, ricom->kernel.kernel_size, trigger);
757  }
758  else
759  {
760  if (*wnd.second.pb_open)
761  {
762  if (b_restarted)
763  {
764  wnd.second.set_nx_ny(ricom->nx, ricom->ny);
765  }
766  wnd.second.render_window(b_redraw, ricom->fr_count, trigger);
767  }
768  }
769  }
770 }
771 
772 void bind_tex(SDL_Surface *srf, GLuint tex_id)
773 {
774  glBindTexture(GL_TEXTURE_2D, (tex_id));
775  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, srf->w, srf->h, 0,
776  GL_BGRA, GL_UNSIGNED_BYTE, srf->pixels);
777 }
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:772
#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:746
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:249
std::array< float, 2 > com_public
Definition: Ricom.h:235
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:268
bool b_recompute_detector
Definition: Ricom.h:230
int n_threads
Definition: Ricom.h:252
std::vector< float > stem_data
Definition: Ricom.h:239
int rep
Definition: Ricom.h:246
bool update_offset
Definition: Ricom.h:225
std::vector< float > ricom_data
Definition: Ricom.h:238
int n_threads_max
Definition: Ricom.h:253
float fr_count
Definition: Ricom.h:256
std::vector< std::complex< float > > e_field_data
Definition: Ricom.h:240
int nx
Definition: Ricom.h:243
Ricom_detector detector
Definition: Ricom.h:232
bool rc_quit
Definition: Ricom.h:261
int queue_size
Definition: Ricom.h:254
bool b_e_mag
Definition: Ricom.h:227
SDL_Surface * srf_cbed
Definition: Ricom.h:267
int fr_total
Definition: Ricom.h:247
std::vector< float > com_map_x
Definition: Ricom.h:236
ProgressMonitor * p_prog_mon
Definition: Ricom.h:223
enum CAMERA::Camera_model select_mode_by_file(const char *filename)
Definition: Ricom.cpp:1062
bool b_plot_cbed
Definition: Ricom.h:228
float fr_freq
Definition: Ricom.h:255
bool b_recompute_kernel
Definition: Ricom.h:231
std::vector< float > com_map_y
Definition: Ricom.h:237
CAMERA::Camera_BASE camera
Definition: Ricom.h:218
int skip_row
Definition: Ricom.h:248
bool b_vSTEM
Definition: Ricom.h:226
int ny
Definition: Ricom.h:244
std::array< float, 2 > offset
Definition: Ricom.h:234
Ricom_kernel kernel
Definition: Ricom.h:233
std::string connection_information
std::string ip
@ 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:1096
@ TCP
Definition: Ricom.h:145
@ FILE
Definition: Ricom.h:144
void run_ricom(Ricom *r, RICOM::modes mode)
Definition: Ricom.cpp:1081