classdef XTPS < handle
%XTPS Main GUI for RDXTPS front end.
%
% Default to contain three SliceViewerPanels for viewing.
%
% TODO:
% Move Geometry and optResults into SliceViewerPanelSource completely so that
% only one copy exist.
% Move color stuff into Geometry so that can be saved.
%
% See also SliceViewerPanel
%
% Author Xiaohu Mo
properties
handles
patient_dir
dosecalcSetup
end
methods
%-------------------------------------------------------------------------------
function obj = XTPS() % XTPS constructor
hMainFigure = figure('MenuBar', 'none', 'Name', 'RDXTPS: No Patient', 'Position', [100 100 800 600], 'Renderer', 'painters');
obj.handles = guihandles(hMainFigure);
obj.handles.hMainFigure = hMainFigure;
% Adding widgets
obj.handles.hROIPanel = uipanel('Parent', obj.handles.hMainFigure, 'Title', 'ROI display');
obj.handles.hROITable = uitable('Parent', obj.handles.hROIPanel);
obj.handles.hDosePanel = uipanel('Parent', obj.handles.hMainFigure, 'Title', 'Dose display');
obj.handles.hDoseTable = uitable('Parent', obj.handles.hDosePanel);
obj.handles.hDoseModeDropdown = uicontrol('Parent', obj.handles.hDosePanel, 'Style', 'popupmenu');
obj.handles.hDoseModeLabel = uicontrol('Parent', obj.handles.hDosePanel, 'Style', 'text');
obj.handles.hMiscPanel = uipanel('Parent', obj.handles.hMainFigure, 'Title', 'Misc settings');
obj.handles.hTCSShow = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'checkbox');
obj.handles.hWWLabel = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'edit');
obj.handles.hWWSlider = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'slider');
obj.handles.hWWEdit = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'edit');
obj.handles.hWLLabel = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'edit');
obj.handles.hWLSlider = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'slider');
obj.handles.hWLEdit = uicontrol('Parent', obj.handles.hMiscPanel, 'Style', 'edit');
obj.handles.hSVPS = SliceViewerPanelSource();
obj.handles.hSliceViewerPanel(1) = SliceViewerPanel(obj.handles.hSVPS, 3, obj.handles.hMainFigure);
obj.handles.hSliceViewerPanel(2) = SliceViewerPanel(obj.handles.hSVPS, 2, obj.handles.hMainFigure);
obj.handles.hSliceViewerPanel(3) = SliceViewerPanel(obj.handles.hSVPS, 1, obj.handles.hMainFigure);
% File menu
obj.handles.hFileMenu = uimenu(obj.handles.hMainFigure, 'Label','File');
obj.handles.hImportDicomRTMenu = uimenu(obj.handles.hFileMenu, 'Label', '1. Import DicomRT Geometry');
obj.handles.hDosecalcMenu = uimenu(obj.handles.hFileMenu, 'Label', '2. Beamlet Dose Calculation');
obj.handles.hOptimMenu = uimenu(obj.handles.hFileMenu, 'Label', '3. Plan Optimization');
obj.handles.hSaveOptimResultsMenu = uimenu(obj.handles.hFileMenu, 'Label', '4. Save Optimization Results');
obj.handles.hLoadGeometryMenu = uimenu(obj.handles.hFileMenu, 'Label', 'Load Geometry', 'Separator','on');
obj.handles.hLoadOptresultsMenu = uimenu(obj.handles.hFileMenu, 'Label', 'Load OptResults');
obj.handles.hSaveAsGeometryMenu = uimenu(obj.handles.hFileMenu, 'Label', 'Save Geometry As...', 'Separator','on');
obj.handles.hSaveAsOptResultsMenu = uimenu(obj.handles.hFileMenu, 'Label', 'Save OptResults As...');
obj.handles.hExportPinnacleMenu = uimenu(obj.handles.hFileMenu, 'Label', 'Export Pinnacle Files', 'Separator','on');
% Tools menu
obj.handles.hToolsMenu = uimenu(obj.handles.hMainFigure, 'Label', 'Tools');
obj.handles.hPlotDVHMenu = uimenu(obj.handles.hToolsMenu, 'Label', 'Plot DVH');
% Associate callbacks
set(obj.handles.hMainFigure, 'ResizeFcn', @obj.set_layout);
set(obj.handles.hROITable, 'CellSelectionCallback', @obj.ROITable_CellSelectionCallback);
set(obj.handles.hDoseTable, 'CellSelectionCallback', @obj.DoseTable_CellSelectionCallback);
set(obj.handles.hDoseTable, 'CellEditCallback', @obj.DoseTable_CellEditCallback);
set(obj.handles.hImportDicomRTMenu, 'Callback', @obj.ImportDicomRTMenu_Callback);
set(obj.handles.hDosecalcMenu, 'Callback', @obj.DosecalcMenu_Callback);
set(obj.handles.hSaveOptimResultsMenu, 'Callback', @obj.SaveOptimResultsMenu_Callback);
set(obj.handles.hOptimMenu, 'Callback', @obj.OptimMenu_Callback);
set(obj.handles.hLoadGeometryMenu, 'Callback', @obj.LoadGeometryMenu_Callback);
set(obj.handles.hLoadOptresultsMenu, 'Callback', @obj.LoadOptresultsMenu_Callback);
set(obj.handles.hSaveAsGeometryMenu, 'Callback', @obj.SaveAsGeometryMenu_Callback);
set(obj.handles.hSaveAsOptResultsMenu, 'Callback', @obj.SaveAsOptResultsMenu_Callback);
set(obj.handles.hExportPinnacleMenu, 'Callback', @obj.ExportPinnacleMenu_Callback);
set(obj.handles.hPlotDVHMenu, 'Callback', @obj.PlotDVHMenu_Callback);
set(obj.handles.hDoseModeDropdown, 'Callback', @obj.DoseModeDropdown_Callback);
set(obj.handles.hWWSlider, 'Callback', @obj.WWSlider_Callback);
set(obj.handles.hWWEdit, 'Callback', @obj.WWEdit_Callback);
set(obj.handles.hWLSlider, 'Callback', @obj.WLSlider_Callback);
set(obj.handles.hWLEdit, 'Callback', @obj.WLEdit_Callback);
set(obj.handles.hTCSShow, 'Callback', @obj.TCSShow_Callback);
obj.set_widget_properties();
obj.set_layout();
obj.updateROITable();
obj.updateDoseTable();
obj.updateCTWindowControls();
end % XTPS constructor
%-------------------------------------------------------------------------------
function set_widget_properties(obj)
%% Set widget default properties, excluding those related to data
h = obj.handles; % shortcut
set(h.hROITable, 'ColumnEditable', logical([1 0 0]));
set(h.hROITable, 'ColumnFormat', {'logical', 'char', 'char'});
set(h.hROITable, 'ColumnName', {'V' 'C' 'ROI'});
set(h.hROITable, 'ColumnWidth',{20 15 'auto'});
set(h.hROITable, 'RowName', {'numbered'});
set(h.hROITable, 'RowStriping', 'off');
set(h.hROITable, 'ToolTipString', 'click to set color');
set(h.hDoseTable, 'ColumnEditable', logical([0 1 1]));
set(h.hDoseTable, 'ColumnFormat', {'char' 'numeric' 'numeric'});
set(h.hDoseTable, 'ColumnName', {'C' 'Dose' 'Alpha'});
set(h.hDoseTable, 'ColumnWidth',{15 'auto' 'auto'});
set(h.hDoseTable, 'RowName', []);
set(h.hDoseTable, 'RowStriping', 'off');
set(h.hDoseTable, 'ToolTipString', 'click to set color');
set(h.hDoseModeLabel, 'String', 'display mode');
set(h.hDoseModeDropdown, 'String', {'None' 'Isodose' 'IsodoseLine' 'Colorwash'});
set(h.hWWLabel, 'String', 'Win. Width');
set(h.hWWSlider, 'Min', 0, 'Max', 2048, 'SliderStep', [10/2048 100/2048]);
set(h.hWLLabel, 'String', 'Win. Level');
set(h.hWLSlider, 'Min', 0, 'Max', 2048, 'SliderStep', [10/2048 100/2048]);
set(h.hTCSShow, 'String', 'Show TCS planes', 'Value', get(h.hTCSShow, 'Max'));
% BackgroundColor (get all ui object handles except uitable)
set(findobj(h.hMainFigure, '-property', 'BackgroundColor', '-not', 'Type', 'uitable'), ...
'BackgroundColor', get(h.hMainFigure, 'Color'));
end
%-------------------------------------------------------------------------------
function set_layout(obj, src, evt)
% Set widget positions and size. Also as ResizeFcn.
h = obj.handles; % shortcut
set(findobj(h.hMainFigure, '-property', 'Units'), 'Units', 'Normalized');
padding = [0.005 0.01];
setPositionXY(h.hDosePanel, [0 0.2], [0.2 0.5], padding);
setPositionXY(h.hROIPanel, [0 0.2], [0.5 1], padding);
setPositionXY(h.hMiscPanel, [0 0.2], [0 0.2], padding);
setPositionXY(h.hSliceViewerPanel(1).hPanel, [0.2 0.7], [0 1], padding);
setPositionXY(h.hSliceViewerPanel(2).hPanel, [0.7 1], [0.5 1], padding);
setPositionXY(h.hSliceViewerPanel(3).hPanel, [0.7 1], [0 0.5], padding);
setPositionXY(h.hROITable, [0 1], [0 1], padding);
setPositionXY(h.hDoseModeDropdown, [0.5 1], [0.9 1], padding);
setPositionXY(h.hDoseModeLabel, [0 0.5], [0.9 1], padding);
setPositionXY(h.hDoseTable, [0 1], [0 0.9], padding);
setPositionXY(h.hWWLabel, [0 0.3], [0.67 1], padding, 1.5, 'characters');
setPositionXY(h.hWWSlider, [0.3 0.85], [0.67 1], padding, 1.5, 'characters');
setPositionXY(h.hWWEdit, [0.85 1], [0.67 1], padding, 1.5, 'characters');
setPositionXY(h.hWLLabel, [0 0.3], [0.33 0.67], padding, 1.5, 'characters');
setPositionXY(h.hWLSlider, [0.3 0.85], [0.33 0.67], padding, 1.5, 'characters');
setPositionXY(h.hWLEdit, [0.85 1], [0.33 0.67], padding, 1.5, 'characters');
setPositionXY(h.hTCSShow, [0 1], [0.0 0.33], padding, 1.5, 'characters');
table_width = getWidthPixel(obj.handles.hROITable);
set(obj.handles.hROITable, 'ColumnWidth', {20 15 table_width-20-15-35});
table_width = getWidthPixel(obj.handles.hDoseTable);
set(obj.handles.hDoseTable, 'ColumnWidth', {15 (table_width-15)/2-2 (table_width-15)/2-2});
% Panel ResizeFcn not executed after init. Has to manually call.
for i = 1:numel(h.hSliceViewerPanel)
h.hSliceViewerPanel(i).set_layout();
end
end
%-------------------------------------------------------------------------------
function load_geometry(obj, geometry_filename)
%% load geometry
if nargin == 2 % load the geometry file if supplied
load(geometry_filename);
elseif nargin == 1 % Geometry should be loaded already
Geometry = obj.handles.hSVPS.Geometry;
if isempty(obj.handles.hSVPS.Geometry)
% TODO appropriate handling
return;
end
end
% add x,y,z axis for plotting purposes
Geometry.x = Geometry.start(1) + Geometry.voxel_size(1) * (0:size(Geometry.rhomw, 1)-1);
Geometry.y = Geometry.start(2) + Geometry.voxel_size(2) * (0:size(Geometry.rhomw, 2)-1);
Geometry.z = Geometry.start(3) + Geometry.voxel_size(3) * (0:size(Geometry.rhomw, 3)-1);
for i = 1:numel(Geometry.ROIS)
% TODO unify curves as N x 3 cell array so we don't need Xcurves & Ycurves,
% but that way will break back-compatibility.
if isfield(Geometry.ROIS{i}, 'Xcurves')
Xcurves_coordinate = cellfun(@(c)c(1,1), Geometry.ROIS{i}.Xcurves);
Geometry.ROIS{i}.Xcurves_slices = ...
round((Xcurves_coordinate - Geometry.start(1)) / Geometry.voxel_size(1)) + 1;
else
% Old version doesn't have XYcurves. Create an empty index to skip plotting.
Geometry.ROIS{i}.Xcurves_slices = [];
end
if isfield(Geometry.ROIS{i}, 'Ycurves')
Ycurves_coordinate = cellfun(@(c)c(1,2), Geometry.ROIS{i}.Ycurves);
Geometry.ROIS{i}.Ycurves_slices = ...
round((Ycurves_coordinate - Geometry.start(2)) / Geometry.voxel_size(2)) + 1;
else
% Old version doesn't have XYcurves. Create an empty index to skip plotting.
Geometry.ROIS{i}.Ycurves_slices = [];
end
Zcurves_coordinate = cellfun(@(c)c(1,3), Geometry.ROIS{i}.curves);
Geometry.ROIS{i}.Zcurves_slices = ...
round((Zcurves_coordinate - Geometry.start(3)) / Geometry.voxel_size(3)) + 1;
end
% Convert data into float
if isinteger(Geometry.data)
Geometry.data = single(Geometry.data);
end
% Init/set display settings if absent
num_ROI = numel(Geometry.ROIS);
for i = 1:num_ROI
if ~isfield(Geometry.ROIS{i}, 'visible')
Geometry.ROIS{i}.visible = false;
end
if ~isfield(Geometry.ROIS{i}, 'color')
Geometry.ROIS{i}.color = rand(1,3);
end
end
% Set/save to SliceViewerPanelSource
obj.handles.hSVPS.Geometry = Geometry;
obj.handles.hSVPS.TCSVisible = ...
(get(obj.handles.hTCSShow, 'Value') == get(obj.handles.hTCSShow, 'Max'));
% updates
obj.handles.hSliceViewerPanel(1).load_data();
obj.handles.hSliceViewerPanel(2).load_data();
obj.handles.hSliceViewerPanel(3).load_data();
obj.updateROITable();
end
%-------------------------------------------------------------------------------
function load_optResults(obj, optresults_filename)
if nargin == 2 % load the optResults file if supplied
load(optresults_filename, 'optResults');
obj.handles.hSVPS.optResults = optResults;
elseif nargin == 1 % optResults should exist already
% Do nothing for now
end
obj.updateDoseTable();
end
%-------------------------------------------------------------------------------
function updateROITable(obj)
% Update ROITable using values in Geometry.ROIS
if isempty(obj.handles.hSVPS.Geometry); return; end
tdata = get(obj.handles.hROITable, 'Data');
% Update table data according to display settings
for i = 1:numel(obj.handles.hSVPS.Geometry.ROIS)
tdata{i, 1} = obj.handles.hSVPS.Geometry.ROIS{i}.visible;
c = obj.handles.hSVPS.Geometry.ROIS{i}.color;
tdata{i, 2} = sprintf('■', rgb2str(c));
tdata{i, 3} = obj.handles.hSVPS.Geometry.ROIS{i}.name;
end
set(obj.handles.hROITable, 'Data', tdata);
drawnow;
obj.set_layout();
end
%-------------------------------------------------------------------------------
function updateDoseTable(obj)
% Update DoseTable using data in SliceViewerPanelSource.dosedisp
% if isempty(obj.handles.hSVPS.Geometry) && isempty(obj.handles.hSVPS.optResults); return; end
tdata = cell(0);
% Update table data according to display settings
for i = 1:numel(obj.handles.hSVPS.dosedisp.level)
tdata{i, 1} = sprintf('■', rgb2str(obj.handles.hSVPS.dosedisp.color(i,:)));
tdata{i, 2} = obj.handles.hSVPS.dosedisp.level(i);
tdata{i, 3} = obj.handles.hSVPS.dosedisp.alpha(i);
end
% disable the callback to prevent recursion
set(obj.handles.hDoseTable, 'CellSelectionCallback', []);
set(obj.handles.hDoseTable, 'Data', tdata);
drawnow; % Flush //IMPORTANT
set(obj.handles.hDoseTable, 'CellSelectionCallback', @obj.DoseTable_CellSelectionCallback);
end
%-------------------------------------------------------------------------------
function updateCTWindowControls(obj)
set(obj.handles.hWWSlider, 'Value', obj.handles.hSVPS.ctWindow.ww);
set(obj.handles.hWWEdit, 'String', num2str(obj.handles.hSVPS.ctWindow.ww));
set(obj.handles.hWLSlider, 'Value', obj.handles.hSVPS.ctWindow.wl);
set(obj.handles.hWLEdit, 'String', num2str(obj.handles.hSVPS.ctWindow.wl));
end
%-------------------------------------------------------------------------------
function ROITable_CellSelectionCallback(obj, hObject, eventdata)
% eventdata structure with the following fields (see UITABLE)
% Indices: row and column indices of the cell(s) currently selecteds
% Avoid "fake" event during initialization
if ~isempty(eventdata.Indices)
roi_idx = eventdata.Indices(1);
if eventdata.Indices(2) == 1
% toggle ROI display in display settings
tdata = get(hObject, 'Data');
obj.handles.hSVPS.Geometry.ROIS{roi_idx}.visible = ~tdata{roi_idx, 1};
else % eventdata.Indices(2) ~= 1
% pick and save color
c = uisetcolor;
if ~(isscalar(c) && c == 0)
obj.handles.hSVPS.Geometry.ROIS{roi_idx}.color = c;
% also make it visible
obj.handles.hSVPS.Geometry.ROIS{roi_idx}.visible = true;
end
end
obj.updateROITable();
end
end
%-------------------------------------------------------------------------------
function DoseTable_CellSelectionCallback(obj, tilde, eventdata)
% eventdata structure with the following fields (see UITABLE)
% Indices: row and column indices of the cell(s) currently selecteds
% Avoid fake event during initialization
if ~isempty(eventdata.Indices)
if eventdata.Indices(2) == 1
% pick and save color
c = uisetcolor;
if ~(isscalar(c) && c == 0)
obj.handles.hSVPS.dosedisp.color(eventdata.Indices(1), :) = c;
obj.updateDoseTable();
end
end
end
end
%-------------------------------------------------------------------------------
function DoseTable_CellEditCallback(obj, tilde, eventdata)
% Update handles.hSVPS.dosedisp
if eventdata.Indices(2) == 2
obj.handles.hSVPS.dosedisp.level(eventdata.Indices(1)) = eventdata.NewData;
elseif eventdata.Indices(2) == 3
obj.handles.hSVPS.dosedisp.alpha(eventdata.Indices(1)) = eventdata.NewData;
end
obj.updateDoseTable();
end
%-------------------------------------------------------------------------------
function WWSlider_Callback(obj, src, evt)
% set window width property of SliceViewerPancelSource
value = round(get(src, 'Value'));
obj.handles.hSVPS.ctWindow.ww = value;
obj.updateCTWindowControls();
end
%-------------------------------------------------------------------------------
function WWEdit_Callback(obj, src, evt)
% set window width property of SliceViewerPancelSource
value = round(str2double(get(src, 'String')));
% clipping to valid value
value = max(value, get(obj.handles.hWWSlider, 'Min'));
value = min(value, get(obj.handles.hWWSlider, 'Max'));
obj.handles.hSVPS.ctWindow.ww = value;
obj.updateCTWindowControls();
end
%-------------------------------------------------------------------------------
function WLSlider_Callback(obj, src, evt)
% set window width property of SliceViewerPancelSource
value = round(get(src, 'Value'));
obj.handles.hSVPS.ctWindow.wl = value;
obj.updateCTWindowControls();
end
%-------------------------------------------------------------------------------
function WLEdit_Callback(obj, src, evt)
% set window width property of SliceViewerPancelSource
value = round(str2double(get(src, 'String')));
% clipping to valid value
value = max(value, get(obj.handles.hWLSlider, 'Min'));
value = min(value, get(obj.handles.hWLSlider, 'Max'));
obj.handles.hSVPS.ctWindow.wl = value;
obj.updateCTWindowControls();
end
%-------------------------------------------------------------------------------
function ImportDicomRTMenu_Callback(obj, src, evt)
[obj.handles.hSVPS.Geometry obj.patient_dir] = RDXTPS_geometry_setup_wizard_ASP();
obj.load_geometry();
end
%-------------------------------------------------------------------------------
function DosecalcMenu_Callback(obj, src, evt)
% TODO fix patient_dir system
% [obj.dosecalcSetup obj.patient_dir] = RDXTPS_dosecalcSetup(obj.dosecalcSetup, obj.patient_dir, obj.handles.hSVPS.Geometry);
[obj.dosecalcSetup obj.patient_dir] = RDXTPS_dosecalcSetup(obj.dosecalcSetup, [], obj.handles.hSVPS.Geometry);
% Temporary fix for Gustavo asking energy limit
answer = inputdlg('Machine energy limit: (MeV)', 'Energy limit', 1, {'250'});
maxMachineEnergyStr = answer{1};
% run beamlet dose calculation
cmdline = ['.' filesep 'RDXdosecalc.exe "' fullfile(obj.patient_dir, 'dosecalc_input.txt') '"' ' ' maxMachineEnergyStr];
if ispc
execline = ['cmd /c ' cmdline ' &'];
elseif isunix
% xterm -e "export LD_LIBRARY_PATH=. ; ./RDXdosecalc.exe '../patients/Knew/dosecalc_input.txt' ; read" &
% TODO fix single quote
cmdline = ['xterm -e "export LD_LIBRARY_PATH=. ; ./RDXdosecalc.exe ' fullfile(obj.patient_dir, 'dosecalc_input.txt') ' ; read" &'];
end
msgbox(sprintf('Start beamlet dose calculation.\nRun this if not started automatically:\n%s', cmdline));
system(execline);
end
%-------------------------------------------------------------------------------
function OptimMenu_Callback(obj, src, evt)
% TODO fix patient_dir system
% if isempty(obj.patient_dir)
obj.patient_dir = uifile('getdir', 'Select the patient directory');
% end
source_filename = fullfile(obj.patient_dir, 'batch_dose.bin');
target_filename = fullfile(obj.patient_dir, 'beamlet_batch_files', 'beamletbatch0.bin');
mkdir(fullfile(obj.patient_dir, 'beamlet_batch_files'));
try
if ispc % MATLAB movefile() super slow on windows
% TODO confirm overwrite
system(['move "' source_filename '" "' target_filename '"']);
else
movefile(source_filename, target_filename);
end
catch
msgbox('Error moving the beamlet file. Please do so manually.');
end
Nbeamlets = read_ryan_beamlets(target_filename, 'info');
RDXTPS_optimSetup(Nbeamlets, obj.patient_dir, obj.handles.hSVPS.Geometry);
% run optimizer
%---
cd('C:\010-work\003-localGit\WiscPlan\WiscPlanPhotonkV125\WiscPlanEXE')
cmdline = ['.' filesep 'RDXoptimizer.exe "' fullfile(obj.patient_dir, 'optInput.txt') '"'];
msgbox(sprintf('Start optimization\nRun this if not started automatically:\n%s', cmdline));
if ispc
execline = ['cmd /c ' cmdline ' &'];
elseif isunix
% TODO fix single quote
execline = [cmdline];
end
system(execline);
end
%-------------------------------------------------------------------------------
function SaveOptimResultsMenu_Callback(obj, src, evt)
% TODO fix patient_dir system
% if isempty(obj.patient_dir)
obj.patient_dir = uifile('getdir', 'Select the patient directory');
% end
[tilde, obj.handles.hSVPS.optResults] = loadOptResults(obj.patient_dir, 'optInput.txt');
obj.load_optResults();
end
%-------------------------------------------------------------------------------
function LoadGeometryMenu_Callback(obj, src, evt)
[filename pathname] = uifile('get', '*.mat', 'Choose Geometry file to load');
% do nothing if cancelled
if isscalar(filename) && filename == 0; return; end
obj.load_geometry(fullfile(pathname, filename));
end
%-------------------------------------------------------------------------------
function LoadOptresultsMenu_Callback(obj, src, evt)
[filename pathname] = uifile('get', '*.mat', 'Choose OptResults file to load');
% do nothing if cancelled
if isscalar(filename) && filename == 0; return; end
obj.load_optResults(fullfile(pathname, filename));
% % Extra dose scaling. Temporary code for comparison with Tomo plans
% answer = inputdlg({'Dose', '% Volume'}, 'Scale final dose');
% if ~isempty(answer)
% % choose target
% ROI_names = cellfun(@(c)c.name, obj.handles.hSVPS.Geometry.ROIS, 'UniformOutput', false);
% [target_idx] = listdlg('ListString', ROI_names, ...
% 'SelectionMode', 'single', 'Name', 'Target Selection', ...
% 'PromptString', 'Please select the target ROI.');
% opt_dose = prctile(obj.handles.hSVPS.optResults.dose{end}(obj.handles.hSVPS.Geometry.ROIS{target_idx}.ind), 100 - str2double(answer{2}));
% obj.handles.hSVPS.optResults.dose{end} = obj.handles.hSVPS.optResults.dose{end} * str2double(answer{1}) / opt_dose;
% end
% % END OF Extra dose scaling
msgbox('Load optResults successfully.')
end
%-------------------------------------------------------------------------------
function ExportPinnacleMenu_Callback(obj, src, evt)
if isempty(obj.handles.hSVPS.Geometry)
obj.LoadGeometryMenu_Callback([], [], obj);
end
if isempty(obj.handles.hSVPS.optResults)
obj.LoadOptresultsMenu_Callback([], [], obj);
end
RDXTPS_exportPinnGrid4Tomo(obj.handles.hSVPS.Geometry, obj.handles.hSVPS.optResults);
end
%-------------------------------------------------------------------------------
function PlotDVHMenu_Callback(obj, src, evt)
answer = inputdlg({'Enter figure number:', 'Enter line style'}, 'Choose figure');
if isempty(answer{1})
return;
else
fig_idx = str2double(answer{1});
end
if isempty(answer{2})
line_style = '-';
else
line_style = answer{2};
end
figure(fig_idx); hold on;
% temp solution for karthik dosimetry data
answer = inputdlg('number of fractions');
nfrac = str2double(answer{1});
for roi_idx = 1:numel(obj.handles.hSVPS.Geometry.ROIS)
if obj.handles.hSVPS.Geometry.ROIS{roi_idx}.visible == true % display == ON
fprintf('%s\n', obj.handles.hSVPS.Geometry.ROIS{roi_idx}.name);
% [dvh dosebins] = dvhist(obj.handles.hSVPS.optResults.dose{end}, obj.handles.hSVPS.Geometry.ROIS{roi_idx}.ind);
% temp solution for karthik dosimetry data
[dvh dosebins] = dvhist(obj.handles.hSVPS.optResults.dose{end}, ...
obj.handles.hSVPS.Geometry, ...
roi_idx, ...
nfrac);
plot(dosebins, dvh, ...
'Color', obj.handles.hSVPS.Geometry.ROIS{roi_idx}.color, ...
'LineStyle', line_style, ...
'DisplayName', obj.handles.hSVPS.Geometry.ROIS{roi_idx}.name);
end
end
set(gca, 'XMinorTick', 'on', 'YMinorTick', 'on');
grid(gca, 'on');
box(gca, 'on');
legend('show');
xlabel('Dose (Gy)');
ylabel('% volume');
hold off;
end
%-------------------------------------------------------------------------------
function DoseModeDropdown_Callback(obj, src, evt)
str = get(obj.handles.hDoseModeDropdown, 'String');
obj.handles.hSVPS.dosedisp.mode = str{get(obj.handles.hDoseModeDropdown, 'Value')};
end
%-------------------------------------------------------------------------------
function TCSShow_Callback(obj, src, evt)
obj.handles.hSVPS.TCSVisible = ...
get(src, 'Value') == get(src, 'Max');
end
%-------------------------------------------------------------------------------
function SaveAsGeometryMenu_Callback(obj, src, evt)
[filename pathname] = uifile('put', '*.mat', 'Save Geometry as...');
% do nothing if cancelled
if isscalar(filename) && filename == 0; return; end
Geometry = obj.handles.hSVPS.Geometry;
for i = 1:numel(Geometry.ROIS)
if isfield(Geometry.ROIS{i}, 'BW')
Geometry.ROIS{i}.BW = [];
end
end
save(fullfile(pathname, filename), 'Geometry');
end
%-------------------------------------------------------------------------------
function SaveAsOptResultsMenu_Callback(obj, src, evt)
[filename pathname] = uifile('put', '*.mat', 'Save OptResults as...');
% do nothing if cancelled
if isscalar(filename) && filename == 0; return; end
optResults = obj.handles.hSVPS.optResults;
save(fullfile(pathname, filename), 'optResults');
end
%-------------------------------------------------------------------------------
end % methods
end