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