Skip to content

Commit 92b1bae

Browse files
Evgeni Raikhelclaude
andcommitted
Add HKR MinZ Control viewer UI, fix embedded-filter-model crash, quiet preset-glob noise
embedded-filter-model.h/.cpp: - Fix a real crash: populate_options()/embedded_filter_enable_disable() read/wrote RS2_OPTION_EMBEDDED_FILTER_ENABLED unconditionally, which throws for any composite-only embedded filter (this predates the MinZ work - the already-shipped temporal filter has the same gap). Now guarded with supports(). - Enumerate composite options separately from scalar ones (own registry, see rs_composite_option.h) via a new _composite_option_ids list. - Add a hardcoded editor for RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL: checkbox/radio/sliders for all 5 rs2_minz_control fields, grouped in a framed box with minimal indent, a send-only button (no auto-readback), and the control's description as a hover tooltip instead of an always-visible line. device-model.cpp: the advanced-mode preset scan calls glob() unconditionally, which throws whenever the presets folder doesn't exist yet (the common, expected case) - added an isDir() pre-check (already available via the existing glob.h include) so that no longer throws at all instead of relying on the surrounding catch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 991a5a5 commit 92b1bae

3 files changed

Lines changed: 198 additions & 15 deletions

File tree

common/device-model.cpp

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -325,24 +325,35 @@ namespace rs2
325325

326326
auto path = rsutils::os::get_special_folder( rsutils::os::special_folder::user_documents );
327327
path += "librealsense2/presets/";
328-
try
328+
// glob_rec() (third-party/filesystem/glob.h) throws whenever opendir() fails - which is
329+
// the common, expected case here (most machines have never created this folder). Check
330+
// first instead of relying on the exception: avoids a first-chance throw/catch on nearly
331+
// every device refresh, which was showing up as debugger noise unrelated to any real bug.
332+
if( isDir( path, nullptr ) )
329333
{
330-
std::string name = dev.get_info(RS2_CAMERA_INFO_NAME);
331-
std::smatch match;
332-
if( ! std::regex_search( name, match, std::regex( "^RealSense (\\S+)" ) ) )
333-
throw std::runtime_error( "cannot parse device name from '" + name + "'" );
334+
try
335+
{
336+
std::string name = dev.get_info(RS2_CAMERA_INFO_NAME);
337+
std::smatch match;
338+
if( ! std::regex_search( name, match, std::regex( "^RealSense (\\S+)" ) ) )
339+
throw std::runtime_error( "cannot parse device name from '" + name + "'" );
334340

335-
glob(
336-
path,
337-
std::string( match[1] ) + " *.preset",
338-
[&]( std::string const & file ) {
339-
advanced_mode_settings_file_names.insert( path + file );
340-
},
341-
false ); // recursive
341+
glob(
342+
path,
343+
std::string( match[1] ) + " *.preset",
344+
[&]( std::string const & file ) {
345+
advanced_mode_settings_file_names.insert( path + file );
346+
},
347+
false ); // recursive
348+
}
349+
catch( const std::exception & e )
350+
{
351+
LOG_WARNING( "Exception caught trying to detect presets: " << e.what() );
352+
}
342353
}
343-
catch( const std::exception & e )
354+
else
344355
{
345-
LOG_WARNING( "Exception caught trying to detect presets: " << e.what() );
356+
LOG_INFO( "Presets folder not found under " << path << ", skipping detection");
346357
}
347358
}
348359

common/embedded-filter-model.cpp

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,150 @@ namespace rs2
5454

