Skip to content

Commit 960ee9e

Browse files
author
Haley Clark
committed
SDL_Viewer: add rudimentary RTPLAN viewer.
This commit adds support for viewing the raw data and metadata associated with RTPLANS, dynamic states (beams), and static states (control points).
1 parent e56acb0 commit 960ee9e

File tree

1 file changed

+149
-5
lines changed

1 file changed

+149
-5
lines changed

src/Operations/SDL_Viewer.cc

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,8 @@ bool SDL_Viewer(Drover &DICOM_data,
832832
bool view_parameter_table = false;
833833
bool view_tables_enabled = true;
834834
bool view_table_metadata_enabled = false;
835+
bool view_tplans_enabled = true;
836+
bool view_tplan_metadata_enabled = false;
835837
bool save_time_profiles = false;
836838
bool view_script_editor_enabled = false;
837839
bool view_script_feedback = true;
@@ -982,6 +984,13 @@ bool SDL_Viewer(Drover &DICOM_data,
982984
//ImVec4 fail_colour = ImVec4(0.600f, 0.100f, 0.000f, 1.00f);
983985
} table_display;
984986

987+
// RT Plans.
988+
using disp_tplan_it_t = decltype(DICOM_data.tplan_data.begin());
989+
long int tplan_num = -1;
990+
long int tplan_dynstate_num = -1;
991+
long int tplan_statstate_num = -1;
992+
993+
985994
// ------------------------------------------ Viewer State --------------------------------------------
986995
auto background_colour = ImVec4(0.025f, 0.087f, 0.118f, 1.00f);
987996

@@ -1732,6 +1741,25 @@ bool SDL_Viewer(Drover &DICOM_data,
17321741
return out;
17331742
};
17341743

1744+
// Recompute tplan iterators for the current tplan
1745+
const auto recompute_tplan_iters = [ &DICOM_data,
1746+
&tplan_num ](){
1747+
std::tuple<bool, disp_tplan_it_t > out;
1748+
std::get<bool>( out ) = false;
1749+
1750+
do{
1751+
const auto has_tplans = DICOM_data.Has_TPlan_Data();
1752+
if( !has_tplans ) break;
1753+
if( !isininc(1, tplan_num+1, DICOM_data.tplan_data.size()) ) break;
1754+
auto tplan_ptr_it = std::next(DICOM_data.tplan_data.begin(), tplan_num);
1755+
if( tplan_ptr_it == std::end(DICOM_data.tplan_data) ) break;
1756+
1757+
std::get<bool>( out ) = true;
1758+
std::get<disp_tplan_it_t>( out ) = tplan_ptr_it;
1759+
}while(false);
1760+
return out;
1761+
};
1762+
17351763

17361764
const auto recompute_scale_bar_image_state = [ &scale_bar_img,
17371765
&scale_bar_texture,
@@ -2545,29 +2573,34 @@ bool SDL_Viewer(Drover &DICOM_data,
25452573
view_toggles.view_contouring_enabled = false;
25462574
tagged_pos = {};
25472575
}
2548-
ImGui::MenuItem("Image Metadata", nullptr, &view_toggles.view_image_metadata_enabled);
25492576
ImGui::MenuItem("Image Hover Tooltips", nullptr, &view_toggles.show_image_hover_tooltips);
25502577
ImGui::MenuItem("Meshes", nullptr, &view_toggles.view_meshes_enabled);
25512578
if(ImGui::MenuItem("Plots", nullptr, &view_toggles.view_plots_enabled)){
25522579
lsamps_visible.clear();
25532580
}
2554-
ImGui::MenuItem("Mesh Metadata", nullptr, &view_toggles.view_mesh_metadata_enabled);
2555-
ImGui::MenuItem("Plot Hover Metadata", nullptr, &view_toggles.view_plots_metadata);
25562581
if(ImGui::MenuItem("Row and Column Profiles", nullptr, &view_toggles.view_row_column_profiles)){
25572582
row_profile.samples.clear();
25582583
col_profile.samples.clear();
25592584
};
25602585
if(ImGui::MenuItem("Time Profiles", nullptr, &view_toggles.view_time_profiles)){
25612586
time_profile.samples.clear();
25622587
};
2563-
ImGui::MenuItem("Parameter Table", nullptr, &view_toggles.view_parameter_table);
2588+
ImGui::MenuItem("RT Plans", nullptr, &view_toggles.view_tplans_enabled);
25642589
ImGui::MenuItem("Tables", nullptr, &view_toggles.view_tables_enabled);
2565-
ImGui::MenuItem("Table Metadata", nullptr, &view_toggles.view_table_metadata_enabled);
25662590
ImGui::MenuItem("Script Editor", nullptr, &view_toggles.view_script_editor_enabled);
25672591
ImGui::MenuItem("Script Feedback", nullptr, &view_toggles.view_script_feedback);
2592+
ImGui::MenuItem("Parameter Table", nullptr, &view_toggles.view_parameter_table);
25682593
ImGui::MenuItem("Shader Editor", nullptr, &view_toggles.view_shader_editor_enabled);
25692594
ImGui::EndMenu();
25702595
}
2596+
if(ImGui::BeginMenu("Metadata")){
2597+
ImGui::MenuItem("Image Metadata", nullptr, &view_toggles.view_image_metadata_enabled);
2598+
ImGui::MenuItem("Mesh Metadata", nullptr, &view_toggles.view_mesh_metadata_enabled);
2599+
ImGui::MenuItem("Plot Hover Metadata", nullptr, &view_toggles.view_plots_metadata);
2600+
ImGui::MenuItem("RT Plan Metadata", nullptr, &view_toggles.view_tplan_metadata_enabled);
2601+
ImGui::MenuItem("Table Metadata", nullptr, &view_toggles.view_table_metadata_enabled);
2602+
ImGui::EndMenu();
2603+
}
25712604
if(ImGui::BeginMenu("Adjust")){
25722605
if(ImGui::BeginMenu("Toggle Style")){
25732606
if(ImGui::MenuItem("Dark Mode", nullptr, nullptr)){
@@ -4808,6 +4841,117 @@ bool SDL_Viewer(Drover &DICOM_data,
48084841
throw;
48094842
}
48104843

4844+
// Display RT plans.
4845+
const auto display_tplans = [&view_toggles,
4846+
&drover_mutex,
4847+
&mutex_dt,
4848+
&tplan_num,
4849+
&tplan_dynstate_num,
4850+
&tplan_statstate_num,
4851+
&recompute_tplan_iters,
4852+
&display_metadata_table,
4853+
&DICOM_data ]() -> void {
4854+
4855+
std::unique_lock<std::shared_timed_mutex> drover_lock(drover_mutex, mutex_dt);
4856+
if(!drover_lock) return;
4857+
4858+
if( !view_toggles.view_tplans_enabled
4859+
|| !DICOM_data.Has_TPlan_Data() ) return;
4860+
4861+
// Display a selection and navigation window.
4862+
ImGui::SetNextWindowSize(ImVec2(450, 400), ImGuiCond_FirstUseEver);
4863+
ImGui::SetNextWindowPos(ImVec2(680, 40), ImGuiCond_FirstUseEver);
4864+
ImGui::Begin("RT Plans", &view_toggles.view_tplans_enabled);
4865+
4866+
// Scroll through RT plans.
4867+
if(DICOM_data.Has_TPlan_Data()){
4868+
int scroll_tplans = tplan_num;
4869+
const int N_tplans = DICOM_data.tplan_data.size();
4870+
ImGui::SliderInt("Plan", &scroll_tplans, 0, N_tplans - 1);
4871+
const long int new_tplan_num = std::clamp(scroll_tplans, 0, N_tplans - 1);
4872+
if(new_tplan_num != tplan_num){
4873+
tplan_num = new_tplan_num;
4874+
}
4875+
}
4876+
4877+
ImGui::Checkbox("View RT plan metadata", &view_toggles.view_tplan_metadata_enabled);
4878+
4879+
if(auto [tplan_is_valid, tplan_ptr_it] = recompute_tplan_iters(); tplan_is_valid){
4880+
4881+
// Display the RT plan.
4882+
//
4883+
// Note: we currently only display the top-level metadata without any visual display.
4884+
ImGui::Begin("RT Plan", &view_toggles.view_tplans_enabled);
4885+
display_metadata_table( (*tplan_ptr_it)->metadata );
4886+
ImGui::End();
4887+
4888+
if( view_toggles.view_tplan_metadata_enabled
4889+
&& !(*tplan_ptr_it)->dynamic_states.empty() ){
4890+
// Scroll through dynamic states.
4891+
int scroll_dynstate = tplan_dynstate_num;
4892+
const int N_dynstates = (*tplan_ptr_it)->dynamic_states.size();
4893+
ImGui::SliderInt("Beam", &scroll_dynstate, 0, N_dynstates - 1);
4894+
const long int new_dynstate_num = std::clamp(scroll_dynstate, 0, N_dynstates - 1);
4895+
if(new_dynstate_num != tplan_dynstate_num){
4896+
tplan_dynstate_num = new_dynstate_num;
4897+
}
4898+
auto *dynstate_ptr = &( (*tplan_ptr_it)->dynamic_states.at(tplan_dynstate_num) );
4899+
4900+
// Display the selected dynamic state (i.e., the beam).
4901+
{
4902+
std::stringstream ss;
4903+
ss << "Beam number: " << std::to_string(dynstate_ptr->BeamNumber) << std::endl;
4904+
ss << "Cumulative meterset: " << std::to_string(dynstate_ptr->FinalCumulativeMetersetWeight) << std::endl;
4905+
ss << "Number of control points: " << std::to_string(dynstate_ptr->static_states.size()) << std::endl;
4906+
ImGui::Text("%s", ss.str().c_str());
4907+
}
4908+
if(view_toggles.view_tplan_metadata_enabled){
4909+
ImGui::SetNextWindowSize(ImVec2(650, 650), ImGuiCond_FirstUseEver);
4910+
ImGui::Begin("Beam view", &view_toggles.view_tplan_metadata_enabled);
4911+
display_metadata_table( dynstate_ptr->metadata );
4912+
ImGui::End();
4913+
}
4914+
4915+
if( !dynstate_ptr->static_states.empty() ){
4916+
// Scroll through static states.
4917+
int scroll_statstate = tplan_statstate_num;
4918+
const int N_statstates = dynstate_ptr->static_states.size();
4919+
ImGui::SliderInt("Control point", &scroll_statstate, 0, N_statstates - 1);
4920+
const long int new_statstate_num = std::clamp(scroll_statstate, 0, N_statstates - 1);
4921+
if(new_statstate_num != tplan_statstate_num){
4922+
tplan_statstate_num = new_statstate_num;
4923+
}
4924+
auto *statstate_ptr = &( dynstate_ptr->static_states.at(tplan_statstate_num) );
4925+
4926+
// Display the selected static state (i.e., the control point).
4927+
{
4928+
std::stringstream ss;
4929+
ss << "Control point index: " << std::to_string(statstate_ptr->ControlPointIndex) << std::endl
4930+
<< "Cumulative meterset: " << std::to_string(statstate_ptr->CumulativeMetersetWeight) << std::endl
4931+
<< "Gantry angle: " << std::to_string(statstate_ptr->GantryAngle) << std::endl;
4932+
ImGui::Text("%s", ss.str().c_str());
4933+
}
4934+
if(view_toggles.view_tplan_metadata_enabled){
4935+
ImGui::SetNextWindowSize(ImVec2(650, 650), ImGuiCond_FirstUseEver);
4936+
ImGui::Begin("Control point view", &view_toggles.view_tplan_metadata_enabled);
4937+
display_metadata_table( statstate_ptr->metadata );
4938+
ImGui::End();
4939+
}
4940+
4941+
}
4942+
}
4943+
}
4944+
4945+
ImGui::End();
4946+
return;
4947+
};
4948+
try{
4949+
display_tplans();
4950+
}catch(const std::exception &e){
4951+
FUNCWARN("Exception in display_tplans(): '" << e.what() << "'");
4952+
throw;
4953+
}
4954+
48114955
// Display the image navigation dialog.
48124956
const auto display_image_navigation = [&view_toggles,
48134957
&drover_mutex,

0 commit comments

Comments
 (0)