Skip to content

Commit aba04e0

Browse files
Evgeni Raikhelclaude
andcommitted
feat(realsense-viewer): viewport grid and metadata overlay
Adds an optional 2D viewport crosshair overlay to the realsense-viewer. Feature summary - Grid lines rendered over each 2D stream viewport using OpenGL (glBegin/GL_LINES) with glPushAttrib(GL_CURRENT_BIT) to prevent color-state bleed into PiP thumbnails - Two new toggle buttons in each stream header: - Grid icon (fa-th ): show/hide the grid overlay - Info icon: show/hide viewport metadata overlay Both buttons start OFF at every launch (state is not persisted) - Per-stream grid config: horizontal lines [1-5], vertical lines [1-5], line width (>=1), RGB color [0-255 each]; out-of-range values fall back to defaults (not clamped) Configuration (realsense-config.json) - Grid settings stored under viewer_model.grid.* using dot-path nested JSON API (get_nested / set_nested / set_nested_default) - Defaults are written only in two cases: 1. New file: config_file tracks _is_new_file (set in the file-based constructor when load_from_file returns nothing); ux_window writes defaults on startup when is_new_file() is true 2. Restore Defaults button: viewer.cpp injects grid defaults into temp_cfg immediately after the reset, so they are saved on OK/Apply - Existing configs without the grid section are left untouched Files changed - CMake/lrs_options.cmake: add BUILD_VIEWPORT_GRID_OVERLAY option (OFF) - common/device-model.h: configurations::viewer::viewport_grid namespace with the six JSON key constants - common/textual-icons.h: textual_icons::grid icon (, fa-th) - common/rs-config.h / rs-config.cpp: is_new_file() accessor backed by _is_new_file member set in the filename constructor - common/stream-model.h / stream-model.cpp: grid state members, toggle buttons, draw_2d_grid() helper; constructor reads grid config from JSON - common/ux-window.cpp: write grid defaults on new-file startup - common/viewer.cpp: write grid defaults when Restore Defaults is pressed - tools/realsense-viewer/CMakeLists.txt: target_compile_definitions for BUILD_VIEWPORT_GRID_OVERLAY - refactor(viewer): make viewport grid overlay a built-in capability - Drop BUILD_VIEWPORT_GRID_OVERLAY cmake option and remove all #ifdef guards — the feature is now always compiled into realsense-viewer. - Rename draw_2d_grid to draw_crosshair per review request. - Update grid overlay button tooltips - Align hover hint text with draw_crosshair rename. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d0a2ebb commit aba04e0

8 files changed

Lines changed: 115 additions & 2 deletions

File tree

common/device-model.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,15 @@ namespace rs2
150150
static const char* lpc_point_size{ "viewer_model.lpc_point_size" };
151151
static const char* show_safety_zones_3d{ "viewer_model.show_safety_zones_3d" };
152152
static const char* show_safety_zones_2d{ "viewer_model.show_safety_zones_2d" };
153+
namespace viewport_grid
154+
{
155+
static const char* horizontal_lines{ "viewer_model.grid.horizontal_lines" };
156+
static const char* vertical_lines { "viewer_model.grid.vertical_lines" };
157+
static const char* line_width { "viewer_model.grid.line_width" };
158+
static const char* line_color_r { "viewer_model.grid.line_color_r" };
159+
static const char* line_color_g { "viewer_model.grid.line_color_g" };
160+
static const char* line_color_b { "viewer_model.grid.line_color_b" };
161+
}
153162
}
154163
namespace window
155164
{

common/rs-config.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ config_file::config_file( std::string const & filename )
123123
}
124124
catch(...)
125125
{
126-
127126
}
128127
_save_thread = std::thread( &config_file::save_loop, this );
129128
}

common/rs-config.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,15 @@ namespace rs2
9595
}
9696

9797
bool contains(const char* key) const;
98-
98+
9999
void save(const char* filename);
100100

101101
void reset();
102102

103103
void remove(const char* key);
104104

105+
bool is_empty() const { return _j.empty(); }
106+
105107
static config_file& instance();
106108

