Program Listing for File MolaViz.h
↰ Return to documentation for file (include/mola_viz/MolaViz.h)
/* _
_ __ ___ ___ | | __ _
| '_ ` _ \ / _ \| |/ _` | Modular Optimization framework for
| | | | | | (_) | | (_| | Localization and mApping (MOLA)
|_| |_| |_|\___/|_|\__,_| https://github.com/MOLAorg/mola
Copyright (C) 2018-2026 Jose Luis Blanco, University of Almeria,
and individual contributors.
SPDX-License-Identifier: GPL-3.0
See LICENSE for full license information.
*/
#pragma once
#include <mola_kernel/interfaces/Dataset_UI.h>
#include <mola_kernel/interfaces/ExecutableBase.h>
#include <mola_kernel/interfaces/VizInterface.h>
#include <mrpt/gui/CDisplayWindowGUI.h>
#include <deque>
#include <future>
#include <memory>
#include <optional>
#include <set>
#include <shared_mutex>
#include <vector>
namespace mola
{
class MolaViz : public ExecutableBase, public VizInterface
{
DEFINE_MRPT_OBJECT(MolaViz, mola)
public:
MolaViz();
~MolaViz() override;
// Non-copyable, non-movable (owns a GUI thread and static singleton).
MolaViz(const MolaViz&) = delete;
MolaViz& operator=(const MolaViz&) = delete;
MolaViz(MolaViz&&) = delete;
MolaViz& operator=(MolaViz&&) = delete;
// ExecutableBase overrides
void initialize(const Yaml& cfg) override;
void spinOnce() override;
// =========================================================================
using window_name_t = std::string;
using subwindow_name_t = std::string;
static const window_name_t DEFAULT_WINDOW_NAME;
static bool IsRunning();
static MolaViz* Instance();
// =========================================================================
[[nodiscard]] const std::string& gui_backend() const noexcept override;
std::future<void> create_subwindow_from_description(
const mola::gui::WindowDescription& desc,
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<void> enqueue_custom_gui_code(const std::function<void()>& userCode) override;
void* get_subwindow_handle(
const std::string& subWindowTitle,
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<std::optional<std::string>> open_file_dialog(
const std::string& title, bool save,
const std::vector<std::pair<std::string, std::string>>& filters = {},
const std::string& default_path = "",
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<void> set_menu_bar(
const mola::gui::MenuBar& bar,
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
MetricChannel::Ptr register_metric(
const std::string& name, const std::string& unit = "") override;
void push_metric(const std::string& name, double t, double value) override;
// =========================================================================
std::future<bool> update_3d_object(
const std::string& objName, const std::shared_ptr<mrpt::opengl::CSetOfObjects>& obj,
const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME,
const std::string& parentFrame = "") override;
std::future<bool> update_3d_object_frame(
const std::string& frameName, const mrpt::math::TPose3D& pose,
const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<bool> insert_point_cloud_with_decay(
const std::shared_ptr<mrpt::opengl::CPointCloudColoured>& cloud, double decay_time_seconds,
const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME,
const std::string& parentFrame = "") override;
std::future<bool> clear_all_point_clouds_with_decay(
const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<bool> update_viewport_look_at(
const mrpt::math::TPoint3Df& lookAt, const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME,
const std::string& parentFrame = "") override;
std::future<bool> update_viewport_camera_azimuth(
double azimuth, bool absolute_falseForRelative = true,
const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<bool> update_viewport_camera_orthographic(
bool orthographic, const std::string& viewportName = "main",
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
std::future<bool> execute_custom_code_on_background_scene(
const std::function<void(mrpt::opengl::Scene&)>& userCode,
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
// =========================================================================
std::future<bool> subwindow_update_visualization(
const mrpt::rtti::CObject::Ptr& obj, const std::string& subWindowTitle,
const mrpt::containers::yaml* extra_parameters = nullptr,
const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
// =========================================================================
std::future<bool> output_console_message(
const std::string& message, const std::string& parentWindow = DEFAULT_WINDOW_NAME) override;
// =========================================================================
using update_handler_t = std::function<void(
const mrpt::rtti::CObject::Ptr&, nanogui::Window* subWin, const window_name_t& parentWin,
MolaViz* instance, const mrpt::containers::yaml* extra_parameters)>;
using class_name_t = std::string;
static void register_gui_handler(const class_name_t& name, const update_handler_t& handler);
// =========================================================================
double console_text_font_size_ = 9.0;
unsigned int max_console_lines_ = 5;
bool show_rgbd_as_point_cloud_ = false;
double assumed_sensor_rate_hz_ = 10.0;
void markWindowForReLayout(const window_name_t& name)
{
auto lck = mrpt::lockHelper(guiThreadPendingTasksMtx_);
guiThreadMustReLayoutTheseWindows_.insert(name);
}
private:
// ---------------------------------------------------------------------------
// Singleton
// ---------------------------------------------------------------------------
static MolaViz* instance_;
static std::shared_mutex instanceMtx_;
// ---------------------------------------------------------------------------
// Per-window state
// ---------------------------------------------------------------------------
struct DecayingCloud
{
DecayingCloud() = default;
DecayingCloud(
std::string opengl_viewport_name_,
const std::shared_ptr<mrpt::opengl::CPointCloudColoured>& cloud_, float initial_alpha_)
: opengl_viewport_name(std::move(opengl_viewport_name_)),
cloud(cloud_),
initial_alpha(initial_alpha_)
{
}
std::string opengl_viewport_name;
std::shared_ptr<mrpt::opengl::CPointCloudColoured> cloud;
mrpt::opengl::CSetOfObjects::Ptr container; // owning container at insert time
float initial_alpha = 1.0f;
};
struct PerWindowData
{
mrpt::gui::CDisplayWindowGUI::Ptr win;
std::vector<std::string> console_messages;
std::deque<DecayingCloud> decaying_clouds;
size_t max_decaying_clouds = 100;
};
std::map<window_name_t, PerWindowData> windows_;
std::map<window_name_t, std::map<subwindow_name_t, nanogui::Window*>> subWindows_;
mrpt::gui::CDisplayWindowGUI::Ptr create_and_add_window(const window_name_t& name);
static std::shared_ptr<mrpt::opengl::CSetOfObjects> get_or_create_frame_node_(
mrpt::opengl::Scene& scene, const std::string& frameName, const std::string& viewportName);
mutable std::shared_mutex subWindowsMtx_;
// ---------------------------------------------------------------------------
// GUI thread and task queue
// ---------------------------------------------------------------------------
std::thread guiThread_;
void gui_thread();
using task_queue_t = std::vector<std::function<void()>>;
task_queue_t guiThreadPendingTasks_;
std::set<window_name_t> guiThreadMustReLayoutTheseWindows_;
std::mutex guiThreadPendingTasksMtx_;
// ---------------------------------------------------------------------------
// LiveString polling (called every GUI frame from gui_thread loop callback)
// ---------------------------------------------------------------------------
void poll_live_strings_in_subwindows_();
std::map<uint64_t, mola::gui::LiveString::Ptr> liveStringRegistry_;
// ---------------------------------------------------------------------------
// Dataset_UI auto-generated panels
// ---------------------------------------------------------------------------
double lastTimeCheckForNewModules_ = 0;
double lastTimeUpdateDatasetUIs_ = 0;
struct DataPerDatasetUI
{
std::weak_ptr<mola::Dataset_UI> module;
bool first_time_seen = true;
mola::gui::LiveString::Ptr lbPlaybackPosition;
// Note: slider, cbPaused, cmRate are now owned by the nanogui widget tree
// created via create_subwindow_from_description(). They are not held here
// because the description callbacks capture weak_ptr<Dataset_UI> directly,
// making raw widget pointers unnecessary.
};
std::map<std::string, DataPerDatasetUI> datasetUIs_;
void dataset_ui_check_new_modules();
void dataset_ui_update();
// ---------------------------------------------------------------------------
// Decaying clouds
// ---------------------------------------------------------------------------
void internal_handle_decaying_clouds();
};
} // namespace mola