5555
id_and_model.second.draw_option( update_read_only_options, is_streaming, error_message, *viewer.not_model );
5656
}
57+
58+
// Composite options have no generic per-field editing UI (see embedded-filter-model.h) -
59+
// RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL gets a hardcoded editor below; everything else
60+
// just shows read-only metadata (description, byte size).
61+
for( auto id : _composite_option_ids )
62+
{
63+
try
64+
{
65+
if( id == RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL )
66+
{
67+
// No always-visible description line here - draw_minz_control_editor shows
68+
// it as a tooltip on hovering the framed control instead, to save vertical
69+
// space in this narrow side panel.
70+
draw_minz_control_editor( error_message );
71+
continue;
72+
}
73+
74+
// TextWrapped, not TextDisabled - this side panel is narrow enough that a
75+
// one-line description reliably clips instead of just looking dim.
76+
ImGui::PushStyleColor( ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled] );
77+
ImGui::TextWrapped( "%s", _embedded_filter->get_composite_option_description( id ) );
78+
ImGui::PopStyleColor();
79+
80+
auto bytes = _embedded_filter->get_composite_option( id );
81+
ImGui::TextDisabled( " (%zu bytes, composite option - no generic editor yet)", bytes.size() );
82+
}
83+
catch( const std::exception& e )
84+
{
85+
error_message = e.what();
86+
}
87+
}
88+
}
89+
90+
void embedded_filter_model::draw_minz_control_editor( std::string & error_message )
91+
{
92+
const auto id = RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL;
93+
94+
if( ! _minz_editor_initialized )
95+
{
96+
try
97+
{
98+
_minz_editor_value = _embedded_filter->get_composite_option_as< rs2_minz_control >( id );
99+
_minz_editor_initialized = true;
100+
}
101+
catch( const std::exception & e )
102+
{
103+
error_message = e.what();
104+
return;
105+
}
106+
}
107+
108+
// Minimal indent - just enough padding that widget text doesn't sit flush on the frame
109+
// border - rather than the tree's full default indent, so the group sits as far left as
110+
// the panel allows.
111+
ImGui::Indent( 4.0f );
112+
ImGui::Dummy( ImVec2( 0, 2 ) );
113+
114+
float frame_left = ImGui::GetCursorScreenPos().x - 4.0f;
115+
float frame_top = ImGui::GetCursorScreenPos().y - 4.0f;
116+
float frame_width = ImGui::GetContentRegionAvail().x + 4.0f;
117+
118+
bool enable = _minz_editor_value.enable != 0;
119+
if( ImGui::Checkbox( "Enable##minz", &enable ) )
120+
_minz_editor_value.enable = enable ? 1 : 0;
121+
122+
// Every label below sits on its own line, with the interactive widget using a "##"-only
123+
// (invisible) label - this side panel is too narrow for "label: [====slider====] value"
124+
// all on one line; ImGui doesn't wrap inline widget labels, it just clips them.
125+
// Downscale ratio only ever takes {1,2,4} - radio buttons, not a slider, so the user
126+
// can't land on an invalid value like 3 (mirrors the field validation tool's own UI).
127+
ImGui::Text( "Downscale Ratio:" );
128+
for( int v : { 1, 2, 4 } )
129+
{
130+
ImGui::SameLine();
131+
if( ImGui::RadioButton( ( std::to_string( v ) + "##minz_ratio" ).c_str(), _minz_editor_value.downscale_ratio == v ) )
132+
_minz_editor_value.downscale_ratio = v;
133+
}
134+
135+
ImGui::Text( "Disparity Shift:" );
136+
int shift = _minz_editor_value.disparity_shift;
137+
if( ImGui::SliderInt( "##minz_shift", &shift, 0, 512 ) )
138+
_minz_editor_value.disparity_shift = shift;
139+
140+
ImGui::Text( "Threshold (mm):" );
141+
int threshold = _minz_editor_value.threshold;
142+
if( ImGui::SliderInt( "##minz_threshold", &threshold, 0, 65535 ) )
143+
_minz_editor_value.threshold = threshold;
144+
145+
bool manual = _minz_editor_value.threshold_mode != 0;
146+
if( ImGui::Checkbox( "Manual Threshold##minz", &manual ) )
147+
_minz_editor_value.threshold_mode = manual ? 1 : 0;
148+
if( ImGui::IsItemHovered() )
149+
ImGui::SetTooltip( "Unchecked = Auto (firmware-computed threshold)" );
150+
151+
// Send-only: writes the edited struct atomically and leaves the displayed fields as the
152+
// user set them. Does not read back - if the device clamps or overrides a value (e.g.
153+
// Auto-mode threshold), the fields won't reflect that until the panel is next reopened
154+
// or re-initialized, which is an accepted tradeoff for a single-purpose button.
155+
if( ImGui::Button( "Send##minz" ) )
156+
{
157+
try
158+
{
159+
_embedded_filter->set_composite_option_from( id, _minz_editor_value );
160+
}
161+
catch( const std::exception & e )
162+
{
163+
error_message = e.what();
164+
}
165+
}
166+
167+
ImGui::Dummy( ImVec2( 0, 2 ) );
168+
float frame_bottom = ImGui::GetCursorScreenPos().y;
169+
ImVec2 frame_min( frame_left, frame_top );
170+
ImVec2 frame_max( frame_left + frame_width, frame_bottom );
171+
ImGui::GetWindowDrawList()->AddRect(
172+
frame_min, frame_max,
173+
ImGui::GetColorU32( ImGuiCol_Border ),
174+
3.0f, // rounding
175+
0, // flags
176+
2.0f ); // thickness - visually ties every MinZ widget to one control
177+
178+
// The description (formerly an always-visible wrapped line above the frame) is now a
179+
// hover tooltip on the frame itself, to save vertical space in this narrow side panel.
180+
if( ImGui::IsMouseHoveringRect( frame_min, frame_max ) )
181+
{
182+
try
183+
{
184+
ImGui::SetTooltip( "%s", _embedded_filter->get_composite_option_description( id ) );
185+
}
186+
catch( const std::exception & )
187+
{
188+
// Best-effort tooltip only - a failure here shouldn't disrupt the editor itself.
189+
}
190+
}
191+
192+
ImGui::Unindent( 4.0f );
57193
}
58194