107109
// Retrieves a value from a nested JSON structure using dot notation

common/stream-model.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "os.h"
88
#include <imgui_internal.h>
99
#include <realsense_imgui.h>
10+
#include <iomanip>
11+
#include <sstream>
1012

1113
struct attribute
1214
{
@@ -28,6 +30,21 @@ namespace rs2
2830
configurations::viewer::show_stream_details, false);
2931
show_safety_zones_2d = config_file::instance().get_or_default(
3032
configurations::viewer::show_safety_zones_2d, true);
33+
{
34+
namespace cfg = configurations::viewer::viewport_grid;
35+
auto& cf = config_file::instance();
36+
37+
auto valid_lines = []( int v ) { return ( v >= 1 && v <= 5 ) ? v : 1; };
38+
grid_h_lines = valid_lines( cf.get_nested<int>( cfg::horizontal_lines, 1 ) );
39+
grid_v_lines = valid_lines( cf.get_nested<int>( cfg::vertical_lines, 1 ) );
40+
int w = cf.get_nested<int>( cfg::line_width, 1 );
41+
if ( w >= 1 ) grid_line_width = w;
42+
int r = cf.get_nested<int>( cfg::line_color_r, 255 );
43+
int g = cf.get_nested<int>( cfg::line_color_g, 255 );
44+
int b = cf.get_nested<int>( cfg::line_color_b, 255 );
45+
if ( r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 )
46+
{ grid_color_r = r; grid_color_g = g; grid_color_b = b; }
47+
}
3148
}
3249

3350
std::shared_ptr<texture_buffer> stream_model::upload_frame(frame&& f)
@@ -131,6 +148,31 @@ namespace rs2
131148
glPopAttrib();
132149
}
133150

151+
static void draw_crosshair(const rect& r, int h_lines, int v_lines, int line_width,
152+
int cr, int cg, int cb)
153+
{
154+
glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT);
155+
glLineWidth(static_cast<GLfloat>(line_width));
156+
glEnable(GL_BLEND);
157+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
158+
glColor4f(cr / 255.f, cg / 255.f, cb / 255.f, 0.7f);
159+
glBegin(GL_LINES);
160+
for (int c = 1; c <= v_lines; ++c)
161+
{
162+
float x = r.x + r.w * c / static_cast<float>(v_lines + 1);
163+
glVertex2f(x, r.y);
164+
glVertex2f(x, r.y + r.h);
165+
}
166+
for (int row = 1; row <= h_lines; ++row)
167+
{
168+
float y = r.y + r.h * row / static_cast<float>(h_lines + 1);
169+
glVertex2f(r.x, y);
170+
glVertex2f(r.x + r.w, y);
171+
}
172+
glEnd();
173+
glPopAttrib();
174+
}
175+
134176
bool stream_model::is_stream_visible() const
135177
{
136178
// Inference streams carry binary data, not displayable video - no tile is shown for them
@@ -418,6 +460,7 @@ namespace rs2
418460
if (RS2_STREAM_DEPTH == profile.stream_type()) ++num_of_buttons; // Color map ruler button
419461
if (RS2_FORMAT_MOTION_XYZ32F == profile.format()) ++num_of_buttons; // Motion graph button
420462
if (RS2_STREAM_OCCUPANCY == profile.stream_type() && _normalized_zoom.w == 1) ++num_of_buttons; // Safety zones button
463+
++num_of_buttons; // Grid overlay button
421464

422465
RsImGui_ScopePushFont(font);
423466
ImGui::PushStyleColor(ImGuiCol_Text, light_grey);
@@ -557,6 +600,31 @@ namespace rs2
557600
}
558601
ImGui::SameLine();
559602

