ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Application.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #ifdef _MSC_VER
11 #define WIN32_LEAN_AND_MEAN
12 #include <windows.h> // so APIENTRY gets defined and GLFW doesn't define it
13 #endif // _MSC_VER
14 
15 #include <FileSystem.h>
16 #include <GLFW/glfw3.h>
17 #include <Image.h>
18 #include <Logging.h>
19 
20 #include <algorithm>
21 #include <chrono>
22 #include <iostream>
23 #include <list>
24 #include <mutex>
25 #include <thread>
26 #include <unordered_set>
27 
34 #include "visualization/gui/Task.h"
36 #include "visualization/gui/Util.h"
43 
44 namespace {
45 
46 const double RUNLOOP_DELAY_SEC = 0.010;
47 
48 std::string FindResourcePath(int argc, const char *argv[]) {
49  namespace cvfs = cloudViewer::utility::filesystem;
50  std::string argv0;
51  if (argc != 0 && argv) {
52  argv0 = argv[0];
53  }
54 
55  // Convert backslash (Windows) to forward slash
56  for (auto &c : argv0) {
57  if (c == '\\') {
58  c = '/';
59  }
60  }
61 
62  // Chop off the process name
63  auto last_slash = argv0.rfind("/");
64  auto path = argv0.substr(0, last_slash);
65 
66  if (argv0[0] == '/' ||
67  (argv0.size() > 3 && argv0[1] == ':' && argv0[2] == '/')) {
68  // is absolute path, we're done
69  } else {
70  // relative path: prepend working directory
71  auto cwd = cvfs::GetWorkingDirectory();
72 #ifdef __APPLE__
73  // When running an app from the command line with the full relative
74  // path (e.g. `bin/CloudViewer.app/Contents/MacOS/CloudViewer`), the
75  // working directory can be set to the resources directory, in which
76  // case a) we are done, and b) cwd + / + argv0 is wrong.
77  if (cwd.rfind("/Contents/Resources") == cwd.size() - 19) {
78  return cwd;
79  }
80 #endif // __APPLE__
81  path = cwd + "/" + path;
82  }
83 
84 #ifdef __APPLE__
85  if (path.rfind("MacOS") == path.size() - 5) { // path is in a bundle
86  return path.substr(0, path.size() - 5) + "Resources";
87  }
88 #endif // __APPLE__
89 
90  for (auto &subpath :
91  {"/resources", "/../resources" /*building with Xcode */,
92  "/share/resources" /* GNU */,
93  "/share/cloudViewer/resources" /* GNU */}) {
94  cloudViewer::utility::LogInfo("Checking for resources in {}",
95  path + subpath);
96  if (cvfs::DirectoryExists(path + subpath)) {
97  return path + subpath;
98  }
99  }
100  cloudViewer::utility::LogError("Could not find resource directory.");
101 
102  return "";
103 }
104 
105 } // namespace
106 
107 namespace cloudViewer {
108 namespace visualization {
109 namespace gui {
110 
112  bool is_initialized_ = false;
113  std::shared_ptr<WindowSystem> window_system_;
114  std::vector<FontDescription> fonts_;
116  double last_time_ = 0.0;
117  bool is_ws_initialized_ = false;
118  bool is_running_ = false;
119  bool should_quit_ = false;
120 
121  std::shared_ptr<Menu> menubar_;
122  std::unordered_set<std::shared_ptr<Window>> windows_;
123  std::unordered_set<std::shared_ptr<Window>> windows_to_be_destroyed_;
124 
125  std::list<Task> running_tasks_; // always accessed from main thread
126  // ----
127  struct Posted {
129  std::function<void()> f;
130 
131  Posted(Window *w, std::function<void()> func) : window(w), f(func) {}
132  };
133  std::mutex posted_lock_;
134  std::vector<Posted> posted_;
135  // ----
136 
138  if (!window_system_) {
139  window_system_ = std::make_shared<GLFWWindowSystem>();
140  }
141 
142  if (!is_ws_initialized_) {
143  window_system_->Initialize();
144  is_ws_initialized_ = true;
145  }
146  }
147 
149  // We already called this in the constructor, but it is possible
150  // that the run loop finished and is starting again.
152 
153  // Initialize rendering
156  kOpenGL);
157  }
158 
160  // Aside from general tidiness in shutting down rendering,
161  // failure to do this causes the Python module to hang on
162  // Windows. (Specifically, if a widget is has been assigned a
163  // Python function as a callback, the Python interpreter will
164  // not delete the objects, the Window's destructor will not be
165  // called, and the Filament threads will not stop, causing the
166  // Python process to remain running even after execution of the
167  // script finishes.
169 
170  if (window_system_) {
171  window_system_->Uninitialize();
172  }
173  is_ws_initialized_ = false;
174  }
175 };
176 
177 constexpr FontId Application::DEFAULT_FONT_ID; // already assigned in header
178 
180  static Application g_app;
181  return g_app;
182 }
183 
184 void Application::ShowMessageBox(const char *title, const char *message) {
185  utility::LogInfo("{}", message);
186 
187  auto alert = std::make_shared<Window>((title ? title : "Alert"),
189  auto em = alert->GetTheme().font_size;
190  auto layout = std::make_shared<Vert>(em, Margins(em));
191  auto msg = std::make_shared<Label>(message);
192  auto ok = std::make_shared<Button>("Ok");
193  ok->SetOnClicked([alert = alert.get() /*avoid shared_ptr cycle*/]() {
194  Application::GetInstance().RemoveWindow(alert);
195  });
196  layout->AddChild(Horiz::MakeCentered(msg));
197  layout->AddChild(Horiz::MakeCentered(ok));
198  alert->AddChild(layout);
200 }
201 
202 Application::Application() : impl_(new Application::Impl()) {
203  Color highlight_color(0.5, 0.5, 0.5);
204 
205  // Note that any values here need to be scaled by the scale factor in Window
206  impl_->theme_.font_path =
207  "Roboto-Medium.ttf"; // full path will be added in Initialize()
208  impl_->theme_.font_bold_path = "Roboto-Bold.ttf";
209  impl_->theme_.font_italic_path = "Roboto-MediumItalic.ttf";
210  impl_->theme_.font_bold_italic_path = "Roboto-BoldItalic.ttf";
211  impl_->theme_.font_mono_path = "RobotoMono-Medium.ttf";
212  impl_->theme_.font_size = 16; // 1 em (font size is em in digital type)
213  impl_->theme_.default_margin = 8; // 0.5 * em
214  impl_->theme_.default_layout_spacing = 6; // 0.333 * em
215 
216  impl_->theme_.background_color = Color(0.175f, 0.175f, 0.175f);
217  impl_->theme_.text_color = Color(0.875f, 0.875f, 0.875f);
218  impl_->theme_.border_width = 1;
219  impl_->theme_.border_radius = 3;
220  impl_->theme_.border_color = Color(0.5f, 0.5f, 0.5f);
221  impl_->theme_.menubar_border_color = Color(0.25f, 0.25f, 0.25f);
222  impl_->theme_.button_color = Color(0.4f, 0.4f, 0.4f);
223  impl_->theme_.button_hover_color = Color(0.6f, 0.6f, 0.6f);
224  impl_->theme_.button_active_color = Color(0.5f, 0.5f, 0.5f);
225  impl_->theme_.button_on_color = Color(0.7f, 0.7f, 0.7f);
226  impl_->theme_.button_on_hover_color = Color(0.9f, 0.9f, 0.9f);
227  impl_->theme_.button_on_active_color = Color(0.8f, 0.8f, 0.8f);
228  impl_->theme_.button_on_text_color = Color(0, 0, 0);
229  impl_->theme_.checkbox_background_off_color = Color(0.333f, 0.333f, .333f);
230  impl_->theme_.checkbox_background_on_color = highlight_color;
231  impl_->theme_.checkbox_background_hover_off_color = Color(0.5f, 0.5f, 0.5f);
232  impl_->theme_.checkbox_background_hover_on_color =
233  highlight_color.Lightened(0.15f);
234  impl_->theme_.checkbox_check_color = Color(0.9f, 0.9f, 0.9f);
235  impl_->theme_.radiobtn_background_off_color = Color(0.333f, 0.333f, .333f);
236  impl_->theme_.radiobtn_background_on_color = highlight_color;
237  impl_->theme_.radiobtn_background_hover_off_color = Color(0.5f, 0.5f, 0.5f);
238  impl_->theme_.radiobtn_background_hover_on_color =
239  highlight_color.Lightened(0.15f);
240  impl_->theme_.toggle_background_off_color =
241  impl_->theme_.checkbox_background_off_color;
242  impl_->theme_.toggle_background_on_color = Color(0.666f, 0.666f, 0.666f);
243  impl_->theme_.toggle_background_hover_off_color =
244  impl_->theme_.checkbox_background_hover_off_color;
245  impl_->theme_.toggle_background_hover_on_color =
246  impl_->theme_.toggle_background_on_color.Lightened(0.15f);
247  impl_->theme_.toggle_thumb_color = Color(1, 1, 1);
248  impl_->theme_.combobox_background_color = Color(0.4f, 0.4f, 0.4f);
249  impl_->theme_.combobox_hover_color = Color(0.5f, 0.5f, 0.5f);
250  impl_->theme_.combobox_arrow_background_color = highlight_color;
251  impl_->theme_.slider_grab_color = Color(0.666f, 0.666f, 0.666f);
252  impl_->theme_.text_edit_background_color = Color(0.1f, 0.1f, 0.1f);
253  impl_->theme_.list_background_color = Color(0.1f, 0.1f, 0.1f);
254  impl_->theme_.list_hover_color = Color(0.6f, 0.6f, 0.6f);
255  impl_->theme_.list_selected_color = Color(0.5f, 0.5f, 0.5f);
256  impl_->theme_.tree_background_color = impl_->theme_.list_background_color;
257  impl_->theme_.tree_selected_color = impl_->theme_.list_selected_color;
258  impl_->theme_.tab_inactive_color = impl_->theme_.button_color;
259  impl_->theme_.tab_hover_color = impl_->theme_.button_hover_color;
260  impl_->theme_.tab_active_color = impl_->theme_.button_active_color;
261  impl_->theme_.dialog_border_width = 1;
262  impl_->theme_.dialog_border_radius = 10;
263 }
264 
266 
268  // We don't have a great way of getting the process name, so let's hope that
269  // the current directory is where the resources are located. This is a
270  // safe assumption when running on macOS and Windows normally.
272  // Copy to C string, as some implementations of std::string::c_str()
273  // return a very temporary pointer.
274  char *argv = strdup(path.c_str());
275  Initialize(1, (const char **)&argv);
276  free(argv);
277 }
278 
279 void Application::Initialize(int argc, const char *argv[]) {
280  Initialize(FindResourcePath(argc, argv).c_str());
281 }
282 
283 void Application::Initialize(const char *resource_path) {
284  // Prepare for running so that we can create windows. Note that although
285  // Application may be initialized, GLFW/Filament may not be, if we finished
286  // Run() and are calling again.
287  impl_->PrepareForRunning();
288 
289  if (impl_->is_initialized_) {
290  return;
291  }
292 
294  std::string uiblit_path = std::string(resource_path) + "/ui_blit.filamat";
295  if (!utility::filesystem::FileExists(uiblit_path)) {
297  "Resource directory does not have CloudViewer resources: {}",
298  resource_path);
299  }
300 
301  impl_->theme_.font_path = std::string(resource_path) + std::string("/") +
302  impl_->theme_.font_path;
303  impl_->theme_.font_bold_path = std::string(resource_path) +
304  std::string("/") +
305  impl_->theme_.font_bold_path;
306  impl_->theme_.font_italic_path = std::string(resource_path) +
307  std::string("/") +
308  impl_->theme_.font_italic_path;
309  impl_->theme_.font_bold_italic_path = std::string(resource_path) +
310  std::string("/") +
311  impl_->theme_.font_bold_italic_path;
312  impl_->theme_.font_mono_path = std::string(resource_path) +
313  std::string("/") +
314  impl_->theme_.font_mono_path;
315  if (impl_->fonts_.empty()) {
318  }
319  impl_->is_initialized_ = true;
320 }
321 
323  if (impl_->is_initialized_) {
324  return;
325  }
326 
327  // Call LogWarning() first because it is easier to visually parse than the
328  // error message.
329  utility::LogWarning("gui::Initialize() was not called");
330 
331  // It would be nice to make this LogWarning() and then call Initialize(),
332  // but Python scripts requires a different heuristic for finding the
333  // resource path than C++.
335  "gui::Initialize() must be called before creating a window or UI "
336  "element.");
337 }
338 
340  auto os_ws =
341  std::dynamic_pointer_cast<GLFWWindowSystem>(impl_->window_system_);
342  return (os_ws != nullptr);
343 }
344 
346  return *impl_->window_system_;
347 }
348 
349 void Application::SetWindowSystem(std::shared_ptr<WindowSystem> ws) {
350  if (impl_->window_system_ != nullptr) {
351  utility::LogError("Cannot set WindowSystem. It is already set.");
352  }
353  impl_->window_system_ = ws;
354  impl_->is_ws_initialized_ = false;
355 }
356 
358  FontId id = impl_->fonts_.size();
359  impl_->fonts_.push_back(fd);
360  SetFont(id, fd); // make sure paths get update properly
361  return id;
362 }
363 
365  auto GetSansSerifPath = [this](FontStyle style) {
366  switch (style) {
367  case FontStyle::BOLD:
368  return impl_->theme_.font_bold_path;
369  case FontStyle::ITALIC:
370  return impl_->theme_.font_italic_path;
372  return impl_->theme_.font_bold_italic_path;
373  default:
374  return impl_->theme_.font_path;
375  }
376  };
377 
378  auto GetStyleName = [](FontStyle style) {
379  switch (style) {
380  case FontStyle::BOLD:
381  return "BOLD";
382  case FontStyle::ITALIC:
383  return "ITALIC";
385  return "BOLD_ITALIC";
386  default:
387  return "NORMAL";
388  }
389  };
390 
391  impl_->fonts_[id] = fd;
392  auto style = impl_->fonts_[id].style_;
393  for (auto &range : impl_->fonts_[id].ranges_) {
394  // Substitute proper paths for default CSS-style virtual fonts
395  if (range.path == FontDescription::SANS_SERIF) {
396  range.path = GetSansSerifPath(style);
397  } else if (range.path == FontDescription::MONOSPACE) {
398  range.path = impl_->theme_.font_mono_path;
399  }
400  // Get the actual path
401  auto path = FindFontPath(range.path, style);
402  if (!path.empty()) {
403  range.path = path;
404  } else {
405  // If we can't find the requested style, try to at least find the
406  // typeface.
407  auto fallback = FindFontPath(range.path, FontStyle::NORMAL);
408  if (fallback.empty()) {
409  // But if that doesn't work, fall back to styled sans-serif.
410  fallback = GetSansSerifPath(style);
411  }
412  utility::LogWarning("Could not find font '{}' with style {}",
413  range.path, GetStyleName(style));
414  range.path = fallback;
415  }
416  }
417 
418  if (id == DEFAULT_FONT_ID && fd.point_size_ > 0) {
419  impl_->theme_.font_size = fd.point_size_;
420  }
421 }
422 
423 const std::vector<FontDescription> &Application::GetFontDescriptions() const {
424  return impl_->fonts_;
425 }
426 
427 double Application::Now() const {
428  static auto g_tzero = std::chrono::steady_clock::now();
429  std::chrono::duration<double> t =
430  std::chrono::steady_clock::now() - g_tzero;
431  return t.count();
432 }
433 
434 std::shared_ptr<Menu> Application::GetMenubar() const {
435  return impl_->menubar_;
436 }
437 
438 void Application::SetMenubar(std::shared_ptr<Menu> menubar) {
439  auto old = impl_->menubar_;
440  impl_->menubar_ = menubar;
441  // If added or removed menubar, the size of the window's content region
442  // may have changed (in not on macOS), so need to relayout.
443  if ((!old && menubar) || (old && !menubar)) {
444  for (auto w : impl_->windows_) {
445  w->OnResize();
446  }
447  }
448 
449 #if defined(__APPLE__)
450  auto *native = menubar->GetNativePointer();
451  if (native) {
452  SetNativeMenubar(native);
453  }
454 #endif // __APPLE__
455 }
456 
457 void Application::AddWindow(std::shared_ptr<Window> window) {
458  window->OnResize(); // so we get an initial resize
459  window->Show();
460  impl_->windows_.insert(window);
461 }
462 
464  if (impl_->should_quit_) {
465  return;
466  }
467 
468  for (auto it = impl_->windows_.begin(); it != impl_->windows_.end(); ++it) {
469  if (it->get() == window) {
470  impl_->windows_to_be_destroyed_.insert(*it);
471  impl_->windows_.erase(it);
472  if (impl_->windows_.empty()) {
473  impl_->should_quit_ = true;
474  }
475  break;
476  }
477  }
478  window->Show(false);
479 }
480 
482  while (!impl_->windows_.empty()) {
483  RemoveWindow(impl_->windows_.begin()->get());
484  }
485 }
486 
488  // Note: if you need to modify this function, you should test that
489  // the following still work:
490  // 1) on macOS, quit by right-clicking on the dock icon and
491  // selecting Quit.
492  // 2) run a Python script that creates a window and exits cleanly.
493  // 3) run a Python script that creates a window and throws a
494  // fatal exception.
495 
496  // This function should work even if called after a successful cleanup
497  // (e.g. after Run() successfully finished, either due to closing the
498  // last window or Quit() called).
499 
500  Quit();
501  // If we are in exit() already (e.g. an exception occurred in a
502  // Python callback and the interpreter is exiting) just clearing
503  // the shared_ptr may not be sufficient to destroy the object.
504  // We need to clean up filament to avoid a crash, but we will
505  // hang if the window still exists.
506  for (auto w : impl_->windows_to_be_destroyed_) {
507  w->DestroyWindow();
508  }
509  impl_->windows_to_be_destroyed_.clear();
510  impl_->CleanupAfterRunning();
511 }
512 
514  for (auto w : impl_->windows_) {
515  if (w->IsActiveWindow()) {
516  w->OnMenuItemSelected(itemId);
517  // This is a menu selection that came from a native menu.
518  // We need to draw twice to ensure that any new dialog
519  // that the menu item may have displayed is properly laid out.
520  // (ImGUI can take up to two iterations to fully layout)
521  // If we post two expose events they get coalesced, but
522  // setting needsLayout forces two (for the reason given above).
523  w->SetNeedsLayout();
524  w->PostRedraw();
525  return;
526  }
527  }
528 }
529 
531  EnvUnlocker noop; // containing env is C++
532  while (RunOneTick(noop));
533 }
534 
536  bool cleanup_if_no_windows /*=true*/) {
537  // Initialize if we have not started yet
538  if (!impl_->is_running_) {
539  // Verify that the resource path is valid. If it is not, display a
540  // message box (std::cerr may not be visible to the user, if we were run
541  // as app).
542  if (!impl_->is_initialized_) {
544  "Internal error: Application::Initialize() was not called");
545  return false;
546  }
547  auto resource_path = rendering::EngineInstance::GetResourcePath();
548  if (!utility::filesystem::DirectoryExists(resource_path)) {
549  std::stringstream err;
550  err << "Could not find resource directory:\n'" << resource_path
551  << "' does not exist";
552  ShowNativeAlert(err.str().c_str());
553  return false;
554  }
555  if (!utility::filesystem::FileExists(impl_->theme_.font_path)) {
556  std::stringstream err;
557  err << "Could not load UI font:\n'" << impl_->theme_.font_path
558  << "' does not exist";
559  ShowNativeAlert(err.str().c_str());
560  return false;
561  }
562 
563  impl_->PrepareForRunning();
564  impl_->is_running_ = true;
565  }
566 
567  // Process the events that have queued up
568  auto status = ProcessQueuedEvents(unlocker);
569 
570  // Cleanup if we are done
571  if (status == RunStatus::DONE) {
572  if (cleanup_if_no_windows) {
573  // Clear all the running tasks. The destructor will wait for them to
574  // finish.
575  for (auto it = impl_->running_tasks_.begin();
576  it != impl_->running_tasks_.end(); ++it) {
577  auto current = it;
578  ++it;
579  impl_->running_tasks_.erase(current); // calls join()
580  }
581 
582  impl_->is_running_ = false;
583  impl_->CleanupAfterRunning();
584  }
585  // reset, otherwise we will be done next time, too.
586  impl_->should_quit_ = false;
587  }
588 
589  return (status == RunStatus::CONTINUE);
590 }
591 
592 Application::RunStatus Application::ProcessQueuedEvents(EnvUnlocker &unlocker) {
593  unlocker.unlock(); // don't want to be locked while we wait
594  impl_->window_system_->WaitEventsTimeout(RUNLOOP_DELAY_SEC);
595  unlocker.relock(); // need to relock in case we call any callbacks to
596  // functions in the containing (e.g. Python) environment
597 
598  // Handle tick messages.
599  double now = Now();
600  if (now - impl_->last_time_ >= 0.95 * RUNLOOP_DELAY_SEC) {
601  for (auto w : impl_->windows_) {
602  w->OnTickEvent(TickEvent());
603  }
604  impl_->last_time_ = now;
605  }
606 
607  // Run any posted functions
608  // To avoid deadlock while PostToMainThread is called in Posted, the
609  // posted_lock_ should not be locked in its invoking.
610  decltype(impl_->posted_) posted;
611  {
612  // The only other place posted_lock_ is used is PostToMainThread.
613  // If pybind is posting a Python function, it acquires posted_lock_,
614  // then locks the GIL. Since we are locked at this point, we (can)
615  // deadlock. (So far only observed on macOS, within about 10 runs)
616  unlocker.unlock();
617  std::lock_guard<std::mutex> lock(impl_->posted_lock_);
618  unlocker.relock();
619  posted = std::move(impl_->posted_);
620  }
621 
622  for (auto &p : posted) {
623  // Make sure this window still exists. Unfortunately, p.window
624  // is a pointer but impl_->windows_ is a shared_ptr, so we can't
625  // use find.
626  if (p.window) {
627  bool found = false;
628  for (auto w : impl_->windows_) {
629  if (w.get() == p.window) {
630  found = true;
631  }
632  }
633  if (!found) {
634  continue;
635  }
636  }
637 
638  void *old = nullptr;
639  if (p.window) {
640  old = p.window->MakeDrawContextCurrent();
641  }
642  p.f();
643  if (p.window) {
644  p.window->RestoreDrawContext(old);
645  p.window->PostRedraw();
646  }
647  }
648 
649  // Clear any tasks that have finished
650  impl_->running_tasks_.remove_if(
651  [](const Task &t) { return t.IsFinished(); });
652 
653  // We can't destroy a GLFW window in a callback, so we need to do it here.
654  // Since these are the only copy of the shared pointers, this will cause
655  // the Window destructor to be called.
656  impl_->windows_to_be_destroyed_.clear();
657 
658  if (impl_->should_quit_) {
659  return RunStatus::DONE;
660  }
661  return RunStatus::CONTINUE;
662 }
663 
664 void Application::RunInThread(std::function<void()> f) {
665  // We need to be on the main thread here.
666  impl_->running_tasks_.emplace_back(f);
667  impl_->running_tasks_.back().Run();
668 }
669 
670 void Application::PostToMainThread(Window *window, std::function<void()> f) {
671  std::lock_guard<std::mutex> lock(impl_->posted_lock_);
672  impl_->posted_.emplace_back(window, f);
673 }
674 
675 const char *Application::GetResourcePath() const {
677 }
678 
679 const Theme &Application::GetTheme() const { return impl_->theme_; }
680 
681 std::shared_ptr<geometry::Image> Application::RenderToImage(
682  rendering::Renderer &renderer,
683  rendering::View *view,
684  rendering::Scene *scene,
685  int width,
686  int height) {
687  std::shared_ptr<geometry::Image> img;
688  auto callback = [&img](std::shared_ptr<geometry::Image> _img) {
689  img = _img;
690  };
691 
692  // Despite the fact that Renderer is created with a width/height, it is
693  // the View's viewport that actually controls the size when rendering to
694  // an image. Set the viewport here, rather than in the pybinds so that
695  // C++ callers do not need to know do this themselves.
696  view->SetViewport(0, 0, width, height);
697 
698  renderer.RenderToImage(view, scene, callback);
699  renderer.BeginFrame();
700  renderer.EndFrame();
701 
702  return img;
703 }
704 
705 std::shared_ptr<geometry::Image> Application::RenderToDepthImage(
706  rendering::Renderer &renderer,
707  rendering::View *view,
708  rendering::Scene *scene,
709  int width,
710  int height,
711  bool z_in_view_space /* =false */) {
712  std::shared_ptr<geometry::Image> img;
713  auto callback = [&img](std::shared_ptr<geometry::Image> _img) {
714  img = _img;
715  };
716 
717  // Despite the fact that Renderer is created with a width/height, it is
718  // the View's viewport that actually controls the size when rendering to
719  // an image. Set the viewport here, rather than in the pybinds so that
720  // C++ callers do not need to know do this themselves.
721  view->SetViewport(0, 0, width, height);
722 
723  renderer.RenderToDepthImage(view, scene, callback, z_in_view_space);
724  renderer.BeginFrame();
725  renderer.EndFrame();
726 
727  return img;
728 }
729 
730 } // namespace gui
731 } // namespace visualization
732 } // namespace cloudViewer
std::function< void(std::shared_ptr< core::Tensor >)> callback
int width
int height
void SetMenubar(std::shared_ptr< Menu > menubar)
const std::vector< FontDescription > & GetFontDescriptions() const
std::shared_ptr< geometry::Image > RenderToImage(rendering::Renderer &renderer, rendering::View *view, rendering::Scene *scene, int width, int height)
static constexpr FontId DEFAULT_FONT_ID
Identifier for font used by default for all UI elements.
Definition: Application.h:57
void SetFont(FontId id, const FontDescription &fd)
std::shared_ptr< Menu > GetMenubar() const
void RunInThread(std::function< void()> f)
FontId AddFont(const FontDescription &fd)
void Run()
Does not return until the UI is completely finished.
void SetWindowSystem(std::shared_ptr< WindowSystem > ws)
void ShowMessageBox(const char *title, const char *message)
void OnMenuItemSelected(Menu::ItemId itemId)
Delivers the itemId to the active window. Used internally.
void AddWindow(std::shared_ptr< Window > window)
Must be called on the same thread that calls Run()
void Quit()
Closes all the windows, which exits as a result.
void PostToMainThread(Window *window, std::function< void()> f)
std::shared_ptr< geometry::Image > RenderToDepthImage(rendering::Renderer &renderer, rendering::View *view, rendering::Scene *scene, int width, int height, bool z_in_view_space=false)
bool RunOneTick(EnvUnlocker &unlocker, bool cleanup_if_no_windows=true)
constexpr static const char * MONOSPACE
Definition: Font.h:23
constexpr static const char * SANS_SERIF
Definition: Font.h:22
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
Definition: Layout.cpp:531
static void SetResourcePath(const std::string &resource_path)
void RenderToImage(View *view, Scene *scene, std::function< void(std::shared_ptr< geometry::Image >)> cb)
Definition: Renderer.cpp:68
void RenderToDepthImage(View *view, Scene *scene, std::function< void(std::shared_ptr< geometry::Image >)> cb, bool z_in_view_space=false)
Definition: Renderer.cpp:95
virtual void SetViewport(std::int32_t x, std::int32_t y, std::uint32_t w, std::uint32_t h)=0
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
static const std::string path
Definition: PointCloud.cpp:59
bool DirectoryExists(const std::string &directory)
Definition: FileSystem.cpp:473
bool FileExists(const std::string &filename)
Definition: FileSystem.cpp:524
void ShowNativeAlert(const char *message)
Definition: NativeLinux.cpp:55
std::string FindFontPath(std::string font, FontStyle style)
Definition: Util.cpp:32
Generic file read and write utility for python interface.
Posted(Window *w, std::function< void()> func)
std::unordered_set< std::shared_ptr< Window > > windows_
std::unordered_set< std::shared_ptr< Window > > windows_to_be_destroyed_
std::shared_ptr< WindowSystem > window_system_