59195
void embedded_filter_model::embedded_filter_enable_disable(bool actual)
60196
{
197+
// Composite-only embedded filters (e.g. HKR MinZ Control) register no scalar options at
198+
// all, including this one - nothing to toggle here for them.
199+
if( ! _embedded_filter->supports( RS2_OPTION_EMBEDDED_FILTER_ENABLED ) )
200+
return;
61201
_embedded_filter->set_option(RS2_OPTION_EMBEDDED_FILTER_ENABLED, actual ? 1.0f : 0.0f);
62202
_enabled = _embedded_filter->get_option(RS2_OPTION_EMBEDDED_FILTER_ENABLED);
63203
}
@@ -76,7 +216,17 @@ namespace rs2
76216
model ? &model->_options_invalidated : nullptr,
77217
error_message );
78218
}
79-
_enabled = _embedded_filter->get_option(RS2_OPTION_EMBEDDED_FILTER_ENABLED);
219+
220+
// Composite options are enumerated separately - completely different registry from
221+
// scalar rs2_option (see rs_composite_option.h) - so this is its own loop, not folded
222+
// into the one above.
223+
_composite_option_ids = _embedded_filter->get_supported_composite_options();
224+
225+
// Composite-only embedded filters (e.g. HKR MinZ Control) don't register this scalar
226+
// option at all - _enabled keeps its default (true, see embedded-filter-model.h) instead
227+
// of throwing here.
228+
if( _embedded_filter->supports( RS2_OPTION_EMBEDDED_FILTER_ENABLED ) )
229+
_enabled = _embedded_filter->get_option(RS2_OPTION_EMBEDDED_FILTER_ENABLED);
80230

81231
try
82232
{

common/embedded-filter-model.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#pragma once
55

66
#include <librealsense2/rs.hpp>
7+
#include <librealsense2/h/rs_hkr_minz_control.h>
78
#include <functional>
89
#include <string>
10+
#include <vector>
911

1012

1113
namespace rs2
@@ -37,6 +39,13 @@ namespace rs2
3739
bool is_streaming,
3840
std::string & error_message );
3941

42+
// PROTOTYPE / DEMO: hardcoded editor for RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL
43+
// specifically, called from draw_options() when that id is among _composite_option_ids.
44+
// There is no generic per-field composite-option editor (would need per-struct schema
45+
// knowledge generic view code doesn't have) - this special-cases the one struct by id,
46+
// same way application code is expected to (see rs_hkr_minz_control.h).
47+
void draw_minz_control_editor( std::string & error_message );
48+
4049
std::shared_ptr<rs2::embedded_filter> get_filter() { return _embedded_filter; }
4150

4251
void enable( bool e = true )
@@ -65,6 +74,19 @@ namespace rs2
6574
bool _enabled = true;
6675
std::shared_ptr<rs2::embedded_filter> _embedded_filter;
6776
std::map< rs2_option, option_model > _options_id_to_model;
77+
// Composite options are a completely separate identity/registry space from scalar
78+
// rs2_option (see rs_composite_option.h) - enumerated and drawn through their own loop
79+
// in populate_options()/draw_options() rather than folded into _options_id_to_model.
80+
// There is no generic per-field editing UI for these yet (that would need per-struct
81+
// schema knowledge no generic view code has) - draw_options() shows read-only metadata
82+
// only (description, byte size).
83+
std::vector< rs2_composite_option_id > _composite_option_ids;
6884
std::string _name;
85+
86+
// PROTOTYPE / DEMO: local editing buffer for draw_minz_control_editor(). Seeded from a
87+
// GET on first draw, then only mutated by user edits until "Apply" sends it and refreshes
88+
// from a readback GET - never sent on every frame.
89+
bool _minz_editor_initialized = false;
90+
rs2_minz_control _minz_editor_value{};
6991
};
7092
}

0 commit comments

Comments
 (0)