603+
label = rsutils::string::from() << textual_icons::grid << "##Grid " << profile.unique_id();
604+
if (show_grid)
605+
{
606+
ImGui::PushStyleColor(ImGuiCol_Text, light_blue);
607+
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, light_blue);
608+
if (ImGui::Button(label.c_str(), { 24, top_bar_height }))
609+
{
610+
show_grid = false;
611+
}
612+
if (ImGui::IsItemHovered())
613+
RsImGui::CustomTooltip("Hide crosshair/grid overlay");
614+
ImGui::PopStyleColor(2);
615+
}
616+
else
617+
{
618+
if (ImGui::Button(label.c_str(), { 24, top_bar_height }))
619+
{
620+
show_grid = true;
621+
}
622+
if (ImGui::IsItemHovered())
623+
RsImGui::CustomTooltip("Show crosshair/grid overlay");
624+
}
625+
ImGui::SameLine();
626+
627+
560628
if (RS2_STREAM_DEPTH == profile.stream_type())
561629
{
562630
label = rsutils::string::from() << textual_icons::bar_chart << "##Color map";
@@ -2045,6 +2113,11 @@ namespace rs2
20452113
}
20462114

20472115
update_ae_roi_rect(stream_rect, g, error_message);
2116+
2117+
if (show_grid)
2118+
draw_crosshair(stream_rect, grid_h_lines, grid_v_lines, grid_line_width,
2119+
grid_color_r, grid_color_g, grid_color_b);
2120+
20482121
}
20492122
texture->show_preview(stream_rect, _normalized_zoom);
20502123

common/stream-model.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ namespace rs2
106106
bool show_metadata = false;
107107
bool show_safety_zones_2d = true;
108108

109+
bool show_grid = false;
110+
int grid_h_lines = 1;
111+
int grid_v_lines = 1;
112+
int grid_line_width = 1;
113+
int grid_color_r = 255;
114+
int grid_color_g = 255;
115+
int grid_color_b = 255;
116+
109117
std::shared_ptr<graph_model> graph;
110118
bool show_graph = false;
111119
bool graph_initialized = false;

common/textual-icons.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace rs2
3434
// A note to a maintainer - preserve order when adding values to avoid duplicates
3535
static const textual_icon search{ u8"\uf002" };
3636
static const textual_icon file_movie{ u8"\uf008" };
37+
static const textual_icon grid{ u8"\uf00a" }; // fa-th: 3x3 grid of squares (viewport grid overlay)
3738
static const textual_icon check{ u8"\uf00c" };
3839
static const textual_icon times{ u8"\uf00d" };
3940
static const textual_icon power_off{ u8"\uf011" };

common/ux-window.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ namespace rs2
8787
config_file::instance().set_default(configurations::viewer::commands_xml, "./Commands.xml");
8888
config_file::instance().set_default(configurations::viewer::hwlogger_xml, "./HWLoggerEvents.xml");
8989

90+
if( config_file::instance().is_empty() )
91+
{
92+
namespace cfg = configurations::viewer::viewport_grid;
93+
auto& cf = config_file::instance();
94+
cf.set_nested_default( cfg::horizontal_lines, 1 );
95+
cf.set_nested_default( cfg::vertical_lines, 1 );
96+
cf.set_nested_default( cfg::line_width, 1 );
97+
cf.set_nested_default( cfg::line_color_r, 255 );
98+
cf.set_nested_default( cfg::line_color_g, 255 );
99+
cf.set_nested_default( cfg::line_color_b, 255 );
100+
}
101+
90102
std::string path;
91103
try
92104
{

common/viewer.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3286,6 +3286,15 @@ namespace rs2
32863286
{
32873287
reload_required = true;
32883288
temp_cfg = config_file();
3289+
{
3290+
namespace cfg = configurations::viewer::viewport_grid;
3291+
temp_cfg.set_nested_default( cfg::horizontal_lines, 1 );
3292+
temp_cfg.set_nested_default( cfg::vertical_lines, 1 );
3293+
temp_cfg.set_nested_default( cfg::line_width, 1 );
3294+
temp_cfg.set_nested_default( cfg::line_color_r, 255 );
3295+
temp_cfg.set_nested_default( cfg::line_color_g, 255 );
3296+
temp_cfg.set_nested_default( cfg::line_color_b, 255 );
3297+
}
32893298
}
32903299
ImGui::SameLine();
32913300
if (ImGui::Button(" Export Settings "))

0 commit comments

Comments
 (0)