diff --git a/AUTHORS.txt b/AUTHORS.txt index 745a12e3c..5a26fe433 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,5 +1,7 @@ List of all matRad developers that contributed code (alphabetical) +* Nelly Abbani +* Nabe Al-Hasnawi * Mark Bangert * Tobias Becher * Amit Ben Antony Bennan diff --git a/IO/matRad_exportGUI.fig b/IO/matRad_exportGUI.fig deleted file mode 100644 index 27769a09b..000000000 Binary files a/IO/matRad_exportGUI.fig and /dev/null differ diff --git a/IO/matRad_exportGUI.m b/IO/matRad_exportGUI.m deleted file mode 100644 index ac0ef2723..000000000 --- a/IO/matRad_exportGUI.m +++ /dev/null @@ -1,490 +0,0 @@ -function varargout = matRad_exportGUI(varargin) -% MATRAD_EXPORTGUI MATLAB code for matRad_exportGUI.fig -% MATRAD_EXPORTGUI, by itself, creates a new MATRAD_EXPORTGUI or raises the existing -% singleton*. -% -% H = MATRAD_EXPORTGUI returns the handle to a new MATRAD_EXPORTGUI or the handle to -% the existing singleton*. -% -% MATRAD_EXPORTGUI('CALLBACK',hObject,eventData,handles,...) calls the local -% function named CALLBACK in MATRAD_EXPORTGUI.M with the given input arguments. -% -% MATRAD_EXPORTGUI('Property','Value',...) creates a new MATRAD_EXPORTGUI or raises -% the existing singleton*. Starting from the left, property value pairs are -% applied to the GUI before matRad_exportGUI_OpeningFcn gets called. An -% unrecognized property name or invalid value makes property application -% stop. All inputs are passed to matRad_exportGUI_OpeningFcn via varargin. -% -% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one -% instance to run (singleton)". -% -% See also: GUIDE, GUIDATA, GUIHANDLES - -% Edit the above text to modify the response to help matRad_exportGUI - -% Last Modified by GUIDE v2.5 07-Jul-2016 14:50:05 -% -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% Copyright 2015 the matRad development team. -% -% This file is part of the matRad project. It is subject to the license -% terms in the LICENSE file found in the top-level directory of this -% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part -% of the matRad project, including this file, may be copied, modified, -% propagated, or distributed except according to the terms contained in the -% LICENSE file. -% -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% Begin initialization code - DO NOT EDIT -gui_Singleton = 1; -gui_State = struct('gui_Name', mfilename, ... - 'gui_Singleton', gui_Singleton, ... - 'gui_OpeningFcn', @matRad_exportGUI_OpeningFcn, ... - 'gui_OutputFcn', @matRad_exportGUI_OutputFcn, ... - 'gui_LayoutFcn', [] , ... - 'gui_Callback', []); -if nargin && ischar(varargin{1}) - gui_State.gui_Callback = str2func(varargin{1}); -end - -if nargout - [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); -else - gui_mainfcn(gui_State, varargin{:}); -end -% End initialization code - DO NOT EDIT - -% --- Executes just before matRad_exportGUI is made visible. -function matRad_exportGUI_OpeningFcn(hObject, eventdata, handles, varargin) -% This function has no output args, see OutputFcn. -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -% varargin command line arguments to matRad_exportGUI (see VARARGIN) - -% Choose default command line output for matRad_exportGUI -handles.output = hObject; - -%Fills structure export table -if evalin('base','exist(''cst'',''var'')') == 1 - cst = evalin( 'base', 'cst' ); - tableData = cell(numel(cst(:,2)),2); - tableData(:,2) = cst(:,2); - tableData(:,1) = {true}; -else - tableData = cell(0); - set(handles.checkbox_CT,'Enable','off'); -end -set(handles.uitable_vois,'data',tableData); - -%Fills result cubes export table -if evalin('base','exist(''resultGUI'',''var'')') - result = evalin( 'base', 'resultGUI' ); - cubeNames = fieldnames(result); - cubeIx = 1; - for f = 1:numel(cubeNames) - if ndims(result.(cubeNames{f})) < 3 - continue; - end - cubes{cubeIx} = cubeNames{f}; - cubeIx = cubeIx + 1; - end - numCubes = cubeIx - 1; - tableData = cell(numCubes,2); - tableData(:,2) = cubes; - tableData(:,1) = {true}; -else - tableData = cell(0); - set(handles.checkbox_dose,'Enable','off'); -end -set(handles.uitable_doseCubes,'data',tableData); - - -% Update handles structure -guidata(hObject, handles); - -initialize_gui(hObject, handles, false); - -% UIWAIT makes matRad_exportGUI wait for user response (see UIRESUME) -% uiwait(handles.figure1); - - -% --- Outputs from this function are returned to the command line. -function varargout = matRad_exportGUI_OutputFcn(hObject, eventdata, handles) -% varargout cell array for returning output args (see VARARGOUT); -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Get default command line output from handles structure -varargout{1} = handles.output; - -% -------------------------------------------------------------------- -function initialize_gui(fig_handle, handles, isreset) -% If the metricdata field is present and the btn_cancel flag is false, it means -% we are we are just re-initializing a GUI by calling it from the cmd line -% while it is up. So, bail out as we dont want to btn_cancel the data. -if isfield(handles, 'metricdata') && ~isreset - return; -end - -%{ -handles.metricdata.density = 0; -handles.metricdata.volume = 0; - -set(handles.density, 'String', handles.metricdata.density); -set(handles.volume, 'String', handles.metricdata.volume); -set(handles.mass, 'String', 0); - -set(handles.unitgroup, 'SelectedObject', handles.english); - -set(handles.text4, 'String', 'lb/cu.in'); -set(handles.text5, 'String', 'cu.in'); -set(handles.text6, 'String', 'lb'); - -% Update handles structure -guidata(handles.figure1, handles); -%} - -% --- Executes on button press in checkbox_CT. -function checkbox_CT_Callback(hObject, eventdata, handles) -% hObject handle to checkbox_CT (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -saveCT = get(hObject,'Value'); - -%Show the VOI-table only if we want to save a CT -if (saveCT) - set(handles.uitable_vois,'Visible', 'on', 'Enable','on'); -else - set(handles.uitable_vois,'Visible', 'off', 'Enable','off'); -end - - -% --- Executes on selection change in listbox_vois. -function uitable_vois_Callback(hObject, eventdata, handles) -% hObject handle to listbox_vois (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns listbox_vois contents as cell array -% contents{get(hObject,'Value')} returns selected item from listbox_vois - - -% --- Executes during object creation, after setting all properties. -function uitable_vois_CreateFcn(hObject, eventdata, handles) -% hObject handle to listbox_vois (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in checkbox_dose. -function checkbox_dose_Callback(hObject, eventdata, handles) -% hObject handle to checkbox_dose (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - - -%Show the Result-table only if we want to save dose cubes -saveDose = get(hObject,'Value'); -if (saveDose) - set(handles.uitable_doseCubes,'Visible', 'on', 'Enable','on'); - -else - set(handles.uitable_doseCubes,'Visible', 'off', 'Enable','off'); - %set(handles.uitable_vois,'data',cell(0)); -end - - -% --- Executes on selection change in listbox_dose. -function uitable_doseCubes_Callback(hObject, eventdata, handles) -% hObject handle to listbox_dose (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns listbox_dose contents as cell array -% contents{get(hObject,'Value')} returns selected item from listbox_dose - - -% --- Executes during object creation, after setting all properties. -function listbox_dose_CreateFcn(hObject, eventdata, handles) -% hObject handle to listbox_dose (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in pushbutton_dir_export_browse. -function exportDir = pushbutton_dir_export_browse_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_dir_export_browse (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -exportDir = uigetdir('', 'Choose the export directory...'); -if exportDir ~= 0 - exportDir = [exportDir filesep]; - set(handles.edit_dir_export,'String',exportDir); - % Update handles structure - guidata(hObject, handles); -end - - -function exportDir = edit_dir_export_Callback(hObject, eventdata, handles) -% hObject handle to edit_dir_export (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of edit_dir_export as text -% str2double(get(hObject,'String')) returns contents of edit_dir_export as a double - -exportDir = get(handles.edit_dir_export,'String'); - -%Add filesperator -if exportDir(end) ~= filesep; - exportDir = [exportDir filesep]; -end - -%Check if the user specified an existing directory -if ~exist(exportDir,'dir') - warndlg(['Folder ' exportDir ' does not exist!']); - exportDir = ''; -end -set(handles.edit_dir_export,'String',exportDir); -guidata(hObject, handles); - -% --- Executes during object creation, after setting all properties. -function edit_dir_export_CreateFcn(hObject, eventdata, handles) -% hObject handle to edit_dir_export (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); - - - -end - - -% --- Executes on selection change in popupmenu_extension. -function popupmenu_extension_Callback(hObject, eventdata, handles) -% hObject handle to popupmenu_extension (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_extension contents as cell array -% contents{get(hObject,'Value')} returns selected item from popupmenu_extension - - -% --- Executes during object creation, after setting all properties. -function popupmenu_extension_CreateFcn(hObject, eventdata, handles) -% hObject handle to popupmenu_extension (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -%These sets up the available extensions -extensions{1} = '*.nrrd'; -extensions{2} = '*.vtk'; -extensions{3} = '*.mha'; -set(hObject,'String',extensions); - -% Hint: popupmenu controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in btn_export. -function btn_export_Callback(hObject, eventdata, handles) -% hObject handle to btn_export (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -%Get the export dir -exportDir = get(handles.edit_dir_export,'String'); - -%Sanity check -if numel(exportDir) == 0 - errordlg('No Export folder selected!'); - return; -elseif ~exist(exportDir,'dir') - errordlg(['Folder ' exportDir ' does not exist!']); - return; -else - %Add file separator if necessary - if exportDir(end) ~= filesep; - exportDir = [exportDir filesep]; - end -end - -%Get the file extension -extensionIndex = get(handles.popupmenu_extension,'Value'); -extensions = get(handles.popupmenu_extension,'String'); -extension = extensions{extensionIndex}; -extension = extension(2:end); - -saveCT = get(handles.checkbox_CT,'Value'); -saveResults = get(handles.checkbox_dose,'Value'); - -%%Prepare for export -%If we export CT, try to create a subdirectory for VOIs -if (saveCT) - voiDir = [exportDir '/vois/']; - if ~exist(voiDir,'dir') - if ~mkdir(voiDir) - warndlg('Could not create subfolder for VOI masks. Masks will be stored in base folder.'); - voiDir = exportDir; - end - end -end -%If we export results, try to create a subdirectory for VOIs -if (saveResults) - resultDir = [exportDir '/results/']; - if ~exist(resultDir,'dir') - if ~mkdir(resultDir) - warndlg('Could not create subfolder for resulting dose cubes. Cubes will be stored in base folder.'); - resultDir = exportDir; - end - end -end - -%prepare metadata -ct = evalin('base','ct'); - -metadata.resolution = [ct.resolution.x ct.resolution.y ct.resolution.z]; -metadata.compress = get(handles.checkbox_compress,'Value'); - -%Check if we have position information -if isfield(ct,'dicomInfo') - if isfield(ct.dicomInfo,'ImagePositionPatient') - metadata.imageOrigin = ct.dicomInfo.ImagePositionPatient; - if ~isrow(metadata.imageOrigin) - metadata.imageOrigin = transpose(metadata.imageOrigin); - end - end -end - -%This is only for the waitbar to get the number of cubes you wanna save -numExportCubes = 0; -if (saveCT) - if isfield(ct,'cubeHU') - numExportCubes = numExportCubes + 1; - end - - if isfield(ct,'cube') - numExportCubes = numExportCubes + 1; - end - voiNames = get(handles.uitable_vois,'Data'); - voiIndices = find([voiNames{:,1}] == true); - numExportCubes = numExportCubes + numel(voiIndices); - -else - numExportCubes = 0; -end - -if saveResults - cubeNames = get(handles.uitable_doseCubes,'data'); - cubeIndices = find([cubeNames{:,1}] == true); - numExportCubes = numExportCubes + numel(cubeIndices); -end - -%Give an error if nothing was selected -if numExportCubes == 0 - errordlg('No data was selected for export!'); - return; -end - -currentCube = 0; - -hWaitbar = waitbar(0,'Exporting...','WindowStyle', 'modal'); -cleanUp = onCleanup(@() close(hWaitbar)); - -%CT and Mask export -if saveCT - - if isfield(ct,'cube') - %Export the CT (ED suffix to clarify it is not in HU) - currentCube = currentCube + 1; - waitbar(currentCube/numExportCubes,hWaitbar,['Exporting CT Intensity values (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); - matRad_writeCube(fullfile(exportDir,['CT_ED' extension]),ct.cube{1},'double',metadata); - end - - if isfield(ct,'cubeHU') - currentCube = currentCube + 1; - waitbar(currentCube/numExportCubes,hWaitbar,['Exporting CT in HU (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); - matRad_writeCube(fullfile(exportDir,['CT_HU' extension]),ct.cubeHU{1},'double',metadata); - end - - %Export VOI masks - cst = evalin('base','cst'); - - for voiIx = voiIndices - %Waitbar - currentCube = currentCube + 1; - waitbar(currentCube/numExportCubes,hWaitbar,['Exporting Segmentation Mask (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); - - %Get the index list - voiRow = find(strcmp(voiNames{voiIx,2},cst(:,2))); - voiIndexList = cst{voiRow,4}{1}; - %Set up the full mask - voiMask = zeros(ct.cubeDim); - voiMask(voiIndexList) = 1; - %Export... - matRad_writeCube(fullfile(voiDir,[voiNames{voiIx,2} extension]),voiMask,'uint8',metadata); - end - -end - -%Results Export -if saveResults - results = evalin('base','resultGUI'); - cubeNames = get(handles.uitable_doseCubes,'data'); - - for cubeIx = cubeIndices - %Export - currentCube = currentCube + 1; - waitbar(currentCube/numExportCubes,hWaitbar,['Exporting Results (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); - matRad_writeCube(fullfile(resultDir,[cubeNames{cubeIx,2} extension]),results.(cubeNames{cubeIx,2}),'double',metadata); - end -end - -close(handles.figure1); - - -% --- Executes on button press in btn_cancel. -function btn_cancel_Callback(hObject, eventdata, handles) -% hObject handle to btn_cancel (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -close(handles.figure1); - -% --- Executes during object creation, after setting all properties. -function btn_cancel_CreateFcn(hObject, eventdata, handles) -% hObject handle to btn_cancel (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% --- Executes on button press in checkbox_compress. -function checkbox_compress_Callback(hObject, eventdata, handles) -% hObject handle to checkbox_compress (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of checkbox_compress diff --git a/IO/matRad_importGUI.fig b/IO/matRad_importGUI.fig deleted file mode 100644 index 3181e7886..000000000 Binary files a/IO/matRad_importGUI.fig and /dev/null differ diff --git a/IO/matRad_importGUI.m b/IO/matRad_importGUI.m deleted file mode 100644 index c9f9e4c51..000000000 --- a/IO/matRad_importGUI.m +++ /dev/null @@ -1,325 +0,0 @@ -function varargout = matRad_importGUI(varargin) -% MATRAD_IMPORTGUI MATLAB code for matRad_importGUI.fig -% MATRAD_IMPORTGUI, by itself, creates a new MATRAD_IMPORTGUI or raises the existing -% singleton*. -% -% H = MATRAD_IMPORTGUI returns the handle to a new MATRAD_IMPORTGUI or the handle to -% the existing singleton*. -% -% MATRAD_IMPORTGUI('CALLBACK',hObject,eventData,handles,...) calls the local -% function named CALLBACK in MATRAD_IMPORTGUI.M with the given input arguments. -% -% MATRAD_IMPORTGUI('Property','Value',...) creates a new MATRAD_IMPORTGUI or raises the -% existing singleton*. Starting from the left, property value pairs are -% applied to the GUI before matRad_importGUI_OpeningFcn gets called. An -% unrecognized property name or invalid value makes property application -% stop. All inputs are passed to matRad_importGUI_OpeningFcn via varargin. -% -% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one -% instance to run (singleton)". -% -% See also: GUIDE, GUIDATA, GUIHANDLES - -% Edit the above text to modify the response to help matRad_importGUI - -% Last Modified by GUIDE v2.5 09-Aug-2018 15:18:30 - -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% Copyright 2015 the matRad development team. -% -% This file is part of the matRad project. It is subject to the license -% terms in the LICENSE file found in the top-level directory of this -% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part -% of the matRad project, including this file, may be copied, modified, -% propagated, or distributed except according to the terms contained in the -% LICENSE file. -% -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% Begin initialization code - DO NOT EDIT -gui_Singleton = 1; -gui_State = struct('gui_Name', mfilename, ... - 'gui_Singleton', gui_Singleton, ... - 'gui_OpeningFcn', @matRad_importGUI_OpeningFcn, ... - 'gui_OutputFcn', @matRad_importGUI_OutputFcn, ... - 'gui_LayoutFcn', [] , ... - 'gui_Callback', []); -if nargin && ischar(varargin{1}) - gui_State.gui_Callback = str2func(varargin{1}); -end - -if nargout - [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); -else - gui_mainfcn(gui_State, varargin{:}); -end -% End initialization code - DO NOT EDIT - - -% --- Executes just before matRad_importGUI is made visible. -function matRad_importGUI_OpeningFcn(hObject, eventdata, handles, varargin) -% This function has no output args, see OutputFcn. -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -% varargin command line arguments to matRad_importGUI (see VARARGIN) - -% Choose default command line output for matRad_importGUI -handles.output = hObject; - -% Update handles structure -guidata(hObject, handles); - -% UIWAIT makes matRad_importGUI wait for user response (see UIRESUME) -% uiwait(handles.figure_importDialog); - - -% --- Outputs from this function are returned to the command line. -function varargout = matRad_importGUI_OutputFcn(hObject, eventdata, handles) -% varargout cell array for returning output args (see VARARGOUT); -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Get default command line output from handles structure -varargout{1} = handles.output; - - - -function edit_ctPath_Callback(hObject, eventdata, handles) -% hObject handle to edit_ctPath (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of edit_ctPath as text -% str2double(get(hObject,'String')) returns contents of edit_ctPath as a double - - -% --- Executes during object creation, after setting all properties. -function edit_ctPath_CreateFcn(hObject, eventdata, handles) -% hObject handle to edit_ctPath (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in pushbutton_ctPath. -function pushbutton_ctPath_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_ctPath (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -[importCTFile,importCTPath,~] = uigetfile({'*.nrrd', 'NRRD-Files'}, 'Choose the CT file...'); -if importCTFile ~= 0 - set(handles.edit_ctPath,'String',fullfile(importCTPath,importCTFile)); - % Update handles structure - guidata(hObject, handles); -end - - - -function listbox_maskPaths_Callback(hObject, eventdata, handles) -% hObject handle to listbox_maskPaths (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of listbox_maskPaths as text -% str2double(get(hObject,'String')) returns contents of listbox_maskPaths as a double - - -% --- Executes during object creation, after setting all properties. -function listbox_maskPaths_CreateFcn(hObject, eventdata, handles) -% hObject handle to listbox_maskPaths (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -set(hObject,'value',[],'max',2,'min',0,'String',cell(0)); - - -% --- Executes on button press in pushbutton_addMaskPaths. -function pushbutton_addMaskPaths_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_addMaskPaths (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -[importMaskFile,importMaskPath,~] = uigetfile({'*.nrrd', 'NRRD-Files'}, 'Choose the binary mask files...','MultiSelect','on'); -if ~isempty(importMaskFile) - if ~iscell(importMaskFile) - tmpName = importMaskFile; - importMaskFile = cell(1); - importMaskFile{1} = tmpName; - end - importMaskFile = cellfun(@(filename) fullfile(importMaskPath,filename),importMaskFile,'UniformOutput',false); - entries = get(handles.listbox_maskPaths,'String'); - newEntries = [entries importMaskFile]; - set(handles.listbox_maskPaths,'String',newEntries); - % Update handles structure - guidata(hObject, handles); -end - - -% --- Executes on button press in pushbutton_import. -function pushbutton_import_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_import (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -ctFile = get(handles.edit_ctPath,'String'); -maskFiles = get(handles.listbox_maskPaths,'String'); - -if isempty(ctFile) || isempty(maskFiles) - errordlg('Please sepecify a CT and at least one mask!'); -end - -convertHU = get(handles.checkbox_huConvert,'Value'); - -if convertHU - [ct,cst] = matRad_importPatient(ctFile,maskFiles,get(handles.edit_hlut,'String')); -else - [ct,cst] = matRad_importPatient(ctFile,maskFiles); -end - -cst = showCheckDialog(cst); - -assignin('base', 'ct', ct); -assignin('base', 'cst', cst); - -delete(handles.figure_importDialog); - - -% --- Executes on button press in pushbutton_cancel. -function pushbutton_cancel_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_cancel (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -delete(handles.figure_importDialog); - - -% --- Executes on button press in pushbutton_addMaskFolders. -function pushbutton_addMaskFolders_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_addMaskFolders (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -importMaskPath = uigetdir('./', 'Choose the folder containing binary mask files...'); -importMaskPath = [importMaskPath filesep]; -if ~isempty(importMaskPath) - entries = get(handles.listbox_maskPaths,'String'); - newEntries = [entries cellstr(importMaskPath)]; - set(handles.listbox_maskPaths,'String',newEntries); - % Update handles structure - guidata(hObject, handles); -end - -% --- Executes on key press with focus on listbox_maskPaths and none of its controls. -function listbox_maskPaths_KeyPressFcn(hObject, eventdata, handles) -% hObject handle to listbox_maskPaths (see GCBO) -% eventdata structure with the following fields (see MATLAB.UI.CONTROL.UICONTROL) -% Key: name of the key that was pressed, in lower case -% Character: character interpretation of the key(s) that was pressed -% Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed -% handles structure with handles and user data (see GUIDATA) -if isequal(eventdata.Key,'delete') || isequal(eventdata.Key,'backspace') - selectIndex = get(hObject,'value'); - entries = get(hObject,'String'); - if numel(entries) == 0 - return; - end - entries(selectIndex) = []; - if selectIndex > numel(entries) && selectIndex > 1 - selectIndex = selectIndex - 1; - end - set(hObject,'String',entries,'value',selectIndex); -end - -% --- Creates a Dialog for the final adaptations to VOIs and CT conversion -function cst = showCheckDialog(cst) - -handle = dialog('Position', [100 100 400 250],'WindowStyle','modal','Name','Confirm Segmentations'); - -%Create Table -hTable = uitable('Parent',handle,'Units','normal','Position',[0.1 0.2 0.8 0.8]); -hTable.Data = cst(:,2:3); -hTable.ColumnName = {'Name','Type'}; -hTable.ColumnWidth = {150,'auto'}; -hTable.RowName = []; -hTable.ColumnEditable = [true true]; -hTable.ColumnFormat = {'char',{'TARGET', 'OAR', 'IGNORED'}}; - -%Create Button -hButton = uicontrol(handle,'Style','pushbutton','String','Confirm','Units','normal','Position',[0.7 0.05 0.2 0.1],'Callback','uiresume(gcbf)');%{@pushbutton_confirm_vois_callback}); -try - uiwait(handle); - cst(:,2:3) = hTable.Data(:,:); -catch - warning('Closed checkdialog without confirmation! Using default cst information!'); -end -delete(handle); - - -% --- Executes on button press in checkbox_huConvert. -function checkbox_huConvert_Callback(hObject, eventdata, handles) -% hObject handle to checkbox_huConvert (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of checkbox_huConvert - -checked = get(hObject,'Value'); - -if checked - fieldState = 'on'; -else - fieldState = 'off'; -end - - -set(handles.edit_hlut,'Enable',fieldState); -set(handles.pushbutton_hlutFile,'Enable',fieldState); - - -function edit_hlut_Callback(hObject, eventdata, handles) -% hObject handle to edit_hlut (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of edit_hlut as text -% str2double(get(hObject,'String')) returns contents of edit_hlut as a double - - -% --- Executes during object creation, after setting all properties. -function edit_hlut_CreateFcn(hObject, eventdata, handles) -% hObject handle to edit_hlut (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in pushbutton_hlutFile. -function pushbutton_hlutFile_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_hlutFile (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -[importHLUTFile,importHLUTPath,~] = uigetfile({'*.hlut', 'matRad HLUT-Files'}, 'Choose the HLUT file...'); -if importHLUTFile ~= 0 - set(handles.edit_hlut,'String',fullfile(importHLUTPath,importHLUTFile)); - % Update handles structure - guidata(hObject, handles); -end diff --git a/MatRad_Config.m b/MatRad_Config.m index 0a3053810..11ad84f46 100644 --- a/MatRad_Config.m +++ b/MatRad_Config.m @@ -36,6 +36,10 @@ %Disable GUI disableGUI = false; + devMode = false; + eduMode = false; + + gui; end properties (SetAccess = private) @@ -60,6 +64,7 @@ % Therefore its constructor is private % For instantiation, use the static MatRad_Config.instance(); + %Set Path obj.matRadRoot = fileparts(mfilename('fullpath')); addpath(genpath(obj.matRadRoot)); @@ -153,6 +158,7 @@ function displayToConsole(obj,type,formatSpec,varargin) function reset(obj) %Set all default properties for matRad's computations obj.setDefaultProperties(); + obj.setDefaultGUIProperties(); end function setDefaultProperties(obj) @@ -163,8 +169,8 @@ function setDefaultProperties(obj) obj.propStf.defaultLongitudinalSpotSpacing = 2; obj.propStf.defaultAddMargin = true; %expand target for beamlet finding - - obj.propDoseCalc.doseGrid.defaultResolution = struct('x',3,'y',3,'z',3); %[mm] + obj.propStf.defaultBixelWidth = 5; + obj.propDoseCalc.defaultResolution = struct('x',3,'y',3,'z',3); %[mm] obj.propDoseCalc.defaultLateralCutOff = 0.995; %[rel.] obj.propDoseCalc.defaultGeometricCutOff = 50; %[mm] obj.propDoseCalc.defaultKernelCutOff = Inf; %[mm] @@ -205,8 +211,12 @@ function setDefaultProperties(obj) obj.propDoseCalc.fineSampling.defaultCalcMode = 'standard'; obj.disableGUI = false; - + obj.defaults.samplingScenarios = 25; + + obj.devMode = false; + obj.eduMode = false; + end %%For testing @@ -219,6 +229,8 @@ function setDefaultPropertiesForTesting(obj) obj.propStf.defaultLongitudinalSpotSpacing = 20; obj.propStf.defaultAddMargin = true; %expand target for beamlet finding + obj.propStf.defaultBixelWidth = 20; + obj.propDoseCalc.defaultResolution = struct('x',5,'y',6,'z',7); %[mm] obj.propDoseCalc.defaultGeometricCutOff = 20; obj.propDoseCalc.defaultLateralCutOff = 0.8; @@ -251,6 +263,49 @@ function setDefaultPropertiesForTesting(obj) obj.defaults.samplingScenarios = 2; obj.disableGUI = true; + + obj.devMode = true; + obj.eduMode = false; + end + + %%for edu mode + function setDefaultPropertiesForEduMode(obj) + obj.logLevel = 1; + + obj.propStf.defaultLongitudinalSpotSpacing = 3; + obj.propStf.defaultAddMargin = true; %expand target for beamlet finding + obj.propStf.defaultBixelWidth = 5; + + obj.propDoseCalc.defaultResolution = struct('x',4,'y',4,'z',4); %[mm] + obj.propDoseCalc.defaultLateralCutOff = 0.975; %[rel.] + obj.propDoseCalc.defaultGeometricCutOff = 50; %[mm] + obj.propDoseCalc.defaultSsdDensityThreshold = 0.05; %[rel.] + obj.propDoseCalc.defaultUseGivenEqDensityCube = false; %Use the given density cube ct.cube and omit conversion from cubeHU. + obj.propDoseCalc.defaultIgnoreOutsideDensities = true; %Ignore densities outside of cst contours + obj.propDoseCalc.defaultUseCustomPrimaryPhotonFluence = false; %Use a custom primary photon fluence + + obj.propOpt.defaultMaxIter = 500; + + obj.propMC.ompMC_defaultHistories = 1e4; + obj.propMC.ompMC_defaultOutputVariance = false; + obj.propMC.MCsquare_defaultHistories = 1e4; + obj.propMC.direct_defaultHistories = 1e4; + + obj.disableGUI = false; + + obj.devMode = false; + obj.eduMode = true; + + end + + function setDefaultGUIProperties(obj) + obj.gui.backgroundColor = [0.5 0.5 0.5]; + obj.gui.elementColor = [0.75 0.75 0.75]; + obj.gui.textColor = [0 0 0]; + + obj.gui.fontSize = 8; + obj.gui.fontWeight = 'bold'; + obj.gui.fontName = 'Helvetica'; end function dispDebug(obj,formatSpec,varargin) diff --git a/dicom/@matRad_DicomExporter/matRad_DicomExporter.m b/dicom/@matRad_DicomExporter/matRad_DicomExporter.m index 625cf68ba..a2804e66b 100644 --- a/dicom/@matRad_DicomExporter/matRad_DicomExporter.m +++ b/dicom/@matRad_DicomExporter/matRad_DicomExporter.m @@ -85,7 +85,6 @@ % Can be called with the structures. If no argument is given, % all structures will be read from the base workspace - matRad_cfg = MatRad_Config.instance(); env = matRad_getEnvironment(); diff --git a/dicom/@matRad_DicomExporter/matRad_exportDicomRTDoses.m b/dicom/@matRad_DicomExporter/matRad_exportDicomRTDoses.m index 65bd3e7cc..5add942a7 100644 --- a/dicom/@matRad_DicomExporter/matRad_exportDicomRTDoses.m +++ b/dicom/@matRad_DicomExporter/matRad_exportDicomRTDoses.m @@ -24,10 +24,7 @@ matRad_cfg = MatRad_Config.instance(); matRad_cfg.dispInfo('Exporting DICOM RTDose...\n'); -env = matRad_getEnvironment(); -isOctave = strcmp(env,'OCTAVE'); - -if isOctave +if matRad_cfg.isOctave matRad_cfg.dispWarning('RTDose export currently not supported by matRad running in Octave using the dicom package! Skipping...'); return; end diff --git a/dicom/@matRad_DicomExporter/matRad_exportDicomRTStruct.m b/dicom/@matRad_DicomExporter/matRad_exportDicomRTStruct.m index 150607bb8..9d517ccdb 100644 --- a/dicom/@matRad_DicomExporter/matRad_exportDicomRTStruct.m +++ b/dicom/@matRad_DicomExporter/matRad_exportDicomRTStruct.m @@ -25,10 +25,7 @@ matRad_cfg = MatRad_Config.instance(); matRad_cfg.dispInfo('Exporting DICOM RTStruct...\n'); -env = matRad_getEnvironment(); -isOctave = strcmp(env,'OCTAVE'); - -if isOctave +if matRad_cfg.isOctave matRad_cfg.dispWarning('RTStruct export currently not supported by matRad running in Octave due to crashing dicomwrite! Skipping...'); return; end @@ -158,7 +155,7 @@ meta.StructureSetROISequence.(['Item_' num2str(i)]) = ROISequenceItem; %Contour Sequence - if ~isOctave + if ~ matRad_cfg.isOctave ROIContourSequenceItem.ROIDisplayColor = int32(round(255 * obj.cst{i,5}.visibleColor)); end @@ -251,7 +248,7 @@ filename = fullfile(filepath,filename); -if isOctave +if matRad_cfg.isOctave dicomwrite(int16(zeros(2)),filename,meta); else obj.rtssExportStatus = dicomwrite([],filename,meta,'CreateMode','copy');%,'TransferSyntax',TransferSyntaxUID); diff --git a/dicom/matRad_checkEnvDicomRequirements.m b/dicom/matRad_checkEnvDicomRequirements.m new file mode 100644 index 000000000..283ca1ef4 --- /dev/null +++ b/dicom/matRad_checkEnvDicomRequirements.m @@ -0,0 +1,51 @@ +function available = matRad_checkEnvDicomRequirements(env) +% matRad function to check if requirements for dicom import / export are +% given. Throws an error if requirements not met +% +% call +% matRad_checkEnvDicomRequirements(env) +% +% input +% env: folder to be scanned +% +% References +% - +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2015 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +available = true; + +matRad_cfg = MatRad_Config.instance(); + +if nargin < 1 + isOctave = matRad_cfg.isOctave; +else + isOctave = strcmp(env,'OCTAVE'); +end + +if isOctave + try + pkg load dicom; + pkg load image; + catch + available = false; + end +else + if ~license('checkout','image_toolbox') + available = false; + end +end + +end + diff --git a/dicom/matRad_exportDicomGUI.m b/dicom/matRad_exportDicomGUI.m new file mode 100644 index 000000000..c3b278daf --- /dev/null +++ b/dicom/matRad_exportDicomGUI.m @@ -0,0 +1,28 @@ +function hGUI = matRad_exportDicomGUI() +% matRad compatability function to call the dicom export widget +% +% call +% matRad_importDicomGUI +% +% References +% - +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2015 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +if nargout > 0 + hGUI = matRad_exportDicomWidget(); +else + matRad_exportDicomWidget(); +end +end \ No newline at end of file diff --git a/dicom/matRad_importDicom.m b/dicom/matRad_importDicom.m index a8a3dac68..927460e1d 100644 --- a/dicom/matRad_importDicom.m +++ b/dicom/matRad_importDicom.m @@ -34,8 +34,7 @@ % LICENSE file. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%[env, ~] = matRad_getEnvironment(); +matRad_cfg = MatRad_Config.instance(); %% if ~exist('dicomMetaBool','var') @@ -54,7 +53,7 @@ resolution.z = files.resz; % [mm] / lps coordinate system if files.useDoseGrid && isfield(files,'rtdose') % get grid from dose cube - if verLessThan('matlab','9') + if matRad_cfg.isOctave || verLessThan('matlab','9') doseInfo = dicominfo(files.rtdose{1,1}); else doseInfo = dicominfo(files.rtdose{1,1},'UseDictionaryVR',true); @@ -161,3 +160,21 @@ resultGUI.w = [resultGUI.w; [stf(i).ray.weight]']; end end + + +%% save ct, cst, pln, dose +matRadFileName = [files.ct{1,3} '.mat']; % use default from dicom +[FileName,PathName] = uiputfile('*','Save as...',matRadFileName); +if ischar(FileName) + % delete unnecessary variables + switch env + case 'MATLAB' + clearvars -except ct cst pln stf resultGUI FileName PathName; + save([PathName, FileName], '-regexp', '^(?!(FileName|PathName)$).','-v7'); + case 'OCTAVE' + clear -x ct cst pln stf resultGUI FileName PathName; + save([PathName, FileName],'-v6'); + end + % save all except FileName and PathName + +end diff --git a/dicom/matRad_importDicomCt.m b/dicom/matRad_importDicomCt.m index 5d0cdd2f5..3b24b6e4e 100644 --- a/dicom/matRad_importDicomCt.m +++ b/dicom/matRad_importDicomCt.m @@ -45,6 +45,9 @@ visBool = 0; end +matRad_checkEnvDicomRequirements(matRad_cfg.env); + + % creation of ctInfo list numOfSlices = size(ctList,1); matRad_cfg.dispInfo('\ncreating info...') @@ -52,7 +55,7 @@ sliceThicknessStandard = true; for i = 1:numOfSlices - if verLessThan('matlab','9') + if matRad_cfg.isOctave || verLessThan('matlab','9') tmpDicomInfo = dicominfo(ctList{i,1}); else tmpDicomInfo = dicominfo(ctList{i,1},'UseDictionaryVR',true); @@ -67,8 +70,8 @@ ctInfo(i).PatientPosition = tmpDicomInfo.PatientPosition; ctInfo(i).Rows = tmpDicomInfo.Rows; ctInfo(i).Columns = tmpDicomInfo.Columns; - ctInfo(i).Width = tmpDicomInfo.Width; - ctInfo(i).Height = tmpDicomInfo.Height; + ctInfo(i).Width = tmpDicomInfo.Columns;%tmpDicomInfo.Width; + ctInfo(i).Height = tmpDicomInfo.Rows;%tmpDicomInfo.Height; ctInfo(i).RescaleSlope = tmpDicomInfo.RescaleSlope; ctInfo(i).RescaleIntercept = tmpDicomInfo.RescaleIntercept; @@ -142,7 +145,12 @@ origCt = zeros(ctInfo(1).Height, ctInfo(1).Width, numOfSlices); for i = 1:numOfSlices currentFilename = ctList{i}; - [currentImage, map] = dicomread(currentFilename); + if matRad_cfg.isOctave + currentImage = dicomread(currentFilename); + map = []; + else + [currentImage, map] = dicomread(currentFilename); + end origCt(:,:,i) = currentImage(:,:); % creation of the ct cube % draw current ct-slice diff --git a/dicom/matRad_importDicomGUI.fig b/dicom/matRad_importDicomGUI.fig deleted file mode 100644 index 6d5cf0418..000000000 Binary files a/dicom/matRad_importDicomGUI.fig and /dev/null differ diff --git a/dicom/matRad_importDicomGUI.m b/dicom/matRad_importDicomGUI.m index d835315f3..a6123c6a5 100644 --- a/dicom/matRad_importDicomGUI.m +++ b/dicom/matRad_importDicomGUI.m @@ -1,29 +1,12 @@ -function varargout = matRad_importDicomGUI(varargin) -% MATRAD_IMPORTDICOMGUI MATLAB code for matRad_importDicomGUI.fig -% MATRAD_IMPORTDICOMGUI, by itself, creates a new MATRAD_IMPORTDICOMGUI or raises the existing -% singleton*. -% -% H = MATRAD_IMPORTDICOMGUI returns the handle to a new MATRAD_IMPORTDICOMGUI or the handle to -% the existing singleton*. -% -% MATRAD_IMPORTDICOMGUI('CALLBACK',hObject,eventData,handles,...) calls the local -% function named CALLBACK in MATRAD_IMPORTDICOMGUI.M with the given input arguments. -% -% MATRAD_IMPORTDICOMGUI('Property','Value',...) creates a new MATRAD_IMPORTDICOMGUI or raises the -% existing singleton*. Starting from the left, property value pairs are -% applied to the GUI before matRad_importDicomGUI_OpeningFcn gets called. An -% unrecognized property name or invalid value makes property application -% stop. All inputs are passed to matRad_importDicomGUI_OpeningFcn via varargin. +function hGUI = matRad_importDicomGUI() +% matRad compatability function to call the dicom importwidget +% +% call +% matRad_importDicomGUI % -% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one -% instance to run (singleton)". +% References +% - % -% See also: GUIDE, GUIDATA, GUIHANDLES - -% Edit the above text to modify the response to help matRad_importDicomGUI - -% Last Modified by GUIDE v2.5 02-Jun-2017 00:45:04 - % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright 2015 the matRad development team. @@ -37,718 +20,11 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Begin initialization code - DO NOT EDIT -gui_Singleton = 1; -gui_State = struct('gui_Name', mfilename, ... - 'gui_Singleton', gui_Singleton, ... - 'gui_OpeningFcn', @matRad_importDicomGUI_OpeningFcn, ... - 'gui_OutputFcn', @matRad_importDicomGUI_OutputFcn, ... - 'gui_LayoutFcn', [] , ... - 'gui_Callback', []); -if nargin && ischar(varargin{1}) - gui_State.gui_Callback = str2func(varargin{1}); -end - -% if nargout - [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); -% else -% gui_mainfcn(gui_State, varargin{:}); -% end -% End initialization code - DO NOT EDIT - - -% --- Executes just before matRad_importDicomGUI is made visible. -function matRad_importDicomGUI_OpeningFcn(hObject, eventdata, handles, varargin) -% This function has no output args, see OutputFcn. -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -% varargin command line arguments to matRad_importDicomGUI (see VARARGIN) - -% Choose default command line output for matRad_importDicomGUI -handles.output = hObject; - -axes(handles.axesMatRadLogo) -[im, ~, alpha] = imread('matrad_logo.png'); -q = image(im); -axis equal off -set(q, 'AlphaData', alpha); -% show dkfz logo -axes(handles.axesDKFZLogo) -[im, ~, alpha] = imread('DKFZ_Logo.png'); -p = image(im); -axis equal off -set(p, 'AlphaData', alpha); -% Update handles structure -guidata(hObject, handles); - -% UIWAIT makes matRad_importDicomGUI wait for user response (see UIRESUME) -% uiwait(handles.figure1); - - -% --- Outputs from this function are returned to the command line. -function varargout = matRad_importDicomGUI_OutputFcn(hObject, eventdata, handles) -% varargout cell array for returning output args (see VARARGOUT); -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Get default command line output from handles structure -varargout{1} = handles.output; - - -% --- Executes on button press in browse_button. -function patDir = browse_button_Callback(hObject, eventdata, handles) -% hObject handle to browse_button (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -%uiwait(warndlg('Choose the input directory')); -patDir = uigetdir('', 'Choose the input directory...'); -if patDir ~= 0 - patDir = [patDir filesep]; - %handles.dir_path_field.String = patDir; - set(handles.dir_path_field,'String',patDir); - % Update handles structure - guidata(hObject, handles); - scan(hObject, eventdata, handles) -end - -function scan(hObject, eventdata, handles) -[fileList, patient_listbox] = matRad_scanDicomImportFolder(get(handles.dir_path_field,'String')); -if iscell(patient_listbox) - handles.fileList = fileList; - %handles.patient_listbox.String = patient_listbox; - set(handles.patient_listbox,'String',patient_listbox,'Value',1); - guidata(hObject, handles); -end - -% --- Executes on selection change in patient_listbox. -function patient_listbox_Callback(hObject, eventdata, handles) -% hObject handle to patient_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns patient_listbox contents as cell array -% contents{get(hObject,'Value')} returns selected item from patient_listbox - -if ~isempty(get(hObject,'String')) - % enable Import button - set(handles.import_button,'Enable','on'); - - % handles.filelist: - % 1. Filepath - % 2. Modality - % 3. PatientID - % 4. SeriesUID - % 5. SeriesNumber - % 9. res_x - % 10. res_y - % 11. res_z - % 12. detailed dose description - currently not in use for GUI user - patient_listbox = get(handles.patient_listbox,'String'); - selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); - % this gets a list of rtss series for this patient - set(handles.rtseries_listbox,'Value',1); % set dummy value to one - set(handles.rtseries_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTSTRUCT') & strcmp(handles.fileList(:,3), selected_patient),4)); - % this gets a list of rt plan series for this patient - set(handles.rtplan_listbox,'Value',[]); % set dummy value to none - set(handles.rtplan_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTPLAN') & strcmp(handles.fileList(:,3), selected_patient),4)); - % this gets a list of dose series for this patient - set(handles.doseseries_listbox,'Value',[]); % set dummy value to none - set(handles.doseseries_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTDOSE') & strcmp(handles.fileList(:,3), selected_patient),4)); - % selectedDose - - if get(handles.SeriesUID_radiobutton,'Value') == 1 - % this gets a list of ct series for this patient - set(handles.ctseries_listbox,'Value',1); % set dummy value to one - set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),4))); - - selectedDoseSeriesString = get(handles.doseseries_listbox,'String'); - % this gets a resolution for this patient - selectedCtSeriesString = get(handles.ctseries_listbox,'String'); - if ~isempty(selectedCtSeriesString) - res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); - res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); - res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); - else - res_x = NaN; res_y = NaN; res_z = NaN; - end - else - set(handles.ctseries_listbox,'Value',1); % set dummy value to one - set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),5))); - selectedCtSeriesString = get(handles.ctseries_listbox,'String'); - if ~isempty(selectedCtSeriesString) - res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); - res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); - res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); - else - res_x = NaN; res_y = NaN; res_z = NaN; - end - end - set(handles.resx_edit,'String',res_x); - set(handles.resy_edit,'String',res_y); - if numel(res_z) > 1 - set(handles.resz_edit,'String','not equi'); - else - set(handles.resz_edit,'String',res_z); - end - % Update handles structure - guidata(hObject, handles); -end - -% --- Executes during object creation, after setting all properties. -function patient_listbox_CreateFcn(hObject, eventdata, handles) -% hObject handle to patient_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on selection change in ctseries_listbox. -function ctseries_listbox_Callback(hObject, eventdata, handles) -% hObject handle to ctseries_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns ctseries_listbox contents as cell array -% contents{get(hObject,'Value')} returns selected item from ctseries_listbox - - -% --- Executes during object creation, after setting all properties. -function ctseries_listbox_CreateFcn(hObject, eventdata, handles) -% hObject handle to ctseries_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on selection change in rtseries_listbox. -function rtseries_listbox_Callback(hObject, eventdata, handles) -% hObject handle to rtseries_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns rtseries_listbox contents as cell array -% contents{get(hObject,'Value')} returns selected item from rtseries_listbox - - -% --- Executes during object creation, after setting all properties. -function rtseries_listbox_CreateFcn(hObject, eventdata, handles) -% hObject handle to rtseries_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in import_button. -function import_button_Callback(hObject, eventdata, handles) -% hObject handle to import_button (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -patient_listbox = get(handles.patient_listbox,'String'); -ctseries_listbox = get(handles.ctseries_listbox,'String'); -rtplan_listbox = get(handles.rtplan_listbox,'String'); -doseseries_listbox = get(handles.rtplan_listbox,'String'); -if ~isempty(patient_listbox) - selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); -end -if ~isempty(ctseries_listbox) - selected_ctseries = ctseries_listbox(get(handles.ctseries_listbox,'Value')); -end -if ~isempty(rtplan_listbox) - selected_rtplan = rtplan_listbox(get(handles.rtplan_listbox,'Value')); -end - -if get(handles.SeriesUID_radiobutton,'Value') == 1 - files.ct = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... - strcmp(handles.fileList(:,4), selected_ctseries),:); - - %files.rtss = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... - % strcmp(handles.fileList(:,4), selected_rtseries),:); -else - files.ct = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... - strcmp(handles.fileList(:,5), selected_ctseries) & strcmp(handles.fileList(:,2),'CT'),:); - - %files.rtss = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... - % strcmp(handles.fileList(:,5), selected_rtseries),:); -end - -allRtss = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,2),'RTSTRUCT'),:); -if ~isempty(allRtss) - files.rtss = allRtss(get(handles.rtseries_listbox,'Value'),:); -else - files.rtss = []; -end - -files.resx = str2double(get(handles.resx_edit,'String')); -files.resy = str2double(get(handles.resy_edit,'String')); -% check if valid assignment is made when z slices are not equi-distant -if strcmp(get(handles.resz_edit,'String'),'not equi') - msgbox('Ct data not sliced equi-distantly in z direction! Chose uniform resolution.', 'Error','error'); - return; -else - files.resz = str2double(get(handles.resz_edit,'String')); -end -% selected RT Plan -rtplan = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,2),'RTPLAN'),:); -if ~isempty(rtplan) && ~isempty(get(handles.rtplan_listbox,'Value')) - files.rtplan = rtplan(get(handles.rtplan_listbox,'Value'),:); -end - -% selected RT Dose -rtdose = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,2),'RTDOSE'),:); -if ~isempty(rtdose) && ~isempty(get(handles.doseseries_listbox,'Value')) - selectedRtDose = get(handles.doseseries_listbox,'String'); - selectedRtDoseIx = NaN*ones(1,numel(selectedRtDose)); - for i = 1:numel(selectedRtDose) - selectedRtDoseIx(i) = find(strcmp(rtdose(:,4),selectedRtDose{i})); - end - files.rtdose = rtdose(selectedRtDoseIx,:); -end - -% check if we should use the dose grid resolution -files.useDoseGrid = get(handles.checkbox3,'Value'); - -% dicomMetaBool: store complete DICOM information and patientName or not -dicomMetaBool = logical(get(handles.checkPatientName,'Value')); -[ct,cst,pln,stf,resultGUI] = matRad_importDicom(files, dicomMetaBool); - - -% Save file -matRadFileName = [files.ct{1,3} '.mat']; % use default from dicom -[FileName,PathName] = uiputfile('*','Save as...',matRadFileName); -if ischar(FileName) - %Hack to get non-empty variables - varNames = {'ct','cst','pln','stf','resultGUI'}; - ix = cellfun(@(s) ~isempty(evalin('caller',s)),varNames); - varNames = varNames(ix); - - FileName = fullfile(PathName,FileName); - - % save all non-empty variables imported - save(FileName,'-v7.3',varNames{:}); -end - - -% --- Executes on button press in cancel_button. -function cancel_button_Callback(hObject, eventdata, handles) -% hObject handle to cancel_button (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -close(handles.figure1); - - -% --- Executes on button press in rescan_button. -function rescan_button_Callback(hObject, eventdata, handles) -% hObject handle to rescan_button (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - - -% --- Executes on mouse press over figure background. -function figure1_ButtonDownFcn(hObject, eventdata, handles) -% hObject handle to figure1 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - - - -function dir_path_field_Callback(hObject, eventdata, handles) -% hObject handle to dir_path_field (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of dir_path_field as text -% str2double(get(hObject,'String')) returns contents of dir_path_field as a double - -patDir = get(handles.dir_path_field,'String'); -if patDir(end) ~= filesep; - patDir = [patDir filesep]; - set(handles.dir_path_field,'String',patDir); - guidata(hObject, handles); -end -scan(hObject, eventdata, handles); - - -% --- Executes on button press in SeriesUID_radiobutton. -function SeriesUID_radiobutton_Callback(hObject, eventdata, handles) -% hObject handle to SeriesUID_radiobutton (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -if get(hObject,'Value') == 1 - set(handles.SeriesNumber_radiobutton,'Value',0); -else - set(hObject,'Value',1); - set(handles.SeriesNumber_radiobutton,'Value',0); -end -if isfield(handles, 'fileList') - patient_listbox = get(handles.patient_listbox,'String'); - selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); - set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),4))); - set(handles.rtseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'RTSTRUCT') & strcmp(handles.fileList(:,3), selected_patient),4))); - set(handles.doseseries_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTDOSE') & strcmp(handles.fileList(:,3), selected_patient),4)); - set(handles.rtplan_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'RTPLAN') & strcmp(handles.fileList(:,3), selected_patient),4))); -else - fprintf('No patient loaded, so just switching default display option to SeriesUID. \n'); -end -guidata(hObject, handles); - -% --- Executes on button press in SeriesNumber_radiobutton. -function SeriesNumber_radiobutton_Callback(hObject, eventdata, handles) -% hObject handle to SeriesNumber_radiobutton (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -if get(hObject,'Value') == 1 - set(handles.SeriesUID_radiobutton,'Value',0); -else - set(hObject,'Value',1); - set(handles.SeriesUID_radiobutton,'Value',0); -end -if isfield(handles, 'fileList') - patient_listbox = get(handles.patient_listbox,'String'); - selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); - set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),5))); -else - fprintf('No patient loaded, so just switching default display option to SeriesNumber. \n'); -end -guidata(hObject, handles); - - - -function resx_edit_Callback(hObject, eventdata, handles) -% hObject handle to resx_edit (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of resx_edit as text -% str2double(get(hObject,'String')) returns contents of resx_edit as a double - - -% --- Executes during object creation, after setting all properties. -function resx_edit_CreateFcn(hObject, eventdata, handles) -% hObject handle to resx_edit (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - - -function resy_edit_Callback(hObject, eventdata, handles) -% hObject handle to resy_edit (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of resy_edit as text -% str2double(get(hObject,'String')) returns contents of resy_edit as a double - - -% --- Executes during object creation, after setting all properties. -function resy_edit_CreateFcn(hObject, eventdata, handles) -% hObject handle to resy_edit (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - - -function resz_edit_Callback(hObject, eventdata, handles) -% hObject handle to resz_edit (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of resz_edit as text -% str2double(get(hObject,'String')) returns contents of resz_edit as a double - - -% --- Executes during object creation, after setting all properties. -function resz_edit_CreateFcn(hObject, eventdata, handles) -% hObject handle to resz_edit (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% -% % --- Executes on selection change in ctseries_listbox. -% function ctseries_listbox_Callback(hObject, eventdata, handles) -% % hObject handle to ctseries_listbox (see GCBO) -% % eventdata reserved - to be defined in a future version of MATLAB -% % handles structure with handles and user data (see GUIDATA) -% -% % Hints: contents = cellstr(get(hObject,'String')) returns ctseries_listbox contents as cell array -% % contents{get(hObject,'Value')} returns selected item from ctseries_listbox -% -% -% % --- Executes during object creation, after setting all properties. -% function ctseries_listbox_CreateFcn(hObject, eventdata, handles) -% % hObject handle to ctseries_listbox (see GCBO) -% % eventdata reserved - to be defined in a future version of MATLAB -% % handles empty - handles not created until after all CreateFcns called -% -% % Hint: listbox controls usually have a white background on Windows. -% % See ISPC and COMPUTER. -% if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) -% set(hObject,'BackgroundColor','white'); -% end -% -% -% % --- Executes on selection change in rtseries_listbox. -% function rtseries_listbox_Callback(hObject, eventdata, handles) -% % hObject handle to rtseries_listbox (see GCBO) -% % eventdata reserved - to be defined in a future version of MATLAB -% % handles structure with handles and user data (see GUIDATA) -% -% % Hints: contents = cellstr(get(hObject,'String')) returns rtseries_listbox contents as cell array -% % contents{get(hObject,'Value')} returns selected item from rtseries_listbox -% -% -% % --- Executes during object creation, after setting all properties. -% function rtseries_listbox_CreateFcn(hObject, eventdata, handles) -% % hObject handle to rtseries_listbox (see GCBO) -% % eventdata reserved - to be defined in a future version of MATLAB -% % handles empty - handles not created until after all CreateFcns called -% -% % Hint: listbox controls usually have a white background on Windows. -% % See ISPC and COMPUTER. -% if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) -% set(hObject,'BackgroundColor','white'); -% end - - -% --- Executes on selection change in doseseries_listbox. -function doseseries_listbox_Callback(hObject, eventdata, handles) -% hObject handle to doseseries_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns doseseries_listbox contents as cell array -% contents{get(hObject,'Value')} returns selected item from doseseries_listbox - -if ~isempty(get(hObject,'Value')) - set(handles.checkbox3,'Enable','on'); -else - set(handles.checkbox3,'Value',0); - set(handles.checkbox3,'Enable','off'); - % retrieve and display resolution for DICOM ct cube - patient_listbox = get(handles.patient_listbox,'String'); - selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); - selectedCtSeriesString = get(handles.ctseries_listbox,'String'); - if get(handles.SeriesUID_radiobutton,'Value') == 1 - if ~isempty(selectedCtSeriesString) - res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); - res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); - res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); - else - res_x = NaN; res_y = NaN; res_z = NaN; - end - else - if ~isempty(selectedCtSeriesString) - res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); - res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); - res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); - else - res_x = NaN; res_y = NaN; res_z = NaN; - end - end - set(handles.resx_edit,'String',res_x); - set(handles.resy_edit,'String',res_y); - if numel(res_z) > 1 - set(handles.resz_edit,'String','not equi'); - else - set(handles.resz_edit,'String',res_z); - end - -end - -% --- Executes during object creation, after setting all properties. -function doseseries_listbox_CreateFcn(hObject, eventdata, handles) -% hObject handle to doseseries_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% --- Executes on selection change in rtplan_listbox. -function rtplan_listbox_Callback(hObject, eventdata, handles) -% hObject handle to rtplan_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -contents = cellstr(get(hObject,'String')); -if ~isempty(get(hObject,'Value')) && numel(get(hObject,'Value')) == 1 - - selectedPlan = contents{get(hObject,'Value')}; - % point at plan in listbox - selectedPlanLoc = strcmp(handles.fileList(:,4),selectedPlan); - - % show only the doses corresponding to the plan - corrDoses = [handles.fileList{selectedPlanLoc,13}]; - numOfDoses = numel(corrDoses); - corrDosesLoc = zeros(size(handles.fileList(:,1),1),1); - for j = 1:numOfDoses - if ~isnan(corrDoses{j}) - corrDosesLoc = corrDosesLoc | strcmp(handles.fileList(:,4),corrDoses{j}); - end - end - - if sum(corrDosesLoc) == 0 - warndlg('no rt dose file directly associated to plan file. showing all rt dose files.'); - corrDosesLoc = strcmp(handles.fileList(:,2),'RTDOSE'); - end - - set(handles.doseseries_listbox,'Value',[]); % set dummy value to one - set(handles.doseseries_listbox,'String',handles.fileList(corrDosesLoc,4)); - - % disable checkbox for use dose grid is currently checked - if get(handles.checkbox3,'Value') == 1 - set(handles.checkbox3,'Value',0); - checkbox3_Callback(handles.checkbox3,[], handles); - end - set(handles.checkbox3,'Enable','off'); - - -elseif numel(get(hObject,'Value')) >=2 - warning('More than one RTPLAN selected. Unsetting selection ...'); - patient_listbox_Callback(hObject, eventdata, handles) -else - patient_listbox_Callback(hObject, eventdata, handles) -end - - -% --- Executes during object creation, after setting all properties. -function rtplan_listbox_CreateFcn(hObject, eventdata, handles) -% hObject handle to rtplan_listbox (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in checkPatientName. -function checkPatientName_Callback(hObject, eventdata, handles) -% hObject handle to checkPatientName (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -%A = get(hObject,'Value'); - -% Hint: get(hObject,'Value') returns toggle state of checkPatientName -%guidata(hObject, handles); - - -% --- Executes on button press in checkbox3. -function checkbox3_Callback(hObject, eventdata, handles) -% hObject handle to checkbox3 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of checkbox3 -if get(hObject,'Value') - set(handles.resx_edit,'Enable', 'off'); - set(handles.resy_edit,'Enable', 'off'); - set(handles.resz_edit,'Enable', 'off'); - % retrieve and display resolution for DICOM dose cube - doseFilesInList = get(handles.doseseries_listbox,'String'); - selectedDoseFiles = get(handles.doseseries_listbox,'Value'); - if isempty(selectedDoseFiles) - set(hObject,'Value',0) - errordlg('no dose file selected'); - return; - end - for i = 1:numel(selectedDoseFiles) - selectedDoseFile = doseFilesInList{selectedDoseFiles(i)}; - if verLessThan('matlab','9') - dicomDoseInfo = dicominfo(handles.fileList{find(strcmp(handles.fileList(:,4),selectedDoseFile)),1}); - else - dicomDoseInfo = dicominfo(handles.fileList{find(strcmp(handles.fileList(:,4),selectedDoseFile)),1},'UseDictionaryVR',true); - end - res_x{i} = dicomDoseInfo.PixelSpacing(1); - res_y{i} = dicomDoseInfo.PixelSpacing(2); - res_z{i} = dicomDoseInfo.SliceThickness; - end +if nargout > 0 + hGUI = matRad_importDicomWidget(); - if numel(unique(cell2mat(res_x)))*numel(unique(cell2mat(res_y)))*numel(unique(cell2mat(res_z))) ~= 1 - set(handles.checkbox3,'Value',0); - warndlg('Different resolutions in dose file(s)'); - set(handles.resx_edit,'Enable', 'on'); - set(handles.resy_edit,'Enable', 'on'); - set(handles.resz_edit,'Enable', 'on'); - else - set(handles.resx_edit,'String',num2str(res_x{1})); - set(handles.resy_edit,'String',num2str(res_y{1})); - set(handles.resz_edit,'String',num2str(res_z{1})); - end - else - set(handles.resx_edit,'Enable', 'on'); - set(handles.resy_edit,'Enable', 'on'); - set(handles.resz_edit,'Enable', 'on'); - % retrieve and display resolution for DICOM ct cube - patient_listbox = get(handles.patient_listbox,'String'); - selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); - selectedCtSeriesString = get(handles.ctseries_listbox,'String'); - if get(handles.SeriesUID_radiobutton,'Value') == 1 - if ~isempty(selectedCtSeriesString) - res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); - res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); - res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); - else - res_x = NaN; res_y = NaN; res_z = NaN; - end - else - if ~isempty(selectedCtSeriesString) - res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); - res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); - res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); - else - res_x = NaN; res_y = NaN; res_z = NaN; - end - end - set(handles.resx_edit,'String',res_x); - set(handles.resy_edit,'String',res_y); - if numel(res_z) > 1 - set(handles.resz_edit,'String','not equi'); - else - set(handles.resz_edit,'String',res_z); - end - + matRad_importDicomWidget(); end +end \ No newline at end of file diff --git a/dicom/matRad_importDicomRTPlan.m b/dicom/matRad_importDicomRTPlan.m index b906826c0..91ef81048 100644 --- a/dicom/matRad_importDicomRTPlan.m +++ b/dicom/matRad_importDicomRTPlan.m @@ -30,6 +30,7 @@ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% matRad_cfg = MatRad_Config.instance(); +matRad_checkEnvDicomRequirements(matRad_cfg.env); %% load plan file % check size of RT Plan @@ -38,7 +39,7 @@ end % read information out of the RT file -if verLessThan('matlab','9') +if matRad_cfg.isOctave || verLessThan('matlab','9') planInfo = dicominfo(rtPlanFiles{1}); else planInfo = dicominfo(rtPlanFiles{1},'UseDictionaryVR',true); diff --git a/dicom/matRad_importDicomRtss.m b/dicom/matRad_importDicomRtss.m index a0446bdc4..bcc3ca2f1 100644 --- a/dicom/matRad_importDicomRtss.m +++ b/dicom/matRad_importDicomRtss.m @@ -37,9 +37,13 @@ if nargin < 3 visBool = 0; end +matRad_cfg = MatRad_Config.instance(); +matRad_checkEnvDicomRequirements(matRad_cfg.env); + + % read dicom info (this includes already all data for the rtss) -if verLessThan('matlab','9') +if matRad_cfg.isOctave || verLessThan('matlab','9') structInfo = dicominfo(filename); else % apply 'UseVRHeuristic' option when available to use a to help read certain % noncompliant files which switch value representation (VR) modes incorrectly @@ -47,9 +51,17 @@ end % list the defined structures -listOfDefStructs = fieldnames(structInfo.StructureSetROISequence); +try + listOfDefStructs = fieldnames(structInfo.StructureSetROISequence); +catch + matRad_cfg.dispError('StructureSetROISequence not defined ') +end % list of contoured structures -listOfContStructs = fieldnames(structInfo.ROIContourSequence); +try + listOfContStructs = fieldnames(structInfo.ROIContourSequence); +catch + matRad_cfg.dispError('ROIContourSequence not defined ') +end %% process structure data numOfDefStructs = numel(listOfDefStructs); @@ -82,11 +94,11 @@ listOfSlices = fieldnames(structInfo.ROIContourSequence.(... listOfContStructs{i}).ContourSequence); else - warning(['Contour ' structures(i).structName ' is empty']) + matRad_cfg.dispWarning(['Contour ' structures(i).structName ' is empty']) continue; end else - warning(['Contour ' structures(i).structName ' is empty']) + matRad_cfg.dispWarning(['Contour ' structures(i).structName ' is empty']) continue; end @@ -109,12 +121,12 @@ % sanity check 1 if numel(unique(structZ)) > 1 - error('Detected contour points outside of single slice\n'); + matRad_cfg.dispError('Detected contour points outside of single slice\n'); end % sanity check 2 if unique(structZ) > max(dicomInfo.SlicePositions) || unique(structZ) < min(dicomInfo.SlicePositions) - warning(['Omitting contour data for ' structures(i).structName ' at slice position ' num2str(unique(structZ)) 'mm - no ct data available.\n']); + matRad_cfg.dispWarning(['Omitting contour data for ' structures(i).structName ' at slice position ' num2str(unique(structZ)) 'mm - no ct data available.\n']); else structures(i).item(j).points = [structX, structY, structZ]; end diff --git a/dicom/matRad_importDicomSteeringParticles.m b/dicom/matRad_importDicomSteeringParticles.m index ff9f0adb3..aa10e95b2 100644 --- a/dicom/matRad_importDicomSteeringParticles.m +++ b/dicom/matRad_importDicomSteeringParticles.m @@ -38,6 +38,9 @@ %% load plan file % load machine data +matRad_cfg = MatRad_Config.instance(); +matRad_checkEnvDicomRequirements(matRad_cfg.env); + dlgBaseDataText = ['Import steering information from DICOM Plan.','Choose corresponding matRad base data for ', ... pln.radiationMode, '.']; % messagebox only necessary for non windows users @@ -51,7 +54,7 @@ pln.machine = fileName(ix(1)+1:end-4); % RT Plan consists only on meta information -if verLessThan('matlab','9') +if matRad_cfg.isOctave || verLessThan('matlab','9') rtPlanInfo = dicominfo(rtPlanFile{1}); else rtPlanInfo = dicominfo(rtPlanFile{1},'UseDictionaryVR',true); diff --git a/dicom/matRad_interpDicomDoseCube.m b/dicom/matRad_interpDicomDoseCube.m index e6fe22513..12378666a 100644 --- a/dicom/matRad_interpDicomDoseCube.m +++ b/dicom/matRad_interpDicomDoseCube.m @@ -29,8 +29,14 @@ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % read information out of the RT file + +matRad_cfg = matRad_Config.instance(); +matRad_checkEnvDicomRequirements(matRad_cfg.env); + + dosefile = currDose{1}; -if verLessThan('matlab','9') + +if matRad_cfg.isOctave || verLessThan('matlab','9') doseInfo = dicominfo(dosefile); else doseInfo = dicominfo(dosefile,'UseDictionaryVR',true); diff --git a/dicom/matRad_scanDicomImportFolder.m b/dicom/matRad_scanDicomImportFolder.m index 8dd09dcaf..878a09ac8 100644 --- a/dicom/matRad_scanDicomImportFolder.m +++ b/dicom/matRad_scanDicomImportFolder.m @@ -40,11 +40,10 @@ %% get all files in search directory % dicom import needs image processing toolbox -> check if available -v = ver; -if ~license('checkout','image_toolbox') - matRad_cfg.dispError('Image Processing Toolbox and/or corresponding license not available'); -elseif ~any(strcmp('Image Processing Toolbox', {v.Name})) - matRad_cfg.dispError('Image Processing Toolbox not installed'); +available = matRad_checkEnvDicomRequirements(matRad_cfg.env); + +if ~available + matRad_cfg.dispError('Image processing toolbox / packages not available!'); end fileList = matRad_listAllFiles(patDir); @@ -60,7 +59,7 @@ for i = numOfFiles:-1:1 waitbar((numOfFiles+1-i) / steps) try % try to get DicomInfo - if verLessThan('matlab','9') + if matRad_cfg.isOctave || verLessThan('matlab','9') info = dicominfo(fileList{i}); else info = dicominfo(fileList{i},'UseDictionaryVR',true); diff --git a/dicom/matrad_logo.png b/dicom/matrad_logo.png deleted file mode 100644 index 296e1e858..000000000 Binary files a/dicom/matrad_logo.png and /dev/null differ diff --git a/examples/matRad_example2_photons.m b/examples/matRad_example2_photons.m index 77ba87929..a74ef0800 100644 --- a/examples/matRad_example2_photons.m +++ b/examples/matRad_example2_photons.m @@ -133,7 +133,7 @@ %% % Enable sequencing and disable direct aperture optimization (DAO) for now. % A DAO optimization is shown in a seperate example. -pln.propOpt.runSequencing = 1; +pln.propSeq.runSequencing = 1; pln.propOpt.runDAO = 0; % retrieve bio model parameters diff --git a/examples/matRad_example3_photonsDAO.m b/examples/matRad_example3_photonsDAO.m index ba412af9c..3a73639b1 100644 --- a/examples/matRad_example3_photonsDAO.m +++ b/examples/matRad_example3_photonsDAO.m @@ -72,8 +72,8 @@ %% % Enable sequencing and direct aperture optimization (DAO). -pln.propOpt.runSequencing = 1; -pln.propOpt.runDAO = 1; +pln.propSeq.runSequencing = true; +pln.propOpt.runDAO = true; %% Generate Beam Geometry STF stf = matRad_generateStf(ct,cst,pln); @@ -98,7 +98,7 @@ % order to modulate the intensity of the beams with multiple static % segments, so that translates each intensity map into a set of deliverable % aperture shapes. -resultGUI = matRad_siochiLeafSequencing(resultGUI,stf,dij,5); +resultGUI = matRad_sequencing(resultGUI,stf,dij,pln); %% DAO - Direct Aperture Optimization % The Direct Aperture Optimization is an optimization approach where we diff --git a/examples/matRad_example4_photonsMC.m b/examples/matRad_example4_photonsMC.m index bf0273f6b..3c393eb4f 100644 --- a/examples/matRad_example4_photonsMC.m +++ b/examples/matRad_example4_photonsMC.m @@ -40,7 +40,7 @@ pln.propStf.bixelWidth = 10; pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct,0); -pln.propOpt.runSequencing = 0; +pln.propSeq.runSequencing = 0; pln.propOpt.runDAO = 0; quantityOpt = 'physicalDose'; diff --git a/examples/matRad_example5_protons.m b/examples/matRad_example5_protons.m index d3c167893..247686529 100644 --- a/examples/matRad_example5_protons.m +++ b/examples/matRad_example5_protons.m @@ -60,7 +60,7 @@ pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct,0); pln.propOpt.runDAO = 0; -pln.propOpt.runSequencing = 0; +pln.propSeq.runSequencing = 0; % Define the flavor of biological optimization for treatment planning along % with the quantity that should be used for optimization. diff --git a/examples/matRad_example6_protonsNoise.m b/examples/matRad_example6_protonsNoise.m index 94d0a2e57..7f8c0d614 100644 --- a/examples/matRad_example6_protonsNoise.m +++ b/examples/matRad_example6_protonsNoise.m @@ -47,6 +47,7 @@ pln.propOpt.runDAO = 0; pln.propOpt.runSequencing = 0; + %% % Define the biological optimization model for treatment planning along % with the quantity that should be used for optimization. Possible model values @@ -68,6 +69,7 @@ % retrieve scenarios for dose calculation and optimziation pln.multScen = matRad_multScen(ct,'nomScen'); % optimize on the nominal scenario + % dose calculation settings pln.propDoseCalc.doseGrid.resolution.x = 3; % [mm] pln.propDoseCalc.doseGrid.resolution.y = 3; % [mm] diff --git a/examples/matRad_example7_carbon.m b/examples/matRad_example7_carbon.m index e65ce0ba5..5db2f6cb3 100644 --- a/examples/matRad_example7_carbon.m +++ b/examples/matRad_example7_carbon.m @@ -67,7 +67,7 @@ pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct,0); pln.propOpt.runDAO = 0; -pln.propOpt.runSequencing = 0; +pln.propSeq.runSequencing = 0; % retrieve bio model parameters pln.bioParam = matRad_bioModel(pln.radiationMode,quantityOpt,modelName); diff --git a/dicom/DKFZ_Logo.png b/gfx/DKFZ_Logo.png similarity index 100% rename from dicom/DKFZ_Logo.png rename to gfx/DKFZ_Logo.png diff --git a/gfx/matrad_logo.png b/gfx/matrad_logo.png new file mode 100644 index 000000000..cb4bc43f7 Binary files /dev/null and b/gfx/matrad_logo.png differ diff --git a/gui/icons8-gamma-16.png b/gui/icons8-gamma-16.png new file mode 100644 index 000000000..2e8aa6d0f Binary files /dev/null and b/gui/icons8-gamma-16.png differ diff --git a/gui/matRad_InfoWidget_uiwrapper.m b/gui/matRad_InfoWidget_uiwrapper.m new file mode 100644 index 000000000..d4656b1f0 --- /dev/null +++ b/gui/matRad_InfoWidget_uiwrapper.m @@ -0,0 +1,132 @@ +classdef matRad_InfoWidget_uiwrapper < matRad_Widget +% Class that defines and deploys the infoWidget to display matRad information +% +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + properties + + end + + methods + function this = matRad_InfoWidget_uiwrapper(handleParent) + if nargin < 1 + handleParent = matRad_uiwrapper('uifigure',... + 'Units','pixels',...%'characters',... + 'Position',[150 450 190 130],...%[100 45 25 7],... + ...%'Visible',1,...%'on',... + 'Color',[0.501960784313725 0.501960784313725 0.501960784313725],... + ...%'IntegerHandle',0,...%'off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... +...% 'MenuBar','none',... + 'Name','MatRad Info',... + ...%'NumberTitle',0,...%'off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + end + this = this@matRad_Widget(handleParent); + end + end + + methods (Access = protected) + function this = createLayout(this) + h94 = this.widgetHandle; + + h95 = matRad_uiwrapper('uibutton',... + 'Parent',h94,... + 'Position',[60 2 70 30],...%[0.238095238095238 0.134831460674157 0.563492063492063 0.280898876404494],... + ...%'Units','pixels',... + 'Tag','btnAbout',... + 'FontSize',7,... + 'text','About',... + 'ButtonPushedFcn',@(hObject,eventdata) btnAbout_Callback(this,hObject,eventdata),... + 'BackgroundColor',[0.501960784313725 0.501960784313725 0.501960784313725],... + 'FontWeight','bold'); + + h96 = matRad_uiwrapper('uilabel',... + 'Parent',h94,... + ...%'Units','normalized',... + ...%'String','v3.0.0',... + 'Position',[20 80 150 40],...%[0.227106227106227 0.752808988764045 0.523809523809524 0.191011235955056],... + 'BackgroundColor',[0.501960784313725 0.501960784313725 0.501960784313725],... + 'Tag','text15',... + 'FontSize',8,... + 'FontWeight','bold'); + + h97 = matRad_uiwrapper('uilabel',... + 'Parent',h94,... + ...%'Units','normalized',... + ...%'String','github.com/e0404/matRad',... + 'Position',[20 40 150 20],...%[0.0384615384615385 0.528089887640449 0.942307692307693 0.168539325842697],... + 'BackgroundColor',[0.501960784313725 0.501960784313725 0.501960784313725],... + 'Tag','text31',... + 'FontSize',8,... + 'FontWeight','bold' ); + + this.createHandles(); + handles=this.handles; + %Alter matRad Version string positioning + vString = matRad_version(); + vPos = get(handles.text15,'Position'); + urlPos = get(handles.text31,'Position'); + btnPos = get(handles.btnAbout,'Position'); + + %vPos([1 3]) = urlPos([1 3]); + %vPos([1 3]) = [0 1]; +% vPos(4) = vPos(4)*1.25; +% btnPos(2) = 0.05; +% urlPos(2) = btnPos(2)+btnPos(4)+0.05; +% vPos(2) = urlPos(2) + urlPos(4) + 0.05; +% vPos(4) = 0.98 - vPos(2); + + set(handles.btnAbout,'Position',btnPos); + set(handles.text31,'String','www.matRad.org','Position',urlPos,'Enable','inactive','ButtonDownFcn', @(~,~) web('www.matrad.org','-browser')); + set(handles.text15,'String',vString,'Position',vPos); + + this.handles=handles; + end + end + + methods (Access = protected) + + function btnAbout_Callback(this, hObject, event) + handles = this.handles; + %msgbox({'https://github.com/e0404/matRad/' 'email: matrad@dkfz.de'},'About'); + + matRad_cfg = MatRad_Config.instance(); + [~,matRadVer] = matRad_version; + + msg{1} = ['matRad ''' matRadVer.name '''']; %Name + if matRad_cfg.eduMode + msg{1} = [msg{1} ' Educational']; + end + msg{end+1} = sprintf('v%d.%d.%d',matRadVer.major,matRadVer.minor,matRadVer.patch); %Version Number + if isdeployed + msg{end+1} = 'Standalone Version'; + elseif ~isempty(matRadVer.branch) && ~isempty(matRadVer.commitID) + msg{end+1} = sprintf('Git: Branch %s, commit %s',matRadVer.branch,matRadVer.commitID(1:8)); + end + + [env,envver] = matRad_getEnvironment(); + msg{end+1} = sprintf('Environment: %s v%s %s',env,envver,version('-release')); + + msg{end+1} = 'Web: www.matrad.org'; + msg{end+1} = 'E-Mail: contact@matrad.org'; + + msgbox(msg,'About matRad'); + + + this.handles = handles; + end + end +end diff --git a/gui/matRad_MainGUI.m b/gui/matRad_MainGUI.m new file mode 100644 index 000000000..9609643af --- /dev/null +++ b/gui/matRad_MainGUI.m @@ -0,0 +1,540 @@ +classdef matRad_MainGUI < handle +% matRad_MainGUI Graphical User Interface configuration class +% This Class is used to create the main GUI interface where all the widgets +% are called and created. +% +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + properties + guiHandle + lastStoragePath = [] + end + + properties (Access = private) + LogoWidget + WorkflowWidget + PlanWidget + OptimizationWidget + VisualizationWidget + ViewerOptionsWidget + StructureVisibilityWidget + InfoWidget + ViewingWidget + DVHStatsWidget + eventListeners + GammaWidget + end + + + + methods(Access = protected) + function this = createMenuBar(this) + h1 = this.guiHandle; + folder = fileparts(mfilename('fullpath')); + loadIcons = load(fullfile(folder,'matRad_iconsGUI.mat'),'icons'); + + icons = loadIcons.icons; + + h60 = uitoolbar(... + 'Parent',h1,... + 'Tag','uitoolbar1'); + + h61 = uipushtool(... + 'Parent',h60,... + 'Children',[],... + 'BusyAction','cancel',... + 'Interruptible','off',... + 'Tag','toolbarLoad',... + 'CData',icons{1},... + 'ClickedCallback',@(hObject,eventdata) toolbarLoad_ClickedCallback(this,hObject,eventdata),... + 'Separator','on',... + 'TooltipString','Open File' ); + + h62 = uipushtool(... + 'Parent',h60,... + 'BusyAction','cancel',... + 'Interruptible','off',... + 'Tag','toolbarSave',... + 'CData',icons{2},... + 'ClickedCallback',@(hObject,eventdata) toolbarSave_ClickedCallback(this,hObject,eventdata),... + 'Separator','on',... + 'TooltipString','Save Figure'); + + + h63 = uipushtool(... + 'Parent',h60,... + 'Tag','uipushtool_screenshot',... + 'CData',icons{3},... + 'ClickedCallback',@(hObject,eventdata)uipushtool_screenshot_ClickedCallback(this, hObject, eventdata),... + 'TooltipString','Take a screenshot of the current dose or profile plot' ); + + h64 = uitoggletool(... + 'Parent',h60,... + 'Tag','toolbarZoomIn',... + 'CData',icons{4},... + 'ClickedCallback',@(hObject, eventdata)toolbarZoomIn_ClickedCallback(this, hObject, eventdata),... + 'Separator','on',... + 'TooltipString','Zoom In'); + + h65 = uitoggletool(... + 'Parent',h60,... + 'Children',[],... + 'Tag','toolbarZoomOut',... + 'CData',icons{5},... + 'ClickedCallback',@(hObject, eventdata)toolbarZoomOut_ClickedCallback(this, hObject, eventdata),... + 'Separator','on',... + 'TooltipString','Zoom Out'); + + h66 = uitoggletool(... + 'Parent',h60,... + 'Tag','toolbarPan',... + 'CData',icons{6},... + 'ClickedCallback',@(hObject, eventdata)toolbarPan_ClickedCallback(this, hObject, eventdata),... + 'Separator','on',... + 'TooltipString','Pan' ); + + h67 = uitoggletool(... + 'Parent',h60,... + 'Tag','toolbarCursor',... + 'CData',icons{7},... + 'ClickedCallback',@(hObject, eventdata)toolbarCursor_ClickedCallback(this, hObject, eventdata),... + 'Separator','on',... + 'TooltipString','Data Cursor' ); + + h68 = uitoggletool(... + 'Parent',h60,... + 'Tag','toolbarLegend',... + 'CData',icons{8},... + 'ClickedCallback',@(hObject, eventdata)toolbarLegend_ClickedCallback(this, hObject, eventdata),... + 'Separator','on',... + 'TooltipString','Insert Legend'); + + h69 = uitoggletool(... + 'Parent',h60,... + 'Tag','uitoggletool8',... + 'CData',icons{9},... + 'ClickedCallback',@(hObject, eventdata)uitoggletool8_ClickedCallback(this, hObject, eventdata),... + 'Separator','on',... + 'TooltipString','Insert Colorbar' ); +% TODO: future dose comparison tool +% h70 = uipushtool(... +% 'Parent',h60,... +% 'Tag','uipushtool_GammaIndex',... +% 'CData',icons{10},... +% 'ClickedCallback',@(hObject,eventdata)gammaIndex_ClickedCallback(this, hObject, eventdata),... +% 'TooltipString','Calculate Gamma Index' ); + + end + end + + methods + function obj = matRad_MainGUI(varargin) + %Panel for Main Widget + + matRad_cfg = MatRad_Config.instance(); + + obj.guiHandle = figure(... + 'Units','normalized',... + 'Position',[0.005 0.04 0.99 0.9],... %approximate fullscreen position + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','matRad (NOT FOR CLINICAL USE!)',... + 'HandleVisibility','callback',... + 'Tag','figure1',... + 'CloseRequestFcn',@(src,hEvent) figure1_CloseRequestFcn(obj,src,hEvent)); + + %WindowState not available in all versions + if isprop(obj.guiHandle,'WindowState') + set(obj.guiHandle,'WindowState','maximized'); + end + + pWorkflow = uipanel(... + 'Parent',obj.guiHandle,... + 'Title','Workflow',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel4',... + 'Clipping','off',... + 'Position',[0.005 0.8 0.425 0.2],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pPlan = uipanel(... + 'Parent',obj.guiHandle,... + 'Title','Plan',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel1',... + 'Clipping','off',... + 'Position',[0.005 0.55 0.425 0.25],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pOptimization = uipanel(... + 'Parent',obj.guiHandle,... + 'Title',strtrim(strjoin({ 'Objectives & constraints'; ' '; ' ' })),... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel3',... + 'Clipping','off',... + 'Position',[0.005 0.21 0.425 0.34],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pVisualization = uipanel(... + 'Parent',obj.guiHandle,... + 'Title',strtrim(strjoin({ 'Visualization'; ' ' })),... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel2',... + 'Clipping','off',... + 'Position',[0.005 0.005 0.425 0.205],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pLogo = uipanel(... + 'Parent',obj.guiHandle,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel13',... + 'Clipping','off',... + 'Position',[0.44 0.89 0.55 0.1],... + 'HighLightColor',[0.501960784313725 0.501960784313725 0.501960784313725],... + 'BorderType','none',... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pViewing = uipanel(... + 'Parent',obj.guiHandle,... + 'Title','Viewing',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel11',... + 'Clipping','off',... + 'Position',[0.435 0.005 0.455 0.885],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pViewerOptions = uipanel(... + 'Parent',obj.guiHandle,... + 'Title','Viewer Options',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel_colormapOptions',... + 'Clipping','off',... + 'Position',[0.895 0.445 0.1 0.445],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pStructureVisibility = uipanel(... + 'Parent',obj.guiHandle,... + 'Title','Structure Visibilty',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel10',... + 'Clipping','off',... + 'Position',[0.895 0.11 0.1 0.33],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pInfo = uipanel(... + 'Parent',obj.guiHandle,... + 'Title','Info',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','uipanel12',... + 'Clipping','off',... + 'Position',[0.895 0.005 0.1 0.1],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + % Initialize Widgets + obj.WorkflowWidget = matRad_WorkflowWidget(pWorkflow); + obj.PlanWidget = matRad_PlanWidget(pPlan); + obj.OptimizationWidget = matRad_OptimizationWidget(pOptimization); + obj.ViewingWidget = matRad_ViewingWidget(pViewing); + obj.ViewingWidget.scrollHandle = obj.guiHandle; + obj.VisualizationWidget = matRad_VisualizationWidget(obj.ViewingWidget,pVisualization); + obj.ViewerOptionsWidget = matRad_ViewerOptionsWidget(obj.ViewingWidget,pViewerOptions); + obj.StructureVisibilityWidget = matRad_StructureVisibilityWidget(pStructureVisibility); + obj.InfoWidget = matRad_InfoWidget(pInfo); % does not need a listener + obj.LogoWidget = matRad_LogoWidget(pLogo); % does not need a listener + + %Initialize event Listeners + switch matRad_cfg.env + case 'MATLAB' + %Event listeners triggered on events + obj.eventListeners.workflow = addlistener(obj.WorkflowWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.plan = addlistener(obj.PlanWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.optimization = addlistener(obj.OptimizationWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.viewing = addlistener(obj.ViewingWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.plot = addlistener(obj.ViewingWidget,'plotUpdated',@(src,hEvent) updateButtons(obj)); + obj.eventListeners.visualization = addlistener(obj.VisualizationWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.viewerOptions = addlistener(obj.ViewerOptionsWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.structureVisibility = addlistener(obj.StructureVisibilityWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + + % only available in MATLAB + obj.ViewingWidget.dcmHandle = datacursormode(obj.guiHandle); + obj.ViewingWidget.panHandle = pan(obj.guiHandle); + obj.ViewingWidget.zoomHandle = zoom(obj.guiHandle); + + case 'OCTAVE' + % addlistener is not yet available in octave + obj.eventListeners.workflow = matRad_addListenerOctave(obj.WorkflowWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.plan = matRad_addListenerOctave(obj.PlanWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.optimization = matRad_addListenerOctave(obj.OptimizationWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.viewing = matRad_addListenerOctave(obj.ViewingWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.plot = matRad_addListenerOctave(obj.ViewingWidget,'plotUpdated',@(src,hEvent) updateButtons(obj)); + obj.eventListeners.visualization = matRad_addListenerOctave(obj.VisualizationWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.viewerOptions = matRad_addListenerOctave(obj.ViewerOptionsWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + obj.eventListeners.structureVisibility = matRad_addListenerOctave(obj.StructureVisibilityWidget,'workspaceChanged',@(src,hEvent) updateWidgets(obj,hEvent)); + + end + + obj.createMenuBar(); + + % update button states + obj.updateButtons(); + + try + % change color of toobar the first time GUI is started + hToolbar = findall(obj.guiHandle,'tag','uitoolbar1'); + jToolbar = get(get(hToolbar,'JavaContainer'),'ComponentPeer'); + jToolbar.setBorderPainted(false); + color = java.awt.Color.gray; + % Remove the toolbar border, to blend into figure contents + jToolbar.setBackground(color); + % Remove the separator line between toolbar and contents + warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame'); + jFrame = get(handle(obj.guiHandle),'JavaFrame'); + jFrame.showTopSeparator(false); + jtbc = jToolbar.getComponents; + for idx=1:length(jtbc) + jtbc(idx).setOpaque(false); + jtbc(idx).setBackground(color); + for childIdx = 1 : length(jtbc(idx).getComponents) + jtbc(idx).getComponent(childIdx-1).setBackground(color); + end + end + catch + matRad_cfg.dispDeprecationWarning('Java properties couldn''t be set'); + end + end + + function this = updateWidgets(this,evt) + matRad_cfg = MatRad_Config.instance(); + if matRad_cfg.logLevel > 3 + if nargin < 2 || ~isa(evt,'matRad_WorkspaceChangedEvent') + changed = {}; + else + changed = evt.changedVariables; + end + + matRad_cfg.dispDebug('GUI Workspace Changed at %s. ',datestr(now,'HH:MM:SS.FFF')); + if ~isempty(changed) + matRad_cfg.dispDebug('Specific Variables: %s.\n',strjoin(changed,'|')); + else + matRad_cfg.dispDebug('\n'); + end + end + + + %%Debug + this.PlanWidget.update(evt); + this.WorkflowWidget.update(evt); + this.OptimizationWidget.update(evt); + this.ViewingWidget.lockUpdate = 0; + this.ViewingWidget.update(evt); + this.StructureVisibilityWidget.update(evt); + %this.ViewerOptionsWidget.update(); + %this.VisualizationWidget.update(); + + end + + function this = updateButtons(this) + %disp(['Plot Changed ' datestr(now,'HH:MM:SS.FFF')]); %Debug + % update the visualization and viewer options widgets + + matRad_cfg = MatRad_Config.instance(); + + if strcmp(matRad_cfg.env,'OCTAVE') + return + end + + set(findobj(this.guiHandle,'tag','toolbarPan'),'State',get(this.ViewingWidget.panHandle,'Enable')); + set(findobj(this.guiHandle,'tag','toolbarCursor'),'State',get(this.ViewingWidget.dcmHandle,'Enable')); + + if this.ViewingWidget.plotColorBar && ~isempty(this.ViewingWidget.cBarHandle) && isvalid(this.ViewingWidget.cBarHandle) + set(findobj(this.guiHandle,'tag','uitoggletool8'),'State','on') + else + set(findobj(this.guiHandle,'tag','uitoggletool8'),'State','off') + end + if this.ViewingWidget.plotLegend && ~isempty(this.ViewingWidget.legendHandle) && isvalid(this.ViewingWidget.legendHandle) + set(findobj(this.guiHandle,'tag','toolbarLegend'),'State',get(this.ViewingWidget.legendHandle,'visible')); + else + set(findobj(this.guiHandle,'tag','toolbarLegend'),'State','off'); + end + if strcmp(get(this.ViewingWidget.zoomHandle,'Enable'),'on') + if strcmp(get(this.ViewingWidget.zoomHandle,'Direction'),'in') + set(findobj(this.guiHandle,'tag','toolbarZoomOut'),'State','off'); + set(findobj(this.guiHandle,'tag','toolbarZoomIn'),'State','on'); + else + set(findobj(this.guiHandle,'tag','toolbarZoomOut'),'State','on'); + set(findobj(this.guiHandle,'tag','toolbarZoomIn'),'State','off'); + end + else + set(findobj(this.guiHandle,'tag','toolbarZoomOut'),'State','off'); + set(findobj(this.guiHandle,'tag','toolbarZoomIn'),'State','off'); + end + + this.ViewerOptionsWidget.update(); + this.VisualizationWidget.update(); + + end + + %% Callbacks + % toolbar load button + function toolbarLoad_ClickedCallback(this,hObject, eventdata) + this.WorkflowWidget.btnLoadMat_Callback(hObject, eventdata); + end + % toolbar save button + function toolbarSave_ClickedCallback(this,hObject, eventdata) + %handles=this.handles; + + + answer = questdlg('Do you wish to save the full workspace or only matRad variables?','Save','Full workspace', 'matRad variables', 'matRad variables'); + + + switch answer + case 'Full workspace' + uisave; + case 'matRad variables' + variables = {'cst','ct','pln','stf','dij','resultGUI'}; + vExists=false(size(variables)); + for i= 1:numel(variables) + var= char(variables(i)); + vExists(i) = evalin('base',['exist(''' var ''',''var'')']); + if vExists(i) + eval([var '=evalin(''base'',''' var ''');']) + end + end + uisave(variables(vExists)); + end + + end + %Toolbar screenshot of the Viewing widget + function uipushtool_screenshot_ClickedCallback(this,hObject, eventdata) + % hObject handle to uipushtool_screenshot (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + + tmpFig = figure('position',[100 100 700 600],'Visible','off','name','Current View'); + cBarHandle = this.ViewingWidget.cBarHandle; %findobj(handles.figure1,'Type','colorbar'); + if ~isempty(cBarHandle) + new_handle = copyobj([this.ViewingWidget.handles.axesFig cBarHandle],tmpFig); + else + new_handle = copyobj(this.ViewingWidget.handles.axesFig,tmpFig); + end + + oldPos = get(this.ViewingWidget.handles.axesFig,'Position'); + set(new_handle(1),'units','normalized', 'Position',oldPos); + + if exist(this.lastStoragePath,'dir') ~= 7 + this.lastStoragePath = []; + end + + [filename, pathname] = uiputfile({'*.jpg;*.tif;*.png;*.gif','All Image Files'; '*.fig','MATLAB figure file'},'Save current view',[this.lastStoragePath 'screenshot.png']); + + this.lastStoragePath = pathname; + + if ~isequal(filename,0) && ~isequal(pathname,0) + set(gcf, 'pointer', 'watch'); + saveas(tmpFig,fullfile(pathname,filename)); + set(gcf, 'pointer', 'arrow'); + close(tmpFig); + uiwait(msgbox('Current view has been succesfully saved!')); + else + uiwait(msgbox('Aborted saving, showing figure instead!')); + set(tmpFig,'Visible','on'); + end + + end + + % Toggle color bar callback + function uitoggletool8_ClickedCallback(this,hObject, eventdata) + % hObject handle to uitoggletool8 (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + this.ViewingWidget.plotColorBar = strcmp(get(hObject,'State'),'on'); + + end + %Toggle Legend Callback + function toolbarLegend_ClickedCallback(this,hObject, eventdata) + + this.ViewingWidget.plotLegend = strcmp(get(hObject,'State'),'on'); + end + %Toggle Zoom in Callback + function toolbarZoomIn_ClickedCallback(this,hObject, eventdata) + set(this.ViewingWidget.zoomHandle,'Enable',char(get(hObject,'State'))); + set(this.ViewingWidget.zoomHandle,'Direction','in'); + set(findobj('tag','toolbarZoomOut'),'State','off'); + end + %Toggle Zoom out Callback + function toolbarZoomOut_ClickedCallback(this,hObject, eventdata) + set(this.ViewingWidget.zoomHandle,'Enable',char(get(hObject,'State'))); + set(this.ViewingWidget.zoomHandle,'Direction','out'); + set(findobj('tag','toolbarZoomIn'),'State','off'); + end + %Toggle Pan Callback + function toolbarPan_ClickedCallback(this,hObject, eventdata) + set(this.ViewingWidget.panHandle,'Enable',char(get(hObject,'State'))); + end + %Toggle cursor Callback + function toolbarCursor_ClickedCallback(this,hObject, eventdata) + set(this.ViewingWidget.dcmHandle,'Enable',get(hObject,'State')); + end + + %Toggle cursor Callback + function gammaIndex_ClickedCallback(this,hObject, eventdata) + % this.GammaWidget = matRad_GammaWidget(); + end + + + % button: close + function figure1_CloseRequestFcn(this,hObject, ~) + matRad_cfg = MatRad_Config.instance(); + set(0,'DefaultUicontrolBackgroundColor',matRad_cfg.gui.backgroundColor); + selection = questdlg('Do you really want to close matRad?',... + 'Close matRad',... + 'Yes','No','Yes'); + + switch selection + case 'Yes' + delete(hObject); + delete(this); + case 'No' + return + end + end + + end + + +end + diff --git a/gui/matRad_Widget.m b/gui/matRad_Widget.m new file mode 100644 index 000000000..41e711983 --- /dev/null +++ b/gui/matRad_Widget.m @@ -0,0 +1,224 @@ +classdef matRad_Widget < handle + + % matRad_Widget Main Class for GUI widget generation + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + properties (GetAccess = public , SetAccess = protected) + widgetHandle %Holds parent widget handle + handles = struct([]); %Holds all handles in parent handle + end + + properties + updateLock = false; %Property to lock updating of the widget + end + + + events + %If the widget changes the workspace, this event should be emitted + %with notify(...). Other widgets can add listeners to update when + %this event is emitted + workspaceChanged + end + + methods + %CONSTRUCTOR + function this = matRad_Widget(handleParent) + this.widgetHandle = handleParent; + this.createLayout(); + this.initialize(); + + matRad_cfg = MatRad_Config.instance(); + + % only enable in matlab + %strcmp(env,'MATLAB') && + if matRad_cfg.isMatlab && strcmp(get(handleParent,'type'),'figure') + set(this.widgetHandle,'ButtonDownFcn',@(src,hEvent) update(this)); + set(this.widgetHandle,'KeyPressFcn',@(src,hEvent) update(this)); + end + end + + function set.handles(obj,handles) + obj.handles = handles; + end + + %INITIALIZE FUNCTION + function this = initialize(this) + this.update(); + end + + function changedWorkspace(this,varargin) + matRad_cfg = MatRad_Config.instance(); + % handle environment + switch matRad_cfg.env + case 'MATLAB' + %the PlanWidget only changes the pln + evt = matRad_WorkspaceChangedEventData(varargin{:}); + notify(this, 'workspaceChanged',evt); + case 'OCTAVE' + evt = matRad_WorkspaceChangedEvent(varargin{:}); + matRad_notifyOctave(this, 'workspaceChanged',evt); + end + end + + function this = update(this,evt) + end + + function handles = showError(this,Message,ME) + matRad_cfg = MatRad_Config.instance(); + handles = this.handles; + if nargin == 3 + %Add exception message + if isfield(handles,'devMode') && handles.devMode + meType = 'extended'; + else + meType = 'basic'; + end + Message = [Message,ME.message];%{Message,ME.getReport(meType,'hyperlinks','off')}; + end + + if isfield(handles,'ErrorDlg') + if ishandle(handles.ErrorDlg) + close(handles.ErrorDlg); + end + end + matRad_cfg.dispError(Message); + errordlg(Message); + this.handles = handles; + end + + function showWarning(this,Message,ME) + matRad_cfg = MatRad_Config.instance(); + + handles = this.handles; + if nargin == 3 + %Add exception message + if isfield(handles,'devMode') && handles.devMode + meType = 'extended'; + else + meType = 'basic'; + end + Message = {Message,ME.message}; + % Future error hyperlinks {Message,ME.getReport(meType,'hyperlinks','off')}; + end + matRad_cfg.dispWarning(Message); + this.handles = handles; + + end + + %function notifyUpdate(this,workSpaceVariables) + % notify(this,'workspaceChanged',workSpaceVariables); + %end + + function enableWindowCallback(this,enable) + + if strcmp(get(this.widgetHandle,'type'),'figure') && enable + set(this.widgetHandle,'ButtonDownFcn',@(src,hEvent) update(this)); + set(this.widgetHandle,'KeyPressFcn',@(src,hEvent) update(this)); + else + set(this.widgetHandle,'ButtonDownFcn',''); + set(this.widgetHandle,'KeyPressFcn',''); + end + end + end + + methods (Access = protected) + + %CREATE LAYOUT FUNCTION + function this = createLayout(this, handleParent) + end + + %Helper function to create handles structure from tags (similar to + %guihandles) + function this = createHandles(this) + %Iterate through all objects + all_h = findall(this.widgetHandle); + + for i = 1:numel(all_h) + this_h = all_h(i); + if isprop(this_h, 'Tag') + tag = get(this_h, 'Tag'); + if ~isempty(tag) && isvarname(tag) % can it be used as a fieldname? + + % if a field of this name already exists, get its contents + if isfield(this.handles, tag) + prev_h = this.handles.(tag); + else + prev_h = []; + end + + % append our handle to whatever was there before. If nothing + % was there before. + if isappdata(this_h, 'Control') + % if this uicontrol is a proxy for external controls, replace it with + % that control + control = getappdata(this_h, 'Control'); + this.handles(1).(tag) = [prev_h control.Instance]; + else + this.handles(1).(tag) = [prev_h this_h]; + end + + end % if legal tag + end + end % loop + end + + %Helper function to check if update is necessary when an event is + %passed + function [doUpdate,whichVars] = checkUpdateNecessary(this,updateVars,evt) + if isa(evt,'matRad_WorkspaceChangedEvent') + changed = evt.changedVariables; + else + changed = {}; + end + + + %If changed is empty, we need an update, if not, we crosscheck + doUpdate = true; + whichVars = []; + + if ~isempty(changed) + cmp = cellfun(@(str) any(strcmp(str,changed)),updateVars); + doUpdate = any(cmp); + if nargout == 2 + whichVars = find(cmp); %indices of variables + end + end + end + + %Helper function to position GUI elements on the Main Widget + function pos = computeGridPos(this,gridPos,buttonGridSize,buttonRelSize) + if nargin < 4 + buttonRelSize = [0.9 0.75]; + end + + gridElementSize = 1./buttonGridSize; + buttonRelSize = buttonRelSize ./ buttonGridSize; + + buttonGridPos = (gridPos-1) ./ buttonGridSize; + buttonGridPos(2) = 1 - gridElementSize(2) - buttonGridPos(2); + offset = (gridElementSize - buttonRelSize)./2; + + pos = [buttonGridPos+offset buttonRelSize]; + end + + end + +end + diff --git a/gui/matRad_WorkspaceChangedEvent.m b/gui/matRad_WorkspaceChangedEvent.m new file mode 100644 index 000000000..fd7c62998 --- /dev/null +++ b/gui/matRad_WorkspaceChangedEvent.m @@ -0,0 +1,47 @@ +classdef matRad_WorkspaceChangedEvent < handle +% matRad_WorkspaceChangedEvent class +% Base class to store event data (no subclass yet for compatability) +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + changedVariables %Cell array with changed variable names + end + + methods + function obj = matRad_WorkspaceChangedEvent(varargin) + %matRad_WorkspaceChangedEvent Construct the event + % varargin is the cell array of arguments (strings) + % containing the variable names + + %obj = obj@event.EventData(); + + obj.changedVariables = varargin; + + + %Debug: + %{ + if isempty(varargin) + changed = 'all'; + else + changed = strjoin(varargin,'|'); + end + fprintf('Changed variables: %s\n',changed); + %} + + end + + end +end + diff --git a/gui/matRad_WorkspaceChangedEventData.m b/gui/matRad_WorkspaceChangedEventData.m new file mode 100644 index 000000000..af0f44f21 --- /dev/null +++ b/gui/matRad_WorkspaceChangedEventData.m @@ -0,0 +1,42 @@ +classdef matRad_WorkspaceChangedEventData < matRad_WorkspaceChangedEvent & event.EventData +% Class matRad_WorkspaceChangedEventData +% EventData subclass to store changed variables when widget changes +% the workspace +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + methods + function obj = matRad_WorkspaceChangedEventData(varargin) + %matRad_WorkspaceChangedEvent Construct the event + % varargin is the cell array of arguments (strings) + % containing the variable names + + obj = obj@event.EventData(); + obj = obj@matRad_WorkspaceChangedEvent(varargin{:}); + + + %Debug: + %{ + if isempty(varargin) + changed = 'all'; + else + changed = strjoin(varargin,'|'); + end + fprintf('Changed variables: %s\n',changed); + %} + end + + end +end + diff --git a/gui/matRad_addListenerOctave.m b/gui/matRad_addListenerOctave.m new file mode 100644 index 000000000..7c112be24 --- /dev/null +++ b/gui/matRad_addListenerOctave.m @@ -0,0 +1,33 @@ +function retval = matRad_addListenerOctave (hSource,eventName,callback) +% matRad_addListenerOctave is a function that creates and accumulates new +% Listeners in eventMap +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + global eventMap; + + persistent warningprinted; + if isempty(warningprinted) + hSource.showWarning('You are using an experimental implementation of ''addlistener'' concept for Octave'); + warningprinted=2; + end + + newListener = struct('src',hSource,'event',eventName,'callback',callback); + if isempty(eventMap) + eventMap = newListener; + else + eventMap(end+1) = newListener; + end + retval=newListener; + +end diff --git a/gui/matRad_iconsGUI.mat b/gui/matRad_iconsGUI.mat new file mode 100644 index 000000000..1b48b1c40 Binary files /dev/null and b/gui/matRad_iconsGUI.mat differ diff --git a/gui/matRad_notifyOctave.m b/gui/matRad_notifyOctave.m new file mode 100644 index 000000000..b8ef2a15f --- /dev/null +++ b/gui/matRad_notifyOctave.m @@ -0,0 +1,56 @@ +function matRad_notifyOctave(hObject,eventName,evt) +% Experimental Function to notify on event for OCTAVE +% +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + global eventMap; + if isempty(eventMap) + return; + end + + persistent warningprinted; + if isempty(warningprinted) + hObject.showWarning('You are using an experimental implementation of ''notify'' concept for Octave'); + warningprinted=2; + end + + objHandles = {eventMap(:).src}; + + %clean Handles + validH = cellfun(@isa,objHandles,repmat({'handle'},size(objHandles))); %cellfun(@ishandle,objHandles); + objHandles = objHandles(validH); + eventMap = eventMap(validH); + + %Find the object + objIx = cellfun(@(h) isequal(hObject,h), objHandles); + + objEvents = eventMap(objIx); + + if isempty(objEvents) + return; + end + + allNames = {objEvents(:).event}; + eventIx = find(strcmp(eventName,allNames)); + + for runIx = 1:numel(eventIx) + runEventIx = eventIx(runIx); + runEvent = objEvents(runEventIx); + if nargin < 3 + runEvent.callback(hObject); + else + runEvent.callback(hObject,evt); + end +end diff --git a/gui/widgets/matRad_3DWidget.m b/gui/widgets/matRad_3DWidget.m new file mode 100644 index 000000000..6eeb3f335 --- /dev/null +++ b/gui/widgets/matRad_3DWidget.m @@ -0,0 +1,210 @@ +classdef matRad_3DWidget < matRad_ViewingWidget + + % matRad_3DWidget class to generate GUI widget for 3D plan visualization + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + viewingWidgetHandle; + end + + events + + end + + methods + %Constructor + function this = matRad_3DWidget(viewingWidgetHandle,handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 2 + handleParent = figure(... + 'Units','normalized',... + 'Position',[0.3 0.2 0.4 0.6],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad 3D',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + + end + + this = this@matRad_ViewingWidget(handleParent); + + if nargin >= 1 + this.viewingWidgetHandle=viewingWidgetHandle; + + end + this.lockUpdate = true; + this.update(); + + end + + function this=initialize(this) + + end + + function this=update(this,~) + if this.lockUpdate + if ~isempty(this.viewingWidgetHandle) && isvalid(this.viewingWidgetHandle) + this.lockUpdate=false; + p = properties(this.viewingWidgetHandle); + % copy all the properties of the viewingwidget except for the widgethandle + for k = 1:length(p) + if ~strcmp(p{k},'widgetHandle') && ~strcmp(p{k},'handles') && ~strcmp(p{k},'lockUpdate') + try + this.(p{k}) = this.viewingWidgetHandle.(p{k}); + catch + %warning('failed to copy property: %s', p{k}); + end + end + end + this.lockUpdate=true; + end + + this.plot3D(); + end + + end + + end + + methods(Access = protected) + function this = createLayout(this) + h88 = this.widgetHandle; + this.createHandles(); + + end + end + + methods + function plot3D(this) + + if evalin('base','exist(''pln'')') && ... + evalin('base','exist(''ct'')') && evalin('base','exist(''cst'')') + + ct = evalin('base','ct'); + cst = evalin('base','cst'); + pln = evalin('base','pln'); + + + if evalin('base','exist(''resultGUI'')') + Result = evalin('base','resultGUI'); + end + + if evalin('base','exist(''stf'')') + stf = evalin('base','stf'); + else + stf = []; + end + else + return + end + + axesFig3D=axes(this.widgetHandle); + view(axesFig3D,3); + oldView = get(axesFig3D,'View'); + + cla(axesFig3D); + + defaultFontSize = 8; + + %Check if we need to precompute the surface data + if size(cst,2) < 8 + cst = matRad_computeAllVoiSurfaces(ct,cst); + assignin('base','cst',cst); + end + + set(this.widgetHandle,'Color',0.5*[1 1 1]); + set(axesFig3D,'Color',1*[0 0 0]); + + %% Plot 3D structures + hold(axesFig3D,'on'); + if this.plotContour && exist('cst','var') && exist('ct','var') %get(handles.radiobtnContour,'Value') && handles.State>0 + voiPatches = matRad_plotVois3D(axesFig3D,ct,cst,this.VOIPlotFlag,colorcube); + end + + %% plot the CT slice + if this.plotCT %get(handles.radiobtnCT,'Value') + window = this.dispWindow{2,1}; %(2 for ct) + ctMap = matRad_getColormap(this.ctColorMap,this.cMapSize); + ctHandle = matRad_plotCtSlice3D(axesFig3D,ct,1,this.plane,this.slice,ctMap,window); + end + + %% plot the dose slice + if exist('Result','var') + doseMap = matRad_getColormap(this.doseColorMap,this.cMapSize); + doseIx = 3; + % if the selected display option doesn't exist then simply display + % the first cube of the Result struct + if ~isfield(Result,this.SelectedDisplayOption) + CubeNames = fieldnames(Result); + this.lockUpdate=false; + this.SelectedDisplayOption = CubeNames{1,1}; + this.lockUpdate=true; + end + + dose = Result.(this.SelectedDisplayOption); + + % dose colorwash + if ~isempty(dose) && ~isvector(dose) + +% if isempty(this.dispWindow{doseIx,2}) +% this.dispWindow{doseIx,2} = [min(dose(:)) max(dose(:))]; % set min and max dose values +% end + + if this.plotDose %get(handles.radiobtnDose,'Value') + [doseHandle,~,~] = matRad_plotDoseSlice3D(axesFig3D,ct,dose,this.plane,this.slice,this.CutOffLevel,this.doseOpacity,doseMap,this.dispWindow{doseIx,1}); + end + if this.plotIsoDoseLines %get(handles.radiobtnIsoDoseLines,'Value') + matRad_plotIsoDoseLines3D(axesFig3D,ct,dose,this.IsoDose_Contours,this.IsoDose_Levels,this.plane,this.slice,doseMap,this.dispWindow{doseIx,1},'LineWidth',1.5); + end + end + end + + if this.plotPlan %get(handles.radiobtnPlan,'Value') + matRad_plotPlan3D(axesFig3D,pln,stf); + end + + %hLight = light('Parent',axesFig3D); + %camlight(hLight,'left'); + %lighting('gouraud'); + + xlabel(axesFig3D,'x [voxels]','FontSize',defaultFontSize) + ylabel(axesFig3D,'y [voxels]','FontSize',defaultFontSize) + zlabel(axesFig3D,'z [voxels]','FontSize',defaultFontSize) + title(axesFig3D,'matRad 3D view'); + + % set axis ratio + ratios = [1 1 1]; %[1/ct.resolution.x 1/ct.resolution.y 1/ct.resolution.z]; + ratios = ratios([2 1 3]); + set(axesFig3D,'DataAspectRatioMode','manual'); + set(axesFig3D,'DataAspectRatio',ratios./max(ratios)); + + set(axesFig3D,'Ydir','reverse'); + + set(axesFig3D,'view',oldView); + + %this.handles = handles; + end + + end +end \ No newline at end of file diff --git a/gui/widgets/matRad_DVHStatsWidget.m b/gui/widgets/matRad_DVHStatsWidget.m new file mode 100644 index 000000000..8429ad1d1 --- /dev/null +++ b/gui/widgets/matRad_DVHStatsWidget.m @@ -0,0 +1,137 @@ +classdef matRad_DVHStatsWidget < matRad_Widget + + % matRad_DVHStatsWidget class to generate GUI widget display DVH and + % stats + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + properties + selectedDisplayOption; + lockUpdate = false; + dvhWidgetHandle = []; + statWidgetHandle = []; + end + + methods + function this = matRad_DVHStatsWidget(selectedDisplayOption,handleParent) + + + matRad_cfg = MatRad_Config.instance(); + if nargin < 2 + + handleParent = figure(... + 'Units','normalized',... + 'OuterPosition',[0 0 0.5 1],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','figure',... + 'ToolBar','figure',... + 'Name','MatRad Plan Analysis',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figDVHStat'); + + end + this = this@matRad_Widget(handleParent); + this.selectedDisplayOption = selectedDisplayOption; + this.dvhWidgetHandle.selectedCube = selectedDisplayOption; + this.statWidgetHandle.selectedCube = selectedDisplayOption; + this.lockUpdate = true; + this.dvhWidgetHandle.lockUpdate = true; + this.statWidgetHandle.lockUpdate = true; + this.update(); + + end + + function this=update(this,evt) + if this.lockUpdate + if nargin == 2 + doUpdate = this.checkUpdateNecessary({'resultGUI','cst','pln'},evt); + + if doUpdate + this.dvhWidgetHandle.update(evt); + this.statWidgetHandle.update(evt); + end + else + %Check for change in the selected cube + if ~strcmp(this.dvhWidgetHandle.selectedCube, this.selectedDisplayOption) + this.dvhWidgetHandle.selectedCube = this.selectedDisplayOption; + this.statWidgetHandle.selectedCube = this.selectedDisplayOption; + + end + this.dvhWidgetHandle = this.dvhWidgetHandle.update(); + this.statWidgetHandle = this.statWidgetHandle.update(); + %Clear previous DVH and stat + if numel(this.dvhWidgetHandle.widgetHandle.Children) > 2 + this.removeOverlap(); + end + + end + end + end + function set.selectedDisplayOption(this,value) + this.selectedDisplayOption = value; + this.update(); + + end + function removeOverlap(this) + % Clear previous plotted objects + delete(this.dvhWidgetHandle.widgetHandle.Children(3)); + + end + end + + methods(Access = protected) + function this = createLayout(this) + h88 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + %DVH Panel + p1 = uipanel(... + 'Parent',h88,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','panelDVH',... + 'Clipping','off',... + 'Position',[0.005 0.505 0.99 0.495],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Title','DVH'); + % Statistics panel + p2 = uipanel(... + 'Parent',h88,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','panelStats',... + 'Clipping','off',... + 'Position',[0.005 0.005 0.99 0.495],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Title','Statistics'); + + %Initiate DVH and Stats Widgets + this.dvhWidgetHandle = matRad_DVHWidget([],p1); + this.statWidgetHandle = matRad_StatisticsWidget([],p2); + this.createHandles(); + + end + + end +end + diff --git a/gui/widgets/matRad_DVHWidget.m b/gui/widgets/matRad_DVHWidget.m new file mode 100644 index 000000000..c045231b5 --- /dev/null +++ b/gui/widgets/matRad_DVHWidget.m @@ -0,0 +1,106 @@ +classdef matRad_DVHWidget < matRad_Widget + % matRad_DVHWidget class to generate GUI widget to display DVH + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + properties + selectedCube; + lockUpdate = false; + + end + + methods + function this = matRad_DVHWidget( SelectedCube,handleParent) + + matRad_cfg = MatRad_Config.instance(); + if nargin<2 + handleParent = figure(... + 'Units','normalized',... + 'Position',[0.005 0.5 0.495 0.45],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','on',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad DVH',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figDVH',... + 'PaperSize',[20.99999864 29.69999902]); + + end + this = this@matRad_Widget(handleParent); + this.selectedCube = SelectedCube; + + end + + function this=initialize(this) + end + + function this=update(this,evt) + + if this.lockUpdate + doUpdate = true; + if nargin == 2 + doUpdate = this.checkUpdateNecessary({'resultGUI','cst','pln'},evt); + end + + if doUpdate && evalin('base','exist(''resultGUI'')') && evalin('base','exist(''cst'')') + this.showDVH(); + if numel(this.widgetHandle.Children) > 2 + this.removeOverlap(); + end + end + end + end + + function removeOverlap(this) + %Clear previous plotted objects from the figure + delete(this.widgetHandle.Children(3)); + end + + + end + + methods(Access = protected) + function this = createLayout(this) + h88 = this.widgetHandle; + this.createHandles(); + + end + end + + methods + + function set.selectedCube(this,value) + this.selectedCube=value; + end + + function showDVH(this) + + resultGUI = evalin('base','resultGUI'); + pln = evalin('base','pln'); + cst = evalin('base','cst'); + % Calculate and show DVH + doseCube = resultGUI.(this.selectedCube); + dvh = matRad_calcDVH(cst,doseCube,'cum'); + matRad_showDVH(axes(this.widgetHandle),dvh,cst,pln); + this.widgetHandle.Children(2).Title.String = strrep(this.selectedCube, '_',' '); + end + + end +end \ No newline at end of file diff --git a/gui/widgets/matRad_GammaWidget.m b/gui/widgets/matRad_GammaWidget.m new file mode 100644 index 000000000..1edb9a3bb --- /dev/null +++ b/gui/widgets/matRad_GammaWidget.m @@ -0,0 +1,544 @@ +classdef matRad_GammaWidget < matRad_Widget + % matRad_GammaWidget : GUI widget for gamma index based comparisons of + % dose cubes stored within resultGUI struct. + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + properties + SelectedDisplayOption1 = 'physicalDose' ; + SelectedDisplayOption2 = 'physicalDose' ; + SelectedDisplayAllOptions = strings; + criteria = [3 3]; + n = 0; + localglobal = 'global'; + resolution; + lockUpdate = false; + maxSlice; + slice; + gammaCube; + gammaPassRateCell; + updated = false; + + end + properties (Constant) + normalization = {'local','global'} ; + end + + events + + end + + methods + function this = matRad_GammaWidget(handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 2 + handleParent = figure(... + 'Units','normalized',... + 'Position',[0.1 0.1 0.7 0.7],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Gamma Analysis',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','GammaWidget'); + + end + this = this@matRad_Widget(handleParent); + + this.initialize(); + this.update(); + end + + function this = initialize(this) + if evalin( 'base', 'exist(''resultGUI'')' ) + resultGUI = evalin('base','resultGUI'); + resultnames = fieldnames(resultGUI) ; + j = 1; + for i = 1:numel(resultnames) + if ndims(resultGUI.(resultnames{i}))==3 + this.SelectedDisplayAllOptions(j) = resultnames{i}; + j=j+1; + end + end + % get and set display options + this.SelectedDisplayAllOptions = pad(this.SelectedDisplayAllOptions); + set(this.handles.popupSelectedDisplayOption1,'String',this.SelectedDisplayAllOptions); + set(this.handles.popupSelectedDisplayOption2,'String',this.SelectedDisplayAllOptions); + this.maxSlice = size(resultGUI.physicalDose,3); + this.slice = round(this.maxSlice/2); + end + if evalin( 'base', 'exist(''ct'')' ) + ct = evalin('base','ct'); + this.resolution = [ct.resolution.x, ct.resolution.y, ct.resolution.z]; + set(this.handles.editResolution,'String', regexprep(num2str(this.resolution),'\s+',' ')); + end + + set(this.handles.editGammaCrit,'String', regexprep(num2str(this.criteria),'\s+',' ')); + + end + + function this = update (this,~) + + if evalin( 'base', 'exist(''resultGUI'')' ) && this.lockUpdate + resultGUI = evalin('base','resultGUI'); + resultnames = fieldnames(resultGUI) ; + j = 1; + for i = 1:numel(resultnames) + if ndims(resultGUI.(resultnames{i}))==3 + this.SelectedDisplayAllOptions(j) = resultnames{i}; + j=j+1; + end + end + % get and set display options + this.SelectedDisplayAllOptions = pad(this.SelectedDisplayAllOptions); + set(this.handles.popupSelectedDisplayOption1,'String',this.SelectedDisplayAllOptions); + set(this.handles.popupSelectedDisplayOption2,'String',this.SelectedDisplayAllOptions); + + %slider options %CAN ALSO SET MIN AND MAX TO NONZERO SLICES + + if size(resultGUI.(this.SelectedDisplayOption1),3) == size(resultGUI.(this.SelectedDisplayOption2),3) + this.maxSlice = size(resultGUI.(this.SelectedDisplayOption1),3); + this.slice = round(this.maxSlice/2); + set(this.handles.sliderSlice,'Min',1,'Max',this.maxSlice,... + 'Value', this.slice, ... + 'SliderStep',[1 1]); + else + error('Mismatch in dimensions of selected cubes') + end + + + this.calcGamma(); + this.plotGamma(); + this.lockUpdate = false; + end + + end + % METHOD FOR WHEN WORKSPACE IS CHANGED + + function set.SelectedDisplayOption1(this, value) + this.SelectedDisplayOption1 = value; + this.lockUpdate = true; + this.update(); + end + + function set.SelectedDisplayOption2(this, value) + this.SelectedDisplayOption2 = value; + this.lockUpdate = true; + this.update(); + end + + function set.resolution(this,value) + this.resolution = value; + this.lockUpdate = true; + this.update(); + end + + function set.criteria(this,value) + this.criteria = value; + this.lockUpdate = true; + this.update(); + end + + function set.localglobal(this,value) + this.localglobal = value; + this.lockUpdate = true; + this.update(); + end + function set.n(this,value) + this.n = value; + this.lockUpdate = true; + this.update(); + end + + + end + + methods (Access = protected) + + function this = createLayout(this) + h20 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + %Create Main Grid layout + gridSize = [6 20]; + elSize = [0.9 0.6]; + [i,j] = ndgrid(1:gridSize(1),1:gridSize(2)); + gridPos = arrayfun(@(i,j) computeGridPos(this,[i j],gridSize,elSize),i,j,'UniformOutput',false); + + %Text for cube 1 + txt = sprintf('Choose Reference Cube from ResultGUI'); + h21 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Reference cube 1:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{3,1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtCube1',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + %Popup menu for cube 1 + h22 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Please select ...',... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{4,1},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)popupSelectedDisplayOption1_Callback(this,hObject,eventdata),... + 'Tag','popupSelectedDisplayOption1',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text for Cube 2 + txt = sprintf('Choose Reference Cube from ResultGUI'); + h23 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Reference cube 2:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{5,1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtCube2',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Popup menu for cube 2 + h24 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Please select ...',... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{6,1},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)popupSelectedDisplayOption2_Callback(this,hObject,eventdata),... + 'Tag','popupSelectedDisplayOption2',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text for Gamma Criteria + txt = sprintf('Gamma Criteria [mm %%]'); + h25 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Gamma Criteria [mm %]:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,3},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtGammaCrit',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Edit gamma Criteria + h26 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','0 0',... + 'Tooltip',txt,... + 'Style','edit',... + 'Position',gridPos{1,4},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)editGammaCrit_Callback(this,hObject,eventdata),... + 'Tag','editGammaCrit',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text for resolution + txt = sprintf('Resolution of cube [mm/voxel]'); + h27 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Resolution of cube [mm/voxel]:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,5},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtResolution',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + % Edit Resolution + h28 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','0 0 0',... + 'Tooltip',txt,... + 'Style','edit',... + 'Position',gridPos{1,6},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)editResolution_Callback(this,hObject,eventdata),... + 'Tag','editResolution',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text for Interpolation + txt = sprintf('Number of interpolations, max suggested value is 3'); + h29 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Number of interpolations n:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,7},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtInterpolations',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Edit Interpolations + h30 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','0',... + 'Tooltip',txt,... + 'Style','edit',... + 'Position',gridPos{1,8},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)editInterpolations_Callback(this,hObject,eventdata),... + 'Tag','editInterpolations',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text Normalizations + txt = sprintf('local and global normalizations'); + h31 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Type of normalization:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,9},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtInterpolations',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Normalization + h32 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String',this.normalization,... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{1,10},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)popupNormalization_Callback(this,hObject,eventdata),... + 'Tag','popupNormalization',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text Slices + txt = sprintf('Choose which slice should be displayed in intensity plots'); + h33 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Slice',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,11},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtInterpolations',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Slice Slider + h34 = uicontrol(... + 'Parent',h20,... + 'Units','normalized',... + 'String','Slider',... + 'Tooltip','Choose which slice should be displayed in intensity plots',... + 'Style','slider',... + 'Callback',@(hObject,eventdata) sliderSlice_Callback(this,hObject,eventdata),... + 'BusyAction','cancel',... + 'Interruptible','off',... + 'Position',gridPos{1,12},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','sliderSlice'); + + p1 = uipanel(... + 'Parent',h20,... + 'Units','normalized',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','panelGammaIdx',... + 'Clipping','off',... + 'Position',[0.22 0.01 0.7 0.9],... + 'FontName',matRad_cfg.gui.fontName,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Title','Gamma Analysis'); + % TODO Future: scroll control + + this.createHandles(); + end + + end + methods (Access = private) + %CALLBACKS + function popupNormalization_Callback(this, hObject, eventdata) + contents = cellstr(get(hObject,'String')); + this.localglobal = contents{get(hObject,'Value')}; + + end + function popupSelectedDisplayOption1_Callback(this,hObject,eventdata) + contents = cellstr(get(hObject,'String')); + this.SelectedDisplayOption1 = strtrim(contents{get(hObject,'Value')}); + + end + function popupSelectedDisplayOption2_Callback(this,hObject,eventdata) + contents = cellstr(get(hObject,'String')); + this.SelectedDisplayOption2 = strtrim(contents{get(hObject,'Value')}); + + end + + function editResolution_Callback(this, hObject, ~) + t = sscanf (get(hObject,'String'), '%f'); + if numel(t) ~=3 + error('Resolution value error') + else + this.resolution = t; + end + end + + function editGammaCrit_Callback(this, hObject, ~) + t = sscanf (get(hObject,'String'), '%f'); + if numel(t) ~=2 + error('Gamma Criterion value error') + else + this.criteria = t; + end + end + + function editInterpolations_Callback(this, hObject, ~) + t = str2double(get(hObject,'String')); + if ~isnumeric(t) + error('Number of Interpolations value error') + else + this.n = t; + end + end + + function sliderSlice_Callback(this,hObject, ~) + % hObject handle to sliderSlice (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'Value') returns position of slider + % get(hObject,'Min') and get(hObject,'Max') to determine range of slider + %UpdatePlot(handles) + + this.slice = round(get(hObject,'Value')); + this.plotGamma(); + end + + + end + + + methods + + %Calculate Gamma + function calcGamma(this) + if evalin( 'base', 'exist(''resultGUI'')' ) + resultGUI = evalin('base','resultGUI'); + else + % no result cube + end + if evalin( 'base', 'exist(''cst'')' ) + cst = evalin('base','cst'); + else + %no cst + end + + [this.gammaCube,this.gammaPassRateCell] = matRad_gammaIndex(resultGUI.(this.SelectedDisplayOption1) ,resultGUI.(this.SelectedDisplayOption2),... + this.resolution,this.criteria,[],this.n,this.localglobal,cst); + end + + %Show Gamma + function plotGamma(this) + this.widgetHandle; + % visualize if applicable + + if ~isempty(this.slice) && ~isempty(this.gammaCube) + if isempty(this.handles.panelGammaIdx(1).Children) + ax = axes('Parent',this.handles.panelGammaIdx); + else + % overwrite previous plot in the panel + ax = this.handles.panelGammaIdx(1).Children(2); + + end + % ax = figure('Parent',this.handles.panelGammaIdx); + % set(ax,'Color',[1 1 1]); + imagesc(ax, this.gammaCube(:,:,this.slice)) + myColormap = matRad_getColormap('gammaIndex'); + + set(ax,'colormap',myColormap); + + colorbar(ax); + titletxt = {[num2str(this.gammaPassRateCell{1,2},5) '% of points > ' num2str(this.criteria(1)) ... + '% pass gamma criterion (' num2str(this.criteria(1)) '% / ' ... + num2str(this.criteria(2)) 'mm)']; ['with ' num2str(2^this.n-1) ' interpolation points']}; + title(ax, titletxt); + % set(this.handles.panelGammaIdx(1).Children(2),'Title',titletxt); + % this.createHandles(); + end + end + + end + +end diff --git a/gui/widgets/matRad_InfoWidget.m b/gui/widgets/matRad_InfoWidget.m new file mode 100644 index 000000000..a4d97df72 --- /dev/null +++ b/gui/widgets/matRad_InfoWidget.m @@ -0,0 +1,144 @@ +classdef matRad_InfoWidget < matRad_Widget + % matRad_InfoWidget class to generate GUI widget to display system and + % version information + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + + end + + methods + function this = matRad_InfoWidget(handleParent) + if nargin < 1 + matRad_cfg = MatRad_Config.instance(); + handleParent = figure(... + 'Units','normalized',... + 'Position',[0.45 0.45 0.1 0.1],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'MenuBar','none',... + 'Name','matRad Info',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + end + this = this@matRad_Widget(handleParent); + end + end + + methods (Access = protected) + function this = createLayout(this) + h94 = this.widgetHandle; + matRad_cfg = MatRad_Config.instance(); + txt = sprintf('Info about\nsoftware environment & version\nmatRad version & branch'); + %About button + h95 = uicontrol(... + 'Parent',h94,... + 'Units','normalized',... + 'String','About',... + 'Tooltip', txt,... + 'Position',[0.2 0.14 0.6 0.28],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnAbout_Callback(this,hObject,eventdata),... + 'Tag','btnAbout',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName); + + %Position String + h96 = uicontrol(... + 'Parent',h94,... + 'Units','normalized',... + 'Style','text',... + 'Position',[0.1 0.75 0.8 0.2],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','text15',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName); + + %URL Position String + h97 = uicontrol(... + 'Parent',h94,... + 'Units','normalized',... + 'Style','text',... + 'Position',[0.05 0.5 0.9 0.17],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Tag','text31',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight','bold'); + + this.createHandles(); + handles=this.handles; + %Alter matRad Version string positioning + vString = matRad_version(); + vPos = get(handles.text15,'Position'); + urlPos = get(handles.text31,'Position'); + btnPos = get(handles.btnAbout,'Position'); + + %vPos([1 3]) = urlPos([1 3]); + vPos([1 3]) = [0 1]; + vPos(4) = vPos(4)*1.25; + btnPos(2) = 0.05; + urlPos(2) = btnPos(2)+btnPos(4)+0.05; + vPos(2) = urlPos(2) + urlPos(4) + 0.05; + vPos(4) = 0.98 - vPos(2); + + set(handles.btnAbout,'Position',btnPos); + set(handles.text31,'String','www.matRad.org','Position',urlPos,'Enable','inactive','ButtonDownFcn', @(~,~) web('www.matrad.org','-browser')); + set(handles.text15,'String',vString,'Position',vPos); + this.handles=handles; + end + end + + methods (Access = protected) + function btnAbout_Callback(this, hObject, event) + handles = this.handles; + %msgbox({'https://github.com/e0404/matRad/' 'email: matrad@dkfz.de'},'About'); + + matRad_cfg = MatRad_Config.instance(); + [~,matRadVer] = matRad_version; + + msg{1} = ['matRad ''' matRadVer.name '''']; %Name + if matRad_cfg.eduMode + msg{1} = [msg{1} ' Educational']; + end + msg{end+1} = sprintf('v%d.%d.%d',matRadVer.major,matRadVer.minor,matRadVer.patch); %Version Number + if isdeployed + msg{end+1} = 'Standalone Version'; + elseif ~isempty(matRadVer.branch) && ~isempty(matRadVer.commitID) + msg{end+1} = sprintf('Git: Branch %s, commit %s',matRadVer.branch,matRadVer.commitID(1:8)); + end + + msg{end+1} = sprintf('Environment: %s v%s %s',matRad_cfg.env,matRad_cfg.envVersion,version('-release')); + + msg{end+1} = 'Web: www.matrad.org'; + msg{end+1} = 'E-Mail: contact@matrad.org'; + + msg{end+1} = 'MATRAD IS NOT A MEDICAL PRODUCT AND THEREFORE NOT SUITABLE FOR CLINICAL USE!'; + + msgbox(msg,'About matRad'); + + + this.handles = handles; + end + end +end diff --git a/gui/widgets/matRad_LogoWidget.m b/gui/widgets/matRad_LogoWidget.m new file mode 100644 index 000000000..1fee45b12 --- /dev/null +++ b/gui/widgets/matRad_LogoWidget.m @@ -0,0 +1,114 @@ +classdef matRad_LogoWidget < matRad_Widget + % matRad_InfoWidget class to display GUI logo widget + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + end + + methods + function this = matRad_LogoWidget(handleParent) + + if nargin < 1 + handleParent = figure(... + 'MenuBar','none',... + 'Units','pixels',... + 'Position',[500 500 1000 200],... + 'Color',[0.5 0.5 0.5],... + 'Name','MatRad Logo',... + 'HandleVisibility','callback',... + 'Tag','figure_importDialog',... + 'WindowStyle','normal',... 'PaperSize',[8.5 11],... + 'PaperType','usletter'); + end + this = this@matRad_Widget(handleParent); + end + + + end + + methods (Access = protected) + function this = createLayout(this) + %mfile = which(mfilename); + %[filepath] = fileparts(mfile); + + matRad_cfg = MatRad_Config.instance(); + + if isdeployed + filepath = [ctfroot filesep 'matRad']; + else + filepath = matRad_cfg.matRadRoot; + end + + h1 = this.widgetHandle; + + + %matRad Logo + [im, ~, alpha] = imread([filepath filesep 'gfx' filesep 'matrad_logo.png']); + dim = size(im); + + h2 = axes(... + 'Parent',h1,... + 'Units','normalized',... + 'FontName','CMU Serif',... + 'Position',[0 0.2 0.4 0.8],...'Position',[- 0.304874274661509 -0.12225992317542 0.994397163120567 1.048719590268886],... + 'SortMethod','childorder',... + 'Tag','axesLogo'); + + f = image(im,'Parent',h2); + axis(h2,'image','off'); + set(f, 'AlphaData', alpha); + + + %dkfz logo + [im, ~, alpha] = imread([filepath filesep 'gfx' filesep 'DKFZ_Logo.png']); + + h7 = axes(... + 'Parent',h1,... + 'Units','normalized',... + 'FontName','CMU Serif',... + 'Position',[0.4 0.15 0.6 0.85],... + 'SortMethod','childorder',... + 'Tag','axesDKFZ'); + + + f = image(im,'Parent',h7); + axis(h7,'image','off'); + axis(h7,'tight'); + set(f, 'AlphaData', alpha); + + hDisc = uicontrol('Parent',h1,... + 'Style','Text',... + 'Units','Normalized',... + 'Position',[0 0.05 1 0.15],... + 'String','matRad is not a medical product! DO NOT USE IT CLINICALLY!',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + this.createHandles(); + + end + end + + % NO CALLBACKS +end + + diff --git a/gui/widgets/matRad_OptimizationWidget.m b/gui/widgets/matRad_OptimizationWidget.m new file mode 100644 index 000000000..658964c0a --- /dev/null +++ b/gui/widgets/matRad_OptimizationWidget.m @@ -0,0 +1,685 @@ +classdef matRad_OptimizationWidget < matRad_Widget + % matRad_OptimizationWidget class to generate GUI widget to set + % optimization options + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + end + + methods + function this = matRad_OptimizationWidget(handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 1 + handleParent = figure(... + 'Units','characters',... + 'Position',[60 20 150 20],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Optimization',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + + + end + this = this@matRad_Widget(handleParent); + + this.update(); + end + + function this=initialize(this) + + end + + function this = update(this,evt) + + doUpdate = true; + if nargin == 2 + %At pln changes and at cst/cst (for Isocenter and new settings) + %we need to update + doUpdate = this.checkUpdateNecessary({'cst'},evt); + end + + if doUpdate + if evalin('base','exist(''ct'')') && evalin('base','exist(''cst'')') + generateCstTable(this, evalin('base','cst')); + else + delete(get(this.widgetHandle,'Children')); + end + end + + end + + end + + methods (Access = protected) + function this = createLayout(this) + h1 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + % handle environment + switch matRad_cfg.env + case 'MATLAB' + set(h1,'SizeChangedFcn',@(hObject,eventdata) widget_SizeChangedFcn(this,hObject,eventdata)); + case 'OCTAVE' + % this creates an infinite loop in octave + %set(h1,'SizeChangedFcn',@(hObject,eventdata) widget_SizeChangedFcn(this,hObject,eventdata)); + end + + this.createHandles(); + + end + end + + methods(Access = private) + + function cst = generateCstTable(this,cst) + matRad_cfg = MatRad_Config.instance(); + + %cst = updateStructureTable(this,cst); + handles = this.handles; + cstPanel = this.widgetHandle; + + cstPanelPos = get(cstPanel,'Position'); + cstPanelPosUnit = get(cstPanel,'Units'); + set(cstPanel,'Units','pixels'); + cstPanelPosPix = get(cstPanel,'Position'); + set(cstPanel,'Units',cstPanelPosUnit); + aspectRatio = cstPanelPosPix(3) / cstPanelPosPix(4); + + %Parameters for line height + objHeight = 0.095;% 22; + lineHeight = 0.1; %25; %Height of a table line + yTopSep = 0.12;%40; %Separation of the first line from the top + %tableViewHeight = cstPanelPos(4) - yTopSep; %Full height of the view + tableViewHeight = 1 - yTopSep; + + %Widths of the fields + buttonW = objHeight / aspectRatio; % Make button squared + nameW = 3*buttonW;%60; + typeW = 3*buttonW;%70; + opW = buttonW;%objHeight; + robustW = 3*buttonW; + functionW = 5*buttonW;%120; + penaltyW = 1.5*buttonW;%40; + paramTitleW = 4*buttonW;%120; + paramW = 1*buttonW;%30; + fieldSep = 0.25*buttonW; %Separation between fields horizontally + + %Scrollbar + cstPanelChildren = get(cstPanel,'Children'); + cstVertTableScroll = findobj(cstPanelChildren,'Style','slider'); + if isempty(cstVertTableScroll) + sliderPos = 0; + else + sliderPos = get(cstVertTableScroll,'Max') - get(cstVertTableScroll,'Value'); + end + %disp(num2str(sliderPos)); + ypos = @(c) tableViewHeight - c*lineHeight + sliderPos; + + delete(cstPanelChildren); + + %Creates a dummy axis to allow for the use of textboxes instead of uicontrol to be able to use the (la)tex interpreter + tmpAxes = axes('Parent',cstPanel,'units','normalized','position',[0 0 1 1],'visible','off', 'FontSize',8); + + organTypes = {'OAR', 'TARGET','IGNORED'}; + + %Get all Classes & classNames + classNames = matRad_getObjectivesAndConstraints(); + + %Get robustness version + robustObj = {'none','STOCH','VWWC','VWWC_INV','COWC','OWC','PROB'}; + + numOfObjectives = 0; + for i = 1:size(cst,1) + if ~isempty(cst{i,6}) + numOfObjectives = numOfObjectives + numel(cst{i,6}); + end + end + %line + % step count + cnt = 0; + + newline = '\n'; + + %Setup Headlines + xPos = 0.01; %5 + + h1 = uicontrol(cstPanel,'Style','text', ... + 'String','+/-', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) buttonW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip','Remove or add Constraint or Objective', ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h1,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h2 = uicontrol(cstPanel,'Style','text', ... + 'String','VOI name', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) nameW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip','Name of the structure with objective/constraint', ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h2,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h3 = uicontrol(cstPanel,'Style','text', ... + 'String','VOI type', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) typeW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip','Segmentation Classification', ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h3,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + txt = sprintf('Overlap Priority\n(Smaller number overlaps higher number)'); + h4 = uicontrol(cstPanel,'Style','text', ... + 'String','OP', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) opW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip',txt, ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h4,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + txt = sprintf('Type of robustness objective'); + h5 = uicontrol(cstPanel,'Style','text', ... + 'String','Robustness', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) robustW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip',txt, ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h5,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h6 = uicontrol(cstPanel,'Style','text', ... + 'String','Function', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) functionW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip','Objective/Constraint function type', ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h6,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h7 = uicontrol(cstPanel,'Style','text', ... + 'String','p', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) penaltyW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip','Optimization penalty', ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h7,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h8 = uicontrol(cstPanel,'Style','text', ... + 'String','| Parameters', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) paramTitleW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'Tooltip','List of parameters', ... + 'HorizontalAlignment','left', ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor); + + tmp_pos = get(h8,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + cnt = cnt + 1; + + %Create Objectives / Constraints controls + for i = 1:size(cst,1) + if strcmp(cst(i,3),'IGNORED')~=1 + %Compatibility Layer for old objective format + if isstruct(cst{i,6}) + cst{i,6} = num2cell(arrayfun(@matRad_DoseOptimizationFunction.convertOldOptimizationStruct,cst{i,6})); + end + for j=1:numel(cst{i,6}) + + obj = cst{i,6}{j}; + + %Convert to class if not + if ~isa(obj,'matRad_DoseOptimizationFunction') + try + obj = matRad_DoseOptimizationFunction.createInstanceFromStruct(obj); + catch ME + matRad_cfg.dispWarning('Objective/Constraint not valid!\n%s',ME.message) + continue; + end + end + + xPos = 0.01;%5; + + h = uicontrol(cstPanel,'Style','pushbutton', ... + 'String','-', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) buttonW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Tooltip','Remove Objective/Constraint', ... + 'Callback',@(hObject,eventdata)btObjRemove_Callback(this,hObject,eventdata),... + 'UserData',[i,j]); + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h = uicontrol(cstPanel','Style','edit', ... + 'String',cst{i,2}, ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) nameW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight,'BackgroundColor',matRad_cfg.gui.elementColor,'ForegroundColor',matRad_cfg.gui.textColor,'Tooltip','Name',... + 'Enable','inactive',... %Disable editing of name atm + 'UserData',[i,2], ... + 'Callback',@(hObject,eventdata)editCstParams_Callback(this,hObject,eventdata)); %Callback added, however, editing is disabled atm + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h = uicontrol(cstPanel,'Style','popupmenu', ... + 'String',organTypes', ... + 'Value',find(strcmp(cst{i,3},organTypes)), ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) typeW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Tooltip','Segmentation Classification',... + 'UserData',[i,3], ... + 'Callback',@(hObject,eventdata)editCstParams_Callback(this,hObject,eventdata)); + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + txt = sprintf('Overlap Priority\n(Smaller number overlaps higher number)'); + h = uicontrol(cstPanel,'Style','edit', ... + 'String',num2str(cst{i,5}.Priority), ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) opW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Tooltip',txt,... + 'UserData',[i,5], ... + 'Callback',@(hObject,eventdata)editCstParams_Callback(this,hObject,eventdata)); + + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h = uicontrol(cstPanel,'Style','popupmenu', ... + 'String',robustObj, ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) robustW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Tooltip','Select Objective/Constraint',... + 'UserData',{[i,j],classNames(1,:)}, ... + 'Callback',@(hObject,eventdata)changeRobustness_Callback(this,hObject,eventdata)); + + if isfield(cst{i,6}{j},'robustness') + set(h,'Value',find(strcmp(cst{i,6}{j}.robustness,robustObj))); + else + set(h, 'Value',find(strcmp('none',robustObj))); + end + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h = uicontrol(cstPanel,'Style','popupmenu', ... + 'String',classNames(2,:)', ... + 'Value',find(strcmp(obj.name,classNames(2,:))), ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) functionW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Tooltip','Select Objective/Constraint',... + 'UserData',{[i,j],classNames(1,:)}, ... + 'Callback',@(hObject,eventdata)changeObjFunction_Callback(this,hObject,eventdata)); + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + + %Check if we have an objective to display penalty + if isa(obj,'DoseObjectives.matRad_DoseObjective') + h = uicontrol(cstPanel,'Style','edit', ... + 'String',num2str(obj.penalty), ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) penaltyW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Tooltip','Objective Penalty', ... + 'UserData',[i,j,0],... + 'Callback',@(hObject,eventdata)editObjParam_Callback(this,hObject,eventdata)); + else + h = uicontrol(cstPanel,'Style','edit','String','----','Units','normalized','Position',[xPos ypos(cnt) penaltyW objHeight], 'FontSize',matRad_cfg.gui.fontSize,'FontName',matRad_cfg.gui.fontName,'FontWeight',matRad_cfg.gui.fontWeight,'BackgroundColor',matRad_cfg.gui.elementColor,'ForegroundColor',matRad_cfg.gui.textColor,'Enable','off'); + end + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + + for p = 1:numel(obj.parameterNames) +% h = text('Parent',tmpAxes,'String',['| ' obj.parameterNames{p} ':'],'VerticalAlignment','middle','Units','normalized','Position',[xPos ypos(cnt)+lineHeight/2],'Interpreter','tex','FontWeight','normal',... +% 'FontSize',get(cstPanel,'FontSize'),'FontName',get(cstPanel,'FontName'),'FontUnits',get(cstPanel,'FontUnits'),'FontWeight','normal');%[xPos ypos(cnt) 100 objHeight]); + % there is no fontsize for cstPanel + h = text('Parent',tmpAxes, ... + 'String',['| ' obj.parameterNames{p} ':'], ... + 'VerticalAlignment','middle', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt)+lineHeight/2], ... + 'Interpreter','tex', ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'Color',matRad_cfg.gui.textColor);%[xPos ypos(cnt) 100 objHeight]); + + tmp_pos = get(h,'Extent'); + xPos = xPos + tmp_pos(3) + fieldSep; + %h = annotation(cstPanel,'textbox','String',obj.parameters{1,p},'Units','pix','Position', [xPos ypos(cnt) 100 objHeight],'Interpreter','Tex'); + + %Check if we have a cell and therefore a parameter list + if iscell(obj.parameterTypes{p}) + h = uicontrol(cstPanel,'Style','popupmenu', ... + 'String',obj.parameterTypes{p}', ... + 'Value',obj.parameters{p}, ... + 'Tooltip',obj.parameterNames{p}, ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) paramW*2 objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'UserData',[i,j,p],... + 'Callback',@(hObject,eventdata)editObjParam_Callback(this,hObject,eventdata)); + else + h = uicontrol(cstPanel,'Style','edit', ... + 'String',num2str(obj.parameters{p}), ... + 'Tooltip',obj.parameterNames{p}, ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) paramW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'UserData',[i,j,p],... + 'Callback',@(hObject,eventdata)editObjParam_Callback(this,hObject,eventdata)); + end + + tmp_pos = get(h,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + end + + cnt = cnt +1; + end + end + end + xPos = 0.01; %5 + hAdd = uicontrol(cstPanel,'Style','pushbutton', ... + 'String','+', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) buttonW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tooltip','Add Objective/Constraint', ... + 'Callback',@(hObject,eventdata)btObjAdd_Callback(this,hObject,eventdata)); %{@btObjAdd_Callback,handles}); + tmp_pos = get(hAdd,'Position'); + xPos = xPos + tmp_pos(3) + fieldSep; + h = uicontrol(cstPanel,'Style','popupmenu', ... + 'String',cst(:,2)', ... + 'Units','normalized', ... + 'Position',[xPos ypos(cnt) nameW objHeight], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.backgroundColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tooltip','String describing the VOI'); + set(hAdd,'UserData',h); + + %Calculate Scrollbar + lastPos = ypos(cnt); + firstPos = ypos(0); + tableHeight = abs(firstPos - lastPos); + + exceedFac = tableHeight / tableViewHeight; + if exceedFac > 1 + sliderFac = exceedFac - 1; + uicontrol(cstPanel,'Style','slider', ... + 'Units','normalized', ... + 'Position',[0.975 0 0.025 1], ... + 'FontSize',matRad_cfg.gui.fontSize, ... + 'FontName',matRad_cfg.gui.fontName, ... + 'FontWeight',matRad_cfg.gui.fontWeight, ... + 'BackgroundColor',matRad_cfg.gui.elementColor, ... + 'ForegroundColor',matRad_cfg.gui.textColor, ... + 'Min',0,'Max',ceil(sliderFac)*tableViewHeight, ... + 'SliderStep',[lineHeight tableViewHeight] ./ (ceil(sliderFac)*tableViewHeight), ... + 'Value',ceil(sliderFac)*tableViewHeight - sliderPos, ... + 'Callback', @(hObject,eventdata)cstTableSlider_Callback(this,hObject,eventdata)); + end + + this.handles = handles; + end + + % --- Executes when the widget is resized. + function widget_SizeChangedFcn(this,hObject, eventdata) + % hObject handle to h1 (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + try + generateCstTable(this,evalin('base','cst')); + catch + end + end + + function btObjAdd_Callback(this,hObject, ~) + % hObject handle to btnuiTableAdd (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles=this.handles; + + popupHandle = get(hObject,'UserData'); + cstIndex = get(popupHandle,'Value'); + + cst = evalin('base','cst'); + %Add Standard Objective + if strcmp(cst{cstIndex,3},'TARGET') + cst{cstIndex,6}{end+1} = struct(DoseObjectives.matRad_SquaredDeviation); + else + cst{cstIndex,6}{end+1} = struct(DoseObjectives.matRad_SquaredOverdosing); + end + + assignin('base','cst',cst); + this.handles=handles; + changedWorkspace(this,'cst'); + + end + function btObjRemove_Callback(this,hObject, ~) + % hObject handle to btnuiTableAdd (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles=this.handles; + ix = get(hObject,'UserData'); + + cst = evalin('base','cst'); + %Add Standard Objective + + cst{ix(1),6}(ix(2)) = []; + + assignin('base','cst',cst); + this.handles=handles; + this.changedWorkspace('cst'); + + %generateCstTable(this,cst); + + end + + function changeObjFunction_Callback(this,hObject, ~) + % hObject handle to btnuiTableAdd (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles=this.handles; + data = get(hObject,'UserData'); + ix = data{1}; + classNames = data{2}; + classToCreate = classNames{get(hObject,'Value')}; + + cst = evalin('base','cst'); + %Add Standard Objective + + %We just check if the user really wanted to change the objective to be + %user-friendly + currentObj = cst{ix(1),6}{ix(2)}; + currentClass = class(currentObj); + if ~strcmp(currentClass,classToCreate) + newObj = eval(classToCreate); + + % Only if we have a penalty value for optimization, apply the new one + % Maybe this check should be more exact? + + %if (isfield(currentObj,'penalty') || isobject (currentObj ) && isprop(currentObj,'penalty')) && isprop(newObj,'penalty') + if (isfield(currentObj,'penalty') || isa(currentObj,'DoseObjectives.matRad_DoseObjective')) && isa(newObj,'DoseObjectives.matRad_DoseObjective') + newObj.penalty = currentObj.penalty; + end + + cst{ix(1),6}{ix(2)} = struct(newObj); + + assignin('base','cst',cst); + this.handles=handles; + this.changedWorkspace('cst'); + + %generateCstTable(this,cst); + end + end + + function changeRobustness_Callback(this,hObject,~) + handles = this.handles; + data = hObject.UserData; + cst = evalin('base','cst'); + ix = data{1}; + + contentRobust = get(hObject,'String'); + cst{ix(1),6}{ix(2)}.robustness = contentRobust{get(hObject,'Value')}; + + assignin('base','cst',cst); + this.handles=handles; + this.changedWorkspace('cst'); + + end + %CST Param Callback + function editCstParams_Callback(this,hObject,~) + handles=this.handles; + data = hObject.UserData; + ix = data(1); + col = data(2); + + cst = evalin('base','cst'); + + str = get(hObject,'String'); + val = get(hObject,'Value'); + + switch col + case 2 + cst{ix,col} = str; + case 3 + cst{ix,col} = str{val}; + case 5 + cst{ix,col}.Priority = uint32(str2double(str)); + %cst{ix,col}=setfield(cst{ix,col},'Priority',uint32(str2double(str))); + otherwise + matRad_cfg.dispWarning('Wrong column assignment in GUI based cst setting'); + end + + assignin('base','cst',cst); + this.handles=handles; + this.changedWorkspace('cst'); + + %generateCstTable(this,cst); + end + + function editObjParam_Callback(this,hObject, ~) + % hObject handle to btnuiTableAdd (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles=this.handles; + ix = get(hObject,'UserData'); + + cst = evalin('base','cst'); + + if ix(3) == 0 + cst{ix(1),6}{ix(2)}.penalty = str2double(hObject.String); + elseif isequal(hObject.Style,'popupmenu') + cst{ix(1),6}{ix(2)}.parameters{ix(3)} = hObject.Value; + else + cst{ix(1),6}{ix(2)}.parameters{ix(3)} = str2double(hObject.String); + end + + assignin('base','cst',cst); + this.handles=handles; + this.changedWorkspace('cst'); + + %generateCstTable(this,cst); + + end + + % --- Executes on slider movement. + function cstTableSlider_Callback(this,~,~) + % hObject handle to cstTableSlider (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'Value') returns position of slider + % get(hObject,'Min') and get(hObject,'Max') to determine range of slider + try + generateCstTable(this,evalin('base','cst')); + catch + end + end + end +end diff --git a/gui/widgets/matRad_PlanWidget.m b/gui/widgets/matRad_PlanWidget.m new file mode 100644 index 000000000..04cf33f0f --- /dev/null +++ b/gui/widgets/matRad_PlanWidget.m @@ -0,0 +1,1400 @@ +classdef matRad_PlanWidget < matRad_Widget + % matRad_PlanWidget class to generate GUI widget to set and get plan parameters + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + State = false + Machines + Optimizations + end + + properties (Access = private) + hTissueWindow; + end + + properties (Constant) + Modalities = {'photons','protons','carbon'}; + end + + methods + function this = matRad_PlanWidget(handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 1 + handleParent = figure(... + 'Units','characters',... + 'Position',[100 50 125 15],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Plan',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + + end + this = this@matRad_Widget(handleParent); + + update(this); + + handles=this.handles; + + if matRad_cfg.eduMode + %Visisbility in Educational Mode + eduHideHandles = {handles.radiobutton3Dconf,... + handles.btnRunDAO}; + eduDisableHandles = {handles.editCouchAngle,handles.popUpMachine}; + cellfun(@(h) set(h,'Visible','Off'),eduHideHandles); + cellfun(@(h) set(h,'Enable','Off'),eduDisableHandles); + end + this.handles=handles; + + end + + function this = initialize(this) + end + + function this = update(this,evt) + doUpdate = true; + if nargin == 2 + %At pln changes and at cst/cst (for Isocenter and new settings) + %we need to update + doUpdate = this.checkUpdateNecessary({'pln','ct','cst'},evt); + end + + if doUpdate + if evalin('base','exist(''pln'')') + getPlnFromWorkspace(this); + else + setPlnDefaultValues(this); + end + end + end + + end + + methods(Access = protected) + function this = createLayout(this) + h12 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + gridSize = [5 8]; + [i,j] = ndgrid(1:gridSize(1),1:gridSize(2)); + gridPos = arrayfun(@(i,j) computeGridPos(this,[i j],gridSize),i,j,'UniformOutput',false); + txt = sprintf('Photons: Choose width (and height) of quadratic photon bixel (i.e. discrete fluence elements)\nParticles: Choose lateral spot distance'); + %Text bixel width + h13 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','bixel width in [mm]',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtBixelWidth',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Edit Bixel Width + txt = sprintf('Photons: Choose width (and height) of quadratic photon bixel (i.e. discrete fluence elements)\nParticles: Choose lateral spot distance'); + h14 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','5',... + 'Tooltip',txt,... + 'Style','edit',... + 'Position',gridPos{2,1},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Tag','editBixelWidth',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text Gantry Angle + txt = sprintf('Define gantry angles according to the matRad coordinate system\nEvery gantry angle defines a beam and needs a couch angle\nSeparate individual angles by blanks'); + h15 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Gantry Angle in °',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,2},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtGantryAngle',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Edit Gantry Angle + txt = sprintf('Define gantry angles according to the matRad coordinate system\nEvery gantry angle defines a beam and needs a couch angle\nSeparate individual angles by blanks'); + h16 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','0',... + 'Tooltip',txt,... + 'Style','edit',... + 'Position',gridPos{2,2},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)standardCallback(this,hObject,eventdata),... + 'Tag','editGantryAngle',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text Couch Angle + txt = sprintf('Define couch angles according to the matRad coordinate system\nEvery couch angle defines a beam and needs a gantry angle\nSeparate individual angles by blanks'); + h17 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Couch Angle in °',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,3},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtCouchAngle',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Edit Couch Angle + txt = sprintf('Define couch angles according to the matRad coordinate system\nEvery couch angle defines a beam and needs a gantry angle\nSeparate individual angles by blanks'); + h18 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','0',... + 'Tooltip',txt,... + 'Style','edit',... + 'Position',gridPos{2,3},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)standardCallback(this,hObject,eventdata),... + 'Tag','editCouchAngle',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %PopUp Menu RadMode + h19 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String',this.Modalities,...,... + 'Tooltip','Choose a radiation modality (photons, protons or carbon)',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{2,4},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)popupRadMode_Callback(this,hObject,eventdata),... + 'Tag','popupRadMode',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text RadMode + h20 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Radiation Mode',... + 'Tooltip','Choose a radiation modality (photons, protons or carbon)',... + 'Style','text',... + 'Position',gridPos{1,4},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtRadMode'); + + %Text # Fractions + h21 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','# Fractions',... + 'Tooltip','Define the number of fractions',... + 'Style','text',... + 'Position',gridPos{1,7},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtNumOfFractions'); + + %Edit # Fraction + h22 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'Tooltip','Define the number of fractions',... + 'String','30',... + 'Style','edit',... + 'Position',gridPos{2,7},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Tag','editFraction',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text Iso Center + txt = sprintf('Choose the isocenter of the treatment plan in voxel coordinates within the ct.cube\nIf Auto. is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell'); + h23 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','IsoCenter in [mm]',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,6},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Tag','txtIsoCenter',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Edit Iso centr + txt = sprintf('Choose the isocenter of the treatment plan in voxel coordinates within the ct.cube\nIf Auto. is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell'); + h24 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','0 0 0',... + 'Tooltip', txt,... + 'Style','edit',... + 'Position',gridPos{2,6},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Enable','off',... + 'Tag','editIsoCenter',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Auto Iso Center Checkbox + h25 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Auto.',... + 'Tooltip','If this is checked, the isocenter is calculated as the center of gravity of all voxels belonging to structures that have been modeled as target volume in the cst cell',... + 'Style','checkbox',... + 'Value',1,... + 'Position',gridPos{3,6},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','checkIsoCenter'); + + %Popup menu for Machine file + txt = sprintf('Choose a base data set\nIf Generic is selected for a photon treatment plan, the already available photons_Generic.mat file is loaded'); + h30 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String',{'Generic','generic_MCsquare'},... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{2,5},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) popUpMachine_Callback(this,hObject,eventdata),... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','popUpMachine'); + + %Text Machine + h31 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Machine',... + 'Tooltip','Choose a base data set',... + 'Style','text',... + 'Position',gridPos{1,5},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtMachine' ); + + %Set tissue button + txt = sprintf('Set the tissue parameters of the VOIs\nThe base data file contains depth-dependent alpha and beta values, which are different depending on the tissue class'); + h32 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Set Tissue',... + 'Tooltip',txt,... + 'Position',gridPos{3,8},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) btnSetTissue_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','btnSetTissue'); + + %Popup menu for Biological model and optimized quantity + txt = sprintf('Choose a quantity to optimize \nPhysical Dose: physical dose is optimized\nRBExD: RBE-weighted dose is optimized\neffect: effect calculated according to LQ model is optimized'); + h33 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String',{ 'physicalDose'; 'RBExD'; 'effect'; },... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{2,8},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) popMenuQuantityOpt_Callback(this,hObject,eventdata),... + 'Tag','popMenuQuantityOpt',... + 'Enable', 'off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Text for Biological model and optimized quantity + txt = sprintf('Choose a quantity to optimize \nPhysical Dose: physical dose is optimized\nRBExD: RBE-weighted dose is optimized\neffect: effect calculated according to LQ model is optimized'); + h34 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Optimized quantity',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',gridPos{1,8},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Interruptible','off',... + 'Tag','txtQuantityOpt',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + % Radiobutton 3d Conformal + pos = gridPos{4,1}; + pos(3) = pos(3)*2; + + h36 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','3D conformal',... + 'Tooltip','Check this if you want to execute 3D conformal planning',... + 'Style','radiobutton',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Enable','off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','radiobutton3Dconf' ); + + %Run Sequencing radiobutton + pos = gridPos{5,1}; + pos(3) = pos(3)*2; + + txt = sprintf('Check this if you want to run a MLC sequencing\nThe number of stratification levels can be adjusted below'); + h26 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Run Sequencing',... + 'Tooltip',txt,... + 'Style','radiobutton',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Enable','off',... + 'Tag','btnRunSequencing'); + + %Text Sequencing + pos = gridPos{4,2}; + pos(3) = pos(3) * 1.5; + + h28 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Stratification Levels:',... + 'Tooltip','Choose the number of stratification levels in case you run a MLC sequencing',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtSequencing' ); + + %Sequencing Level Edit + pos = gridPos{5,2}; + pos(3) = pos(3) / 2; + + h29 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','7',... + 'Tooltip','Choose the number of stratification levels in case you run a MLC sequencing',... + 'Style','edit',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata)standardCallback(this,hObject,eventdata),... + 'Enable','off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','editSequencingLevel'); + + %Text Sequencer + h40 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Sequencer : ',... + 'Tooltip','Set the sequencing algorithm',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'Position',gridPos{4,3},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Interruptible','off',... + 'Tag','txtSequencer',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Popup menu selectin sequencing algorithm# + txt = sprintf('Choose a sequencing algorithm (siochi, xia or engel)'); + h41 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String',{ 'siochi','xia','engel' },... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{5,3},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) popUpMenuSequencer_Callback(this,hObject,eventdata),... + 'Tag','popUpMenuSequencer',... + 'Enable', 'off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + % Direct Aperture Optimization radiobutton + pos = gridPos{4,4}; + pos(3) = pos(3)*2; + + h27 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Run Direct Aperture Optimization',... + 'Tooltip','Check this if you want to run an additional direct aperture optimization',... + 'Style','radiobutton',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata)standardCallback(this,hObject,eventdata),... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Enable','off',... + 'Tag','btnRunDAO' ); + + %Biological Model + pos = gridPos{4,5}; + pos(3) = pos(3) * 1.5; + + h45 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Biological model:',... + 'Tooltip','Choose the biological model',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtBioModel'); + + pos = gridPos{5,5}; + pos(3) = pos(3) / 2; + h55 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String',{ 'none';'constRBE'; 'MCN';'WED';'LEM';'HEL'; },... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) popMenuBioModel_Callback(this,hObject,eventdata),... + 'Tag','popMenuBioModel',... + 'Enable', 'off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pos = gridPos{4,6}; + pos(3) = pos(3) * 1.5; + + h46 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Scenario Selection:',... + 'Tooltip','Choose the scenario sampling method',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtMultScen' ); + + + pos = gridPos{5,6}; + pos(3) = pos(3) / 2; + h56 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String',{ 'nomScen','wcScen','impScen','rndScen' },... + 'Tooltip',txt,... + 'Style','popupmenu',... + 'Value',1,... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) popMenuMultScen_Callback(this,hObject,eventdata),... + 'Tag','popMenuMultScen',... + 'Enable', 'off',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + + % Text for dose Grid resolution + pos = gridPos{4,7}; + pos(3) = pos(3)*2; + + h35 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','Dose Grid Resolution: ',... + 'Tooltip','Set the size of an individual voxel in the dose cube',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Interruptible','off',... + 'Tag','textDoseGrid',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + % Edit dose grid x + pos = gridPos{4,8}; + pos(3) = pos(3)*0.5; + + h37 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','5',... + 'Tooltip','Set the size of an individual voxel in the dose cube in x-direction',... + 'Style','edit',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Tag','editDoseX',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + % positioning dose grid size input boxes + pos(1) = pos(1) + pos(3) + 0.005; + % Edit dose grid y + h38 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','5',... + 'Tooltip','Set the size of an individual voxel in the dose cube in y-direction',... + 'Style','edit',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Tag','editDoseY',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pos(1) = pos(1) + pos(3) + 0.005; + % Edit dose grid z + h39 = uicontrol(... + 'Parent',h12,... + 'Units','normalized',... + 'String','5',... + 'Tooltip','Set the size of an individual voxel in the dose cube in z-direction',... + 'Style','edit',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Callback',@(hObject,eventdata) standardCallback(this,hObject,eventdata),... + 'Tag','editDoseZ',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + pos(1) = pos(1) + pos(3) + 0.005; + % text dose grid dimension [mm] + h42 = uicontrol(... + 'Parent',h12,...setpln + 'Units','normalized',... + 'String','[mm]',... + 'Tooltip','Set the size of an individual voxel in the dose cube',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,.... + 'Interruptible','off',... + 'Tag','txtGridmm',... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + + this.createHandles(); + end + + %Set default values for the PLN on matRadGUI startup + function this = setPlnDefaultValues(this) + + handles = this.handles; + + this.getMachines() + + % + vChar = get(handles.editGantryAngle,'String'); + if strcmp(vChar(1,1),'0') && length(vChar)==6 + set(handles.editGantryAngle,'String','0'); + end + vChar = get(handles.editCouchAngle,'String'); + if strcmp(vChar(1,1),'0') && length(vChar)==3 + set(handles.editCouchAngle,'String','0') + end + + % do not calculate / suggest isoCenter new by default + %this.checkIsoCenter_Callback(handles.checkIsoCenter); + set(handles.editIsoCenter,'Enable','on'); + set(handles.popMenuQuantityOpt,'Value',1); + set(handles.popMenuBioModel,'Value',1); + set(handles.popMenuMultScen,'Value',1); + this.handles=handles; + updatePlnInWorkspace(this); + end + + %Get pln from workspace and update the Settings displayed in GUI + function this = getPlnFromWorkspace(this) + pln = evalin('base', 'pln'); + handles = this.handles; + + + % sanity check of isoCenter + if size(pln.propStf.isoCenter,1) ~= pln.propStf.numOfBeams && size(pln.propStf.isoCenter,1) == 1 + pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * pln.propStf.isoCenter(1,:); + elseif size(pln.propStf.isoCenter,1) ~= pln.propStf.numOfBeams && size(pln.propStf.isoCenter,1) ~= 1 + error('Isocenter in plan file are incosistent.'); + end + + + set(handles.editBixelWidth,'String',num2str(pln.propStf.bixelWidth)); + set(handles.editGantryAngle,'String',num2str(pln.propStf.gantryAngles)); + set(handles.editCouchAngle,'String',num2str(pln.propStf.couchAngles)); + + modIx = find(strcmp(pln.radiationMode,this.Modalities)); + set(handles.popupRadMode,'Value',modIx); + + getMachines(this); + modIy = find(strcmp(pln.machine,this.Machines{modIx})); + set(handles.popUpMachine,'Value',modIy); + + if isfield(pln.propStf,'isoCenter') + if size(unique(pln.propStf.isoCenter,'rows'),1) == 1 + set(handles.editIsoCenter,'String',regexprep(num2str((round(pln.propStf.isoCenter(1,:)*10))./10), '\s+', ' ')); + set(handles.checkIsoCenter,'Enable','on'); + if get(handles.checkIsoCenter,'Value') + set(handles.editIsoCenter,'Enable','off'); + else + set(handles.editIsoCenter,'Enable','on'); + end + + else + set(handles.editIsoCenter,'String','multiple isoCenter'); + set(handles.editIsoCenter,'Enable','off'); + set(handles.checkIsoCenter,'Value',0); + set(handles.checkIsoCenter,'Enable','off'); + end + end + + set(handles.editFraction,'String',num2str(pln.numOfFractions)); + + + contentPopUpQuantityOpt = get(handles.popMenuQuantityOpt,'String'); + ix = find(strcmp(pln.bioParam.quantityOpt,contentPopUpQuantityOpt)); + set(handles.popMenuQuantityOpt,'Value',ix); + + contentPopUpBioModel = get(handles.popMenuBioModel,'String'); + ix = find(strcmp(pln.bioParam.model,contentPopUpBioModel)); + set(handles.popMenuBioModel,'Value',ix); + + if evalin('base','exist(''ct'')') + contentPopUpMultScen = get(handles.popMenuMultScen,'String'); + ix = find(strcmp(pln.multScen.name,contentPopUpMultScen)); + set(handles.popMenuMultScen,'Value',ix); + end + + + set(handles.btnRunDAO,'Value',pln.propOpt.runDAO); + if isfield(pln, 'propSeq') && isfield(pln.propSeq, 'sequencingLevel') + set(handles.btnRunSequencing,'Value',pln.propSeq.runSequencing); + set(handles.editSequencingLevel,'String',num2str(pln.propSeq.sequencingLevel)); + else + set(handles.btnRunSequencing,'Value', 0 ); + end + if isfield (pln.propOpt, 'conf3D') + set(handles.radiobutton3Dconf,'Value',pln.propOpt.conf3D); + end + + set(handles.editDoseX,'String',num2str(pln.propDoseCalc.doseGrid.resolution.x)); + set(handles.editDoseY,'String',num2str(pln.propDoseCalc.doseGrid.resolution.y)); + set(handles.editDoseZ,'String',num2str(pln.propDoseCalc.doseGrid.resolution.z)); + + this.handles=handles; + this.switchEnables(); + end + + %Update the workspace pln from the Widget + function updatePlnInWorkspace(this) + this.getMachines(); + handles = this.handles; + + % evalin pln (if existant) in order to decide whether isoCenter should be calculated + % automatically + if evalin('base','exist(''pln'',''var'')') + pln = evalin('base','pln'); + end + + pln.propStf.bixelWidth = this.parseStringAsNum(get(handles.editBixelWidth,'String'),false); % [mm] / also corresponds to lateral spot spacing for particles + pln.propStf.gantryAngles = this.parseStringAsNum(get(handles.editGantryAngle,'String'),true); % [???] + if numel(this.parseStringAsNum(get(handles.editCouchAngle,'String'),true))==1 % Feature: autofill couch angles to single plane by entering a single value + pln.propStf.couchAngles = this.parseStringAsNum(get(handles.editCouchAngle,'String'),true) * ones(1,numel(pln.propStf.gantryAngles)); + else + pln.propStf.couchAngles = this.parseStringAsNum(get(handles.editCouchAngle,'String'),true); % [???] + end + pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); + pln.propStf.isoCenter = this.parseStringAsNum(get(handles.editIsoCenter,'String'),true); + + % switch machines depending on radmode selection + selectedMachine = get(handles.popUpMachine,'Value'); + popupMachines = get(handles.popUpMachine,'String'); + pln.machine = popupMachines{selectedMachine}; + + + pln.propDoseCalc.doseGrid.resolution.x = this.parseStringAsNum(get(handles.editDoseX,'String'),false); + pln.propDoseCalc.doseGrid.resolution.y = this.parseStringAsNum(get(handles.editDoseY,'String'),false); + pln.propDoseCalc.doseGrid.resolution.z = this.parseStringAsNum(get(handles.editDoseZ,'String'),false); + + + + pln.numOfFractions = this.parseStringAsNum(get(handles.editFraction,'String'),false); + contents = get(handles.popupRadMode,'String'); + pln.radiationMode = contents{get(handles.popupRadMode,'Value')}; % either photons / protons / carbon + contents = get(handles.popUpMachine,'String'); + + % Biological model set + contentQuantityOpt = get(handles.popMenuQuantityOpt,'String'); + contentBioModel = get(handles.popMenuBioModel,'String'); + contentMultScen = get(handles.popMenuMultScen,'String'); + pln.bioParam = matRad_bioModel(pln.radiationMode, contentQuantityOpt{get(handles.popMenuQuantityOpt,'Value'),:}, contentBioModel{get(handles.popMenuBioModel,'Value'),:}); + if evalin('base','exist(''ct'')') + ct = evalin('base','ct'); + pln.numOfVoxels = prod(ct.cubeDim); + pln.voxelDimensions = ct.cubeDim; + pln.multScen = matRad_multScen(ct,contentMultScen{get(handles.popMenuMultScen,'Value')}); + end + % Sequencing options set + contents = get(handles.popUpMenuSequencer,'String'); + pln.propSeq.sequencer = contents{get(handles.popUpMenuSequencer,'Value')}; + pln.propSeq.runSequencing = logical(get(handles.btnRunSequencing,'Value')); + pln.propSeq.sequencingLevel = this.parseStringAsNum(get(handles.editSequencingLevel,'String'),false); + pln.propOpt.runDAO = logical(get(handles.btnRunDAO,'Value')); + pln.propOpt.conf3D = logical(get(handles.radiobutton3Dconf,'Value')); + + + % checkIsoCenter checkbox + W = evalin('base','whos'); + doesPlnExist = ismember('pln',{W(:).name}) && evalin('base','exist(''cst'')') && evalin('base','exist(''ct'')'); + + if get(handles.checkIsoCenter,'Value') && doesPlnExist + try + %pln = evalin('base','pln'); + if ~isfield(pln.propStf,'isoCenter') + pln.propStf.isoCenter = NaN; + end + tmpIsoCenter = matRad_getIsoCenter(evalin('base','cst'),evalin('base','ct')); + if ~isequal(tmpIsoCenter,pln.propStf.isoCenter) + pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1)*tmpIsoCenter; + %handles.State = 1; + %UpdateState(handles); + end + set(handles.editIsoCenter,'String',regexprep(num2str((round(tmpIsoCenter*10))./10), '\s+', ' ')); + set(handles.editIsoCenter,'Enable','off') + assignin('base','pln',pln); + catch ME + warning(ME.identifier,'could not set isocenter in pln update! Reason: %s\n',ME.message) + end + else + set(handles.editIsoCenter,'Enable','on') + end + + + % editIsoCenter textbox + tmpIsoCenter = str2num(get(handles.editIsoCenter,'String')); + + if length(tmpIsoCenter) == 3 + if sum(any(unique(pln.propStf.isoCenter,'rows')~=tmpIsoCenter)) + pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1)*tmpIsoCenter; + + end + else + handles = showError(this,'EditIsoCenterCallback: Could not set iso center'); + end + + if evalin('base','exist(''cst'')') + try + cst = evalin('base','cst'); + if (sum(strcmp('TARGET',cst(:,3))) > 0 && get(handles.checkIsoCenter,'Value')) || ... + (sum(strcmp('TARGET',cst(:,3))) > 0 && ~isfield(pln.propStf,'isoCenter')) + pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct); + set(handles.checkIsoCenter,'Value',1); + else + if ~strcmp(get(handles.editIsoCenter,'String'),'multiple isoCenter') + pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * str2num(get(handles.editIsoCenter,'String')); + end + end + catch ME + + showWarning(this,'Could not set isocenter in pln update! Reason: %s\n',ME.message) %TODO: showWarning vs warning + end + end + + handles.pln = pln; + assignin('base','pln',pln); + this.handles = handles; + this.changedWorkspace('pln'); + end + end + + methods(Access = private) + function standardCallback(this, hObject, eventdata) + updatePlnInWorkspace(this); + + end + % Enable/disable functionality in PlnWidget depending on the active + % Radmode + function switchEnables(this) + handles = this.handles; + hObject = handles.popupRadMode; + + contents = cellstr(get(hObject,'String')); + RadIdentifier = contents{get(hObject,'Value')}; + contentPopUpQuantityOpt = get(handles.popMenuQuantityOpt,'String'); + contentPopUpBioModel = get(handles.popMenuBioModel,'String'); + switch RadIdentifier + case 'photons' + + set(handles.popMenuQuantityOpt,'Enable','on'); +% ix = find(strcmp(contentPopUpQuantityOpt,'physicalDose')); +% set(handles.popMenuQuantityOpt,'Value',ix); + ix = find(strcmp(contentPopUpBioModel,'none')); + set(handles.popMenuBioModel,'Value',ix); + set(handles.popMenuBioModel,'Enable','off'); + set(handles.btnSetTissue,'Enable','off'); + + set(handles.btnRunSequencing,'Enable','on'); + set(handles.btnRunDAO,'Enable','on'); + set(handles.radiobutton3Dconf,'Enable','on'); + set(handles.txtSequencing,'Enable','on'); + set(handles.editSequencingLevel,'Enable','on'); + set(handles.popUpMenuSequencer,'Enable','on'); + set(handles.txtSequencer,'Enable','on'); + set(handles.popMenuMultScen, 'Enable','on'); + + if ~(get(handles.btnRunSequencing,'Value') || get(handles.btnRunDAO,'Value')) + + set(handles.txtSequencing,'Enable','off'); + set(handles.editSequencingLevel,'Enable','off'); + set(handles.popUpMenuSequencer,'Enable','off'); + set(handles.txtSequencer,'Enable','off'); + else + set(handles.txtSequencing,'Enable','on'); + set(handles.editSequencingLevel,'Enable','on'); + set(handles.popUpMenuSequencer,'Enable','on'); + set(handles.txtSequencer,'Enable','on'); + end + + case 'protons' + set(handles.popMenuQuantityOpt,'Enable','on'); + set(handles.popMenuBioModel,'Enable','on'); + set(handles.popMenuMultScen, 'Enable','on'); + set(handles.btnSetTissue,'Enable','on'); + + set(handles.btnRunSequencing,'Enable','off'); + set(handles.btnRunDAO,'Enable','off'); + set(handles.radiobutton3Dconf,'Enable','off'); + set(handles.txtSequencing,'Enable','off'); + set(handles.editSequencingLevel,'Enable','off'); + set(handles.popUpMenuSequencer,'Enable','off'); + set(handles.txtSequencer,'Enable','off'); + + case 'carbon' + + set(handles.popMenuQuantityOpt,'Enable','on'); + set(handles.popMenuBioModel,'Enable','on'); + set(handles.btnSetTissue,'Enable','on'); + set(handles.popMenuMultScen, 'Enable','on'); + + set(handles.btnRunSequencing,'Enable','off'); + set(handles.btnRunDAO,'Enable','off'); + set(handles.radiobutton3Dconf,'Enable','off'); + set(handles.txtSequencing,'Enable','off'); + set(handles.editSequencingLevel,'Enable','off'); + set(handles.popUpMenuSequencer,'Enable','off'); + set(handles.txtSequencer,'Enable','off'); + + end + + selectedQuantityOpt = get(handles.popMenuQuantityOpt,'Value'); + if strcmp(contentPopUpQuantityOpt{selectedQuantityOpt},'physicalDose') + set(handles.btnSetTissue,'Enable','off'); + else + set(handles.btnSetTissue,'Enable','on'); + end + + this.handles = handles; + end + + function manageRadModeSpecificDisplay(this) + handles = this.handles; + hObject = this.popupRadMode('hObject'); + + this.handles = handles; + end +%% CALLBACKS + function popupRadMode_Callback(this, hObject, eventdata) + handles = this.handles; + contents = cellstr(get(hObject,'String')); + RadIdentifier = contents{get(hObject,'Value')}; + contentPopUp = get(handles.popMenuQuantityOpt,'String'); + + switch RadIdentifier + case 'protons' + ix = find(strcmp(contentPopUp,'RBExD')); + set(handles.popMenuQuantityOpt,'Value',ix); + + case 'carbon' + ix = find(strcmp(contentPopUp,'RBExD')); + set(handles.popMenuQuantityOpt,'Value',ix); + end + + pln = evalin('base','pln'); + + % new radiation modality is photons -> just keep physicalDose + if strcmp(contents(get(hObject,'Value')),'photons') + try + AllVarNames = evalin('base','who'); + if ismember('resultGUI',AllVarNames) + resultGUI = evalin('base','resultGUI'); + if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha'); end + if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end + if isfield(resultGUI,'RBExDose'); resultGUI = rmfield(resultGUI,'RBExDose');end + if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end + assignin('base','resultGUI',resultGUI); + %handles = updateIsoDoseLineCache(handles); + end + catch + end + elseif strcmp(contents(get(hObject,'Value')),'protons') + try + AllVarNames = evalin('base','who'); + if ismember('resultGUI',AllVarNames) + resultGUI = evalin('base','resultGUI'); + if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha');end + if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end + if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end + assignin('base','resultGUI',resultGUI); + %handles = updateIsoDoseLineCache(handles); + end + catch + end + end + this.handles = handles; + updatePlnInWorkspace(this); + end + + + function popUpMenuSequencer_Callback(this, hObject, eventdata) + handles = this.handles; + contents = cellstr(get(hObject,'String')); + SeqIdentifier = contents{get(hObject,'Value')}; + contentPopUp = get(handles.popUpMenuSequencer,'String'); + + switch SeqIdentifier + case 'siochi' + ix = find(strcmp(contentPopUp,'siochi')); + set(handles.popUpMenuSequencer,'Value',ix); + + case 'xia' + ix = find(strcmp(contentPopUp,'xia')); + set(handles.popUpMenuSequencer,'Value',ix); + case 'engel' + ix = find(strcmp(contentPopUp,'engel')); + set(handles.popUpMenuSequencer,'Value',ix); + end + + pln = evalin('base','pln'); + + + this.handles = handles; + updatePlnInWorkspace(this); + end + + function popUpMachine_Callback(this, hObject, eventdata) + % MOEGLICHER FEHLER WEGEN VALUE WERT! + handles = this.handles; + contents = cellstr(get(hObject,'String')); + MachineIdentifier = contents{get(hObject,'Value')}; + % contentPopUp = get(handles.) + flag=checkRadiationComposition(this); + if ~flag + this.showWarning(['No base data available for machine: ' MachineIdentifier '. Selecting default machine.']); + set(handles.popUpMachine,'Value',1); + end + getMachines(this); + pln = evalin('base','pln'); + + % MOEGLICHEE FEHLER HIER VALUE UND GENERIC WERDEN VERGLICHEN + if strcmp(contents(get(hObject,'Value')),'Generic') + try + AllVarNames = evalin('base','who'); + if ismember('resultGUI',AllVarNames) + resultGUI = evalin('base','resultGUI'); + if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha'); end + if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end + if isfield(resultGUI,'RBExDose'); resultGUI = rmfield(resultGUI,'RBExDose');end + if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end + assignin('base','resultGUI',resultGUI); + %handles = updateIsoDoseLineCache(handles); + end + catch + end + % MOEGLICHEE FEHLER HIER VALUE UND GENERIC WERDEN VERGLICHEN + elseif strcmp(contents(get(hObject,'Value')),'generic_MCsquare') + try + AllVarNames = evalin('base','who'); + if ismember('resultGUI',AllVarNames) + resultGUI = evalin('base','resultGUI'); + if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha');end + if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end + if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end + assignin('base','resultGUI',resultGUI); + %handles = updateIsoDoseLineCache(handles); + end + catch + end + end + + this.handles = handles; + updatePlnInWorkspace(this); + end + + function btnSetTissue_Callback(this, hObject, eventdata) + handles = this.handles; + + if evalin('base','exist(''cst'')') && evalin('base','exist(''pln'')') + try + %parse variables from base-workspace + cst = evalin('base','cst'); + pln = evalin('base','pln'); + + + fileName = [pln.radiationMode '_' pln.machine]; + load(fileName); + + % check for available cell types characterized by alphaX and betaX + for i = 1:size(machine.data(1).alphaX,2) + CellType{i} = [num2str(machine.data(1).alphaX(i)) ' ' num2str(machine.data(1).betaX(i))]; + end + + %fill table data array + for i = 1:size(cst,1) + data{i,1} = cst{i,2}; + data{i,2} = [num2str(cst{i,5}.alphaX) ' ' num2str(cst{i,5}.betaX)]; + data{i,3} = (cst{i,5}.alphaX / cst{i,5}.betaX ); + end + + Width = 400; + Height = 300 + 20*size(data,1); + ScreenSize = get(0,'ScreenSize'); + % show "set tissue parameter" window + figHandles = get(0,'Children'); + if ~isempty(figHandles) + IdxHandle = strcmp(get(figHandles,'Name'),'Set Tissue Parameters'); + else + IdxHandle = []; + end + + %check if window is already exists + if any(IdxHandle) + IdxTable = find(strcmp({figHandles(IdxHandle).Children.Type},'uitable')); + set(figHandles(IdxHandle).Children(IdxTable), 'Data', []); + figTissue = figHandles(IdxHandle); + %set focus + figure(figTissue); + else + figTissue = figure('Name','Set Tissue Parameters','Color',[.5 .5 .5],'NumberTitle','off','OuterPosition',... + [ceil(ScreenSize(3)/2) 100 Width Height]); + end + + % define the tissue parameter table + cNames = {'VOI','alphaX betaX','alpha beta ratio'}; + columnformat = {'char',CellType,'numeric'}; + + tissueTable = uitable('Parent', figTissue,'Data', data,'ColumnEditable',[false true false],... + 'ColumnName',cNames, 'ColumnFormat',columnformat,'Position',[50 150 10 10]); + set(tissueTable,'CellEditCallback',@(hObject,eventdata) tissueTable_CellEditCallback(this,hObject,eventdata)); + % set width and height + currTablePos = get(tissueTable,'Position'); + currTableExt = get(tissueTable,'Extent'); + currTablePos(3) = currTableExt(3); + currTablePos(4) = currTableExt(4); + set(tissueTable,'Position',currTablePos); + + % define two buttons with callbacks + uicontrol('Parent', figTissue,'Style', 'pushbutton', 'String', 'Save&Close',... + 'Position', [Width-(0.25*Width) 0.1 * Height 70 30],... + 'Callback', @(hpb,eventdata)SaveTissueParameters(this,hpb,eventdata)); + + uicontrol('Parent', figTissue,'Style', 'pushbutton', 'String', 'Cancel&Close',... + 'Position', [Width-(0.5*Width) 0.1 * Height 80 30],... + 'Callback', 'close'); + catch ME + warning(ME.identifier,'Could not set Tissue parameter update! Reason: %s\n',ME.message) + end + end + this.handles = handles; + + end + + function popMenuBioModel_Callback(this, hObject, eventdata) + handles = this.handles; + + pln = evalin('base','pln'); + contentBioModel = get(handles.popMenuBioModel,'String'); + NewBioModel = contentBioModel(get(handles.popMenuBioModel,'Value'),:); + +% if (strcmp(pln.propOpt.bioOptimization,'LEMIV_effect') && strcmp(NewBioOptimization,'LEMIV_RBExD')) ||... +% (strcmp(pln.propOpt.bioOptimization,'LEMIV_RBExD') && strcmp(NewBioOptimization,'LEMIV_effect')) +% % do nothing - re-optimization is still possible +% elseif ((strcmp(pln.propOpt.bioOptimization,'const_RBE') && strcmp(NewBioOptimization,'none')) ||... +% (strcmp(pln.propOpt.bioOptimization,'none') && strcmp(NewBioOptimization,'const_RBE'))) && isequal(pln.radiationMode,'protons') +% % do nothing - re-optimization is still possible +% end +% + this.handles = handles; + updatePlnInWorkspace(this); + end + function popMenuMultScen_Callback(this, hObject, eventdata) + + updatePlnInWorkspace(this); + end + + function popMenuQuantityOpt_Callback(this, hObject, eventdata) +% handles = this.handles; +% +% pln = evalin('base','pln'); +% contentQuantityOpt = get(handles.popMenuQuantityOpt,'String'); +% NewQuantityOpt = contentQuantityOpt(get(handles.popMenuQuantityOpt,'Value'),:); +% +% % if (strcmp(pln.propOpt.bioOptimization,'LEMIV_effect') && strcmp(NewBioOptimization,'LEMIV_RBExD')) ||... +% % (strcmp(pln.propOpt.bioOptimization,'LEMIV_RBExD') && strcmp(NewBioOptimization,'LEMIV_effect')) +% % % do nothing - re-optimization is still possible +% % elseif ((strcmp(pln.propOpt.bioOptimization,'const_RBE') && strcmp(NewBioOptimization,'none')) ||... +% % (strcmp(pln.propOpt.bioOptimization,'none') && strcmp(NewBioOptimization,'const_RBE'))) && isequal(pln.radiationMode,'protons') +% % % do nothing - re-optimization is still possible +% % end +% % +% this.handles = handles; + updatePlnInWorkspace(this); + end + + function tissueTable_CellEditCallback(this,hObject, eventdata) + if eventdata.Indices(2) == 2 + alphaXBetaX = str2num(eventdata.NewData); + data = get(hObject,'Data'); + data{eventdata.Indices(1),3} = alphaXBetaX(1)/alphaXBetaX(2); + set(hObject,'Data',data); + end + end +%% END OF CALLBACKS + + % load Machine File + function getMachines(this) + matRad_cfg = MatRad_Config.instance(); + %seach for availabes machines + handles = this.handles; + this.Machines=cell(size(this.Modalities)); + %Loop over all modalities to find machine per modalitiy + for i = 1:length(this.Modalities) + pattern = [this.Modalities{1,i} '_*']; + if isdeployed + baseroot = [ctfroot filesep 'matRad']; + else + baseroot = matRad_cfg.matRadRoot; + end + Files = dir([baseroot filesep 'basedata' filesep pattern]); + + for j = 1:length(Files) + if ~isempty(Files) + MachineName = Files(j).name(numel(this.Modalities{1,i})+2:end-4); + this.Machines{i}{j} = MachineName; + end + end + end + + selectedRadMod = get(handles.popupRadMode,'Value'); + nMachines = numel(this.Machines{selectedRadMod}); + selectedMachine = get(handles.popUpMachine,'Value'); + + if get(handles.popUpMachine,'Value') > nMachines + selectedMachine = 1; + end + + set(handles.popUpMachine,'Value',selectedMachine,'String',this.Machines{selectedRadMod}); + this.handles = handles; + end + + %String to num parser for edit fields + function number = parseStringAsNum(this,stringIn,isVector) + if isnumeric(stringIn) + number = stringIn; + else + number = str2num(stringIn); + if isempty(number) || length(number) > 1 && ~isVector + warndlg(['could not parse all parameters (pln, optimization parameter)']); + number = NaN; + elseif isVector && iscolumn(number) + number = number'; + end + end + end + + %Check if Machine File is available and correct + function flag = checkRadiationComposition(this) + matRad_cfg = MatRad_Config.instance(); + handles = this.handles; + + flag = true; + contents = cellstr(get(handles.popUpMachine,'String')); + Machine = contents{get(handles.popUpMachine,'Value')}; + contents = cellstr(get(handles.popupRadMode,'String')); + radMod = contents{get(handles.popupRadMode,'Value')}; + + if isdeployed + baseroot = [ctfroot filesep 'matRad']; + else + baseroot = [fileparts(mfilename('fullpath')) filesep '..']; + end + FoundFile = dir([baseroot filesep 'basedata' filesep radMod '_' Machine '.mat']); + + if isempty(FoundFile) + matRad_cfg.dispWarning(['No base data available for machine: ' Machine '. Selecting default machine.']); + flag = false; + % set(handles.popUpMachine,'Value',1); + end + this.handles = handles; + end + + %Save Tissue Parameters to cst + function SaveTissueParameters(this,~, ~) + cst = evalin('base','cst'); + % get handle to uiTable + figHandles = get(0,'Children'); + IdxHandle = find(strcmp(get(figHandles,'Name'),'Set Tissue Parameters')); + % find table in window + + figHandleChildren = get(figHandles(IdxHandle),'Children'); + IdxTable = find(strcmp(get(figHandleChildren,'Type'),'uitable')); + uiTable = figHandleChildren(IdxTable); + % retrieve data from uitable + data = get(uiTable,'data'); + + for i = 1:size(cst,1) + for j = 1:size(data,1) + if strcmp(cst{i,2},data{j,1}) + alphaXBetaX = str2num(data{j,2}); + cst{i,5}.alphaX = alphaXBetaX(1); + cst{i,5}.betaX = alphaXBetaX(2); + end + end + end + assignin('base','cst',cst); + close + updatePlnInWorkspace(this); + end + + + + + end +end diff --git a/gui/widgets/matRad_StatisticsWidget.m b/gui/widgets/matRad_StatisticsWidget.m new file mode 100644 index 000000000..8ac9e2780 --- /dev/null +++ b/gui/widgets/matRad_StatisticsWidget.m @@ -0,0 +1,113 @@ +classdef matRad_StatisticsWidget < matRad_Widget + % matRad_StatisticsWidget class to generate GUI widget to display plan + % statistics. + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + properties + selectedCube; + lockUpdate = false; + + end + + events + + end + + methods + + function this = matRad_StatisticsWidget(SelectedCube,handleParent) % use (varargin) ? + + matRad_cfg = MatRad_Config.instance(); + if nargin < 2 + + handleParent = figure(... + 'Units','normalized',... + 'Position',[0.005 0.05 0.495 0.45],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Statistics',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figStat',... + 'PaperSize',[20.99999864 29.69999902]); + + + end + this = this@matRad_Widget(handleParent); + this.selectedCube = SelectedCube; + end + + + function this=update(this) + + if this.lockUpdate + doUpdate = true; + if nargin == 2 + doUpdate = this.checkUpdateNecessary({'resultGUI','cst','pln'},evt); + end + + if ~doUpdate + return; + end + + if evalin('base','exist(''resultGUI'')') + this.showStatistics(); + end + end + end + end + + methods(Access = protected) + function this = createLayout(this) + h88 = this.widgetHandle; + this.createHandles(); + + end + end + + methods + function set.selectedCube(this,value) + this.selectedCube=value; + end + function showStatistics(this) + + resultGUI = evalin('base','resultGUI'); + pln = evalin('base','pln'); + cst = evalin('base','cst'); + doseCube = resultGUI.(this.selectedCube); + + if ~exist('refVol', 'var') + refVol = []; + end + + if ~exist('refGy', 'var') + refGy = []; + end + + qi = matRad_calcQualityIndicators(cst,pln,doseCube,refGy,refVol); + ixVoi = cellfun(@(c) c.Visible == 1,cst(:,5)); + qi = qi(ixVoi); + matRad_showQualityIndicators(this.widgetHandle,qi); + + end + + end +end \ No newline at end of file diff --git a/gui/widgets/matRad_StructureVisibilityWidget.m b/gui/widgets/matRad_StructureVisibilityWidget.m new file mode 100644 index 000000000..27e146ac5 --- /dev/null +++ b/gui/widgets/matRad_StructureVisibilityWidget.m @@ -0,0 +1,196 @@ +classdef matRad_StructureVisibilityWidget < matRad_Widget + % matRad_StructureVisibilityWidget class to generate GUI widget to set + % visibility of structure in viewing widget + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + methods + function this = matRad_StructureVisibilityWidget(handleParent) + if nargin < 1 + matRad_cfg = MatRad_Config.instance(); + handleParent = figure(... + 'Units','characters',... + 'Position',[200 10 30 30],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'MenuBar','none',... + 'Name','MatRad Structure Visibility',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + end + this = this@matRad_Widget(handleParent); + end + + function this=update(this,evt) + if evalin('base','exist(''ct'')') && evalin('base','exist(''cst'')') + updateStructureTable(this, evalin('base','cst')); + else + set(this.handles.legendTable,'String','no data loaded'); + end + + end + + end + + + methods (Access = protected) + function this = createLayout(this) + h86 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + % List box of stuctures that can be selected for display + h87 = uicontrol(... + 'Parent',h86,... + 'Units','normalized',... + 'Tooltip','Choose which structures should be displayed in the GUI',... + 'HorizontalAlignment','left',... + 'Max',1,... + 'Style','listbox',... + 'Value',1,... + 'Position',[0.02 0.01 0.97 0.98],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Callback',@(hObject,eventdata) legendTable_Callback(this,hObject,eventdata),... + 'Tag','legendTable'); + + this.createHandles(); + + end + end + + methods (Access = protected) + function legendTable_Callback(this, hObject, event) + % hObject handle to legendTable (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: contents = cellstr(get(hObject,'String')) returns legendTable contents as cell array + % contents{get(hObject,'Value')} returns selected item from legendTable + + if strcmp(get(hObject,'String'),'no data loaded') + return; + end + + handles = this.handles; + cst = evalin('base','cst'); + + idx = get(hObject,'Value'); + clr = dec2hex(round(cst{idx,5}.visibleColor(:)*255),2)'; + clr = ['#';clr(:)]'; + + %Get the string entries + tmpString = get(handles.legendTable,'String'); + + matRad_cfg = MatRad_Config.instance(); + + % html not supported in octave + if handles.VOIPlotFlag(idx) + handles.VOIPlotFlag(idx) = false; + cst{idx,5}.Visible = false; + switch matRad_cfg.env + case 'OCTAVE' + tmpString{idx} = ['☠' cst{idx,2}]; + otherwise + tmpString{idx} = ['
',cst{idx,2},'
']; + end + elseif ~handles.VOIPlotFlag(idx) + handles.VOIPlotFlag(idx) = true; + cst{idx,5}.Visible = true; + switch matRad_cfg.env + case 'OCTAVE' + tmpString{idx} = ['☑ ' cst{idx,2}]; + otherwise + tmpString{idx} = ['
',cst{idx,2},'
']; + end + end + + set(handles.legendTable,'String',tmpString); + + % update cst in workspace accordingly + assignin('base','cst',cst) + this.handles = handles; + this.changedWorkspace('cst'); + %UpdatePlot(handles) + end + + %Update cst with Visibility settings + function cst = updateStructureTable(this,cst) + handles=this.handles; + colorAssigned = true; + + + % check whether all structures have an assigned color + for i = 1:size(cst,1) + if ~isfield(cst{i,5},'visibleColor') + colorAssigned = false; + break; + elseif isempty(cst{i,5}.visibleColor) + colorAssigned = false; + break; + end + end + + % assign color if color assignment is not already present or inconsistent + if colorAssigned == false + m = 64; + colorStep = ceil(m/size(cst,1)); + colors = colorcube(colorStep*size(cst,1)); + % spread individual VOI colors in the colorcube color palette + colors = colors(1:colorStep:end,:); + + for i = 1:size(cst,1) + cst{i,5}.visibleColor = colors(i,:); + end + end + + matRad_cfg = MatRad_Config.instance(); + + for s = 1:size(cst,1) + handles.VOIPlotFlag(s) = cst{s,5}.Visible; + clr = dec2hex(round(cst{s,5}.visibleColor(:)*255),2)'; + clr = ['#';clr(:)]'; + % html is not supported in octave + + switch matRad_cfg.env + case 'OCTAVE' + if handles.VOIPlotFlag(s) + tmpString{s} = ["o " cst{s,2}]; + else + tmpString{s} = ["x " cst{s,2}]; + end + otherwise + if handles.VOIPlotFlag(s) + tmpString{s} = ['
',cst{s,2},'
']; + else + tmpString{s} = ['
',cst{s,2},'
']; + end + end + + + end + set(handles.legendTable,'String',tmpString); + this.handles = handles; + end + end +end + diff --git a/gui/widgets/matRad_ViewerOptionsWidget.m b/gui/widgets/matRad_ViewerOptionsWidget.m new file mode 100644 index 000000000..0913efb87 --- /dev/null +++ b/gui/widgets/matRad_ViewerOptionsWidget.m @@ -0,0 +1,942 @@ +classdef matRad_ViewerOptionsWidget < matRad_Widget + % matRad_ViewerOptionsWidget class to generate GUI widget to set + % options for the plan ViewingWidget + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + viewingWidgetHandle; + colormapLocked = false; + windowPresets; + end + + methods + function this = matRad_ViewerOptionsWidget(viewingWidgetHandle,handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 2 + handleParent = figure(... + 'Units','characters',... + 'Position',[170 15 30 30],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Viewer Options',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','menuViewerOption'); + + end + this = this@matRad_Widget(handleParent); + + handles = this.handles; + + %Set up the colormap selection box + availableColormaps = matRad_getColormap(); + set(handles.popupmenu_chooseColormap,'String',availableColormaps); + + % setup ct window list + % data and values from CERR https://github.com/adityaapte/CERR + windowNames = {'Custom','Full','Abd/Med', 'Head', 'Liver', 'Lung', 'Spine', 'Vrt/Bone'}; + windowCenter = {NaN, NaN, -10, 45, 80, -500, 30, 400}; + windowWidth = {NaN, NaN, 330, 125, 305, 1500, 300, 1500}; + windowPresets = cell2struct([windowNames', windowCenter', windowWidth'], {'name', 'center', 'width'},2); + + + this.windowPresets = windowPresets; + + selectionList = {windowPresets(:).name}; + set(handles.popupmenu_windowPreset,'String',selectionList(:)); + set(handles.popupmenu_windowPreset,'Value',1); + + this.handles=handles; + if nargin>=1 + this.viewingWidgetHandle=viewingWidgetHandle; + UpdateColormapOptions(this); + else + UpdateButtonState(this,'off'); + end + end + + function this = initialize(this) + end + + function this = update(this) + + try + % minVal=num2str(this.viewingWidgetHandle.dispWindow{selectionIndex,2}(1,1)); + % maxVal=num2str(this.viewingWidgetHandle.dispWindow{selectionIndex,2}(1,2)); + if isa(this.viewingWidgetHandle,'matRad_ViewingWidget') %... + % && (~strcmp(get(this.handles.txtMinVal,'String'),minVal) ... + % || ~strcmp(get(this.handles.txtMaxVal,'String'),maxVal)) %% new data is loaded + this.getFromViewingWidget(); + end + catch + end + this.UpdateColormapOptions(); + end + + % function viewingWidgetHandle=get.viewingWidgetHandle(this) + % viewingWidgetHandle=this.viewingWidgetHandle; + % end + + function set.viewingWidgetHandle(this,value) + %handles=this.handles; + if isa(value,'matRad_ViewingWidget') + this.viewingWidgetHandle=value; + + getFromViewingWidget(this); + + else + % disable all buttons + UpdateButtonState(this,'off'); + end + %this.handles=handles; + end + end + + methods (Access = protected) + function this = createLayout(this) + h98 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + %Create Main Grid layout + gridSize = [1 18]; + elSize = [0.9 0.9]; + [i,j] = ndgrid(1:gridSize(1),1:gridSize(2)); + gridPos = arrayfun(@(i,j) computeGridPos(this,[i j],gridSize,elSize),i,j,'UniformOutput',false); + + %Display info about current displayed cube + h84 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','Data Info:',... + 'Tooltip','Info about the currently displayed distribution',... + 'Style','text',... + 'HorizontalAlignment','left',... + 'Position',gridPos{1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','DataInfo'); + + %Text Minimum Value + h84 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','min value:',... + 'Tooltip','Minimum value of the currently displayed distribution',... + 'Style','text',... + 'HorizontalAlignment','left',... + 'Position',gridPos{2} .* [1 1 0.5 1],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','MinVal'); + + h84 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','',... + 'Tooltip','Minimum value of the currently displayed distribution',... + 'Style','text',... + 'HorizontalAlignment','left',... + 'Position',(gridPos{2} + [0.5 0 0 0]) .* [1 1 0.5 1],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtMinVal'); + + + %Text Maximum Value + h116 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','max value:',... + 'Tooltip','Maximum value of the currently displayed distribution',... + 'Style','text',... + 'HorizontalAlignment','left',... + 'Position',gridPos{3} .* [1 1 0.5 1],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','MaxVal'); + + h117 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','',... + 'Tooltip','Maximum value of the currently displayed distribution',... + 'Style','text',... + 'HorizontalAlignment','left',... + 'Position',(gridPos{3} + [0.5 0 0 0]) .* [1 1 0.5 1],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtMaxVal'); + + %Set IsoDose Levels + h85 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','Set IsoDose Levels',... + 'Tooltip','Set iso dose levels for displaying the distributions in the GUI',... + 'Position',gridPos{4},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)btnSetIsoDoseLevels_Callback(this,hObject,eventdata),... + 'Tag','btnSetIsoDoseLevels'); + + %Text Colorbar & Map section header + txt = sprintf('Choose to display the CT or the plan result &\nchoose a colormap'); + h84 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','Colorbar & -map:',... + 'Tooltip',txt,... + 'Style','text',... + 'HorizontalAlignment','left',... + 'Position',gridPos{5},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtColorbarMap'); + + %popUp Menu CT and plan result + h101 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String',{'CT (HU)','Plan Result'},... + 'Tooltip','Choose to display the CT or the plan result',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{6},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)popupmenu_chooseColorData_Callback(this,hObject,eventdata),... + 'Tag','popupmenu_chooseColorData'); + + %Popup Menu choose colormap + h104 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','Choose Colormap...',... + 'Tooltip','Choose a colormap for displaying the distributions in the GUI',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{7},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)popupmenu_chooseColormap_Callback(this,hObject,eventdata),... + 'Tag','popupmenu_chooseColormap'); + + %Text for CT window preset + h112 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','CT Window Presets N/A',... + 'Tooltip','Choose a customized CT window or a preset one',... + 'Style','text',... + 'Position',gridPos{8},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text_windowPreset' ); + + % Popup for CT window preset + h113 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String',{ 'Custom'; 'Full'; 'Abd/Med'; 'Head'; 'Liver'; 'Lung'; 'Spine'; 'Vrt/Bone' },... + 'Tooltip','Choose a customized CT window or a preset one',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{9},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)popupmenu_windowPreset_Callback(this,hObject,eventdata),... + 'Visible','on',... % Default should be off! + 'Tag','popupmenu_windowPreset'); + + %Text for CT window center + h99 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Window Center:',... + 'Tooltip','Set the window center',... + 'Style','text',... + 'Position',gridPos{10},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text_windowCenter' ); + + %Slider for CT window center + h102 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'SliderStep',[0.01 0.05],... + 'String','slider',... + 'Tooltip','Set the window center',... + 'Style','slider',... + 'Value',0.5,... + 'Position',gridPos{11} .* [1 1 0.7 1],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)slider_windowCenter_Callback(this,hObject,eventdata),... + 'Tag','slider_windowCenter'); + + %Edit CT window center + h107 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','0.5',... + 'Tooltip','Set the window center',... + 'Style','edit',... + 'Value',1,... + 'Position',gridPos{11} .* [1 1 0.25 1] + [0.7 0 0 0],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)edit_windowCenter_Callback(this,hObject,eventdata),... + 'Tag','edit_windowCenter'); + + %Text for CT window Width + h103 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Window Width:',... + 'Tooltip','Set the window width',... + 'Style','text',... + 'Position',gridPos{12},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text_windowWidth'); + + %slider for CT window Width + h114 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'SliderStep',[0.01 0.05],... + 'String','slider',... + 'Tooltip','Set the window width',... + 'Style','slider',... + 'Value',1,... + 'Position',gridPos{13} .* [1 1 0.7 1],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)slider_windowWidth_Callback(this, hObject, eventdata),... + 'Tag','slider_windowWidth'); + + %Edit for CT window Width + h108 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','1.0',... + 'Tooltip','Set the window width',... + 'Style','edit',... + 'Position',gridPos{13} .* [1 1 0.25 1] + [0.7 0 0 0],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)edit_windowWidth_Callback(this,hObject,eventdata),... + 'Tag','edit_windowWidth'); + + + %Text for CT window Range + h105 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Range:',... + 'Tooltip','Set the window range',... + 'Style','text',... + 'Position',gridPos{14},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text_windowRange' ); + + %Edit CT window Range + h106 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','0 1',... + 'Tooltip','Set the window range',... + 'Style','edit',... + 'Position',gridPos{15},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'BackgroundColor',[1 1 1],... + 'Callback',@(hObject,eventdata)edit_windowRange_Callback(this,hObject,eventdata),... + 'Tag','edit_windowRange'); + + %Text Dose overlay Opacity + h100 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Overlay opacity:',... + 'Tooltip','Set the opacity of the displayed distribution',... + 'Style','text',... + 'Position',gridPos{16},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','textDoseOpacity' ); + + % Slider Dose overlay Opacity + h109 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'SliderStep',[0.01 0.05],... + 'String','slider',... + 'Tooltip','Set the opacity of the displayed distribution',... + 'Style','slider',... + 'Value',0.6,... + 'Position',gridPos{17},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)sliderOpacity_Callback(this,hObject,eventdata),... + 'Tag','sliderOpacity'); + + %LockSettings + h115 = uicontrol(... + 'Parent',h98,... + 'Units','normalized',... + 'String','Lock Settings',... + 'Tooltip','Lock current viewer settings',... + 'Style','checkbox',... + 'Position',gridPos{18},... + 'Value',this.colormapLocked,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata)checkbox_lockColormap_Callback(this,hObject,eventdata),... + 'Tag','checkbox_lockColormap' ); + + + this.createHandles(); + end + + end + + methods + + % H101 + function popupmenu_chooseColorData_Callback(this,hObject, ~) + % hObject handle to popupmenu_chooseColorData (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_chooseColorData contents as cell array + % contents{get(hObject,'Value')} returns selected item from popupmenu_chooseColorData + + this.viewingWidgetHandle.colorData = hObject.Value; + UpdateColormapOptions(this); + end + + % H102 + function slider_windowCenter_Callback(this, hObject, event) + % hObject handle to slider_windowCenter (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'Value') returns position of slider + % get(hObject,'Min') and get(hObject,'Max') to determine range of slider + + handles = this.handles; + + + newCenter = get(hObject,'Value'); + range = get(handles.slider_windowWidth,'Value'); + selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + + this.viewingWidgetHandle.dispWindow{selectionIndex,1} = [newCenter-range/2 newCenter+range/2]; + + + + %handles.cBarChanged = true; + + this.handles = handles; + %UpdatePlot(handles); + UpdateColormapOptions(this); + end + + % H 104 + function popupmenu_chooseColormap_Callback(this,hObject, eventdata) + % hObject handle to popupmenu_chooseColormap (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_chooseColormap contents as cell array + % contents{get(hObject,'Value')} returns selected item from popupmenu_chooseColormap + + handles = this.handles; + + index = get(hObject,'Value'); + strings = get(hObject,'String'); + + selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + + switch selectionIndex + case 1 + this.viewingWidgetHandle.ctColorMap = strings{index}; + case 2 + this.viewingWidgetHandle.doseColorMap = strings{index}; + otherwise + end + + %handles.cBarChanged = true; + + this.handles = handles; + %UpdatePlot(handles); + this.UpdateColormapOptions(); + end + + % H106 + function edit_windowRange_Callback(this, hObject, eventdata) + % hObject handle to edit_windowRange (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'String') returns contents of edit_windowRange as text + % str2double(get(hObject,'String')) returns contents of edit_windowRange as a double + + handles = this.handles; + + selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + + vRange = str2num(get(hObject,'String')); + % matlab adds a zero in the beginning when text field is changed + if numel(vRange) == 3 + vRange = vRange(vRange~=0); + end + + this.viewingWidgetHandle.dispWindow{selectionIndex,1} = sort(vRange); + + this.handles = handles; + %UpdatePlot(handles); + this.UpdateColormapOptions(); + end + + % H107 + function edit_windowCenter_Callback(this, hObject, eventdata) + % hObject handle to edit_windowCenter (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'String') returns contents of edit_windowCenter as text + % str2double(get(hObject,'String')) returns contents of edit_windowCenter as a double + + + handles = this.handles; + + newCenter = str2double(get(hObject,'String')); + width = get(handles.slider_windowWidth,'Value'); + selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + this.viewingWidgetHandle.dispWindow{selectionIndex,1} = [newCenter-width/2 newCenter+width/2]; + + this.handles = handles; + % UpdatePlot(handles); + UpdateColormapOptions(this); + end + + % H108 + function edit_windowWidth_Callback(this, hObject, eventdata) + % hObject handle to edit_windowWidth (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'String') returns contents of edit_windowWidth as text + % str2double(get(hObject,'String')) returns contents of edit_windowWidth as a double + handles = this.handles; + + newWidth = str2double(get(hObject,'String')); + center = get(handles.slider_windowCenter,'Value'); + selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + this.viewingWidgetHandle.dispWindow{selectionIndex,1} = [center-newWidth/2 center+newWidth/2]; + %handles.cBarChanged = true; + + this.handles = handles; + %this.viewingWidgetHandle.UpdatePlot(); + %UpdatePlot(handles); + UpdateColormapOptions(this); + end + + % H109 + function sliderOpacity_Callback(this,hObject, eventdata) + % hObject handle to sliderOpacity (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + %handles = this.handles; + + this.viewingWidgetHandle.doseOpacity = get(hObject,'Value'); + + %this.handles = handles; + %UpdatePlot(handles); + + + end + + % H113 + function popupmenu_windowPreset_Callback(this, hObject, event) + % hObject handle to popupmenu_windowPreset (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_windowPreset contents as cell array + % contents{get(hObject,'Value')} returns selected item from popupmenu_windowPreset + handles = this.handles; + + selectionIndexCube = 1; % working on ct only + selectionIndexWindow = get(handles.popupmenu_windowPreset,'Value'); + newCenter = this.windowPresets(selectionIndexWindow).center; + newWidth = this.windowPresets(selectionIndexWindow).width; + + this.viewingWidgetHandle.dispWindow{selectionIndexCube,1} = [newCenter - newWidth/2 newCenter + newWidth/2]; + + this.handles = handles; + %UpdatePlot(handles); + UpdateColormapOptions(this); + end + + % H114 + function slider_windowWidth_Callback(this,hObject, eventdata) + % hObject handle to slider_windowWidth (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'Value') returns position of slider + % get(hObject,'Min') and get(hObject,'Max') to determine range of slider + + handles = this.handles; + + newWidth = get(hObject,'Value'); + center = get(handles.slider_windowCenter,'Value'); + selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + this.viewingWidgetHandle.dispWindow{selectionIndex,1} = [center-newWidth/2 center+newWidth/2]; + + this.handles = handles; + %UpdatePlot(handles); + UpdateColormapOptions(this); + end + + % H115 Callback + function checkbox_lockColormap_Callback(this, hObject, ~) + % hObject handle to checkbox_lockColormap (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of checkbox_lockColormap + + handles = this.handles; + this.colormapLocked = get(hObject,'Value'); + if isa(this.viewingWidgetHandle,'matRad_ViewingWidget') + this.viewingWidgetHandle.lockColorSettings=this.colormapLocked; + end + if this.colormapLocked + state = 'Off'; %'Inactive'; + else + state = 'On'; + end + + set(handles.popupmenu_chooseColorData,'Enable',state); + set(handles.popupmenu_windowPreset,'Enable',state); + set(handles.slider_windowWidth,'Enable',state); + set(handles.slider_windowCenter,'Enable',state); + set(handles.edit_windowWidth,'Enable',state); + set(handles.edit_windowCenter,'Enable',state); + set(handles.edit_windowRange,'Enable',state); + set(handles.popupmenu_chooseColormap,'Enable',state); + + this.handles = handles; + end + + % button: set iso dose levels + function btnSetIsoDoseLevels_Callback(this,hObject, eventdata) + handles = this.handles; + prompt = {['Enter iso dose levels in [Gy]. Enter space-separated numbers, e.g. 1.5 2 3 4.98. Enter 0 to use default values']}; + if isequal(this.viewingWidgetHandle.IsoDose_Levels,0) || ~isvector(this.viewingWidgetHandle.IsoDose_Levels) || any(~isnumeric(this.viewingWidgetHandle.IsoDose_Levels)) || any(isnan(this.viewingWidgetHandle.IsoDose_Levels)) + defaultLine = {'1 2 3 '}; + else + if isrow(this.viewingWidgetHandle.IsoDose_Levels) + defaultLine = cellstr(num2str(this.viewingWidgetHandle.IsoDose_Levels,'%.2g ')); + else + defaultLine = cellstr(num2str(this.viewingWidgetHandle.IsoDose_Levels','%.2g ')); + end + end + + try + Input = inputdlg(prompt,'Set iso dose levels ', [1 70],defaultLine); + if ~isempty(Input) + this.viewingWidgetHandle.IsoDose_Levels = (sort(str2num(Input{1}))); + if length(this.viewingWidgetHandle.IsoDose_Levels) == 1 && (this.viewingWidgetHandle.IsoDose_Levels(1) ~= 0) + this.viewingWidgetHandle.IsoDose_Levels = [this.viewingWidgetHandle.IsoDose_Levels this.viewingWidgetHandle.IsoDose_Levels]; + end + %handles.IsoDose.NewIsoDoseFlag = true; + end + catch + warning('Couldnt parse iso dose levels - using default values'); + this.viewingWidgetHandle.IsoDose_Levels = 0; + end + this.handles = handles; + %handles = updateIsoDoseLineCache(handles); + %this.viewingWidgetHandle.NewIsoDoseFlag = false; + %UpdatePlot(handles); + end + + % Save and update the Colormap + function UpdateColormapOptions(this) + handles=this.handles; + if this.colormapLocked + return; + end + + %selectionIndex = this.handles.popupmenu_chooseColorData; + + % save the lock state + updateLockState = this.viewingWidgetHandle.lockUpdate; + %colorSettingLockState = this.viewingWidgetHandle.lockColorSettings; + this.viewingWidgetHandle.lockUpdate = true; + %this.viewingWidgetHandle.lockColorSettings = true; + + + cMapOptionsSelectList = {}; %get(handles.popupmenu_chooseColorData,'String'); + %Set up the colordata selection box + if evalin('base','exist(''ct'')') + + ct = evalin('base','ct'); + + if isfield(ct, 'cubeHU') + cMapOptionsSelectList{end+1} = 'CT (HU)'; + set(handles.popupmenu_windowPreset,'Visible','on'); + set(handles.text_windowPreset,'String','CT Window Preset'); + else + cMapOptionsSelectList{end+1} = 'CT (ED)'; + set(handles.popupmenu_windowPreset,'Visible','off'); + set(handles.text_windowPreset,'String','CT Window Presets N/A'); + end + + + if evalin('base','exist(''resultGUI'')') + cMapOptionsSelectList{end+1} = 'Plan result'; + elseif this.viewingWidgetHandle.colorData>1 + this.viewingWidgetHandle.colorData = 1; + end + else %no data is loaded + %disable all buttons + UpdateButtonState(this,'off'); + cMapOptionsSelectList = {'No Data'}; + end + + + set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList) + + selectionIndex=this.viewingWidgetHandle.colorData; + set(handles.popupmenu_chooseColorData,'Value',selectionIndex); + + %selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); + + if ~isempty(this.viewingWidgetHandle.dispWindow{selectionIndex,2}) + set(handles.txtMinVal,'String', num2str(this.viewingWidgetHandle.dispWindow{selectionIndex,2}(1,1))); + set(handles.txtMaxVal,'String', num2str(this.viewingWidgetHandle.dispWindow{selectionIndex,2}(1,2))); + end + cMapStrings = get(handles.popupmenu_chooseColormap,'String'); + + + try + if selectionIndex == 1 + ct = evalin('base','ct'); + currentMap = this.viewingWidgetHandle.ctColorMap; + window = this.viewingWidgetHandle.dispWindow{selectionIndex,1}; + if isfield(ct, 'cubeHU') + minMax = [min(ct.cubeHU{1}(:)) max(ct.cubeHU{1}(:))]; + else + minMax = [min(ct.cube{1}(:)) max(ct.cube{1}(:))]; + end + % adjust value for custom window to current + this.windowPresets(1).width = max(window) - min(window); + this.windowPresets(1).center = mean(window); + % update full window information + this.windowPresets(2).width = minMax(2) - minMax(1); + this.windowPresets(2).center = mean(minMax); + elseif selectionIndex == 2 + result = evalin('base','resultGUI'); + dose = result.(this.viewingWidgetHandle.SelectedDisplayOption); + currentMap = this.viewingWidgetHandle.doseColorMap; + minMax = [min(dose(:)) max(dose(:))]; + window = this.viewingWidgetHandle.dispWindow{selectionIndex,1}; + else + window = [0 1]; + minMax = window; + currentMap = 'bone'; + end + catch + window = [0 1]; + minMax = window; + currentMap = 'bone'; + end + + valueRange = minMax(2) - minMax(1); + + windowWidth = window(2) - window(1); + windowCenter = mean(window); + + %This are some arbritrary settings to configure the sliders + sliderCenterMinMax = [minMax(1)-valueRange/2 minMax(2)+valueRange/2]; + sliderWidthMinMax = [0 valueRange*2]; + + %if we have selected a value outside this range, we adapt the slider + %windows + if windowCenter < sliderCenterMinMax(1) + sliderCenterMinMax(1) = windowCenter; + end + if windowCenter > sliderCenterMinMax(2) + sliderCenterMinMax(2) = windowCenter; + end + if windowWidth < sliderWidthMinMax(1) + sliderWidthMinMax(1) = windowWidth; + end + if windowWidth > sliderWidthMinMax(2) + sliderWidthMinMax(2) = windowWidth; + end + + set(handles.edit_windowCenter,'String',num2str(windowCenter,3)); + set(handles.edit_windowWidth,'String',num2str(windowWidth,3)); + set(handles.edit_windowRange,'String',num2str(window,4)); + set(handles.slider_windowCenter,'Min',sliderCenterMinMax(1),'Max',sliderCenterMinMax(2),'Value',windowCenter); + set(handles.slider_windowWidth,'Min',sliderWidthMinMax(1),'Max',sliderWidthMinMax(2),'Value',windowWidth); + + cMapPopupIndex = find(strcmp(currentMap,cMapStrings)); + set(handles.popupmenu_chooseColormap,'Value',cMapPopupIndex); + this.viewingWidgetHandle.lockUpdate=updateLockState; + this.handles=handles; + end + %Update button enable/disables + function UpdateButtonState(this,state) + % state is on or off + handles=this.handles; + + set(handles.checkbox_lockColormap,'Value',this.colormapLocked); + if this.colormapLocked + state='off'; + end + + %set(handles.checkbox_lockColormap,'Enable',state); + set(handles.popupmenu_chooseColorData,'Enable',state); + set(handles.popupmenu_windowPreset,'Enable',state); + set(handles.slider_windowWidth,'Enable',state); + set(handles.slider_windowCenter,'Enable',state); + set(handles.edit_windowWidth,'Enable',state); + set(handles.edit_windowCenter,'Enable',state); + set(handles.edit_windowRange,'Enable',state); + set(handles.popupmenu_chooseColormap,'Enable',state); + set(handles.sliderOpacity,'Enable',state); + set(handles.btnSetIsoDoseLevels,'Enable',state); + + this.handles=handles; + end + + %get Viewing settings from the Viewer Widget + function getFromViewingWidget(this) + if evalin('base','exist(''ct'')') + % enable all buttons + UpdateButtonState(this,'on'); + else + % no data loaded, disable all buttons + UpdateButtonState(this,'off'); + end + + handles=this.handles; + + % get the default value from the viewer widget + set(handles.popupmenu_chooseColorData,'Value',this.viewingWidgetHandle.colorData); + + availableColormaps = matRad_getColormap(); + currentCtMapIndex = find(strcmp(availableColormaps,this.viewingWidgetHandle.ctColorMap)); + currentDoseMapIndex = find(strcmp(availableColormaps,this.viewingWidgetHandle.doseColorMap)); + + if evalin('base','exist(''resultGUI'')') % state 3 + set(handles.popupmenu_chooseColormap,'Value',currentDoseMapIndex); + else + set(handles.popupmenu_chooseColormap,'Value',currentCtMapIndex); + end + this.handles=handles; + end + end + +end + diff --git a/gui/widgets/matRad_ViewingWidget.m b/gui/widgets/matRad_ViewingWidget.m new file mode 100644 index 000000000..b19af6acc --- /dev/null +++ b/gui/widgets/matRad_ViewingWidget.m @@ -0,0 +1,1328 @@ +classdef matRad_ViewingWidget < matRad_Widget + % matRad_ViewingWidget class to generate GUI widget to display plan + % dose distributions and ct + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + plane = 3; + slice = 1; + maxSlice; + SliceSliderStep; + selectedBeam = 1; + numOfBeams=1; + profileOffset=0; + OffsetSliderStep; + OffsetMinMax; + typeOfPlot = 1; + colorData; + doseColorMap = 'jet'; + ctColorMap = 'bone'; + cMapSize = 64; + plotCT = true; + plotContour = true; + plotIsoCenter = true; + plotPlan = false; + plotDose = true; + plotIsoDoseLines = true; + plotIsoDoseLinesLabels = false; + plotLegend = false; + plotColorBar = true; + ProfileType = 'lateral'; + SelectedDisplayOption = 'physicalDose'; + SelectedDisplayAllOptions = ''; + CutOffLevel = 0.01; + dispWindow = cell(3,2); + doseOpacity = 0.6; + IsoDose_Levels= []; + NewIsoDoseFlag = true; + cBarHandle; + dcmHandle; + panHandle; + zoomHandle; + legendHandle; + scrollHandle; + lockUpdate = false; + lockColorSettings = false; + %plotlegend=false; + end + + properties (SetAccess=private) + + IsoDose_Contours; %only updated from within this class + VOIPlotFlag; + DispInfo; + AxesHandlesVOI; + cst; + vIsoCenter; + sliceContourLegend; + end + + events + plotUpdated + end + + methods + function this = matRad_ViewingWidget(handleParent) + if nargin < 1 + handleParent = figure(... + 'Units','normalized',... + 'Position',[0.3 0.2 0.4 0.6],... + 'Visible','on',... + 'Color',[0.501960784313725 0.501960784313725 0.501960784313725],... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Viewing',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + + end + + this = this@matRad_Widget(handleParent); + + matRad_cfg = MatRad_Config.instance(); + + if nargin < 1 + % create the handle objects if there's no parent + this.scrollHandle = this.widgetHandle; + + % only available in MATLAB + if matRad_cfg.isMatlab + this.dcmHandle = datacursormode(this.widgetHandle); + this.panHandle = pan(this.widgetHandle); + this.zoomHandle = zoom(this.widgetHandle); + end + end + this.update(); + end + + function this=initialize(this) +% updateIsoDoseLineCache(this); + %update(this); + + end + + function this=update(this,evt) + if ~this.lockUpdate + + doUpdate = false; + if nargin == 2 + %At pln changes and at cst/cst (for Isocenter and new settings) + %we need to update + doUpdate = this.checkUpdateNecessary({'pln','ct','cst','resultGUI'},evt); + end + + if ~doUpdate || this.checkUpdateNecessary({'pln','ct','resultGUI'},evt) + this.initValues(); + end + + this.updateValues(); + this.updateIsoDoseLineCache(); + % Update plot only if there are changes to ct, resultGUI. + % for matRad Gui startup/ intializing viewing widget + % evt does not exist, then catch segment + + try + if this.checkUpdateNecessary({'ct','resultGUI'},evt) + this.UpdatePlot(); + end + catch + this.UpdatePlot(); + end + end + + end + + function notifyPlotUpdated(obj) + % handle environment + matRad_cfg = MatRad_Config.instance(); + switch matRad_cfg.env + case 'MATLAB' + notify(obj, 'plotUpdated'); + case 'OCTAVE' + matRad_notifyOctave(obj, 'plotUpdated'); + end + + end + + %% SET FUNCTIONS + function set.plane(this,value) + this.plane=value; + this.UpdatePlot(); + end + + function set.slice(this,value) + % project to allowed set (between min and max value) + newSlice = max(value,1); + if evalin('base','exist(''ct'')') + ct=evalin('base','ct'); + newSlice = min(newSlice,ct.cubeDim(this.plane)); + else + newSlice=1; + end + + this.slice=newSlice; + this.UpdatePlot(); + end + + function set.maxSlice(this,value) + this.maxSlice=value; + this.UpdatePlot(); + end + + function set.SliceSliderStep(this,value) + this.SliceSliderStep=value; + this.UpdatePlot(); + end + + function set.selectedBeam(this,value) + this.selectedBeam=value; + this.UpdatePlot(); + end + + function set.numOfBeams(this,value) + this.numOfBeams=value; + this.UpdatePlot(); + end + + function set.profileOffset(this,value) + this.profileOffset=value; + this.UpdatePlot(); + end + + function set.OffsetSliderStep(this,value) + this.OffsetSliderStep=value; + this.UpdatePlot(); + end + + function set.OffsetMinMax(this,value) + this.OffsetMinMax=value; + this.UpdatePlot(); + end + + + function set.typeOfPlot(this,value) + this.typeOfPlot=value; + this.UpdatePlot(); + end + + function set.colorData(this,value) + this.colorData=value; + this.UpdatePlot(); + end + + function set.doseColorMap(this,value) + this.doseColorMap=value; + this.UpdatePlot(); + end + + function set.ctColorMap(this,value) + this.ctColorMap=value; + this.UpdatePlot(); + end + + function set.cMapSize(this,value) + this.cMapSize=value; + this.UpdatePlot(); + end + + function set.plotCT(this,value) + this.plotCT=value; + this.UpdatePlot(); + end + + function set.plotContour(this,value) + this.plotContour=value; + this.UpdatePlot(); + end + + function set.plotIsoCenter(this,value) + this.plotIsoCenter=value; + this.UpdatePlot(); + end + + function set.plotPlan(this,value) + this.plotPlan=value; + this.UpdatePlot(); + end + + function set.plotDose(this,value) + this.plotDose=value; + this.UpdatePlot(); + end + + function set.plotIsoDoseLines(this,value) + this.plotIsoDoseLines=value; + this.UpdatePlot(); + end + + function set.plotIsoDoseLinesLabels(this,value) + this.plotIsoDoseLinesLabels=value; + this.UpdatePlot(); + end + + function set.plotLegend(this,value) + this.plotLegend=value; + this.legendToggleFunction(); + end + + function set.plotColorBar(this,value) + this.plotColorBar=value; + this.colorBarToggleFunction(); + end + + function set.ProfileType(this,value) + this.ProfileType=value; + this.UpdatePlot(); + end + + function set.SelectedDisplayOption(this,value) + this.SelectedDisplayOption=value; + this.updateValues(); + this.UpdatePlot(); + end + + function set.SelectedDisplayAllOptions(this,value) + this.SelectedDisplayAllOptions=value; + this.UpdatePlot(); + end + + + function set.CutOffLevel(this,value) + this.CutOffLevel=value; + this.UpdatePlot(); + end + + function set.dispWindow(this,value) + this.dispWindow=value; + this.UpdatePlot(); + end + + function set.doseOpacity(this,value) + this.doseOpacity=value; + this.UpdatePlot(); + end + + function set.IsoDose_Levels(this,value) + this.IsoDose_Levels=value; + updateIsoDoseLineCache(this); + this.UpdatePlot(); + end + + function set.IsoDose_Contours(this,value) + this.IsoDose_Contours=value; + this.UpdatePlot(); + end + + function set.NewIsoDoseFlag(this,value) + this.NewIsoDoseFlag=value; + this.updateIsoDoseLineCache(); + this.UpdatePlot(); + end + + function set.dcmHandle(this,value) + this.dcmHandle=value; + set(this.dcmHandle,'DisplayStyle','window'); + %Add the callback for the datacursor display + set(this.dcmHandle,'UpdateFcn',@(hObject, eventdata)dataCursorUpdateFunction(this, hObject, eventdata)); + end + + function set.scrollHandle(this,value) + this.scrollHandle=value; + % set callback for scroll wheel function + set(this.scrollHandle,'WindowScrollWheelFcn',@(src,event)matRadScrollWheelFcn(this,src,event)); + end + end + %% + methods(Access = protected) + function this = createLayout(this) + %Viewer Widget + h88 = this.widgetHandle; + + h89 = axes(... + 'Parent',h88,... + 'XTick',[0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1],... + 'XTickLabel',{ '0'; '0.1'; '0.2'; '0.3'; '0.4'; '0.5'; '0.6'; '0.7'; '0.8'; '0.9'; '1' },... + 'YTick',[0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1],... + 'YTickLabel',{ '0'; '0.1'; '0.2'; '0.3'; '0.4'; '0.5'; '0.6'; '0.7'; '0.8'; '0.9'; '1' },... + 'Position',[0.0718390804597701 0.0654391371340524 0.902298850574712 0.899121725731895],... + 'Tag','axesFig'); + + %Title + h90 = get(h89,'title'); + + set(h90,... + 'Parent',h89,... + 'Units','data',... + 'FontUnits','points',... + 'Color',[0 0 0],... + 'Position',[0.500000554441759 1.00453467465753 0.5],... + 'PositionMode','auto',... + 'Interpreter','tex',... + 'Rotation',0,... + 'RotationMode','auto',... + 'FontName','Helvetica',... + 'FontSize',10.593,... + 'FontAngle','normal',... + 'FontWeight','normal',... + 'HorizontalAlignment','center',... + 'HorizontalAlignmentMode','auto',... + 'VerticalAlignment','bottom',... + 'VerticalAlignmentMode','auto',... + 'EdgeColor','none',... + 'LineStyle','-',... + 'LineWidth',0.5,... + 'BackgroundColor','none',... + 'Margin',2,... + 'Clipping','off',... + 'XLimInclude','on',... + 'YLimInclude','on',... + 'ZLimInclude','on',... + 'Visible','on',... + 'HandleVisibility','off',... + 'BusyAction','queue',... + 'Interruptible','on',... + 'HitTest','on'); + + %X Label + h91 = get(h89,'xlabel'); + + set(h91,... + 'Parent',h89,... + 'Units','data',... + 'FontUnits','points',... + 'Color',[0.15 0.15 0.15],... + 'Position',[0.500000476837158 -0.0373767115122652 0],... + 'PositionMode','auto',... + 'Interpreter','tex',... + 'Rotation',0,... + 'RotationMode','auto',... + 'FontName','CMU Serif',... + 'FontSize',10.593,... + 'FontAngle','normal',... + 'FontWeight','normal',... + 'HorizontalAlignment','center',... + 'HorizontalAlignmentMode','auto',... + 'VerticalAlignment','top',... + 'VerticalAlignmentMode','auto',... + 'EdgeColor','none',... + 'LineStyle','-',... + 'LineWidth',0.5,... + 'BackgroundColor','none',... + 'Margin',3,... + 'Clipping','off',... + 'XLimInclude','on',... + 'YLimInclude','on',... + 'ZLimInclude','on',... + 'Visible','on',... + 'HandleVisibility','off',... + 'BusyAction','queue',... + 'Interruptible','on',... + 'HitTest','on'); + + %Y label + h92 = get(h89,'ylabel'); + + set(h92,... + 'Parent',h89,... + 'Units','data',... + 'FontUnits','points',... + 'Color',[0.15 0.15 0.15],... + 'Position',[-0.0474647368237942 0.500000476837158 0],... + 'PositionMode','auto',... + 'Interpreter','tex',... + 'Rotation',90,... + 'RotationMode','auto',... + 'FontName','CMU Serif',... + 'FontSize',10.593,... + 'FontAngle','normal',... + 'FontWeight','normal',... + 'HorizontalAlignment','center',... + 'HorizontalAlignmentMode','auto',... + 'VerticalAlignment','bottom',... + 'VerticalAlignmentMode','auto',... + 'EdgeColor','none',... + 'LineStyle','-',... + 'LineWidth',0.5,... + 'BackgroundColor','none',... + 'Margin',3,... + 'Clipping','off',... + 'XLimInclude','on',... + 'YLimInclude','on',... + 'ZLimInclude','on',... + 'Visible','on',... + 'HandleVisibility','off',... + 'BusyAction','queue',... + 'Interruptible','on',... + 'HitTest','on'); + + %Z label + h93 = get(h89,'zlabel'); + + set(h93,... + 'Parent',h89,... + 'Units','data',... + 'FontUnits','points',... + 'Color',[0.15 0.15 0.15],... + 'Position',[0 0 0],... + 'PositionMode','auto',... + 'Interpreter','tex',... + 'Rotation',0,... + 'RotationMode','auto',... + 'FontName','CMU Serif',... + 'FontSize',10,... + 'FontAngle','normal',... + 'FontWeight','normal',... + 'HorizontalAlignment','left',... + 'HorizontalAlignmentMode','auto',... + 'VerticalAlignment','middle',... + 'VerticalAlignmentMode','auto',... + 'EdgeColor','none',... + 'LineStyle','-',... + 'LineWidth',0.5,... + 'BackgroundColor','none',... + 'Margin',3,... + 'Clipping','off',... + 'XLimInclude','on',... + 'YLimInclude','on',... + 'ZLimInclude','on',... + 'Visible','off',... + 'HandleVisibility','off',... + 'BusyAction','queue',... + 'Interruptible','on',... + 'HitTest','on'); + + this.createHandles(); + + end + end + + methods + function UpdatePlot(this) + if this.lockUpdate + return + end + + matRad_cfg = MatRad_Config.instance(); + + handles = this.handles; + + %profile on; + axes(handles.axesFig); + + % this is necessary to prevent multiple callbacks of update plot drawing on + % top of each other in matlab <2014 + drawnow; + + defaultFontSize = 8; + currAxes = axis(handles.axesFig); + AxesHandlesVOI = cell(0); + + AxesHandlesCT_Dose = cell(0); + AxesHandlesIsoDose = cell(0); + + if evalin('base','exist(''ct'')') && evalin('base','exist(''pln'')') + ct = evalin('base','ct'); + pln = evalin('base','pln'); + else + cla(handles.axesFig, 'reset') + return + end + + %% If resultGUI exists, then an optimization has been performed + if evalin('base','exist(''resultGUI'')') + Result = evalin('base','resultGUI'); + end + + %% set and get required variables + %plane = get(handles.popupPlane,'Value'); + %slice = round(get(handles.sliderSlice,'Value')); + hold(handles.axesFig,'on'); + if this.typeOfPlot==1 %get(handles.popupTypeOfPlot,'Value')==1 + set(handles.axesFig,'YDir','Reverse'); + end + + selectIx = this.colorData; %get(handles.popupmenu_chooseColorData,'Value'); + + cla(handles.axesFig); + %% plot ct - if a ct cube is available and type of plot is set to 1 and not 2; 1 indicate cube plotting and 2 profile plotting + if ~isempty(ct) && this.typeOfPlot==1 + + if selectIx == 2 + ctIx = 1; + else + ctIx = selectIx; + end + if isfield(ct, 'cubeHU') + plotCtCube = ct.cubeHU; + else + plotCtCube = ct.cube; + end + ctMap = matRad_getColormap(this.ctColorMap,this.cMapSize); + +% if isempty(this.dispWindow{ctIx,2}) +% this.dispWindow{ctIx,2} = [min(reshape([ct.cubeHU{:}],[],1)) max(reshape([ct.cubeHU{:}],[],1))]; +% end + + if this.plotCT %get(handles.radiobtnCT,'Value') + [AxesHandlesCT_Dose{end+1},~,~] = matRad_plotCtSlice(handles.axesFig,plotCtCube,1,this.plane,this.slice,ctMap,this.dispWindow{ctIx,1}); + + % plot colorbar? If 1 the user asked for the CT. + % not available in octave + if strcmp(matRad_cfg.env,'MATLAB') && this.colorData == 1 + %Plot the colorbar + this.cBarHandle = matRad_plotColorbar(handles.axesFig,ctMap,this.dispWindow{ctIx,1},'fontsize',defaultFontSize); + + if this.plotColorBar + set(this.cBarHandle,'Visible','on') + else + set(this.cBarHandle,'Visible','off') + end + + %adjust lables + if isfield(ct,'cubeHU') + set(get(this.cBarHandle,'ylabel'),'String', 'Hounsfield Units','fontsize',defaultFontSize); + else + set(get(this.cBarHandle,'ylabel'),'String', 'Electron Density','fontsize',defaultFontSize); + end + % do not interprete as tex syntax + set(get(this.cBarHandle,'ylabel'),'interpreter','none'); + end + end + end + + %% plot dose cube + if this.typeOfPlot== 1 && exist('Result','var') % handles.State >= 1 && + doseMap = matRad_getColormap(this.doseColorMap,this.cMapSize); + doseIx = 2; + + dose = Result.(this.SelectedDisplayOption); + + % dose colorwash + if ~isempty(dose) && ~isvector(dose) + + if this.plotDose + [doseHandle,~,~] = matRad_plotDoseSlice(handles.axesFig,dose,this.plane,this.slice,this.CutOffLevel,this.doseOpacity,doseMap,this.dispWindow{doseIx,1}); + AxesHandlesCT_Dose{end+1} = doseHandle; + end + + % plot colorbar + if matRad_cfg.isMatlab && this.colorData > 1 + %Plot the colorbar + this.cBarHandle = matRad_plotColorbar(handles.axesFig,doseMap,this.dispWindow{selectIx,1},'fontsize',defaultFontSize); + + if this.plotColorBar + set(this.cBarHandle,'Visible','on') + else + set(this.cBarHandle,'Visible','off') + end + %adjust lables + Idx = find(strcmp(this.SelectedDisplayOption,this.DispInfo(:,1))); + set(get(this.cBarHandle,'ylabel'),'String', [this.DispInfo{Idx,1} ' ' this.DispInfo{Idx,3} ],'fontsize',defaultFontSize); + % do not interprete as tex syntax + set(get(this.cBarHandle,'ylabel'),'interpreter','none'); + end + end + + + %% plot iso dose lines + if this.plotIsoDoseLines + plotLabels = this.plotIsoDoseLinesLabels; +% +% %Sanity Check for Contours, which actually should have been +% %computed before calling UpdatePlot +% if isempty(this.IsoDose_Contours) %~isfield(handles.IsoDose,'Contours') +% try +% this.IsoDose_Contours = matRad_computeIsoDoseContours(dose,this.IsoDose_Levels); +% catch +% %If the computation didn't work, we set the field to +% %empty, which will force matRad_plotIsoDoseLines to use +% %matlabs contour function instead of repeating the +% %failing computation every time +% this.IsoDose_Contours = []; +% warning('Could not compute isodose lines! Will try slower contour function!'); +% end +% end + AxesHandlesIsoDose = matRad_plotIsoDoseLines(handles.axesFig,dose,this.IsoDose_Contours,this.IsoDose_Levels,plotLabels,this.plane,this.slice,doseMap,this.dispWindow{doseIx,1},'LineWidth',1.5); + end + end + + %% plot VOIs + if this.plotContour && this.typeOfPlot==1 && exist('ct','var') %&& get(handles.radiobtnContour,'Value') && handles.State>0 + [AxVOI, this.sliceContourLegend] = matRad_plotVoiContourSlice(handles.axesFig,this.cst,ct,1,this.VOIPlotFlag,this.plane,this.slice,[],'LineWidth',2); + AxesHandlesVOI = [AxesHandlesVOI AxVOI]; + end + this.AxesHandlesVOI=AxesHandlesVOI; + + %% Set axis labels and plot iso center + matRad_plotAxisLabels(handles.axesFig,ct,this.plane,this.slice,defaultFontSize); + + if this.plotIsoCenter && this.typeOfPlot == 1 && ~isempty(pln) %get(handles.radioBtnIsoCenter,'Value') == 1 + hIsoCenterCross = matRad_plotIsoCenterMarker(handles.axesFig,pln,ct,this.plane,this.slice); + end + + if this.plotPlan && ~isempty(pln) %get(handles.radiobtnPlan,'value') == 1 + matRad_plotProjectedGantryAngles(handles.axesFig,pln,ct,this.plane); + end + + %set axis ratio + ratios = [1/ct.resolution.x 1/ct.resolution.y 1/ct.resolution.z]; + set(handles.axesFig,'DataAspectRatioMode','manual'); + if this.plane == 1 + res = [ratios(3) ratios(2)]./max([ratios(3) ratios(2)]); + set(handles.axesFig,'DataAspectRatio',[res 1]) + elseif this.plane == 2 % sagittal plane + res = [ratios(3) ratios(1)]./max([ratios(3) ratios(1)]); + set(handles.axesFig,'DataAspectRatio',[res 1]) + elseif this.plane == 3 % Axial plane + res = [ratios(2) ratios(1)]./max([ratios(2) ratios(1)]); + set(handles.axesFig,'DataAspectRatio',[res 1]) + end + + + %% profile plot + if this.typeOfPlot == 2 && exist('Result','var') + % set SAD + fileName = [pln.radiationMode '_' pln.machine]; + try + load(fileName); + SAD = machine.meta.SAD; + catch + error(['Could not find the following machine file: ' fileName ]); + end + + % clear view and initialize some values + cla(handles.axesFig,'reset') + set(handles.axesFig,'YDir','normal'); + ylabel('{\color{black}dose [Gy]}') + cColor={'black','green','magenta','cyan','yellow','red','blue'}; + + % Rotate the system into the beam. + % passive rotation & row vector multiplication & inverted rotation requires triple matrix transpose + rotMat_system_T = transpose(matRad_getRotationMatrix(pln.propStf.gantryAngles(this.selectedBeam),pln.propStf.couchAngles(this.selectedBeam))); + + if strcmp(this.ProfileType,'longitudinal') + sourcePointBEV = [this.profileOffset -SAD 0]; + targetPointBEV = [this.profileOffset SAD 0]; + elseif strcmp(this.ProfileType,'lateral') + sourcePointBEV = [-SAD this.profileOffset 0]; + targetPointBEV = [ SAD this.profileOffset 0]; + end + + rotSourcePointBEV = sourcePointBEV * rotMat_system_T; + rotTargetPointBEV = targetPointBEV * rotMat_system_T; + + % perform raytracing on the central axis of the selected beam, use unit + % electron density for plotting against the geometrical depth + [~,l,rho,~,ix] = matRad_siddonRayTracer(pln.propStf.isoCenter(this.selectedBeam,:),ct.resolution,rotSourcePointBEV,rotTargetPointBEV,{0*ct.cubeHU{1}+1}); + d = [0 l .* rho{1}]; + % Calculate accumulated d sum. + vX = cumsum(d(1:end-1)); + + % plot physical dose + %Content =this.SelectedDisplayOption; %get(this.popupDisplayOption,'String'); + SelectedCube = this.SelectedDisplayOption; %Content{get(this.popupDisplayOption,'Value')}; + if sum(strcmp(SelectedCube,{'physicalDose','effect','RBExDose','alpha','beta','RBE'})) > 0 + Suffix = ''; + else + Idx = find(SelectedCube == '_'); + Suffix = SelectedCube(Idx:end); + end + + mPhysDose = Result.(['physicalDose' Suffix]); + PlotHandles{1} = plot(handles.axesFig,vX,mPhysDose(ix),'color',cColor{1,1},'LineWidth',3); hold(handles.axesFig,'on'); + PlotHandles{1,2} ='physicalDose'; + ylabel(handles.axesFig,'dose in [Gy]'); + set(handles.axesFig,'FontSize',defaultFontSize); + + % plot counter + Cnt=2; + + if isfield(Result,['RBE' Suffix]) + + %disbale specific plots + %this.DispInfo{6,2}=0; + %this.DispInfo{5,2}=0; + %this.DispInfo{2,2}=0; + + % generate two lines for ylabel + StringYLabel1 = '\fontsize{8}{\color{red}RBE x dose [Gy(RBE)] \color{black}dose [Gy] '; + StringYLabel2 = ''; + for i=1:1:size(this.DispInfo,1) + if this.DispInfo{i,2} && sum(strcmp(this.DispInfo{i,1},{['effect' Suffix],['alpha' Suffix],['beta' Suffix]})) > 0 + %physicalDose is already plotted and RBExD vs RBE is plotted later with plotyy + if ~strcmp(this.DispInfo{i,1},['RBExDose' Suffix]) &&... + ~strcmp(this.DispInfo{i,1},['RBE' Suffix]) && ... + ~strcmp(this.DispInfo{i,1},['physicalDose' Suffix]) + + mCube = Result.([this.DispInfo{i,1}]); + PlotHandles{Cnt,1} = plot(handles.axesFig,vX,mCube(ix),'color',cColor{1,Cnt},'LineWidth',3); hold(handles.axesFig,'on'); + PlotHandles{Cnt,2} = this.DispInfo{i,1}; + StringYLabel2 = [StringYLabel2 ' \color{' cColor{1,Cnt} '}' this.DispInfo{i,1} ' [' this.DispInfo{i,3} ']']; + Cnt = Cnt+1; + end + end + end + StringYLabel2 = [StringYLabel2 '}']; + % always plot RBExD against RBE + mRBExDose = Result.(['RBExDose' Suffix]); + vBED = mRBExDose(ix); + mRBE = Result.(['RBE' Suffix]); + vRBE = mRBE(ix); + + % plot biological dose against RBE + [ax, PlotHandles{Cnt,1}, PlotHandles{Cnt+1,1}]=plotyy(handles.axesFig,vX,vBED,vX,vRBE,'plot');hold(handles.axesFig,'on'); + PlotHandles{Cnt,2}='RBExDose'; + PlotHandles{Cnt+1,2}='RBE'; + + % set plotyy properties + set(get(ax(2),'Ylabel'),'String','RBE [a.u.]','FontSize',8); + ylabel({StringYLabel1;StringYLabel2}) + set(PlotHandles{Cnt,1},'Linewidth',4,'color','r'); + set(PlotHandles{Cnt+1,1},'Linewidth',3,'color','b'); + set(ax(1),'ycolor','r') + set(ax(2),'ycolor','b') + set(ax,'FontSize',8); + Cnt=Cnt+2; + end + + % asses target coordinates + tmpPrior = intmax; + tmpSize = 0; + for i=1:size(this.cst,1) + if strcmp(this.cst{i,3},'TARGET') && tmpPrior >= this.cst{i,5}.Priority && tmpSize= 1 +% plane = get(handles.popupPlane,'Value'); +% slice = round(get(handles.sliderSlice,'Value')); + + %Get the CT values + ct = evalin('base','ct'); + + %We differentiate between pos and ix, since the user may put + %the datatip on an isoline which returns a continous position + cubePos = zeros(1,3); + cubePos(this.plane) = this.slice; + cubePos(1:end ~= this.plane) = fliplr(pos); + cubeIx = round(cubePos); + + %Here comes the index permutation stuff + %Cube Index + cursorText{end+1,1} = ['Cube Index: ' mat2str(cubeIx)]; + %Space Coordinates + coords = zeros(1,3); + coords(1) = cubePos(2)*ct.resolution.y; + coords(2) = cubePos(1)*ct.resolution.x; + coords(3) = cubePos(3)*ct.resolution.z; + cursorText{end+1,1} = ['Space Coordinates: ' mat2str(coords,5) ' mm']; + + ctVal = ct.cubeHU{1}(cubeIx(1),cubeIx(2),cubeIx(3)); + cursorText{end+1,1} = ['HU Value: ' num2str(ctVal,3)]; + end + catch + cursorText{end+1,1} = 'Error while retreiving CT Data!'; + end + + + %Add dose information if available + if evalin('base','exist(''resultGUI'')') %handles.State == 3 + %get result structure + result = evalin('base','resultGUI'); + + %get all cubes from the ResultGUI + resultNames = fieldnames(result); %get(handles.popupDisplayOption,'String'); + + %Display all values of fields found in the resultGUI struct + for runResult = 1:numel(resultNames) + if ~isstruct(result.(resultNames{runResult,1})) && ~isvector(result.(resultNames{runResult,1})) + %try + name = resultNames{runResult}; + if isfield(result,name) % (check the dimensions, same as CT) + field = result.(name); + val = field(cubeIx(1),cubeIx(2),cubeIx(3)); + cursorText{end+1,1} = [name ': ' num2str(val,3)]; + end + %catch + %cursorText{end+1,1} = 'Error while retreiving Data!'; + end + end + end + + else %Profile view + cursorText = cell(2,1); + cursorText{1} = ['Radiological Depth: ' num2str(pos(1),3) ' mm']; + cursorText{2} = [get(target,'DisplayName') ': ' num2str(pos(2),3)]; + end + + end + + %Scroll wheel update + function matRadScrollWheelFcn(this,src,event) + % compute new slice + this.slice= this.slice - event.VerticalScrollCount; + + end + + %Toggle Legend + function legendToggleFunction(this,src,event) + if isempty(this.legendHandle) || ~isobject(this.legendHandle) + return; + end + if this.plotLegend + set(this.legendHandle,'Visible','on') + else + set(this.legendHandle,'Visible','off') + end + end + + %Toggle Colorbar + function colorBarToggleFunction(this,src,event) + if isempty(this.cBarHandle) || ~isobject(this.cBarHandle) || this.lockUpdate + return; + end + if this.plotColorBar + + if evalin('base','exist(''resultGUI'')') + this.colorData=2; + else evalin('base','exist(''ct'')') + this.colorData=1; + end + set(this.cBarHandle,'Visible','on') + else + set(this.cBarHandle,'Visible','off'); + end + % send a notification that the plot has changed (to update the options) + %this.notifyPlotUpdated(); + end + + % + function initValues(this) + lockState=this.lockUpdate; + + if lockState + return; + end + + this.lockUpdate=true; + + if isempty(this.plane) + this.plane=3; + end + + if evalin('base','exist(''ct'')') && evalin('base','exist(''cst'')') && evalin('base','exist(''pln'')') + % update slice, beam and offset sliders parameters + pln= evalin('base','pln'); + ct = evalin('base','ct'); + cst = evalin('base','cst'); + cst = matRad_computeVoiContoursWrapper(cst,ct); + assignin('base','cst',cst); + this.cst = cst; + + % define context menu for structures + this.VOIPlotFlag=false(size(this.cst,1),1); + for i = 1:size(this.cst,1) + if this.cst{i,5}.Visible + this.VOIPlotFlag(i) = true; + end + end + + if this.plane == 1 + this.slice= ceil(pln.propStf.isoCenter(1,2)/ct.resolution.x); + elseif this.plane == 2 + this.slice= ceil(pln.propStf.isoCenter(1,1)/ct.resolution.y); + elseif this.plane == 3 + this.slice= ceil(pln.propStf.isoCenter(1,3)/ct.resolution.z); + end + + this.maxSlice=ct.cubeDim(this.plane); + this.SliceSliderStep=[1/(ct.cubeDim(this.plane)-1) 1/(ct.cubeDim(this.plane)-1)]; + this.numOfBeams=pln.propStf.numOfBeams; + + % set profile offset slider + this.OffsetMinMax = [-100 100]; + vRange = sum(abs(this.OffsetMinMax)); + + if strcmp(this.ProfileType,'lateral') + this.OffsetSliderStep = vRange/ct.resolution.x; + else + this.OffsetSliderStep = vRange/ct.resolution.y; + end + this.OffsetSliderStep=[1/this.OffsetSliderStep 1/this.OffsetSliderStep]; + + + selectionIndex=1; + this.plotColorBar=true; + + if isfield(ct, 'cubeHU') + minMax = [min(ct.cubeHU{1}(:)) max(ct.cubeHU{1}(:))]; + else + minMax = [min(ct.cube{1}(:)) max(ct.cube{1}(:))]; + end + + if evalin('base','exist(''resultGUI'')') + this.colorData=2; + this.plotColorBar=true; + selectionIndex=1; + + Result = evalin('base','resultGUI'); + + this.DispInfo = fieldnames(Result); + for i = 1:size(this.DispInfo,1) + + % delete weight vectors in Result struct for plotting + if isstruct(Result.(this.DispInfo{i,1})) || isvector(Result.(this.DispInfo{i,1})) + Result = rmfield(Result,this.DispInfo{i,1}); + this.DispInfo{i,2}=false; + else + %second dimension indicates if it should be plotted + this.DispInfo{i,2} = true; + % determine units + if strfind(this.DispInfo{i,1},'physicalDose') + this.DispInfo{i,3} = '[Gy]'; + elseif strfind(this.DispInfo{i,1},'alpha') + this.DispInfo{i,3} = '[Gy^{-1}]'; + elseif strfind(this.DispInfo{i,1},'beta') + this.DispInfo{i,3} = '[Gy^{-2}]'; + elseif strfind(this.DispInfo{i,1},'RBExD') + this.DispInfo{i,3} = '[Gy(RBE)]'; + elseif strfind(this.DispInfo{i,1},'LET') + this.DispInfo{i,3} = '[keV/um]'; + else + this.DispInfo{i,3} = '[a.u.]'; + end + this.DispInfo{i,4} = []; % optional for the future: color range for plotting + this.DispInfo{i,5} = []; % optional for the future: min max values + end + end + + this.SelectedDisplayAllOptions=fieldnames(Result); + +% if strcmp(pln.radiationMode,'carbon') || strcmp(pln.bioParam.quantityOpt,'RBExD') +% this.SelectedDisplayOption = 'RBExDose'; +% else +% this.SelectedDisplayOption = 'physicalDose'; +% end + + switch pln.bioParam.quantityOpt + case 'physicalDose' + this.SelectedDisplayOption = 'physicalDose'; + case 'RBExD' + this.SelectedDisplayOption = 'RBExDose'; + case 'effect' + this.SelectedDisplayOption = 'effect'; + end + + if sum(strcmp(this.SelectedDisplayOption,fieldnames(Result))) == 0 + this.SelectedDisplayOption = this.DispInfo{find([this.DispInfo{:,2}],1,'first'),1}; + end + + dose = Result.(this.SelectedDisplayOption); + + %if the workspace has changed update the display parameters + if isempty(this.dispWindow{3,1}) || ~this.lockColorSettings + this.dispWindow{2,1} = [min(dose(:)) max(dose(:))]; % set default dose range + this.dispWindow{2,2} = [min(dose(:)) max(dose(:))]; % set min max values + end + + minMaxRange = this.dispWindow{2,1}; + % if upper colorrange is defined then use it otherwise 120% iso dose + upperMargin = 1; + if abs((max(dose(:)) - this.dispWindow{2,1}(1,2))) < 0.01 * max(dose(:)) + upperMargin = 1.2; + end + + if (length(this.IsoDose_Levels) == 1 && this.IsoDose_Levels(1,1) == 0) + vLevels = [0.1:0.1:0.9 0.95:0.05:upperMargin]; + referenceDose = (minMaxRange(1,2))/(upperMargin); + this.IsoDose_Levels = minMaxRange(1,1) + (referenceDose-minMaxRange(1,1)) * vLevels; + this.IsoDose_Contours = matRad_computeIsoDoseContours(dose,this.IsoDose_Levels); + end + else + this.colorData=1; + if evalin('base','exist(''resultGUI'')') + this.SelectedDisplayAllOptions ='physicalDose'; + this.SelectedDisplayOption ='physicalDose'; + else + this.SelectedDisplayAllOptions = 'no option available'; + this.SelectedDisplayOption = ''; + end + end + else %no data is loaded + this.slice=1; + this.maxSlice=1; + this.SliceSliderStep=[1 1]; + this.numOfBeams=1; + this.OffsetMinMax = [1 1]; + this.profileOffset=1; + this.OffsetSliderStep=[1 1]; + this.colorData=1; + this.plotColorBar=false; + selectionIndex=1; + minMax = [0 1]; + this.SelectedDisplayAllOptions = 'no option available'; + this.SelectedDisplayOption = ''; + end + + this.dispWindow{selectionIndex,1} = minMax; + this.dispWindow{selectionIndex,2} = minMax; + + this.lockUpdate=lockState; + end + + %update the Viewer + function updateValues(this) + lockState=this.lockUpdate; + + if lockState + return; + end + + this.lockUpdate=true; + + if evalin('base','exist(''ct'')') && evalin('base','exist(''cst'')') && evalin('base','exist(''pln'')') + % update slice, beam and offset sliders parameters + pln= evalin('base','pln'); + ct = evalin('base','ct'); + cst = evalin('base','cst'); + this.cst = cst; + + % define context menu for structures + this.VOIPlotFlag=false(size(this.cst,1),1); + for i = 1:size(this.cst,1) + if this.cst{i,5}.Visible + this.VOIPlotFlag(i) = true; + end + end + % set isoCenter values + % Note: only defined for the first Isocenter + uniqueIsoCenters = unique(pln.propStf.isoCenter,'rows'); + this.vIsoCenter = round(uniqueIsoCenters(1,:)./[ct.resolution.x ct.resolution.y ct.resolution.z]); + + + % set profile offset slider + this.OffsetMinMax = [-100 100]; + vRange = sum(abs(this.OffsetMinMax)); + + if strcmp(this.ProfileType,'lateral') + this.OffsetSliderStep = vRange/ct.resolution.x; + else + this.OffsetSliderStep = vRange/ct.resolution.y; + end + this.OffsetSliderStep=[1/this.OffsetSliderStep 1/this.OffsetSliderStep]; + + + if evalin('base','exist(''resultGUI'')') + + Result = evalin('base','resultGUI'); + + if ~any(strcmp(this.SelectedDisplayOption,fieldnames(Result))) + this.SelectedDisplayOption = this.DispInfo{find([this.DispInfo{:,2}],1,'first'),1}; + end + + dose = Result.(this.SelectedDisplayOption); + + %if the workspace has changed update the display parameters + if isempty(this.dispWindow{2,1}) || ~this.lockColorSettings + this.dispWindow{2,1} = [min(dose(:)) max(dose(:))]; % set default dose range + this.dispWindow{2,2} = [min(dose(:)) max(dose(:))]; % set min max values + + % if upper colorrange is defined then use it otherwise 120% iso dose + upperMargin = 1; + if abs((max(dose(:)) - this.dispWindow{2,1}(1,2))) < 0.01 * max(dose(:)) + upperMargin = 1.2; + end + end + + minMaxRange = this.dispWindow{2,1}; + + if (length(this.IsoDose_Levels) == 1 && this.IsoDose_Levels(1,1) == 0) + + vLevels = [0.1:0.1:0.9 0.95:0.05:upperMargin]; + referenceDose = (minMaxRange(1,2))/(upperMargin); + this.IsoDose_Levels = minMaxRange(1,1) + (referenceDose-minMaxRange(1,1)) * vLevels; + this.IsoDose_Contours = matRad_computeIsoDoseContours(dose,this.IsoDose_Levels); + end + + % update cached IsoDose contours + vLevels = [0.1:0.1:0.9 0.95:0.05:upperMargin]; + referenceDose = (minMaxRange(1,2))/(upperMargin); + this.IsoDose_Levels = minMaxRange(1,1) + (referenceDose-minMaxRange(1,1)) * vLevels; + this.IsoDose_Contours = matRad_computeIsoDoseContours(dose,this.IsoDose_Levels); + + end + + this.lockUpdate=lockState; + end + end + end +end \ No newline at end of file diff --git a/gui/widgets/matRad_VisualizationWidget.m b/gui/widgets/matRad_VisualizationWidget.m new file mode 100644 index 000000000..9111659de --- /dev/null +++ b/gui/widgets/matRad_VisualizationWidget.m @@ -0,0 +1,888 @@ +classdef matRad_VisualizationWidget < matRad_Widget + % matRad_VisualizationWidget class to generate GUI widget to set + % viewing options + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + properties + viewingWidgetHandle; + dvhStatWidgetHandle; + + end + + methods + function this = matRad_VisualizationWidget(viewingWidgetHandle,handleParent) + if nargin < 2 + matRad_cfg = MatRad_Config.instance(); + handleParent = figure(... + 'Units','characters',... + 'Position',[170 45 140 15],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... 'CloseRequestFcn',@(hObject,eventdata) figure1_CloseRequestFcn(this,hObject,eventdata),... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Visualization',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1'); + + end + this = this@matRad_Widget(handleParent); + + handles=this.handles; + + if nargin>=1 + this.viewingWidgetHandle=viewingWidgetHandle; + else + set(handles.btnDVH,'Enable','off'); + set(handles.popupDisplayOption,'Enable','off'); + set(handles.popupProfileType,'Enable','off'); + set(handles.popupTypeOfPlot,'Enable','off'); + set(handles.popupPlane,'Enable','off'); + set(handles.radiobtnCT,'Enable','off'); + set(handles.radiobtnContour,'Enable','off'); + set(handles.radiobtnDose,'Enable','off'); + set(handles.radiobtnIsoDoseLines,'Enable','off'); + set(handles.sliderSlice,'Enable','off'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','off'); + set(handles.radioBtnIsoCenter,'Enable','off'); + set(handles.radiobtnPlan,'Enable','off'); + set(handles.btn3Dview,'Enable','off'); + end + this.handles = handles; + end + + + function this = initialize(this) + + end + + + + function this = update(this) + if isa(this.viewingWidgetHandle,'matRad_ViewingWidget') + % get the default values from the viewer widget + this.getFromViewingWidget(); + else + handles = this.handles; + % disable all buttons + set(handles.popupDisplayOption,'Enable','off'); + set(handles.popupProfileType,'Enable','off'); + set(handles.popupTypeOfPlot,'Enable','off'); + set(handles.popupPlane,'Enable','off'); + set(handles.radiobtnCT,'Enable','off'); + set(handles.radiobtnContour,'Enable','off'); + set(handles.radiobtnDose,'Enable','off'); + set(handles.radiobtnIsoDoseLines,'Enable','off'); + set(handles.sliderSlice,'Enable','off'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','off'); + set(handles.radioBtnIsoCenter,'Enable','off'); + set(handles.radiobtnPlan,'Enable','off'); + this.handles = handles; + end + end + +% function viewingWidgetHandle=get.viewingWidgetHandle(this) +% viewingWidgetHandle=this.viewingWidgetHandle; +% end + + function set.viewingWidgetHandle(this,value) + if isa(value,'matRad_ViewingWidget') + this.viewingWidgetHandle=value; + + % get the default values from the viewer widget + this.getFromViewingWidget(); + + else + handles=this.handles; + % disable all buttons + set(handles.popupDisplayOption,'Enable','off'); + set(handles.popupProfileType,'Enable','off'); + set(handles.popupTypeOfPlot,'Enable','off'); + set(handles.popupPlane,'Enable','off'); + set(handles.radiobtnCT,'Enable','off'); + set(handles.radiobtnContour,'Enable','off'); + set(handles.radiobtnDose,'Enable','off'); + set(handles.radiobtnIsoDoseLines,'Enable','off'); + set(handles.sliderSlice,'Enable','off'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','off'); + set(handles.radioBtnIsoCenter,'Enable','off'); + set(handles.radiobtnPlan,'Enable','off'); + this.handles=handles; + end + end + end + + methods (Access = protected) + function this = createLayout(this) + h36 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + %Create Main Grid layout + gridSize = [7 4]; + elSize = [0.9 0.6]; + [i,j] = ndgrid(1:gridSize(1),1:gridSize(2)); + gridPos = arrayfun(@(i,j) computeGridPos(this,[i j],gridSize,elSize),i,j,'UniformOutput',false); + + %First column + h40 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Slice Selection',... + 'Tooltip','Choose which slice should be displayed in intensity plots',... + 'Style','text',... + 'Position',gridPos{1,1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','textSliceSelection'); + + h48 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Beam Selection',... + 'Tooltip','Choose which beam should be displayed in profile plots',... + 'Style','text',... + 'Position',gridPos{1,2},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtBeamSelection' ); + + h54 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Offset',... + 'Tooltip','Define an offset value for profile plots',... + 'Style','text',... + 'Position',gridPos{1,3},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','textOffset'); + + %Second Column (Sliders) + h38 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Slider',... + 'Tooltip','Choose which slice should be displayed in intensity plots',... + 'Style','slider',... + 'Callback',@(hObject,eventdata) sliderSlice_Callback(this,hObject,eventdata),... + 'BusyAction','cancel',... + 'Interruptible','off',... + 'Position',gridPos{2,1},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','sliderSlice'); + + h49 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','SliderBeamSelection',... + 'Tooltip','Choose which beam should be displayed in profile plots',... + 'Style','slider',... + 'BackgroundColor',[0.9 0.9 0.9],... + 'Callback',@(hObject,eventdata) sliderBeamSelection_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'Position',gridPos{2,2},... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','sliderBeamSelection'); + + h55 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','SliderOffset',... + 'Tooltip','Define an offset value for profile plots',... + 'Style','slider',... + 'Position',gridPos{2,3},... + 'BackgroundColor',[0.9 0.9 0.9],... + 'Callback',@(hObject,eventdata) sliderOffset_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','sliderOffset'); + h25 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Recenter',... + 'Tooltip','Recenter viewing widget to isocenter',... + 'Position',gridPos{2,4},... + 'BackgroundColor',[0.8 0.8 0.8],... + 'Callback',@(hObject,eventdata) btnRecenter_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'Tag','btnRecenter',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Third Column + h44 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Type of plot',... + 'Tooltip','Display intensity or profile plot',... + 'Style','text',... + 'Position',gridPos{3,1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtTypeOfPlot' ); + + h39 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Plane Selection',... + 'Tooltip','Display coronal, sagital or axial plane in intensity plots',... + 'Style','text',... + 'Position',gridPos{3,2},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtPlanSelection'); + + h46 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Display option',... + 'Tooltip','Select the result distribution which should be displayed',... + 'Style','text',... + 'Position',gridPos{3,3},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','txtDisplayOption' ); + + %Fourth Column + + h45 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String',{ 'intensity'; 'profile' },... + 'Tooltip','Display intensity or profile plot',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{4,1},... + 'Callback',@(hObject,eventdata) popupTypeOfPlot_Callback(this,hObject,eventdata),... + 'Tag','popupTypeOfPlot',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + h37 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String',{ 'coronal'; 'sagital'; 'axial' },... + 'Tooltip','Display coronal, sagital or axial plane in intensity plots',... + 'Style','popupmenu',... + 'Value',3,... + 'Position',gridPos{4,2},... + 'Callback',@(hObject,eventdata) popupPlane_Callback(this,hObject,eventdata),... + 'Tag','popupPlane',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + h47 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Please select...',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{4,3},... + 'Callback',@(hObject,eventdata)popupDisplayOption_Callback(this,hObject,eventdata),... + 'Tag','popupDisplayOption',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Fifth Column + h50 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String',{'depth','lateral'},... + 'Tooltip','Select which 1D profile through the isocenter you would like to see',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',gridPos{5,1},... + 'Callback',@(hObject,eventdata) popupProfileType_Callback(this,hObject,eventdata),... + 'Tag','popupProfileType',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %{ + %Old display with button + pos = gridPos{5,1}; + pos(3) = pos(3)*0.4975; + h51 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Profile:',... + 'Tooltip','Display depth or lateral profile',... + 'Style','text',... + 'Position',pos,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text16' ); + + pos = gridPos{5,1}; + pos(3) = pos(3)*0.4975; + pos(1) = pos(1) + pos(3) + 0.0025; + + h50 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','lateral',... + 'Tooltip','Display depth or lateral profile',... + 'Position',pos,... + 'Callback',@(hObject,eventdata) popupProfileType_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'Tag','popupProfileType',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + %} + + + h57 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Open 3D-View',... + 'Tooltip','Get a 3 dimensional illustration of the patient',... + 'Position',gridPos{5,2},... + 'Callback',@(hObject,eventdata) btn3Dview_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'Tag','btn3Dview',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + h52 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','Show DVH/QI',... + 'Tooltip','Render the dose volume histogram along with quality indicators',... + 'Position',gridPos{5,3},... + 'Callback',@(hObject,eventdata) btnDVH_Callback(this,hObject,eventdata),... + 'Enable','off',... + 'Tag','btnDVH',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight); + + %Sixth Column + %Recompute Grid to allow 7 rows + gridSize = [7 7]; + elSize = [0.9 0.6]; + [i,j] = ndgrid(1:gridSize(1),1:gridSize(2)); + newPos = arrayfun(@(i,j) computeGridPos(this,[i j],gridSize,elSize),i,j,'UniformOutput',false); + + %We need only the 6th col and make it double wide + newPos = newPos(6,:); + newPos = cellfun(@(pos) pos .* [1 1 2 1],newPos,'UniformOutput',false); + + h58 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','plot CT',... + 'Tooltip','If this checked, the CT is displayed',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',newPos{1},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radiobtnCT_Callback(this,hObject,eventdata),... + 'Tag','radiobtnCT'); + + h41 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','plot contour',... + 'Tooltip','If this checked, the contours of the VOIs are displayed',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',newPos{2},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radiobtnContour_Callback(this,hObject,eventdata),... + 'Tag','radiobtnContour' ); + + h42 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','plot dose',... + 'Tooltip','If this checked, the optimized distribution is displayed',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',newPos{3},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radiobtnDose_Callback(this,hObject,eventdata),... + 'Tag','radiobtnDose' ); + + h43 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','plot isolines',... + 'Tooltip','If this checked, isolines are displayed',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',newPos{4},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radiobtnIsoDoseLines_Callback(this,hObject,eventdata),... + 'Tag','radiobtnIsoDoseLines'); + + h53 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','plot isolines labels',... + 'Tooltip','If this checked, isoline labels are displayed',... + 'Style','radiobutton',... + 'Position',newPos{5},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radiobtnIsoDoseLinesLabels_Callback(this,hObject,eventdata),... + 'Tag','radiobtnIsoDoseLinesLabels'); + + h56 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','plot iso center',... + 'Tooltip','If this checked, the iso center is displayed',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',newPos{6},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radioBtnIsoCenter_Callback(this,hObject,eventdata),... + 'Tag','radioBtnIsoCenter'); + + h59 = uicontrol(... + 'Parent',h36,... + 'Units','normalized',... + 'String','visualize plan/beams',... + 'Tooltip','If this checked, the beam angles are displayed',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',newPos{7},... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,eventdata) radiobtnPlan_Callback(this,hObject,eventdata),... + 'Tag','radiobtnPlan'); + + + this.createHandles(); + end + end + + methods (Access = protected) + function getFromViewingWidget(this) + handles = this.handles; + if strcmp(this.viewingWidgetHandle.ProfileType,'lateral') + set(handles.popupProfileType,'Value',2); + else + set(handles.popupProfileType,'Value',1); + end + + set(handles.popupTypeOfPlot,'Value',this.viewingWidgetHandle.typeOfPlot); + set(handles.popupPlane,'Value',this.viewingWidgetHandle.plane); + set(handles.radiobtnContour,'Value',this.viewingWidgetHandle.plotContour); + set(handles.radiobtnDose,'Value',this.viewingWidgetHandle.plotDose); + set(handles.radiobtnIsoDoseLines,'Value',this.viewingWidgetHandle.plotIsoDoseLines); + set(handles.radiobtnIsoDoseLinesLabels,'Value',this.viewingWidgetHandle.plotIsoDoseLinesLabels); + set(handles.radioBtnIsoCenter,'Value',this.viewingWidgetHandle.plotIsoCenter); + set(handles.radiobtnPlan,'Value',this.viewingWidgetHandle.plotPlan); + + % update the sliders + set(handles.sliderSlice,'Min',1,'Max',this.viewingWidgetHandle.maxSlice,... + 'Value', this.viewingWidgetHandle.slice, ... + 'SliderStep',this.viewingWidgetHandle.SliceSliderStep); + + if this.viewingWidgetHandle.numOfBeams>1 + set(handles.sliderBeamSelection,'Min',1,'Max',this.viewingWidgetHandle.numOfBeams,... + 'Value',this.viewingWidgetHandle.selectedBeam,... + 'SliderStep',[1/(this.viewingWidgetHandle.numOfBeams-1) 1/(this.viewingWidgetHandle.numOfBeams-1)]); + else + set(handles.sliderBeamSelection,'Min',1,'Max',1, 'Value',1,'SliderStep',[1 1]); + end + + set(handles.sliderOffset,'Min',this.viewingWidgetHandle.OffsetMinMax(1),'Max',this.viewingWidgetHandle.OffsetMinMax(2),... + 'Value',this.viewingWidgetHandle.profileOffset,... + 'SliderStep',this.viewingWidgetHandle.OffsetSliderStep); + + set(handles.popupDisplayOption,'String',this.viewingWidgetHandle.SelectedDisplayAllOptions); + if ~strcmp(this.viewingWidgetHandle.SelectedDisplayOption,'') + set(handles.popupDisplayOption,'Value',find(strcmp(this.viewingWidgetHandle.SelectedDisplayOption,this.viewingWidgetHandle.SelectedDisplayAllOptions))); + end + + if strcmp(this.viewingWidgetHandle.SelectedDisplayOption,'') % no data is loaded + % disable 3D and DVH button + set(handles.btn3Dview,'Enable','off'); + set(handles.btnDVH,'Enable','off'); + else + set(handles.btn3Dview,'Enable','on'); + + if evalin('base','exist(''resultGUI'')') + set(handles.btnDVH,'Enable','on'); + else + set(handles.btnDVH,'Enable','off'); + end + + %% enable and diasble buttons according to type of plot + % intensity plot + if this.viewingWidgetHandle.typeOfPlot == 1 + + set(handles.sliderBeamSelection,'Enable','off') + set(handles.sliderOffset,'Enable','off') + set(handles.popupDisplayOption,'Enable','on') + set(handles.popupProfileType,'Enable','off'); + set(handles.popupPlane,'Enable','on'); + set(handles.radiobtnCT,'Enable','on'); + set(handles.radiobtnContour,'Enable','on'); + set(handles.radiobtnDose,'Enable','on'); + set(handles.radiobtnIsoDoseLines,'Enable','on'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','on'); + set(handles.sliderSlice,'Enable','on'); + set(handles.btnRecenter, 'Enable','on'); + + % profile plot + elseif this.viewingWidgetHandle.typeOfPlot == 2 + + set(handles.popupDisplayOption,'Enable','on'); + set(handles.popupProfileType,'Enable','on'); + set(handles.popupPlane,'Enable','off'); + set(handles.radiobtnCT,'Enable','off'); + set(handles.radiobtnContour,'Enable','off'); + set(handles.radiobtnDose,'Enable','off'); + set(handles.radiobtnIsoDoseLines,'Enable','off'); + set(handles.sliderSlice,'Enable','off'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','off'); + set(handles.popupProfileType,'Enable','on'); + set(handles.btnRecenter, 'Enable','off'); + end + + end + this.handles = handles; + end + + % H37 Calback + function popupPlane_Callback(this, hObject, event) + % hObject handle to popupPlane (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: contents = cellstr(get(hObject,'String')) returns popupPlane contents as cell array + % contents{get(hObject,'Value')} returns selected item from popupPlane + + % set slice slider + handles = this.handles; + this.viewingWidgetHandle.plane = get(handles.popupPlane,'value'); + this.handles = handles; + end + + %45 Callback + function popupTypeOfPlot_Callback(this, hObject, event) + this.viewingWidgetHandle.typeOfPlot = get(hObject,'Value'); + handles = this.handles; + + % intensity plot + if get(hObject,'Value') == 1 + + set(handles.sliderBeamSelection,'Enable','off') + set(handles.sliderOffset,'Enable','off') + set(handles.popupDisplayOption,'Enable','on') + set(handles.popupProfileType,'Enable','off'); + set(handles.popupPlane,'Enable','on'); + set(handles.radiobtnCT,'Enable','on'); + set(handles.radiobtnContour,'Enable','on'); + set(handles.radiobtnDose,'Enable','on'); + set(handles.radiobtnIsoDoseLines,'Enable','on'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','on'); + set(handles.sliderSlice,'Enable','on'); + + % profile plot + elseif get(hObject,'Value') == 2 + + if evalin('base','exist(''pln'')') && evalin('base','exist(''ct'')') + if this.viewingWidgetHandle.numOfBeams>1 + set(handles.sliderBeamSelection,'Enable','on'); + end + set(handles.sliderOffset,'Enable','on'); + end + + set(handles.popupDisplayOption,'Enable','on'); + set(handles.popupProfileType,'Enable','on'); + set(handles.popupPlane,'Enable','off'); + set(handles.radiobtnCT,'Enable','off'); + set(handles.radiobtnContour,'Enable','off'); + set(handles.radiobtnDose,'Enable','off'); + set(handles.radiobtnIsoDoseLines,'Enable','off'); + set(handles.sliderSlice,'Enable','off'); + set(handles.radiobtnIsoDoseLinesLabels,'Enable','off'); + + set(handles.popupProfileType,'Enable','on') + +% end + end + + + + this.handles = handles; + end + + % 47 Callback + function popupDisplayOption_Callback(this, hObject, event) + %this.updateIsodoseLine(); + content = get(hObject,'String'); + if strcmp(content,'no option available') + return + end + + handles = this.handles; + this.viewingWidgetHandle.SelectedDisplayOption = content{get(hObject,'Value'),1}; + this.handles = handles; + % if matRad Plan Analysis exists use that + fh = findobj( 'Type', 'Figure', 'Name', 'MatRad Plan Analysis' ); + if ~isempty(fh) + this.dvhStatWidgetHandle.selectedDisplayOption = content{get(hObject,'Value'),1}; + end + + + end + + % H49 Callback + function sliderBeamSelection_Callback(this, hObject, event) + % hObject handle to sliderBeamSelection (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'Value') returns position of slider + % get(hObject,'Min') and get(hObject,'Max') to determine range of slider + + handles = this.handles; + this.viewingWidgetHandle.selectedBeam = round(get(hObject,'Value')); + set(hObject, 'Value', this.viewingWidgetHandle.selectedBeam); +% handles.rememberCurrAxes = false; +% UpdatePlot(handles); +% handles.rememberCurrAxes = true; + + this.handles = handles; + end + + % 50 Callback + function popupProfileType_Callback(this, hObject, event) + % hObject handle to popupProfileType (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + %handles = this.handles; + + val = get(hObject,'Value'); + + switch val + case 1 + this.viewingWidgetHandle.ProfileType = 'longitudinal'; + case 2 + this.viewingWidgetHandle.ProfileType = 'lateral'; + otherwise + this.showError('Invalid selection for Profile Plot!'); + end + %this.handles = handles; + end + + % 52 Callback + function btnDVH_Callback(this, hObject, event) + this.dvhStatWidgetHandle = matRad_DVHStatsWidget(this.viewingWidgetHandle.SelectedDisplayOption); % pass fieldname in resultGUI + end + + %H55 Callback + function sliderOffset_Callback(this, hObject, event) + this.viewingWidgetHandle.profileOffset = get(hObject,'Value'); + %UpdatePlot(handles); + end + + % 57 Callback + function btn3Dview_Callback(this,hObject, event) + matRad_3DWidget(this.viewingWidgetHandle); + end + % --- Executes on button press in radiobtnContour. + function radiobtnContour_Callback(this,hObject, ~) + % hObject handle to radiobtnContour (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of radiobtnContour + %UpdatePlot(handles) + this.viewingWidgetHandle.plotContour = get(hObject,'Value'); + end + % --- Executes on slider movement. + function sliderSlice_Callback(this,hObject, ~) + % hObject handle to sliderSlice (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hints: get(hObject,'Value') returns position of slider + % get(hObject,'Min') and get(hObject,'Max') to determine range of slider + %UpdatePlot(handles) + + this.viewingWidgetHandle.slice = round(get(hObject,'Value')); + end + + function radiobtnCT_Callback(this,hObject, ~) + % hObject handle to radiobtnCT (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of radiobtnCT + %UpdatePlot(handles) + this.viewingWidgetHandle.plotCT = get(hObject,'Value'); + end + + % --- Executes on button press in radiobtnPlan. + function radiobtnPlan_Callback(this,hObject, ~) + % hObject handle to radiobtnPlan (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of radiobtnPlan + %UpdatePlot(handles) + this.viewingWidgetHandle.plotPlan = get(hObject,'Value'); + end + + % --- Executes on button press in radiobtnIsoDoseLines. + function radiobtnIsoDoseLines_Callback(this,hObject, ~) + % hObject handle to radiobtnIsoDoseLines (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of radiobtnIsoDoseLines + this.viewingWidgetHandle.plotIsoDoseLines = get(hObject,'Value'); + + end + + % --- Executes on button press in radiobtnDose. + function radiobtnDose_Callback(this,hObject, ~) + % hObject handle to radiobtnDose (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of radiobtnDose + %UpdatePlot(handles) + this.viewingWidgetHandle.plotDose=get(hObject,'Value'); + end + + % radio button: plot isolines labels + function radiobtnIsoDoseLinesLabels_Callback(this,hObject, ~) + %UpdatePlot(handles); + this.viewingWidgetHandle.plotIsoDoseLinesLabels = get(hObject,'Value'); + end + + % --- Executes on button press in radioBtnIsoCenter. + function radioBtnIsoCenter_Callback(this,hObject, eventdata) + % hObject handle to radioBtnIsoCenter (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + %UpdatePlot(handles) + % Hint: get(hObject,'Value') returns toggle state of radioBtnIsoCenter + this.viewingWidgetHandle.plotIsoCenter = get(hObject,'Value'); + end + + % --- Executes on button press in btnRecenter + % currently imlpemented for one single isocenter for all beams + function btnRecenter_Callback(this,hObject, eventdata) + % hObject handle to radioBtnIsoCenter (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + %UpdatePlot(handles) + % Hint: get(hObject,'Value') returns toggle state of radioBtnIsoCenter + + isoSlice = this.viewingWidgetHandle.vIsoCenter(this.viewingWidgetHandle.plane); + this.viewingWidgetHandle.slice = isoSlice; + end + + end +end diff --git a/gui/widgets/matRad_WorkflowWidget.m b/gui/widgets/matRad_WorkflowWidget.m new file mode 100644 index 000000000..dc14cd01d --- /dev/null +++ b/gui/widgets/matRad_WorkflowWidget.m @@ -0,0 +1,857 @@ +classdef matRad_WorkflowWidget < matRad_Widget + % matRad_WorkflowWidget class to generate GUI widget to run through the + % treatment planning workflow + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + end + + methods + function this = matRad_WorkflowWidget(handleParent) + if nargin < 1 + matRad_cfg = MatRad_Config.instance(); + handleParent = figure(... + 'Units','characters',... + 'Position',[130 45 150 15],... + 'Visible','on',... + 'Color',matRad_cfg.gui.backgroundColor,... + 'IntegerHandle','off',... + 'Colormap',[0 0 0.5625;0 0 0.625;0 0 0.6875;0 0 0.75;0 0 0.8125;0 0 0.875;0 0 0.9375;0 0 1;0 0.0625 1;0 0.125 1;0 0.1875 1;0 0.25 1;0 0.3125 1;0 0.375 1;0 0.4375 1;0 0.5 1;0 0.5625 1;0 0.625 1;0 0.6875 1;0 0.75 1;0 0.8125 1;0 0.875 1;0 0.9375 1;0 1 1;0.0625 1 1;0.125 1 0.9375;0.1875 1 0.875;0.25 1 0.8125;0.3125 1 0.75;0.375 1 0.6875;0.4375 1 0.625;0.5 1 0.5625;0.5625 1 0.5;0.625 1 0.4375;0.6875 1 0.375;0.75 1 0.3125;0.8125 1 0.25;0.875 1 0.1875;0.9375 1 0.125;1 1 0.0625;1 1 0;1 0.9375 0;1 0.875 0;1 0.8125 0;1 0.75 0;1 0.6875 0;1 0.625 0;1 0.5625 0;1 0.5 0;1 0.4375 0;1 0.375 0;1 0.3125 0;1 0.25 0;1 0.1875 0;1 0.125 0;1 0.0625 0;1 0 0;0.9375 0 0;0.875 0 0;0.8125 0 0;0.75 0 0;0.6875 0 0;0.625 0 0;0.5625 0 0],... + 'MenuBar','none',... + 'Name','MatRad Workflow',... + 'NumberTitle','off',... + 'HandleVisibility','callback',... + 'Tag','figure1',... + 'PaperSize',[20.99999864 29.69999902]); + + set(handleParent,'Units','normalized') + pos = get(handleParent,'Position'); + pos(1:2) = [0.5 0.5] - pos(3:4)./2; + set(handleParent,'Position'); + + end + this = this@matRad_Widget(handleParent); + end + + function this = initialize(this) + this.update(); + end + + function this = update(this,evt) + getFromWorkspace(this); + %updateInWorkspace(this); + end + + + % moved so it can be called from the toolbar button + % H74 Callback + function btnLoadMat_Callback(this, hObject, event) + handles = this.handles; + [FileName, FilePath] = uigetfile('*.mat'); + if FileName == 0 % user pressed cancel --> do nothing. + return; + end + + try + % delete existing workspace - parse variables from base workspace + AllVarNames = evalin('base','who'); + RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; + + for i = 1:length(RefVarNames) + if sum(ismember(AllVarNames,RefVarNames{i}))>0 + evalin('base',['clear ', RefVarNames{i}]); + end + end + + % read new data + load([FilePath FileName]); + + catch ME + this.handles=handles; + getFromWorkspace(this); + showError(this,'LoadMatFileFnc: Could not load *.mat file',ME); + return + end + + try + %cst = generateCstTable(this,cst); + %handles.TableChanged = false; + %set(handles.popupTypeOfPlot,'Value',1); + %cst = matRad_computeVoiContoursWrapper(cst,ct); + + assignin('base','ct',ct); + assignin('base','cst',cst); + + catch ME + showError(this,'LoadMatFileFnc: Could not load *.mat file',ME); + end + + % check if a optimized plan was loaded + if exist('stf','var') + assignin('base','stf',stf); + end + if exist('pln','var') + assignin('base','pln',pln); + end + if exist('dij','var') + assignin('base','dij',dij); + end + + if exist('resultGUI','var') + assignin('base','resultGUI',resultGUI); + end + + this.handles=handles; + %updateInWorkspace(this); + this.changedWorkspace(); + %getFromWorkspace(this); %update the buttons + end + end + + methods (Access = protected) + function this = createLayout(this) + + parent = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + + h72 = this.addControlToGrid([2 4],... + 'Style','text',... + 'String','Status:',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtStatus',... + 'FontSize',round(matRad_cfg.gui.fontSize*1.2)); + + + h73 = this.addControlToGrid([3 4],... + 'String','no data loaded',... + 'Style','text',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Tag','txtInfo',... + 'FontSize',round(matRad_cfg.gui.fontSize*1.2)); + + hMatLoad = this.addControlToGrid([2 1],... + 'String','Load *.mat data',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnLoadMat_Callback(this,hObject,eventdata),... + 'Tag','btnLoadMat'); + + hDijCalc = this.addControlToGrid([3 1],... + 'String','Calc. Dose Influence',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnCalcDose_Callback(this,hObject,eventdata),... + 'Tag','btnCalcDose'); + + hOpt = this.addControlToGrid([4 1],... + 'Parent',parent,... + 'String','Optimize',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnOptimize_Callback(this,hObject,eventdata),... + 'Tag','btnOptimize'); + + hLoadDicom = this.addControlToGrid([2 2],... + 'String','Load DICOM',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnLoadDicom_Callback(this,hObject,eventdata),... + 'Tag','btnLoadDicom'); + + + hRefresh = this.addControlToGrid([1 1],... + 'String','Refresh',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnRefresh_Callback(this,hObject,eventdata),... + 'Tag','btnRefresh'); + + hRecalc = this.addControlToGrid([4 2],... + 'String','Recalculate Dose',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) pushbutton_recalc_Callback(this,hObject,eventdata),... + 'Tag','pushbutton_recalc'); + + hKeep = this.addControlToGrid([5 1],... + 'String','Save/Keep Result',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btnSaveToGUI_Callback(this,hObject,eventdata),... + 'Tag','btnSaveToGUI'); + + hExportBin = this.addControlToGrid([5 2],... + 'String','Export Binary',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) btn_export_Callback(this,hObject,eventdata),... + 'Children',[],... + 'Tag','btn_export'); + + hImportDose = this.addControlToGrid([4 3],... + 'String','Import Dose',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) importDoseButton_Callback(this, hObject,eventdata),... + 'Tag','importDoseButton'); + + hImportBin = this.addControlToGrid([2 3],... + 'String','Import from Binary',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) pushbutton_importFromBinary_Callback(this,hObject,eventdata),... + 'TooltipString','Imports a patient data set from binary datafiles describing CT and segmentations',... + 'Tag','pushbutton_importFromBinary'); + + hExportDicom = this.addControlToGrid([5 3],... + 'String','Export Dicom',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'Callback',@(hObject,eventdata) exportDicomButton_Callback(this, hObject,eventdata),... + 'Tag','exportDicomButton'); + + this.createHandles(); + + handles=this.handles; + matRad_cfg = MatRad_Config.instance(); + if matRad_cfg.eduMode + %Visisbility in Educational Mode + eduHideHandles = {handles.pushbutton_importFromBinary,... + handles.btnLoadDicom,... + handles.btn_export,... + handles.exportDicomButton,... + handles.importDoseButton}; + cellfun(@(h) set(h,'Visible','Off'),eduHideHandles); + end + this.handles=handles; + end + + function this = getFromWorkspace(this) + handles = this.handles; + + % no data loaded, disable the buttons + set(handles.txtInfo,'String','no data loaded'); + set(handles.btnCalcDose,'Enable','off'); + set(handles.btnOptimize ,'Enable','off'); + set(handles.pushbutton_recalc,'Enable','off'); + set(handles.btnSaveToGUI,'Enable','off'); + set(handles.importDoseButton,'Enable','off'); + set(handles.btn_export,'Enable','off'); + set(handles.exportDicomButton,'Enable','off'); + + + if evalin('base','exist(''ct'')') && ... + evalin('base','exist(''cst'')') + + set(handles.txtInfo,'String','loaded and ready'); + + if evalin('base','exist(''pln'')') + + + % ct cst and pln available; ready for dose calculation + set(handles.txtInfo,'String','ready for dose calculation'); + set(handles.btnCalcDose,'Enable','on'); + set(handles.btn_export,'Enable','on'); + set(handles.exportDicomButton,'Enable','on'); + + if evalin('base','exist(''resultGUI'')') + % plan is optimized + % check if dij, stf and pln match + if matRad_comparePlnDijStf(evalin('base','pln'),evalin('base','stf'),evalin('base','dij')) + set(handles.txtInfo,'String','plan is optimized'); + set(handles.btnOptimize ,'Enable','on'); + end + + set(handles.pushbutton_recalc,'Enable','on'); + set(handles.btnSaveToGUI,'Enable','on'); + % resultGUI struct needs to be available to import dose + % otherwise inconsistent states can be achieved + set(handles.importDoseButton,'Enable','on'); + + elseif evalin('base','exist(''dij'')') && evalin('base','exist(''stf'')') + % check if dij, stf and pln match + if matRad_comparePlnDijStf(evalin('base','pln'),evalin('base','stf'),evalin('base','dij')) + % plan is ready for optimization + set(handles.txtInfo,'String','ready for optimization'); + set(handles.btnOptimize ,'Enable','on'); + end + end + end + end + this.handles=handles; + end + + end + methods (Access = private) + + function h = addControlToGrid(this,gridPos,varargin) + matRad_cfg = MatRad_Config.instance(); + parent = this.widgetHandle; + + %Use a 5 x 5 grid + pos = this.computeGridPos(gridPos,[5 5]); + + h = uicontrol('Parent',parent,... + 'Units','normalized',... + 'Position',pos,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + varargin{:}); + end + + + % H75 Callback + function btnCalcDose_Callback(this, hObject, eventdata) + % hObject handle to btnCalcDose (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % http://stackoverflow.com/questions/24703962/trigger-celleditcallback-before-button-callback + % http://www.mathworks.com/matlabcentral/newsreader/view_thread/332613 + % wait some time until the CallEditCallback is finished + % Callback triggers the cst saving mechanism from GUI + + handles = this.handles; + try + % indicate that matRad is busy + % change mouse pointer to hour glass + Figures = gcf;%findobj('type','figure'); + set(Figures, 'pointer', 'watch'); + drawnow; + % disable all active objects + InterfaceObj = findobj(Figures,'Enable','on'); + set(InterfaceObj,'Enable','off'); + + + % read plan from gui and save it to workspace + %handles=getPlnFromGUI(this); + + % get default iso center as center of gravity of all targets if not + % already defined + pln = evalin('base','pln'); + + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'CalcDoseCallback: Error in preprocessing!',ME); + return; + end + + % generate steering file + try + currPln = evalin('base','pln'); + % % if we run 3d conf opt -> hijack runDao to trigger computation of + % % connected bixels + % if strcmp(pln.radiationMode,'photons') && get(handles.radiobutton3Dconf,'Value') + % currpln.propOpt.runDAO = true; + % end + stf = matRad_generateStf(evalin('base','ct'),... + evalin('base','cst'),... + currPln); + assignin('base','stf',stf); + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'CalcDoseCallback: Error in steering file generation!',ME); + return; + end + + % carry out dose calculation + try + if strcmp(pln.radiationMode,'photons') + dij = matRad_calcPhotonDose(evalin('base','ct'),stf,pln,evalin('base','cst')); + elseif strcmp(pln.radiationMode,'protons') || strcmp(pln.radiationMode,'carbon') + dij = matRad_calcParticleDose(evalin('base','ct'),stf,pln,evalin('base','cst')); + end + + % assign results to base worksapce + assignin('base','dij',dij); + + + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'CalcDoseCallback: Error in dose calculation!',ME); + return; + end + + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + this.changedWorkspace('stf','dij'); + %getFromWorkspace(this); + end + + % H76 Callback + function btnOptimize_Callback(this, hObject, eventdata) + % hObject handle to btnOptimize (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles = this.handles; + try + % indicate that matRad is busy + % change mouse pointer to hour glass + Figures = gcf;%findobj('type','figure'); + set(Figures, 'pointer', 'watch'); + drawnow; + % disable all active objects + InterfaceObj = findobj(Figures,'Enable','on'); + set(InterfaceObj,'Enable','off'); + + pln = evalin('base','pln'); + dij = evalin('base','dij'); + cst = evalin('base','cst'); + % optimize + [resultGUIcurrentRun,usedOptimizer] = matRad_fluenceOptimization(dij,cst,pln); + if pln.propOpt.conf3D && strcmp(pln.radiationMode,'photons') + resultGUIcurrentRun.w = resultGUIcurrentRun.w .* ones(dij.totalNumOfBixels,1); + resultGUIcurrentRun.wUnsequenced = resultGUIcurrentRun.w; + end + + %if resultGUI already exists then overwrite the "standard" fields + AllVarNames = evalin('base','who'); + if ismember('resultGUI',AllVarNames) + resultGUI = evalin('base','resultGUI'); + sNames = fieldnames(resultGUIcurrentRun); + oldNames = fieldnames(resultGUI); + if(length(oldNames) > length(sNames)) + for j = 1:length(oldNames) + if strfind(oldNames{j}, 'beam') + resultGUI = rmfield(resultGUI, oldNames{j}); + end + end + end + for j = 1:length(sNames) + resultGUI.(sNames{j}) = resultGUIcurrentRun.(sNames{j}); + end + else + resultGUI = resultGUIcurrentRun; + end + + assignin('base','resultGUI',resultGUI); + + if ~pln.propOpt.runDAO || ~strcmp(pln.radiationMode,'photons') + CheckOptimizerStatus(this,usedOptimizer,'Fluence') + end + + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'OptimizeCallback: Could not optimize!',ME); + return; + end + + % perform sequencing and DAO + try + %% sequencing + + resultGUI = matRad_sequencing(resultGUI,evalin('base','stf'),dij,pln); + assignin('base','resultGUI',resultGUI); + + + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'OptimizeCallback: Could not perform sequencing',ME); + return; + end + + try + %% DAO + if strcmp(pln.radiationMode,'photons') && pln.propOpt.runDAO + + showWarning(this,['Observe: You are running direct aperture optimization' filesep 'This is experimental code that has not been thoroughly debugged - especially in combination with constrained optimization.']); % was assigned to handles WHY ? + [resultGUI,usedOptimizer] = matRad_directApertureOptimization(evalin('base','dij'),evalin('base','cst'),... + resultGUI.apertureInfo,resultGUI,pln); + assignin('base','resultGUI',resultGUI); + % check IPOPT status and return message for GUI user + CheckOptimizerStatus(this,usedOptimizer,'DAO'); + end + + + if strcmp(pln.radiationMode,'photons') && (pln.propSeq.runSequencing || pln.propOpt.runDAO) + + matRad_visApertureInfo(resultGUI.apertureInfo); + end + + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'OptimizeCallback: Could not perform direct aperture optimization',ME); + return; + end + + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + + this.changedWorkspace('resultGUI'); + %getFromWorkspace(this); + end + + % H77 Callback + function btnLoadDicom_Callback(this, hObject, event) + % hObject handle to btnLoadDicom (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles = this.handles; + try + % delete existing workspace - parse variables from base workspace + AllVarNames = evalin('base','who'); + RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; + for i = 1:length(RefVarNames) + if sum(ismember(AllVarNames,RefVarNames{i}))>0 + evalin('base',['clear ', RefVarNames{i}]); + end + end + matRad_importDicomWidget; + + catch ME + showError(this,'DicomImport: Could not import data', ME); + end + + this.handles = handles; + + end + + % H78 Callback - button: refresh + function btnRefresh_Callback(this, hObject, event) + % notify so all widgets refresh + this.changedWorkspace(); + end + + + % H79 Callback + function pushbutton_recalc_Callback(this, hObject, eventdata) + + handles = this.handles; + + try + % indicate that matRad is busy + % change mouse pointer to hour glass + Figures = gcf; + set(Figures, 'pointer', 'watch'); + drawnow; + % disable all active objects + InterfaceObj = findobj(Figures,'Enable','on'); + set(InterfaceObj,'Enable','off'); + + % get all data from workspace + pln = evalin('base','pln'); + stf = evalin('base','stf'); + ct = evalin('base','ct'); + cst = evalin('base','cst'); + resultGUI = evalin('base','resultGUI'); + + + if sum([stf.totalNumOfBixels]) ~= length(resultGUI.w)%(['w' Suffix])) + warndlg('weight vector does not corresponding to current steering file'); + return + end + + % change isocenter if that was changed and do _not_ recreate steering + % information + for i = 1:numel(pln.propStf.gantryAngles) + stf(i).isoCenter = pln.propStf.isoCenter(i,:); + end + + % recalculate influence matrix + if strcmp(pln.radiationMode,'photons') + dij = matRad_calcPhotonDose(ct,stf,pln,cst); + elseif strcmp(pln.radiationMode,'protons') || strcmp(pln.radiationMode,'carbon') + dij = matRad_calcParticleDose(ct,stf,pln,cst); + end + + % recalculate cubes in resultGUI + resultGUIreCalc = matRad_calcCubes(resultGUI.w,dij); %(['w' Suffix]) + + % delete old variables to avoid confusion + if isfield(resultGUI,'effect') + resultGUI = rmfield(resultGUI,'effect'); + resultGUI = rmfield(resultGUI,'RBExDose'); + resultGUI = rmfield(resultGUI,'RBE'); + resultGUI = rmfield(resultGUI,'alpha'); + resultGUI = rmfield(resultGUI,'beta'); + end + + % overwrite the "standard" fields + sNames = fieldnames(resultGUIreCalc); + for j = 1:length(sNames) + resultGUI.(sNames{j}) = resultGUIreCalc.(sNames{j}); + end + + % assign results to base worksapce + assignin('base','dij',dij); + assignin('base','resultGUI',resultGUI); + + + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + + this.handles = handles; + this.changedWorkspace('dij','resultGUI'); + + catch ME + % change state from busy to normal + set(Figures, 'pointer', 'arrow'); + set(InterfaceObj,'Enable','on'); + this.handles = handles; + showError(this,'CalcDoseCallback: Error in dose recalculation!',ME); + return; + + end + + end + + % H80 Callback + function btnSaveToGUI_Callback(this, hObject, eventdata) + handles = this.handles; + + Width = 400; + Height = 200; + ScreenSize = get(0,'ScreenSize'); + + % show "Provide result name" window + figHandles = get(0,'Children'); + if ~isempty(figHandles) + IdxHandle = strcmp(get(figHandles,'Name'),'Provide result name'); + else + IdxHandle = []; + end + + %check if window is already exists + if any(IdxHandle) + figDialog = figHandles(IdxHandle); + %set focus + figure(figDialog); + else + figDialog = dialog('Position',[ceil(ScreenSize(3)/2) ceil(ScreenSize(4)/2) Width Height],'Name','Provide result name','Color',[0.5 0.5 0.5]); + + uicontrol('Parent',figDialog,... + 'Style','text',... + 'Position',[20 Height - (0.35*Height) 350 60],... + 'String','Please provide a decriptive name for your optimization result:','FontSize',10,'BackgroundColor',[0.5 0.5 0.5]); + + uicontrol('Parent',figDialog,... + 'Style','edit',... + 'Position',[30 60 350 60],... + 'String','Please enter name here...','FontSize',10,'BackgroundColor',[0.55 0.55 0.55]); + + uicontrol('Parent', figDialog,'Style', 'pushbutton', 'String', 'Save','FontSize',10,... + 'Position', [0.42*Width 0.1 * Height 70 30],... + 'Callback', @(hpb,eventdata)SaveResultToGUI(this,hpb,eventdata)); + end + + uiwait(figDialog); + this.handles = handles; + + end + + function SaveResultToGUI(this, ~, ~) + AllFigHandles = get(0,'Children'); + ixHandle = strcmp(get(AllFigHandles,'Name'),'Provide result name'); + uiEdit = get(AllFigHandles(ixHandle),'Children'); + + if strcmp(get(uiEdit(2),'String'),'Please enter name here...') + + formatOut = 'mmddyyHHMM'; + Suffix = ['_' datestr(now,formatOut)]; + else + % delete special characters + Suffix = get(uiEdit(2),'String'); + logIx = isstrprop(Suffix,'alphanum'); + Suffix = ['_' Suffix(logIx)]; + end + + pln = evalin('base','pln'); + resultGUI = evalin('base','resultGUI'); + + if isfield(resultGUI,'physicalDose') + resultGUI.(['physicalDose' Suffix]) = resultGUI.physicalDose; + end + if isfield(resultGUI,'w') + resultGUI.(['w' Suffix]) = resultGUI.w; + end + + + if ~strcmp(pln.propOpt.bioOptimization,'none') + + if isfield(resultGUI,'RBExDose') + resultGUI.(['RBExDose' Suffix]) = resultGUI.RBExDose; + end + + if strcmp(pln.radiationMode,'carbon') == 1 + if isfield(resultGUI,'effect') + resultGUI.(['effect' Suffix])= resultGUI.effect; + end + + if isfield(resultGUI,'RBE') + resultGUI.(['RBE' Suffix]) = resultGUI.RBE; + end + if isfield(resultGUI,'alpha') + resultGUI.(['alpha' Suffix]) = resultGUI.alpha; + end + if isfield(resultGUI,'beta') + resultGUI.(['beta' Suffix]) = resultGUI.beta; + end + end + end + + close(AllFigHandles(ixHandle)); + assignin('base','resultGUI',resultGUI); + + this.changedWorkspace('resultGUI'); + %getFromWorkspace(this); + end + + % H81 Callback + function btn_export_Callback(this, hObject, eventdata) + % hObject handle to btn_export (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + try + matRad_exportWidget; + catch ME + showError(this,'Could not export data. Reason: ', ME); + end + end + + % H82 Callback + function importDoseButton_Callback(this, hObject, eventdata) + % hObject handle to importDoseButton (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles = this.handles; + + extensions{1} = '*.nrrd'; + [filenames,filepath,~] = uigetfile(extensions,'MultiSelect','on'); + + %Import aborted + if filenames == 0 + return; + end + + %Something was selected + try + if ~iscell(filenames) + tmp = filenames; + filenames = cell(1); + filenames{1} = tmp; + end + + ct = evalin('base','ct'); + resultGUI = evalin('base','resultGUI'); + + for filename = filenames + [~,name,~] = fileparts(filename{1}); + [cube,~] = matRad_readCube(fullfile(filepath,filename{1})); + if ~isequal(ct.cubeDim, size(cube)) + errordlg('Dimensions of the imported cube do not match with ct','Import failed!','modal'); + continue; + end + + fieldname = ['import_' matlab.lang.makeValidName(name, 'ReplacementStyle','delete')]; + resultGUI.(fieldname) = cube; + end + + assignin('base','resultGUI',resultGUI); + catch ME + this.handles = handles; + showError(this,'Dose Import: Could not import data. Reason: ', ME); + return; + end + this.handles = handles; + this.changedWorkspace('resultGUI'); + %getFromWorkspace(this); + end + + % H83 Callback + function pushbutton_importFromBinary_Callback(this, hObject, eventdata) + % hObject handle to pushbutton_importFromBinary (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + handles = this.handles; + + try + %call the gui + h=matRad_importWidget; + uiwait(h.widgetHandle); + + this.handles = handles; + this.changedWorkspace(); + catch ME + this.handles = handles; + getFromWorkspace(this); + showError(this,'Binary Patient Import: Could not import data. Reason: ', ME); + return; + end + + + %getFromWorkspace(this); + end + + % H84 Callback + function exportDicomButton_Callback(this, hObject, eventdata) + % hObject handle to exportDicom (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + try + matRad_exportDicomWidget; + catch ME + showError(this,'DicomImport: Could not export data', ME); + end + end + + function CheckOptimizerStatus(this, usedOptimizer,OptCase) + + [statusmsg,statusflag] = usedOptimizer.GetStatus(); + + if statusflag == 0 || statusflag == 1 + status = 'none'; + else + status = 'warn'; + end + + msgbox(['Optimizer finished with status ' num2str(statusflag) ' (' statusmsg ')'],'Optimizer',status,'modal'); + end + end +end + + diff --git a/gui/widgets/matRad_exportDicomWidget.m b/gui/widgets/matRad_exportDicomWidget.m new file mode 100644 index 000000000..07993f0aa --- /dev/null +++ b/gui/widgets/matRad_exportDicomWidget.m @@ -0,0 +1,297 @@ +classdef matRad_exportDicomWidget < matRad_Widget + % matRad_exportDicomWidget class to generate GUI widget to export plan + % to dicom files + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + variables = {'ct','cst','resultGUI'}; %variables to export + end + + methods + function this = matRad_exportDicomWidget(handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 1 + handleParent = figure(... + 'IntegerHandle','off',... + 'MenuBar','none',... + 'NumberTitle','off',... + 'PaperUnits','inches',... + 'Position', [450 170 480 350],... + 'Color',matRad_cfg.gui.backgroundColor,... + 'Name','Export Dicom'); + end + this = this@matRad_Widget(handleParent); + + update(this); + end + function this = initialize(this) + + end + + + function this = update(this,evt) + doUpdate = true; + if nargin == 2 + %At pln changes and at cst/cst (for Isocenter and new settings) + %we need to update + doUpdate = this.checkUpdateNecessary(this.variables,evt); + end + + if doUpdate + handles = this.handles; + + % load table with available variables that can be exported + vExists=false(1,numel(this.variables)); + for i= 1:numel(this.variables) + var= char(this.variables(i)); + vExists(i) = evalin('base',['exist(''' var ''',''var'')']); + end + + if find(vExists,1) % not empty + tableData(:,2)= this.variables(vExists); + tableData(:,1) ={true}; + set(handles.uitable_variables,'ColumnEditable',[true,false]); + else + tableData(1,2) = {'No variables to export'}; + set(handles.btn_export,'Enable','off'); + set(handles.uitable_variables,'ColumnEditable',[false,false]); + end + set(handles.uitable_variables,'data',tableData); + + this.handles = handles; + end + end + end + + + methods (Access = protected) + + function this = createLayout(this) + + h1 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + %EXPORT BUTTON + h2 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Export',... + 'UserData',[],... + 'Position',[0.75 0.1 0.2 0.1],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'Tooltip', 'Export selected variables to selected folder',... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','btn_export',... + 'Callback',@this.btn_export_Callback); + + %TEXT + h6 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'FontUnits',get(0,'defaultuicontrolFontUnits'),... + 'HorizontalAlignment','left',... + 'String','Select export folder:',... + 'Tooltip', 'Select the folder you want to export to',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Style','text',... + 'Position',[0.035 0.8 0.7 0.1 ],... + 'Tag','label_dir_export'); + + %BROWSER PUSHBUTTON + h7 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'String','Browse',... + 'Tooltip', 'Choose the export directory',... + 'Position',[0.75 0.75 0.2 0.1],... + 'Callback',@this.pushbutton_dir_export_browse_Callback,... + 'Tag','pushbutton_dir_export_browse'); + + %EDIT TEXTFELD + h8 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'Style','edit',... + 'Position',[0.035 0.75 0.7 0.1 ],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tooltip', 'Export path',... + 'Callback',@this.edit_dir_export_Callback,... + 'Tag','edit_dir_export'); + %Text Variable to export + h10 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'FontUnits',get(0,'defaultuicontrolFontUnits'),... + 'HorizontalAlignment','left',... + 'String','Select variables to export:',... + 'Tooltip', 'Select the variables you want to export',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Style','text',... + 'Position',[0.035 0.63 0.7 0.04 ],... + 'Tag','label_variables'); + + % Table variables to export + h11 = uitable(... + 'Parent',h1,... + 'Units','normalized',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'ColumnWidth',{ 20 278 },... + 'Position',[0.035 0.3 0.915 0.3],... + 'ColumnEditable',[true false],... + 'ColumnFormat',{ 'logical' 'char' },... + 'RowName',{},... + 'ColumnName',{},... + 'Tag','uitable_variables'); + + set(h11,'Units','pixels'); + pos = get(h11,'Position'); + set(h11,'ColumnWidth',{20 pos(3)-39}); + + this.createHandles(); + end + + end + + + %CALLBACK METHODS + methods + % CALLBACK FOR H2 BUTTON EXPORT + function this = btn_export_Callback(this, hObject, event) + handles = this.handles; + + exportDir = get(handles.edit_dir_export,'String'); + + %Sanity check- + if numel(exportDir) == 0 + errordlg('No Export folder selected!'); + return; + elseif ~exist(exportDir,'dir') + errordlg(['Folder ' exportDir ' does not exist!']); + return; + else + %Add file separator if necessary + if exportDir(end) ~= filesep + exportDir = [exportDir filesep]; + end + this.handles = handles; + end + + try + dcmExport = matRad_DicomExporter; + dcmExport.dicomDir = exportDir; + + varData = get(handles.uitable_variables,'Data'); + var_selected=false; + for i= 1:size(varData,1) + if varData{i,1} == true + var_selected=true; + switch varData{i,2} + case 'ct' + dcmExport.matRad_exportDicomCt(); + case 'cst' + dcmExport.matRad_exportDicomRTStruct(); + case 'resultGUI' + dcmExport.matRad_exportDicomRTDoses(); + end + end + end + catch ME + warning(ME.identifier,'couldn''t export! Reason: %s\n',ME.message) + end + + + if ~var_selected + errordlg('No variables selected!'); + return; + end + + + end + + %------------CALLBACK FOR H3 BUTTON CANCEL + function this = btn_cancel_Callback(this, hObject, event, guidata) + close; + % close(handles.figure1); + end + + + %-------------CALLBACK FOR H7 PUSHBUTTON DIR EXPORT BROWSE + function this = pushbutton_dir_export_browse_Callback(this, hObject, event) + handles = this.handles; + + exportDir = uigetdir('', 'Choose the export directory...'); + if exportDir ~= 0 + exportDir = [exportDir filesep]; + set(handles.edit_dir_export,'String',exportDir); + % Update handles structure + this.handles = handles; + end + end + + %------------CALLBACK FOR H8 EDIT DIR EXPORT + function this = edit_dir_export_Callback(this, hObject, event) + handles = this.handles; + + exportDir = get(handles.edit_dir_export,'String'); + + %Add filesperator + if exportDir(end) ~= filesep + exportDir = [exportDir filesep]; + end + + %Check if the user specified an existing directory + if ~exist(exportDir,'dir') + warndlg(['Folder ' exportDir ' does not exist!']); + exportDir = ''; + end + + set(handles.edit_dir_export,'String',exportDir); + this.handles = handles; + end + + end +end + + + diff --git a/gui/widgets/matRad_exportWidget.m b/gui/widgets/matRad_exportWidget.m new file mode 100644 index 000000000..e8b62ff09 --- /dev/null +++ b/gui/widgets/matRad_exportWidget.m @@ -0,0 +1,546 @@ +classdef matRad_exportWidget < matRad_Widget + + % matRad_exportWidget class to generate GUI widget to export plan as + % dicom, nrrd etc. + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + end + + methods + function this = matRad_exportWidget(handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 1 + handleParent = figure(... + 'IntegerHandle','off',... + 'MenuBar','none',... + 'NumberTitle','off',... + 'PaperUnits','inches',... + 'Position', [450 170 440 500],... + 'Color',matRad_cfg.gui.backgroundColor,... + 'Name','Export Patient'); + end + this = this@matRad_Widget(handleParent); + end + + function this = update(this,evt) + + doUpdate = true; + if nargin == 2 + doUpdate = this.checkUpdateNecessary({'resultGUI','ct','cst'},evt); + end + + if ~doUpdate + return; + end + + handles = this.handles; + % handles = guidata(this.widgetHandle); + + %Fills structure export table + if evalin('base','exist(''cst'',''var'')') == 1 + cst = evalin( 'base', 'cst' ); + tableData = cell(numel(cst(:,2)),2); + tableData(:,2) = cst(:,2); + tableData(:,1) = {true}; + else + tableData = cell(0); + set(handles.checkbox_CT,'Enable','off'); + end + set(handles.uitable_vois,'data',tableData); + + %Fills result cubes export table + if evalin('base','exist(''resultGUI'',''var'')') + result = evalin( 'base', 'resultGUI' ); + cubeNames = fieldnames(result); + cubeIx = 1; + for f = 1:numel(cubeNames) + if ndims(result.(cubeNames{f})) < 3 + continue; + end + cubes{cubeIx} = cubeNames{f}; + cubeIx = cubeIx + 1; + end + numCubes = cubeIx - 1; + tableData = cell(numCubes,2); + tableData(:,2) = cubes; + tableData(:,1) = {true}; + else + tableData = cell(0); + set(handles.checkbox_dose,'Enable','off'); %CHANGED CODE!ALTE VERSION: set(handles.checkbox_dose,'Enable','off'); + end + set(handles.uitable_doseCubes,'data',tableData); + + % Update handles structure + this.handles = handles; + end + end + + + methods (Access = protected) + + function this = createLayout(this) + + h1 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + %EXPORT BUTTON + h2 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Export',... + 'Tooltip', 'Export selected quantites to selected folder',... + 'UserData',[],... + 'Position',[0.75 0.025 0.2 0.05],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','btn_export',... + 'Callback',@this.btn_export_Callback); + + %CT CHECKBOX + h4 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','CT',... + 'Tooltip', 'Export CT of selected structures',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Style','checkbox',... + 'Position',[0.035 0.8 0.7 0.05],... + 'Callback',@this.checkbox_CT_Callback,... + 'Tag','checkbox_CT'); + + + %Compress CHECKBOX + h14 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Compress',... + 'Tooltip', 'Export compressed data',... + 'Style','checkbox',... + 'Value',1,... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Position',[0.035 0.13 0.7 0.05],... + 'Tag','checkbox_compress',... + 'Callback',@this.checkbox_Compress_Callback); + + + % RESULT CUBES CHECKBOX + h5 = uicontrol(... + 'Parent',h1,... + 'HorizontalAlignment','left',... + 'Units','normalized',... + 'String','Result Cubes',... + 'Tooltip', 'Export selected result cubes',... + 'Style','checkbox',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Position',[0.035 0.45 0.7 0.04],... + 'Callback',@this.checkbox_dose_Callback,... + 'Tag','checkbox_dose'); + + %TEXT + h6 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Select export folder:',... + 'Tooltip', 'Select the folder you want to export to',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Style','text',... + 'Position',[0.035 0.925 0.915 0.05 ],... + 'Tag','label_dir_export'); + + %BROWSER PUSHBUTTON + h7 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'String','Browse',... + 'Tooltip', 'Choose the export directory',... + 'Position',[0.75 0.875 0.2 0.05],... + 'Callback',@this.pushbutton_dir_export_browse_Callback,... + 'Tag','pushbutton_dir_export_browse'); + + %EDIT TEXTFELD + h8 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'Style','edit',... + 'Position',[0.035 0.875 0.7 0.05 ],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@this.edit_dir_export_Callback,... + 'Tooltip', 'Export path',... + 'Tag','edit_dir_export'); + + %EXTENSION TEXT + h9 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Extension',... + 'Tooltip', 'Select file format',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Style','text',... + 'Position',[0.035 0.225 0.7 0.05],... + 'Tag','text_extension'); + + % DROPDOWN MENU + h10 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String',{ '*.nrrd'; '*.vtk'; '*.mha' },... + 'Tooltip', 'File format',... + 'Style','popupmenu',... + 'Value',1,... + 'Position',[0.035 0.21 0.915 0.03],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@this.popupmenu_extension_Callback,... + 'Tag','popupmenu_extension'); + + % CT-TABLE + h11 = uitable(... + 'Parent',h1,... + 'Units','normalized',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'ColumnWidth',{20 400},... + 'Position',[0.035 0.5 0.915 0.3],... + 'ColumnEditable',[true false],... + 'ColumnFormat',{ 'logical' 'char' },... + 'ColumnName',{},... + 'RowName',{},... + 'Enable','off',... + 'Tag','uitable_vois'); + + set(h11,'Units','pixels'); + pos = get(h11,'Position'); + set(h11,'ColumnWidth',{20 pos(3)-39}); + + + %RESLT CUBES-TABLES + h12 = uitable(... + 'Parent',h1,... + 'Units','normalized',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'ColumnName',{},... + 'ColumnWidth',{20 400},... + 'RowName',{},... + 'Position',[0.035 0.3 0.915 0.15],... + 'ColumnEditable',[true false],... + 'ColumnFormat',{ 'logical' 'char' },... + 'Tag','uitable_doseCubes',... + 'Enable','off',... + 'UserData',[]); + + set(h12,'Units','pixels'); + pos = get(h12,'Position'); + set(h12,'ColumnWidth',{20 pos(3)-39}); + + this.createHandles(); + end + + end + + + %CALLBACK METHODAS + methods + %---------------CALLBACK FOR H2 BUTTON EXPORT + function this = btn_export_Callback(this, hObject, event) + handles = this.handles; + + exportDir = get(handles.edit_dir_export,'String'); + + %Sanity check + if numel(exportDir) == 0 + errordlg('No Export folder selected!'); + return; + elseif ~exist(exportDir,'dir') + errordlg(['Folder ' exportDir ' does not exist!']); + return; + else + %Add file separator if necessary + if exportDir(end) ~= filesep + exportDir = [exportDir filesep]; + end + this.handles = handles; + end + + %Get the file extension + extensionIndex = get(handles.popupmenu_extension,'Value'); + extensions = get(handles.popupmenu_extension,'String'); + extension = extensions{extensionIndex}; + extension = extension(2:end); + + saveCT = get(handles.checkbox_CT,'Value'); + saveResults = get(handles.checkbox_dose,'Value'); + + if (saveCT) + voiDir = [exportDir '/vois/']; + if ~exist(voiDir,'dir') + if ~mkdir(voiDir) + warndlg('Could not create subfolder for VOI masks. Masks will be stored in base folder.'); + voiDir = exportDir; + end + end + end + + %If we export results, try to create a subdirectory for VOIs + if (saveResults) + resultDir = [exportDir '/results/']; + if ~exist(resultDir,'dir') + if ~mkdir(resultDir) + warndlg('Could not create subfolder for resulting dose cubes. Cubes will be stored in base folder.'); + resultDir = exportDir; + end + end + end + + try + %prepare metadata + ct = evalin('base','ct'); + + metadata.resolution = [ct.resolution.x ct.resolution.y ct.resolution.z]; + metadata.compress = get(handles.checkbox_compress,'Value'); + + %Check if we have position information + if isfield(ct,'dicomInfo') + if isfield(ct.dicomInfo,'ImagePositionPatient') + metadata.imageOrigin = ct.dicomInfo.ImagePositionPatient; + if ~isrow(metadata.imageOrigin) + metadata.imageOrigin = transpose(metadata.imageOrigin); + end + end + end + + %This is only for the waitbar to get the number of cubes you wanna save + numExportCubes = 0; + if (saveCT) + if isfield(ct,'cubeHU') + numExportCubes = numExportCubes + 1; + end + + if isfield(ct,'cube') + numExportCubes = numExportCubes + 1; + end + voiNames = get(handles.uitable_vois,'Data'); + voiIndices = find([voiNames{:,1}] == true); + numExportCubes = numExportCubes + numel(voiIndices); + + else + numExportCubes = 0; + end + + if saveResults + cubeNames = get(handles.uitable_doseCubes,'data'); + cubeIndices = find([cubeNames{:,1}] == true); + numExportCubes = numExportCubes + numel(cubeIndices); + end + + %Give an error if nothing was selected + if numExportCubes == 0 + errordlg('No data was selected for export!'); + return; + end + + currentCube = 0; + + hWaitbar = waitbar(0,'Exporting...','WindowStyle', 'modal'); + cleanUp = onCleanup(@() close(hWaitbar)); + + %CT and Mask export + if saveCT + + if isfield(ct,'cube') + %Export the CT (ED suffix to clarify it is not in HU) + currentCube = currentCube + 1; + waitbar(currentCube/numExportCubes,hWaitbar,['Exporting CT Intensity values (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); + matRad_writeCube(fullfile(exportDir,['CT_ED' extension]),ct.cube{1},'double',metadata); + end + + if isfield(ct,'cubeHU') + currentCube = currentCube + 1; + waitbar(currentCube/numExportCubes,hWaitbar,['Exporting CT in HU (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); + matRad_writeCube(fullfile(exportDir,['CT_HU' extension]),ct.cubeHU{1},'double',metadata); + end + + %Export VOI masks + cst = evalin('base','cst'); + + for voiIx = voiIndices + %Waitbar + currentCube = currentCube + 1; + waitbar(currentCube/numExportCubes,hWaitbar,['Exporting Segmentation Mask (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); + + %Get the index list + voiRow = find(strcmp(voiNames{voiIx,2},cst(:,2))); + voiIndexList = cst{voiRow,4}{1}; + %Set up the full mask + voiMask = zeros(ct.cubeDim); + voiMask(voiIndexList) = 1; + %Export... + matRad_writeCube(fullfile(voiDir,[voiNames{voiIx,2} extension]),voiMask,'uint8',metadata); + end + + end + catch ME + warning(ME.identifier,'couldn''t export! Reason: %s\n',ME.message) + end + %Results Export + if saveResults + results = evalin('base','resultGUI'); + cubeNames = get(handles.uitable_doseCubes,'data'); + + for cubeIx = cubeIndices + %Export + currentCube = currentCube + 1; + waitbar(currentCube/numExportCubes,hWaitbar,['Exporting Results (' num2str(currentCube) '/' num2str(numExportCubes) ') ...']); + matRad_writeCube(fullfile(resultDir,[cubeNames{cubeIx,2} extension]),results.(cubeNames{cubeIx,2}),'double',metadata); + end + end + + %close(data.figure1); + + end + + %------------CALLBACK FOR H3 BUTTON CANCEL + function this = btn_cancel_Callback(this, hObject, event, guidata) + close; + % close(handles.figure1); + end + + %------------CALLBACK FOR H4 BUTTON CHECKBOX CT + function this = checkbox_CT_Callback(this, hObject, event) + handles = this.handles; + saveCT = get(hObject,'Value'); + %Show the VOI-table only if we want to save a CT + if (saveCT) + set(handles.uitable_vois, 'Enable','on'); + else + set(handles.uitable_vois,'Enable','off'); + end + this.handles = handles; + end + + %-------------CALLBACK FOR H5 BUTTON CHECKBOX DOSE + function this = checkbox_dose_Callback(this, hObject, event) + handles = this.handles; + saveDose = get(hObject,'Value'); + if (saveDose) + set(handles.uitable_doseCubes, 'Enable','on'); + %ORIGINAL: set(guidata.uitable_doseCubes,'Visible', 'on', 'Enable','on'); + + else + set(handles.uitable_doseCubes,'Enable','off'); + %ORIGINAL: set(guidata.uitable_doseCubes,'Visible', 'off', 'Enable','off'); + + end + this.handles = handles; + end + + %-------------CALLBACK FOR H7 PUSHBUTTON DIR EXPORT BROWSE + function this = pushbutton_dir_export_browse_Callback(this, hObject, event) + handles = this.handles; + + exportDir = uigetdir('', 'Choose the export directory...'); + if exportDir ~= 0 + exportDir = [exportDir filesep]; + set(handles.edit_dir_export,'String',exportDir); + % Update handles structure + this.handles = handles; + end + end + + %------------CALLBACK FOR H8 EDIT DIR EXPORT + function this = edit_dir_export_Callback(this, hObject, event) + handles = this.handles; + + exportDir = get(handles.edit_dir_export,'String'); + + %Add filesperator + if exportDir(end) ~= filesep + exportDir = [exportDir filesep]; + end + + %Check if the user specified an existing directory + if ~exist(exportDir,'dir') + warndlg(['Folder ' exportDir ' does not exist!']); + exportDir = ''; + end + + set(handles.edit_dir_export,'String',exportDir); + this.handles = handles; + end + + %------------CALLBACK FOR H10 DROPDOWN MENU EXTENSION + function this = popupmenu_extension_Callback(this, hObject, event) + + end + + %------------CALLBACK FOR H14 CHECKBOX COMPRESS + function this = checkbox_Compress_Callback(this, hObject, event) + + end + end +end + + + diff --git a/gui/widgets/matRad_importDicomWidget.m b/gui/widgets/matRad_importDicomWidget.m new file mode 100644 index 000000000..ba2505ccf --- /dev/null +++ b/gui/widgets/matRad_importDicomWidget.m @@ -0,0 +1,996 @@ +classdef matRad_importDicomWidget < matRad_Widget + % matRad_importDicomWidget class to generate GUI widget to import dicom + % files + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + end + + methods + + function this = matRad_importDicomWidget(handleParent) + %MATRAD_IMPORTDICOMWIDGET Construct an instance of this class + + matRad_cfg = MatRad_Config.instance(); + if nargin < 1 + handleParent = figure(... + 'IntegerHandle','off',... + 'Units','characters',... + 'MenuBar','none',... + 'NumberTitle','off',... + 'Position',[25 10 220 40],... + 'Color',matRad_cfg.gui.backgroundColor,... + 'Name','Import Dicom'); + end + this = this@matRad_Widget(handleParent); + end + + + + % INITIALIZE FUNCTION + function this = initialize(this) + handles = this.handles; + handles.output = handles; + + axes(handles.axesMatRadLogo) + [path,name,ext] = fileparts(mfilename('fullpath')); + + [im, ~, alpha] = imread([path filesep '..' filesep '..' filesep 'gfx/matrad_logo.png']); + q = image(im); + axis equal off + set(q, 'AlphaData', alpha); + % show dkfz logo + axes(handles.axesDKFZLogo) + [im, ~, alpha] = imread([path filesep '..' filesep '..' filesep 'gfx/DKFZ_Logo.png']); + p = image(im); + axis equal off + set(p, 'AlphaData', alpha); + + % Update handles structure + % guidata(hObject, handles); + this.handles = handles; + end + + end + + % CALLBACKS + methods + + % H15 BROWSER BUTTON CALLBACK + function patDir = browse_button_Callback(this, hObject, eventdata) + handles = this.handles; + patDir = uigetdir('', 'Choose the input directory...'); + if patDir ~= 0 + patDir = [patDir filesep]; + %handles.dir_path_field.String = patDir; + set(handles.dir_path_field,'String',patDir); + % Update handles structure + % guidata(hObject, handles); + this.handles = handles; + this.scan(hObject, eventdata) + end + end + + % H17 PATIENT LISTBOX CALLBACK + function this = patient_listbox_Callback(this, hObject, eventdata) + handles = this.handles; + if ~isempty(get(hObject,'String')) + % enable Import button + set(handles.import_button,'Enable','on'); + + % handles.filelist: + % 1. Filepath + % 2. Modality + % 3. PatientID + % 4. SeriesUID + % 5. SeriesNumber + % 9. res_x + % 10. res_y + % 11. res_z + % 12. detailed dose description - currently not in use for GUI user + patient_listbox = get(handles.patient_listbox,'String'); + selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); + % this gets a list of rtss series for this patient + set(handles.rtseries_listbox,'Value',1); % set dummy value to one + set(handles.rtseries_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTSTRUCT') & strcmp(handles.fileList(:,3), selected_patient),4)); + % this gets a list of rt plan series for this patient + set(handles.rtplan_listbox,'Value',[]); % set dummy value to none + set(handles.rtplan_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTPLAN') & strcmp(handles.fileList(:,3), selected_patient),4)); + % this gets a list of dose series for this patient + set(handles.doseseries_listbox,'Value',[]); % set dummy value to none + set(handles.doseseries_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTDOSE') & strcmp(handles.fileList(:,3), selected_patient),4)); + % selectedDose + + if get(handles.SeriesUID_radiobutton,'Value') == 1 + % this gets a list of ct series for this patient + set(handles.ctseries_listbox,'Value',1); % set dummy value to one + set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),4))); + + selectedDoseSeriesString = get(handles.doseseries_listbox,'String'); + % this gets a resolution for this patient + selectedCtSeriesString = get(handles.ctseries_listbox,'String'); + if ~isempty(selectedCtSeriesString) + res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); + res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); + res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); + else + res_x = NaN; res_y = NaN; res_z = NaN; + end + else + set(handles.ctseries_listbox,'Value',1); % set dummy value to one + set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),5))); + selectedCtSeriesString = get(handles.ctseries_listbox,'String'); + if ~isempty(selectedCtSeriesString) + res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); + res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); + res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); + else + res_x = NaN; res_y = NaN; res_z = NaN; + end + end + set(handles.resx_edit,'String',res_x); + set(handles.resy_edit,'String',res_y); + if numel(res_z) > 1 + set(handles.resz_edit,'String','not equi'); + else + set(handles.resz_edit,'String',res_z); + end + % Update handles structure + % guidata(hObject, handles); + this.handles = handles; + end + end + + % H22 IMPORT BUTTON CALLBACK + function this = import_button_Callback(this, hObject, eventdata) + handles = this.handles; + patient_listbox = get(handles.patient_listbox,'String'); + ctseries_listbox = get(handles.ctseries_listbox,'String'); + rtplan_listbox = get(handles.rtplan_listbox,'String'); + doseseries_listbox = get(handles.rtplan_listbox,'String'); + if ~isempty(patient_listbox) + selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); + end + if ~isempty(ctseries_listbox) + selected_ctseries = ctseries_listbox(get(handles.ctseries_listbox,'Value')); + end + if ~isempty(rtplan_listbox) + selected_rtplan = rtplan_listbox(get(handles.rtplan_listbox,'Value')); + end + + if get(handles.SeriesUID_radiobutton,'Value') == 1 + files.ct = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... + strcmp(handles.fileList(:,4), selected_ctseries),:); + + %files.rtss = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... + % strcmp(handles.fileList(:,4), selected_rtseries),:); + else + files.ct = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... + strcmp(cellfun(@num2str,handles.fileList(:,5),'UniformOutput',false), selected_ctseries) & strcmp(handles.fileList(:,2),'CT'),:); + + %files.rtss = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & ... + % strcmp(handles.fileList(:,5), selected_rtseries),:); + end + + allRtss = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,2),'RTSTRUCT'),:); + if ~isempty(allRtss) + files.rtss = allRtss(get(handles.rtseries_listbox,'Value'),:); + else + files.rtss = []; + end + + files.resx = str2double(get(handles.resx_edit,'String')); + files.resy = str2double(get(handles.resy_edit,'String')); + % check if valid assignment is made when z slices are not equi-distant + if strcmp(get(handles.resz_edit,'String'),'not equi') + msgbox('Ct data not sliced equi-distantly in z direction! Chose uniform resolution.', 'Error','error'); + return; + else + files.resz = str2double(get(handles.resz_edit,'String')); + end + % selected RT Plan + rtplan = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,2),'RTPLAN'),:); + if ~isempty(rtplan) && ~isempty(get(handles.rtplan_listbox,'Value')) + files.rtplan = rtplan(get(handles.rtplan_listbox,'Value'),:); + end + + % selected RT Dose + rtdose = handles.fileList(strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,2),'RTDOSE'),:); + if ~isempty(rtdose) && ~isempty(get(handles.doseseries_listbox,'Value')) + selectedRtDose = get(handles.doseseries_listbox,'String'); + selectedRtDoseIx = NaN*ones(1,numel(selectedRtDose)); + for i = 1:numel(selectedRtDose) + selectedRtDoseIx(i) = find(strcmp(rtdose(:,4),selectedRtDose{i})); + end + files.rtdose = rtdose(selectedRtDoseIx,:); + end + + % check if we should use the dose grid resolution + files.useDoseGrid = get(handles.checkbox3,'Value'); + + % dicomMetaBool: store complete DICOM information and patientName or not + dicomMetaBool = logical(get(handles.checkPatientName,'Value')); + matRad_importDicom(files, dicomMetaBool); + + this.handles = handles; + end + + % H23 CANCEL BUTTON CALLBACK + function this = cancel_button_Callback(this, hObject, eventdata) + + close; + end + + % RESCAN BUTTON CALLBACK; NICHT IN CREATELAYOUT VORHANDEN + function this = rescan_button_Callback(this, hObject, eventdata) + end + + % H24 SERIES UID RADIOBUTTON CALLBACK + function this = SeriesUID_radiobutton_Callback(this, hObject, eventdata) + handles = this.handles; + if get(hObject,'Value') == 1 + set(handles.SeriesNumber_radiobutton,'Value',0); + else + set(hObject,'Value',1); + set(handles.SeriesNumber_radiobutton,'Value',0); + end + if isfield(handles, 'fileList') + patient_listbox = get(handles.patient_listbox,'String'); + selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); + set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),4))); + set(handles.rtseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'RTSTRUCT') & strcmp(handles.fileList(:,3), selected_patient),4))); + set(handles.doseseries_listbox,'String',handles.fileList(strcmp(handles.fileList(:,2), 'RTDOSE') & strcmp(handles.fileList(:,3), selected_patient),4)); + set(handles.rtplan_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'RTPLAN') & strcmp(handles.fileList(:,3), selected_patient),4))); + else + fprintf('No patient loaded, so just switching default display option to SeriesUID. \n'); + end + %guidata(hObject, handles); + this.handles = handles; + end + + % H25 SERIESNUMBER RADIO BUTTON CALLBACK + function this = SeriesNumber_radiobutton_Callback(this, hObject, eventdata) + handles = this.handles; + + if get(hObject,'Value') == 1 + set(handles.SeriesUID_radiobutton,'Value',0); + else + set(hObject,'Value',1); + set(handles.SeriesUID_radiobutton,'Value',0); + end + if isfield(handles, 'fileList') + patient_listbox = get(handles.patient_listbox,'String'); + selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); + % set(handles.ctseries_listbox,'String',unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),5))); + set(handles.ctseries_listbox,'String',unique(cell2mat(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient),5)))); + + else + fprintf('No patient loaded, so just switching default display option to SeriesNumber. \n'); + end + % guidata(hObject, handles); + this.handles = handles; + end + + + % H34 DOSESERIES LISTBOX CALLBACK + function this = doseseries_listbox_Callback(this, hObject, eventdata) + handles = this.handles; + + if ~isempty(get(hObject,'Value')) + set(handles.checkbox3,'Enable','on'); + else + set(handles.checkbox3,'Value',0); + set(handles.checkbox3,'Enable','off'); + % retrieve and display resolution for DICOM ct cube + patient_listbox = get(handles.patient_listbox,'String'); + selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); + selectedCtSeriesString = get(handles.ctseries_listbox,'String'); + if get(handles.SeriesUID_radiobutton,'Value') == 1 + if ~isempty(selectedCtSeriesString) + res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); + res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); + res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); + else + res_x = NaN; res_y = NaN; res_z = NaN; + end + else + if ~isempty(selectedCtSeriesString) + res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); + res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); + res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); + else + res_x = NaN; res_y = NaN; res_z = NaN; + end + end + set(handles.resx_edit,'String',res_x); + set(handles.resy_edit,'String',res_y); + if numel(res_z) > 1 + set(handles.resz_edit,'String','not equi'); + else + set(handles.resz_edit,'String',res_z); + end + + end + + this.handles = handles; + end + + % H35 RTPLAN LISTBOX CALLBACK + function this = rtplan_listbox_Callback(this, hObject, eventdata) + handles = this.handles; + + contents = cellstr(get(hObject,'String')); + if ~isempty(get(hObject,'Value')) && numel(get(hObject,'Value')) == 1 + + selectedPlan = contents{get(hObject,'Value')}; + % point at plan in listbox + selectedPlanLoc = strcmp(handles.fileList(:,4),selectedPlan); + + % show only the doses corresponding to the plan + corrDoses = [handles.fileList{selectedPlanLoc,13}]; + numOfDoses = numel(corrDoses); + corrDosesLoc = zeros(size(handles.fileList(:,1),1),1); + for j = 1:numOfDoses + if ~isnan(corrDoses{j}) + corrDosesLoc = corrDosesLoc | strcmp(handles.fileList(:,4),corrDoses{j}); + end + end + + if sum(corrDosesLoc) == 0 + warndlg('no rt dose file directly associated to plan file. showing all rt dose files.'); + corrDosesLoc = strcmp(handles.fileList(:,2),'RTDOSE'); + end + + set(handles.doseseries_listbox,'Value',[]); % set dummy value to one + set(handles.doseseries_listbox,'String',handles.fileList(corrDosesLoc,4)); + + % disable checkbox for use dose grid is currently checked + if get(handles.checkbox3,'Value') == 1 + set(handles.checkbox3,'Value',0); + checkbox3_Callback(handles.checkbox3,[], handles); + end + set(handles.checkbox3,'Enable','off'); + + elseif numel(get(hObject,'Value')) >=2 + warning('More than one RTPLAN selected. Unsetting selection ...'); + patient_listbox_Callback(this, hObject, eventdata); + else + patient_listbox_Callback(this, hObject, eventdata); + end + + this.handles = handles; + + end + + % H36 DIR PATH FIELD CALLBACK + function this = dir_path_field_Callback(this, hObject, eventdata) + handles = this.handles; + patDir = get(handles.dir_path_field,'String'); + if patDir(end) ~= filesep; + patDir = [patDir filesep]; + set(handles.dir_path_field,'String',patDir); + % guidata(hObject, handles); + this.handles = handles; + end + scan(hObject, eventdata); + end + + % H37 CHECK PATIENTNAME CALLBACK + function this = checkPatientName_Callback(this, hObject, eventdata) + handles = this.handles; + % hObject handle to checkPatientName (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + %A = get(hObject,'Value'); + + % Hint: get(hObject,'Value') returns toggle state of checkPatientName + %guidata(hObject, handles); + this.handles = handles; + + end + + % H38 CHECKBOX USE RT DOSE GRID CALLBACK + function this = checkUseRTdoseGrid_Callback(this, hObject, eventdata) + % hObject handle to checkbox3 (see GCBO) + % eventdata reserved - to be defined in a future version of MATLAB + % handles structure with handles and user data (see GUIDATA) + + % Hint: get(hObject,'Value') returns toggle state of checkbox3 + handles = this.handles; + + if get(hObject,'Value') + set(handles.resx_edit,'Enable', 'off'); + set(handles.resy_edit,'Enable', 'off'); + set(handles.resz_edit,'Enable', 'off'); + % retrieve and display resolution for DICOM dose cube + doseFilesInList = get(handles.doseseries_listbox,'String'); + selectedDoseFiles = get(handles.doseseries_listbox,'Value'); + if isempty(selectedDoseFiles) + set(hObject,'Value',0) + errordlg('no dose file selected'); + return; + end + for i = 1:numel(selectedDoseFiles) + selectedDoseFile = doseFilesInList{selectedDoseFiles(i)}; + if verLessThan('matlab','9') + dicomDoseInfo = dicominfo(handles.fileList{find(strcmp(handles.fileList(:,4),selectedDoseFile)),1}); + else + dicomDoseInfo = dicominfo(handles.fileList{find(strcmp(handles.fileList(:,4),selectedDoseFile)),1},'UseDictionaryVR',true); + end + res_x{i} = dicomDoseInfo.PixelSpacing(1); + res_y{i} = dicomDoseInfo.PixelSpacing(2); + res_z{i} = dicomDoseInfo.SliceThickness; + end + + if numel(unique(cell2mat(res_x)))*numel(unique(cell2mat(res_y)))*numel(unique(cell2mat(res_z))) ~= 1 + set(handles.checkbox3,'Value',0); + warndlg('Different resolutions in dose file(s)'); + set(handles.resx_edit,'Enable', 'on'); + set(handles.resy_edit,'Enable', 'on'); + set(handles.resz_edit,'Enable', 'on'); + else + set(handles.resx_edit,'String',num2str(res_x{1})); + set(handles.resy_edit,'String',num2str(res_y{1})); + set(handles.resz_edit,'String',num2str(res_z{1})); + end + + else + set(handles.resx_edit,'Enable', 'on'); + set(handles.resy_edit,'Enable', 'on'); + set(handles.resz_edit,'Enable', 'on'); + % retrieve and display resolution for DICOM ct cube + patient_listbox = get(handles.patient_listbox,'String'); + selected_patient = patient_listbox(get(handles.patient_listbox,'Value')); + selectedCtSeriesString = get(handles.ctseries_listbox,'String'); + if get(handles.SeriesUID_radiobutton,'Value') == 1 + if ~isempty(selectedCtSeriesString) + res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); + res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); + res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,4), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); + else + res_x = NaN; res_y = NaN; res_z = NaN; + end + else + if ~isempty(selectedCtSeriesString) + res_x = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),9)); + res_y = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),10)); + res_z = unique(handles.fileList(strcmp(handles.fileList(:,2), 'CT') & strcmp(handles.fileList(:,3), selected_patient) & strcmp(handles.fileList(:,5), selectedCtSeriesString{get(handles.ctseries_listbox,'Value')}),11)); + else + res_x = NaN; res_y = NaN; res_z = NaN; + end + end + set(handles.resx_edit,'String',res_x); + set(handles.resy_edit,'String',res_y); + if numel(res_z) > 1 + set(handles.resz_edit,'String','not equi'); + else + set(handles.resz_edit,'String',res_z); + end + end + + this.handles = handles; + end + + end + + methods (Access = protected) + function this = createLayout(this,handleParent) + + h1 = this.widgetHandle; + + matRad_cfg = MatRad_Config.instance(); + + %Create all handles + % LOGO + h2 = axes(... + 'Parent',h1,... + 'CameraPosition',[0.5 0.5 9.16025403784439],... + 'CameraTarget',[0.5 0.5 0.5],... + 'CameraViewAngle',6.60861036031192,... + 'PlotBoxAspectRatio',[1 0.204545454545455 0.204545454545455],... + 'XTick',[0 0.2 0.4 0.6 0.8 1],... + 'XTickLabel',{ '0'; '0.2'; '0.4'; '0.6'; '0.8'; '1' },... + 'YTick',[0 0.5 1],... + 'YTickLabel',{ '0'; '0.5'; '1' },... + 'Position',[0.659070191431176 0.0633333333333333 0.320875113947129 0.13953488372093],... + 'ActivePositionProperty','position',... + 'LooseInset',[0.21882384176291 0.326703619171829 0.159909730519049 0.222752467617156],... + 'FontSize',8,... + 'SortMethod','childorder',... + 'Tag','axesDKFZLogo'); + + + %MATRAD Logo + h7 = axes(... + 'Parent',h1,... + 'CameraPosition',[0.5 0.5 9.16025403784439],... + 'CameraTarget',[0.5 0.5 0.5],... + 'CameraViewAngle',6.60861036031192,... + 'PlotBoxAspectRatio',[1 0.301587301587302 0.301587301587302],... + 'Colormap',[0.2422 0.1504 0.6603;0.250390476190476 0.164995238095238 0.707614285714286;0.257771428571429 0.181780952380952 0.751138095238095;0.264728571428571 0.197757142857143 0.795214285714286;0.270647619047619 0.21467619047619 0.836371428571429;0.275114285714286 0.234238095238095 0.870985714285714;0.2783 0.255871428571429 0.899071428571429;0.280333333333333 0.278233333333333 0.9221;0.281338095238095 0.300595238095238 0.941376190476191;0.281014285714286 0.322757142857143 0.957885714285714;0.279466666666667 0.344671428571429 0.971676190476191;0.275971428571429 0.366680952380952 0.982904761904762;0.269914285714286 0.3892 0.9906;0.260242857142857 0.412328571428571 0.995157142857143;0.244033333333333 0.435833333333333 0.998833333333333;0.220642857142857 0.460257142857143 0.997285714285714;0.196333333333333 0.484719047619048 0.989152380952381;0.183404761904762 0.507371428571429 0.979795238095238;0.178642857142857 0.528857142857143 0.968157142857143;0.176438095238095 0.549904761904762 0.952019047619048;0.168742857142857 0.570261904761905 0.935871428571428;0.154 0.5902 0.9218;0.146028571428571 0.609119047619048 0.907857142857143;0.13802380952381 0.627628571428572 0.897290476190476;0.124814285714286 0.645928571428571 0.888342857142857;0.111252380952381 0.6635 0.876314285714286;0.0952095238095238 0.679828571428571 0.859780952380952;0.0688714285714285 0.694771428571429 0.839357142857143;0.0296666666666667 0.708166666666667 0.816333333333333;0.00357142857142857 0.720266666666667 0.7917;0.00665714285714287 0.731214285714286 0.766014285714286;0.0433285714285715 0.741095238095238 0.739409523809524;0.096395238095238 0.75 0.712038095238095;0.140771428571429 0.7584 0.684157142857143;0.1717 0.766961904761905 0.655442857142857;0.193766666666667 0.775766666666667 0.6251;0.216085714285714 0.7843 0.5923;0.246957142857143 0.791795238095238 0.556742857142857;0.290614285714286 0.797290476190476 0.518828571428572;0.340642857142857 0.8008 0.478857142857143;0.3909 0.802871428571428 0.435447619047619;0.445628571428572 0.802419047619048 0.390919047619048;0.5044 0.7993 0.348;0.561561904761905 0.794233333333333 0.304480952380953;0.617395238095238 0.787619047619048 0.261238095238095;0.671985714285714 0.779271428571429 0.2227;0.7242 0.769842857142857 0.191028571428571;0.773833333333333 0.759804761904762 0.164609523809524;0.820314285714286 0.749814285714286 0.153528571428571;0.863433333333333 0.7406 0.159633333333333;0.903542857142857 0.733028571428571 0.177414285714286;0.939257142857143 0.728785714285714 0.209957142857143;0.972757142857143 0.729771428571429 0.239442857142857;0.995647619047619 0.743371428571429 0.237147619047619;0.996985714285714 0.765857142857143 0.219942857142857;0.995204761904762 0.789252380952381 0.202761904761905;0.9892 0.813566666666667 0.188533333333333;0.978628571428571 0.838628571428572 0.176557142857143;0.967647619047619 0.8639 0.164290476190476;0.961009523809524 0.889019047619048 0.153676190476191;0.959671428571429 0.913457142857143 0.142257142857143;0.962795238095238 0.937338095238095 0.126509523809524;0.969114285714286 0.960628571428571 0.106361904761905;0.9769 0.9839 0.0805],... + 'XTick',[0 0.2 0.4 0.6 0.8 1],... + 'XTickLabel',{ '0'; '0.2'; '0.4'; '0.6'; '0.8'; '1' },... + 'YTick',[0 0.5 1],... + 'YTickLabel',{ '0'; '0.5'; '1' },... + 'Position',[0.125 0.85 0.229433272394881 0.147058823529412],... + 'ActivePositionProperty','position',... + 'LooseInset',[0.265885262648529 0.319135999073457 0.194300768858541 0.217592726640994],... + 'FontSize',8,... + 'SortMethod','childorder',... + 'Tag','axesMatRadLogo' ); + + % Import text + h33 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','DICOM Import',... + 'Tooltip', 'Import selected quantites from selected folder',... + 'Style','text',... + 'Position',[0.375 0.8325 0.609323583180987 0.1708823529411765],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'Children',[],... + 'ForegroundColor',[0.0980392156862745 0.305882352941176 0.615686274509804],... + 'Tag','text12',... + 'FontSize',56,... + 'FontName','Century',... + 'FontWeight','demi' ); + + %SelectRTPlan text + h12 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','RT Plan (SOPInstanceUID)',... + 'Tooltip', 'Select an RT plan',... + 'Style','text',... + 'Position',[0.319051959890611 0.677961527418892 0.152067622441369 0.0391903531438416],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text11'); + + % Text selectRT dose + h13 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','RT Dose (SOPInstanceUID)',... + 'Tooltip', 'Select an RT dose',... + 'Style','text',... + 'Position',[0.660893345487694 0.677961527418892 0.226377724372255 0.0391903531438416],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text10' ); + + %Text choose directory + h14 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Directory',... + 'Tooltip', 'Choose the import directory',... + 'Style','text',... + 'Position',[0.0438756855575868 0.790661764705882 0.0722120658135283 0.0386029411764706],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','directory_label'); + + % Browse button + h15 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Browse',... + 'Tooltip', 'Choose the input directory',... + 'Position',[0.913162705667276 0.748382352941176 0.0630712979890311 0.0404411764705882],... + 'Callback',@(hObject,event) browse_button_Callback(this,hObject,event),... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','browse_button' ); + + %Text Patient ID + h16 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','Patient ID',... + 'Tooltip', 'Choose a patient',... + 'Style','text',... + 'Position',[0.0447897623400366 0.654632352941177 0.0557586837294333 0.059917312661499],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','patient_label' ); + + % Listbox Choose patient + h17 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'Style','listbox',... + 'Value',1,... + 'Position',[0.0446672743846855 0.491627906976744 0.22971741112124 0.186046511627907],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) patient_listbox_Callback(this,hObject,event),... + 'Tag','patient_listbox',... + 'Tooltip', 'Choose a patient'); + + %Text CT series + h18 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','CT ( ',... + 'Tooltip', 'Choose CT series',... + 'Style','text',... + 'Position',[0.318223253501284 0.431622164800459 0.0464075578022706 0.0335917312661499],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','ct_label' ); + + %Listbox CT series + h19 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'Style','listbox',... + 'Value',1,... + 'Position',[0.318140382862352 0.247441860465116 0.320875113947128 0.186046511627907],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tooltip', 'Choose CT series',... + 'Tag','ctseries_listbox'); + + % Test RTstructure set + h20 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','RT Structure Set (SOPInstanceUID)',... + 'Tooltip', 'Choose RT structure set',... + 'Style','text',... + 'Position',[0.66172205187702 0.426023542922768 0.179000580094473 0.0391903531438415],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','struct_label'); + + %Listbox choose RT structure set + h21 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'Style','listbox',... + 'Value',1,... + 'Position',[0.660893345487694 0.245503875968992 0.320875113947129 0.186046511627907],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Children',[],... + 'Tooltip', 'Choose RT structure set',... + 'Tag','rtseries_listbox'); + + % Import Button + h22 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Import',... + 'Position',[0.181403828623519 0.233875968992248 0.0628988149498633 0.0406976744186047],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) import_button_Callback(this,hObject,event),... + 'Enable','off',... + 'Tag','import_button'); + + % Cancel button + h23 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Cancel',... + 'Position',[0.181403828623519 0.185736434108527 0.0628988149498633 0.0406976744186047],... + 'Callback',@(hObject,event) cancel_button_Callback(this,hObject,event),... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','cancel_button' ); + + % radiobutton to choose Series UID + h24 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Series Instance UID /',... + 'Tooltip', 'Choose a series instance UID',... + 'Style','radiobutton',... + 'Value',1,... + 'Position',[0.339283500455788 0.434421475739305 0.143118422143035 0.0345248349124318],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) SeriesUID_radiobutton_Callback(this,hObject,event),... + 'Tag','SeriesUID_radiobutton' ); + + %Radiobutton to Choose Series Number + h25 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Series Number )',... + 'Tooltip', 'Choose a series number',... + 'Style','radiobutton',... + 'Position',[0.46272826717494 0.432555268446741 0.0999864092152151 0.0345248349124318],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) SeriesNumber_radiobutton_Callback(this,hObject,event),... + 'Tag','SeriesNumber_radiobutton' ); + + % new panel + h26 = uipanel(... + 'Parent',h1,... + 'Title','Resolution',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','uipanel1',... + 'Clipping','off',... + 'Position',[0.0446672743846855 0.131162790697674 0.103919781221513 0.174418604651163] ); + + %Text Resolution in X + txt = sprintf('Resolution of the chosen image series\nAdjust it to import an interpolated cube'); + h32 = uicontrol(... + 'Parent',h26,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','x step:',... + 'Style','text',... + 'Tooltip', txt,... + 'Position',[0.0727272727272727 0.614457831325301 0.381818181818182 0.253012048192771],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text7' ); + + + %Text Resolution in Y + txt = sprintf('Resolution of the chosen image series\nAdjust it to import an interpolated cube'); + h27 = uicontrol(... + 'Parent',h26,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','y step:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',[0.0727272727272727 0.337349397590362 0.718181818181818 0.253012048192771],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text8' ); + + %Text Resolution in Z + txt = sprintf('Resolution of the chosen image series\nAdjust it to import an interpolated cube'); + h28 = uicontrol(... + 'Parent',h26,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'String','z step:',... + 'Tooltip',txt,... + 'Style','text',... + 'Position',[0.0727272727272727 0.0602409638554217 0.718181818181818 0.253012048192771],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tag','text9' ); + + %Edit Resolution of interpolated cube : X + txt = sprintf('Resolution of the chosen image series\nAdjust it to import an interpolated cube'); + h29 = uicontrol(... + 'Parent',h26,... + 'Units','normalized',... + 'Style','edit',... + 'Position',[0.463636363636364 0.653846153846154 0.4 0.217948717948718],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tooltip',txt,... + 'Tag','resx_edit'); + + %Edit Resolution of interpolated cube : Y + txt = sprintf('Resolution of the chosen image series\nAdjust it to import an interpolated cube'); + h30 = uicontrol(... + 'Parent',h26,... + 'Units','normalized',... + 'Style','edit',... + 'Position',[0.463636363636364 0.385542168674699 0.4 0.216867469879518],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tooltip',txt,... + 'Tag','resy_edit'); + + + %Edit Resolution of interpolated cube : Z + txt = sprintf('Resolution of the chosen image series\nAdjust it to import an interpolated cube'); + h31 = uicontrol(... + 'Parent',h26,... + 'Units','normalized',... + 'Style','edit',... + 'Position',[0.463636363636364 0.120481927710843 0.4 0.216867469879518],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Tooltip',txt,... + 'Tag','resz_edit'); + + %Listbox Select RT dose + h34 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'Max',32,... + 'Style','listbox',... + 'Value',1,... + 'Position',[0.660893345487694 0.493565891472868 0.320875113947129 0.186046511627907],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) doseseries_listbox_Callback(this,hObject,event),... + 'Tooltip', 'Select an RT dose',... + 'Tag','doseseries_listbox'); + + %Listbox to choose RT plan + h35 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'Max',2,... + 'Style','listbox',... + 'Value',1,... + 'Position',[0.318140382862352 0.493565891472868 0.320875113947128 0.186046511627907],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) rtplan_listbox_Callback(this,hObject,event),... + 'Tooltip', 'Select an RT plan',... + 'Tag','rtplan_listbox'); + + %Edit choose patient case directories + h36 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'HorizontalAlignment','left',... + 'Style','edit',... + 'Position',[0.0447897623400366 0.742867647058823 0.857404021937843 0.0477941176470588],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) dir_path_field_Callback(this,hObject,event),... + 'Tooltip', 'Choose the input directory',... + 'Tag','dir_path_field' ); + + %Import Patient name + h37 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String',{ 'Import patient name & DICOM meta information'; ' '; ' ' },... + 'Style','checkbox',... + 'Value',1,... + 'Position',[0.0446672743846855 0.395661785816824 0.258805834092981 0.0484496124031008],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) checkPatientName_Callback(this,hObject,event),... + 'Tag','checkPatientName' ); + + %Checkbox use RT Dose Grid resolution + h38 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String',{ 'Use RT Dose grid'; ' '; ' ' },... + 'Tooltip', 'Use the resolution of the dose grid',... + 'Style','checkbox',... + 'Position',[0.0446672743846855 0.349150157909848 0.228805834092981 0.0484496124031008],... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontName',matRad_cfg.gui.fontName,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'Callback',@(hObject,event) checkUseRTdoseGrid_Callback(this,hObject,event),... + 'Enable','off',... + 'Tag','checkbox3' ); + + this.createHandles(); + + end + end + + methods (Access = private) + % SCAN FUNKTION + function this = scan(this, hObject, eventdata) + handles = this.handles; + [fileList, patient_listbox] = matRad_scanDicomImportFolder(get(handles.dir_path_field,'String')); + if iscell(patient_listbox) + handles.fileList = fileList; + %handles.patient_listbox.String = patient_listbox; + set(handles.patient_listbox,'String',patient_listbox,'Value',1); + % guidata(hObject, handles); + this.handles = handles; + end + end + end + + + +end + diff --git a/gui/widgets/matRad_importWidget.m b/gui/widgets/matRad_importWidget.m new file mode 100644 index 000000000..fbead76c1 --- /dev/null +++ b/gui/widgets/matRad_importWidget.m @@ -0,0 +1,409 @@ +classdef matRad_importWidget < matRad_Widget + % matRad_importWidget class to generate GUI widget to import various + % formats : dicom, nrrd, etc. + % + % + % References + % - + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % Copyright 2020 the matRad development team. + % + % This file is part of the matRad project. It is subject to the license + % terms in the LICENSE file found in the top-level directory of this + % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part + % of the matRad project, including this file, may be copied, modified, + % propagated, or distributed except according to the terms contained in the + % LICENSE file. + % + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + properties + + end + + methods + function this = matRad_importWidget(handleParent) + matRad_cfg = MatRad_Config.instance(); + if nargin < 1 + handleParent = figure(... + 'MenuBar','none',... + 'Units','characters',... + 'Position',[25 10 89.2 18.3846153846154],... + 'Color',matRad_cfg.gui.backgroundColor,... + 'Name','Import Patient',... + 'HandleVisibility','callback',... + 'Tag','figure_importDialog',... + 'WindowStyle','normal'); + end + this = this@matRad_Widget(handleParent); + end + + %OPENING/INITIALIZE FUNKTION + function this = initialize(this) + % handles = this.handles; + % this.handles = handles; + % + %handles = guidata(this.widgetHandle); + %handles.output = this.widgetHandle; + %guidata(this.widgetHandle, handles); + + end + end + + methods (Access = protected) + function this = createLayout(this) + matRad_cfg = MatRad_Config.instance(); + + h1 = this.widgetHandle; + + + h2 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Patient CT file [HU]:',... + 'Tooltip','Choose a patient CT',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.03 0.85 0.4 0.1],... + 'Tag','text2'); + + + h3 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'Style','edit',... + 'Position',[0.03 0.78 0.73 0.1],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Callback',@(hObject,event) edit_ctPath_Callback(this,hObject, event),... + 'Tag','edit_ctPath',... + 'Tooltip','Choose the input directory'); + + + h4 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Browse...',... + 'Tooltip','Choose the input directory',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.78 0.78 0.2 0.1],... + 'Callback',@this.pushbutton_ctPath_Callback,... + 'Tag','pushbutton_ctPath'); + + + h5 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Binary Masks/Segmentations',... + 'Tooltip','Select binary masks/segmentations',... + 'HorizontalAlignment','left',... + 'Style','text',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.03 0.435 0.5 0.1],... + 'Tag','text_masks'); + + + h6 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'Max',2,... + 'Style','listbox',... + 'Tooltip','Select binary masks/segmentations',... + 'Position',[0.03 0.15 0.73 0.32],... [1.8 3.38461538461539 66.2 4.69230769230769],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Callback',@this.listbox_maskPaths_Callback,... + 'Tag','listbox_maskPaths',... + 'KeyPressFcn',@(hObject,eventdata)matRad_importGUI('listbox_maskPaths_KeyPressFcn',hObject,eventdata,guidata(hObject))); + + + h7 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Add File(s)...',... + 'Tooltip','Choose the binary mask files',... + 'Style','pushbutton',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.78 0.37 0.2 0.1],... + 'Callback',@this.pushbutton_addMaskPaths_Callback,... + 'Tag','pushbutton_addMaskPaths'); + + + h8 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Import',... + 'Tooltip','Import the selected CT and binary masks',... + 'Position',[0.78 0.02 0.2 0.1],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Callback',@this.pushbutton_import_Callback,... + 'Tag','pushbutton_import'); + + h10 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Add Folder...',... + 'Tooltip','Choose the folder containing binary mask files',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.78 0.22 0.2 0.1],... + 'Callback',@this.pushbutton_addMaskFolders_Callback,... + 'Tag','pushbutton_addMaskFolders'); + + + h11 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','Convert from HU?',... + 'Style','checkbox',... + 'BackgroundColor',matRad_cfg.gui.backgroundColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.03 0.68 0.4 0.1],... + 'Callback',@this.checkbox_huConvert_Callback,... + 'Tooltip','If this is checked, the import assumes that this is a CT given in HU and will convert with the given (or the default) HLUT',... + 'Tag','checkbox_huConvert'); + + + h12 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','matRad_default.hlut',... + 'HorizontalAlignment','left',... + 'Style','edit',... + 'Enable','off',... + 'Position',[0.03 0.57 0.73 0.1],... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Callback',@this.edit_hlut_Callback,... + 'Tag','edit_hlut'); + + + h13 = uicontrol(... + 'Parent',h1,... + 'Units','normalized',... + 'String','HLUT file...',... + 'Tooltip','Choose the HLUT file',... + 'BackgroundColor',matRad_cfg.gui.elementColor,... + 'ForegroundColor',matRad_cfg.gui.textColor,... + 'FontSize',matRad_cfg.gui.fontSize,... + 'FontWeight',matRad_cfg.gui.fontWeight,... + 'FontName',matRad_cfg.gui.fontName,... + 'Position',[0.78 0.57 0.2 0.1],... [69.8 10.6923076923077 18.2 1.76923076923077],... + 'Callback',@this.pushbutton_hlutFile_Callback,... + 'Tag','pushbutton_hlutFile'); + + this.createHandles(); + end + + end + + methods + + % %OUTPUT FUNKTION + % function varargout = OutputFcn(hObject, eventdata, handles) + % varargout{1} = handles.output; + % end + + %CALLBACK FOR H3 EDIT CREATE PATH + function this = edit_ctPath_Callback(this, hObject, event) + end + + %CALLBACK FOR H4 PUSHBUTTON CREATE PATH + function this = pushbutton_ctPath_Callback(this, hObject, event) + handles = this.handles; + [importCTFile,importCTPath,~] = uigetfile({'*.nrrd', 'NRRD-Files'}, 'Choose the CT file...'); + + if importCTFile ~= 0 + set(handles.edit_ctPath,'String',fullfile(importCTPath,importCTFile)); % NON EXISTING FIELD 'edit_ctPath' + % Update handles structure + this.handles = handles; + end + end + + %CALLBACK FOR H6 LISTBOX MASK PATHS LEER + function this = listbox_maskPaths_Callback(this, hObject, event) + + end + + %CALLBACK FOR PUSHBUTTON ADD MASKPATHS + function this = pushbutton_addMaskPaths_Callback(this, hObject, event) + handles = this.handles; + [importMaskFile,importMaskPath,~] = uigetfile({'*.nrrd', 'NRRD-Files'}, 'Choose the binary mask files...','MultiSelect','on'); + if ~isempty(importMaskFile) + if ~iscell(importMaskFile) + tmpName = importMaskFile; + importMaskFile = cell(1); + importMaskFile{1} = tmpName; + end + importMaskFile = cellfun(@(filename) fullfile(importMaskPath,filename),importMaskFile,'UniformOutput',false); + entries = get(handles.listbox_maskPaths,'String'); + newEntries = [entries importMaskFile]; + set(handles.listbox_maskPaths,'String',newEntries); + % Update handles structure + this.handles = handles; + end + end + + function cst = showCheckDialog(this,cst) + handles = this.handles; + handle = dialog('Position', [100 100 400 250],'WindowStyle','modal','Name','Confirm Segmentations'); + + %Create Table + hTable = uitable('Parent',handle,'Units','normal','Position',[0.1 0.2 0.8 0.8]); + set(hTable,'Data',cst(:,2:3)); + set(hTable,'ColumnName',{'Name','Type'}); + set(hTable,'ColumnWidth',{150,'auto'}); + set(hTable,'RowName',char([])); + set(hTable,'ColumnEditable',[true true]); + set(hTable,'ColumnFormat',{'char',{'TARGET', 'OAR', 'IGNORED'}}); + + %Create Button + hButton = uicontrol(handle,'Style','pushbutton','String','Confirm','Units','normal','Position',[0.7 0.05 0.2 0.1],'Callback','uiresume(gcbf)');%{@pushbutton_confirm_vois_callback}); + try + uiwait(handle); + tmp = get(hTable,'Data'); + cst(:,2:3) = tmp(:,:); + catch + warning('Closed checkdialog without confirmation! Using default cst information!'); + end + delete(handle); + this.handles = handles(); + + end + + %CALLBACK FOR H8 PUSHBUTTON IMPORT + function this = pushbutton_import_Callback(this, hObject, event) + handles = this.handles; + + ctFile = get(handles.edit_ctPath,'String'); + maskFiles = get(handles.listbox_maskPaths,'String'); + + if isempty(ctFile) || isempty(maskFiles) + errordlg('Please sepecify a CT and at least one mask!'); + end + + convertHU = get(handles.checkbox_huConvert,'Value'); + + if convertHU + [ct,cst] = matRad_importPatient(ctFile,maskFiles,get(handles.edit_hlut,'String')); + else + [ct,cst] = matRad_importPatient(ctFile,maskFiles); + end + + cst = this.showCheckDialog(cst); + + % delete existing workspace - parse variables from base workspace + %set(handles.popupDisplayOption,'String','no option available'); + AllVarNames = evalin('base','who'); + RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; + ChangedVarNames = {}; + for i = 1:length(RefVarNames) + if sum(ismember(AllVarNames,RefVarNames{i}))>0 + evalin('base',['clear ', RefVarNames{i}]); + end + end + + assignin('base', 'ct', ct); + assignin('base', 'cst', cst); + + %delete(handles.figure_importDialog); + + this.handles = handles; + this.changedWorkspace('ct','cst'); + delete(this.widgetHandle); + + end + + % %CALLBACK FOR H9 PUSHBUTTON CANCEL + % function this = pushbutton_cancel_Callback(this, hObject, event) + % handles = this.handles; + % delete(handles.figure_importDialog); + % this.handles = handles; + % end + + %CALLBACK FOR H10 PUSHBUTTON ADD MASK FOLDERS + function this = pushbutton_addMaskFolders_Callback(this, hObject, event) + handles = this.handles; + importMaskPath = uigetdir('./', 'Choose the folder containing binary mask files...'); + importMaskPath = [importMaskPath filesep]; + if ~isempty(importMaskPath) + entries = get(handles.listbox_maskPaths,'String'); + newEntries = [entries cellstr(importMaskPath)]; + set(handles.listbox_maskPaths,'String',newEntries); + % Update handles structure + this.handles = handles; + end + end + + %CALLBACK FOR H11 CHECKBOX HU CONVERT + function this = checkbox_huConvert_Callback(this, hObject, event) + handles = this.handles; + checked = get(hObject,'Value'); + if checked + fieldState = 'on'; + else + fieldState = 'off'; + end + + set(handles.edit_hlut,'Enable',fieldState); + set(handles.pushbutton_hlutFile,'Enable',fieldState); + this.handles = handles; + end + + %CALLBACK FOR H12 EDIT HLUT + function this = edit_hlut_Callback(this, hObject, event) + %bleibt leer + end + + %CALLBACK FOR H13 PUSHBUTTON HLUT FILE + function this = pushbutton_hlutFile_Callback(this, hObject, event) + [importHLUTFile,importHLUTPath,~] = uigetfile({'*.hlut', 'matRad HLUT-Files'}, 'Choose the HLUT file...'); + if importHLUTFile ~= 0 + set(handles.edit_hlut,'String',fullfile(importHLUTPath,importHLUTFile)); + % Update handles structure + this.handles = handles; + end + end + + + end +end + diff --git a/matRad.m b/matRad.m index e60170e95..4adee44da 100644 --- a/matRad.m +++ b/matRad.m @@ -31,8 +31,8 @@ % beam geometry settings pln.propStf.bixelWidth = 5; % [mm] / also corresponds to lateral spot spacing for particles -pln.propStf.gantryAngles = [0:72:359]; % [?] ; -pln.propStf.couchAngles = [0 0 0 0 0]; % [?] ; +pln.propStf.gantryAngles = [0:72:359]; % [°] ; +pln.propStf.couchAngles = [0 0 0 0 0]; % [°] ; pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct,0); % optimization settings @@ -56,6 +56,14 @@ % retrieve scenarios for dose calculation and optimziation pln.multScen = matRad_multScen(ct,scenGenType); +% optimization settings +pln.propOpt.optimizer = 'IPOPT'; +% pln.propOpt.bioOptimization = 'none'; % none: physical optimization; const_RBExD; constant RBE of 1.1; +% % LEMIV_effect: effect-based optimization; LEMIV_RBExD: optimization of RBE-weighted dose +pln.propOpt.runDAO = false; % 1/true: run DAO, 0/false: don't / will be ignored for particles +pln.propSeq.runSequencing = true; % true: run sequencing, false: don't / will be ignored for particles and also triggered by runDAO below + + %% initial visualization and change objective function settings if desired matRadGUI @@ -75,11 +83,8 @@ resultGUI = matRad_fluenceOptimization(dij,cst,pln); %% sequencing -if strcmp(pln.radiationMode,'photons') && (pln.propOpt.runSequencing || pln.propOpt.runDAO) - %resultGUI = matRad_xiaLeafSequencing(resultGUI,stf,dij,5); - %resultGUI = matRad_engelLeafSequencing(resultGUI,stf,dij,5); - resultGUI = matRad_siochiLeafSequencing(resultGUI,stf,dij,5); -end +resultGUI = matRad_sequencing(resultGUI,stf,dij,pln); + %% DAO if strcmp(pln.radiationMode,'photons') && pln.propOpt.runDAO diff --git a/matRadGUI.fig b/matRadGUI.fig deleted file mode 100644 index 66b63595c..000000000 Binary files a/matRadGUI.fig and /dev/null differ diff --git a/matRadGUI.m b/matRadGUI.m index f76b2c86d..563bc7c00 100644 --- a/matRadGUI.m +++ b/matRadGUI.m @@ -1,412 +1,35 @@ -function varargout = matRadGUI(varargin) -% matRad GUI +function hGUI = matRadGUI(varargin) +% matRad compatability function to call the matRad_MainGUI +% The function checks input parameters and handles the GUI as a +% singleton, so following calls will not create new windows % % call -% MATRADGUI, by itself, creates a new MATRADGUI or raises the existing -% singleton*. +% hGUI = matRadGUI +% matRadGUI % -% H = MATRADGUI returns the handle to a new MATRADGUI or the handle to -% the existing singleton*. -% -% MATRADGUI('CALLBACK',hObject,eventData,handles,...) calls the local -% function named CALLBACK in MATRADGUI.M with the given input arguments. -% -% MATRADGUI('Property','Value',...) creates a new MATRADGUI or raises the -% existing singleton*. Starting from the left, property value pairs are -% applied to the GUI before matRadGUI_OpeningFcn gets called. An -% unrecognized property name or invalid value makes property application -% stop. All inputs are passed to matRadGUI_OpeningFcn via varargin. -% -% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one -% instance to run (singleton)". -% -% See also: GUIDE, GUIDATA, GUIHANDLES +% References +% - % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -% Copyright 2015 the matRad development team. -% -% This file is part of the matRad project. It is subject to the license -% terms in the LICENSE file found in the top-level directory of this -% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part -% of the matRad project, including this file, may be copied, modified, -% propagated, or distributed except according to the terms contained in the +% Copyright 2015 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the % LICENSE file. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -matRad_cfg = MatRad_Config.instance(); - -if matRad_cfg.disableGUI - matRad_cfg.dispInfo('matRad GUI disabled in matRad_cfg!\n'); - return; -end - -if ~isdeployed - addpath(genpath(matRad_cfg.matRadRoot)); -end - -[env, versionString] = matRad_getEnvironment(); - -% abort for octave -switch env - case 'MATLAB' - - case 'OCTAVE' - matRad_cfg.dispInfo(['matRad GUI not available for ' env ' ' versionString ' \n']); - return; - otherwise - matRad_cfg.dispInfo('not yet tested'); -end - - -% Begin initialization code - DO NOT EDIT -% set platform specific look and feel -try - if ispc - lf = 'com.sun.java.swing.plaf.windows.WindowsLookAndFeel'; - elseif isunix - lf = 'com.jgoodies.looks.plastic.Plastic3DLookAndFeel'; - elseif ismac - lf = 'com.apple.laf.AquaLookAndFeel'; - end - javax.swing.UIManager.setLookAndFeel(lf); -catch ME - matRad_cfg.dispDebug('Could not change Java look due to %s\n',ME.message); -end -gui_Singleton = 1; -gui_State = struct('gui_Name', mfilename, ... - 'gui_Singleton', gui_Singleton, ... - 'gui_OpeningFcn', @matRadGUI_OpeningFcn, ... - 'gui_OutputFcn', @matRadGUI_OutputFcn, ... - 'gui_LayoutFcn', [] , ... - 'gui_Callback', []); -if nargin && ischar(varargin{1}) - gui_State.gui_Callback = str2func(varargin{1}); -end - -if nargout - [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); -else - gui_mainfcn(gui_State, varargin{:}); -end - -% End initialization code - DO NOT EDIT - -function handles = resetGUI(hObject, handles, varargin) - -% Choose default command line output for matRadGUI -handles.output = hObject; -%show matrad logo -axes(handles.axesLogo) -[im, ~, alpha] = imread('matrad_logo.png'); -f = image(im); -axis equal off -set(f, 'AlphaData', alpha); -% show dkfz logo -axes(handles.axesDKFZ) -[im, ~, alpha] = imread('DKFZ_Logo.png'); -f = image(im); -axis equal off; -set(f, 'AlphaData', alpha); - -% turn off the datacursormode (since it does not allow to set scrolling -% callbacks -handles.dcm_obj = datacursormode(handles.figure1); -set(handles.dcm_obj,'DisplayStyle','window'); -if strcmpi(get(handles.dcm_obj,'Enable'),'on') - set(handles.dcm_obj,'Enable','off'); -end -%Add the callback for the datacursor display -set(handles.dcm_obj,'UpdateFcn',@dataCursorUpdateFunction); - -% set callback for scroll wheel function -set(gcf,'WindowScrollWheelFcn',@matRadScrollWheelFcn); - -% change color of toobar but only the first time GUI is started -if handles.initialGuiStart - try - hToolbar = findall(hObject,'tag','uitoolbar1'); - jToolbar = get(get(hToolbar,'JavaContainer'),'ComponentPeer'); - jToolbar.setBorderPainted(false); - color = java.awt.Color.gray; - % Remove the toolbar border, to blend into figure contents - jToolbar.setBackground(color); - % Remove the separator line between toolbar and contents - warning('off', 'MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame'); - warning('off', 'MATLAB:ui:javaframe:PropertyToBeRemoved'); - %warning('off', 'MATLAB:ui:java:JavaFrame'); - jFrame = get(handle(hObject),'JavaFrame'); - jFrame.showTopSeparator(false); - jtbc = jToolbar.getComponents; - for idx=1:length(jtbc) - jtbc(idx).setOpaque(false); - jtbc(idx).setBackground(color); - for childIdx = 1 : length(jtbc(idx).getComponents) - jtbc(idx).getComponent(childIdx-1).setBackground(color); - end - end - catch ME - matRad_cfg.dispDebug('Could not change Toolbar colors due to %s\n',ME.message); - end -end - - -set(handles.legendTable,'String',{'no data loaded'}); -% clear VOIPlotFlag -if isfield(handles,'VOIPlotFlag') - handles = rmfield(handles,'VOIPlotFlag'); -end - -%search for availabes machines -handles.Modalities = {'photons','protons','helium','carbon'}; -for i = 1:length(handles.Modalities) - pattern = [handles.Modalities{1,i} '_*']; - if isdeployed - baseroot = [ctfroot filesep 'matRad']; - else - baseroot = fileparts(mfilename('fullpath')); - end - Files = dir([baseroot filesep 'basedata' filesep pattern]); - - for j = 1:length(Files) - if ~isempty(Files) - MachineName = Files(j).name(numel(handles.Modalities{1,i})+2:end-4); - if isfield(handles,'Machines') - if sum(strcmp(handles.Machines,MachineName)) == 0 - handles.Machines{size(handles.Machines,2)+1} = MachineName; - end - else - handles.Machines = cell(1); - handles.Machines{1} = MachineName; - end - end - end -end -set(handles.popUpMachine,'String',handles.Machines); - -%TODO: replace with class crawling -multScenDummy = matRad_multScen([],'nomScen'); -set(handles.popupmenuScenGen,'String',multScenDummy.AvailableScenCreationTYPE); - -vChar = get(handles.editGantryAngle,'String'); -if strcmp(vChar(1,1),'0') && length(vChar)==6 - set(handles.editGantryAngle,'String','0'); -end -vChar = get(handles.editCouchAngle,'String'); -if strcmp(vChar(1,1),'0') && length(vChar)==3 - set(handles.editCouchAngle,'String','0') -end -%% -% handles.State=0 no data available -% handles.State=1 ct cst and pln available; ready for dose calculation -% handles.State=2 ct cst and pln available and dij matric(s) are calculated; -% ready for optimization -% handles.State=3 plan is optimized - - -% if plan is changed go back to state 1 -% if table VOI Type or Priorities changed go to state 1 -% if objective parameters changed go back to state 2 -handles.CutOffLevel = 0.01; % relative cut off level for dose vis -handles.IsoDose.NewIsoDoseFlag = false; -handles.TableChanged = false; -handles.State = 0; -handles.doseOpacity = 0.6; -handles.IsoDose.Levels = 0; -handles.dispWindow = cell(3,2); % first dimension refers to the selected - -% do not calculate / suggest isoCenter new by default -set(handles.checkIsoCenter, 'Value', 0); -set(handles.editIsoCenter,'Enable','on') - -% suppose no ct cube in HU is available (because no ct could be available) -handles.cubeHUavailable = false; -% initial startup finished -handles.initialGuiStart = false; -guidata(hObject, handles); -% eof resetGUI - - -% --- Initializes the slice slider for the current ct & isocenter (or sets -% it to default) -function initViewSliceSlider(handles) -% handles structure with handles and user data (see GUIDATA) - -if handles.State > 0 - ct = evalin('base', 'ct'); - - %Helpers for managing the resolution and Matlab xy permutation - planePermute = [2 1 3]; - ctRes = [ct.resolution.x ct.resolution.y ct.resolution.z]; - planePermIx = planePermute(handles.plane); - planeDim = ct.cubeDim(handles.plane); - - %Try to select the slice from defined isocenter - try - if evalin('base','exist(''pln'',''var'')') - currPln = evalin('base','pln'); - - if sum(currPln.propStf.isoCenter(:)) ~= 0 - %currSlice = round((currPln.propStf.isoCenter(1,planePermIx)+ctRes(planePermix)/2)/ctRes(planePermIx))-1; - currSlice = round(currPln.propStf.isoCenter(1,planePermIx) / ctRes(planePermIx)); - else - currSlice = 0; - end - end - catch - currSlice = 0; %Set to zero for next if - end - - %If no isocenter or wrong value, choose central slice - if currSlice < 1 || currSlice > planeDim - currSlice = ceil(planeDim/2); - end - - set(handles.sliderSlice,'Min',1,'Max',planeDim,... - 'Value',currSlice,... - 'SliderStep',[1/(planeDim-1) 1/(planeDim-1)]); -else - % reset slider when nothing is loaded - set(handles.sliderSlice,'Min',0,'Max',1,'Value',0,'SliderStep',[1 1]); -end - - - -function handles = reloadGUI(hObject, handles, ct, cst) -AllVarNames = handles.AllVarNames; - -if nargin >=3 && ismember('ct',AllVarNames) - % compute HU values - if ~isfield(ct, 'cubeHU') - ct = matRad_electronDensitiesToHU(ct); - assignin('base','ct',ct); - end - if ~isfield(ct, 'cubeHU') - handles.cubeHUavailable = false; - else - handles.cubeHUavailable = true; - end -end - -%set plan if available - if not create one -try - if ismember('pln',AllVarNames) && handles.State > 0 - % check if you are working with a valid pln - pln = evalin('base','pln'); - if ~isfield(pln,'propStf') - handles = showWarning(handles,'GUI OpeningFunc: Overwriting outdated pln format with default GUI pln'); - evalin('base','clear pln'); - getPlnFromGUI(handles); - end - setPln(handles); - elseif handles.State > 0 - getPlnFromGUI(handles); - setPln(handles); - end - -catch ME - handles.State = 0; - handles = showError(handles,sprintf('GUI OpeningFunc: Could not set or get pln: %s',ME.message)); -end - -% check for dij structure -if ismember('dij',AllVarNames) - handles.State = 2; -end - -% check for optimized results -if ismember('resultGUI',AllVarNames) - handles.State = 3; -end - -% set some default values -if handles.State == 2 || handles.State == 3 - set(handles.popupDisplayOption,'String','physicalDose'); - handles.SelectedDisplayOption ='physicalDose'; - handles.SelectedDisplayOptionIdx=1; -else - handles.resultGUI = []; - set(handles.popupDisplayOption,'String','no option available'); - handles.SelectedDisplayOption=''; - handles.SelectedDisplayOptionIdx=1; -end - -% precompute iso dose lines -if handles.State == 3 - handles = updateIsoDoseLineCache(handles); -end - -%per default the first beam is selected for the profile plot -handles.selectedBeam = 1; -handles.plane = get(handles.popupPlane,'Value'); -handles.DijCalcWarning = false; - -% set slice slider -initViewSliceSlider(handles); - -% define context menu for structures -if handles.State > 0 - for i = 1:size(cst,1) - if cst{i,5}.Visible - handles.VOIPlotFlag(i) = true; - else - handles.VOIPlotFlag(i) = false; - end - end -end - -%Initialize colormaps and windows -handles.doseColorMap = 'jet'; -handles.ctColorMap = 'bone'; -handles.cMapSize = 64; -handles.cBarChanged = true; - -%Set up the colormap selection box -availableColormaps = matRad_getColormap(); -set(handles.popupmenu_chooseColormap,'String',availableColormaps); - -currentCtMapIndex = find(strcmp(availableColormaps,handles.ctColorMap)); -currentDoseMapIndex = find(strcmp(availableColormaps,handles.doseColorMap)); - -if handles.State >= 1 - set(handles.popupmenu_chooseColormap,'Value',currentCtMapIndex); -end - -if handles.State >= 3 - set(handles.popupmenu_chooseColormap,'Value',currentDoseMapIndex); -end - -% Update handles structure -handles.profileOffset = 0; -UpdateState(handles) -axes(handles.axesFig) +persistent hMatRadGUI; -handles.rememberCurrAxes = false; -UpdatePlot(handles) -handles.rememberCurrAxes = true; -guidata(hObject, handles); -% eof reloadGUI - -% --- Executes just before matRadGUI is made visible. -function matRadGUI_OpeningFcn(hObject, ~, handles, varargin) -%#ok<*DEFNU> -%#ok<*AGROW> -% This function has no output args, see OutputFcn. -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -% varargin command line arguments to matRadGUI (see VARARGIN) - -% variable to check whether GUI is opened or just refreshed / new data -% loaded, since resetGUI needs to distinguish at one point - -handles.initialGuiStart = true; p = inputParser; -addParameter(p,'devMode',false,@validateModeValue); -addParameter(p,'eduMode',false,@validateModeValue); +addParameter(p,'devMode',false,@(x) validateModeValue(x)); +addParameter(p,'eduMode',false,@(x) validateModeValue(x)); p.KeepUnmatched = true; %No error with incorrect parameters parse(p,varargin{:}); @@ -420,3930 +43,58 @@ function matRadGUI_OpeningFcn(hObject, ~, handles, varargin) parsedInput.eduMode = str2double(parsedInput.eduMode); end +matRad_cfg = MatRad_Config.instance(); + %If devMode is true, error dialogs will include the full stack trace of the error %If false, only the basic error message is shown (works for errors that %handle the MException object) -handles.devMode = logical(parsedInput.devMode); -if handles.devMode +matRad_cfg.devMode = logical(parsedInput.devMode); +if matRad_cfg.devMode disp('matRadGUI starting in developer mode!'); end -%Enables simple educational mode which removes certain functionality from +%Enables simple educational mode which removes certain functionality from %the GUI -handles.eduMode = logical(parsedInput.eduMode); -if handles.eduMode +matRad_cfg.eduMode = logical(parsedInput.eduMode); +if matRad_cfg.eduMode disp('matRadGUI starting in educational mode!'); end - -set(handles.radiobtnPlan,'value',0); - - - -if handles.eduMode - %Visisbility in Educational Mode - eduHideHandles = {handles.radiobutton3Dconf,... - handles.btnRunDAO,... - handles.pushbutton_importFromBinary,... - handles.btnLoadDicom,... - handles.btn_export,... - handles.importDoseButton}; - eduDisableHandles = {handles.editCouchAngle,handles.popUpMachine}; - cellfun(@(h) set(h,'Visible','Off'),eduHideHandles); - cellfun(@(h) set(h,'Enable','Off'),eduDisableHandles); -end - - -%Alter matRad Version string positioning -vString = matRad_version(); -vPos = get(handles.text15,'Position'); -urlPos = get(handles.text31,'Position'); -btnPos = get(handles.btnAbout,'Position'); - -%vPos([1 3]) = urlPos([1 3]); -vPos([1 3]) = [0 1]; -vPos(4) = vPos(4)*1.25; -btnPos(2) = 0.05; -urlPos(2) = btnPos(2)+btnPos(4)+0.05; -vPos(2) = urlPos(2) + urlPos(4) + 0.05; -vPos(4) = 0.98 - vPos(2); - -set(handles.btnAbout,'Position',btnPos); -set(handles.text31,'String','www.matRad.org','Position',urlPos,'Enable','inactive','ButtonDownFcn', @(~,~) web('www.matrad.org','-browser')); -set(handles.text15,'String',vString,'Position',vPos); - -handles = resetGUI(hObject, handles); - -%% parse variables from base workspace -AllVarNames = evalin('base','who'); -handles.AllVarNames = AllVarNames; -try - if ismember('ct',AllVarNames) && ismember('cst',AllVarNames) - ct = evalin('base','ct'); - cst = evalin('base','cst'); - %cst = setCstTable(handles,cst); - cst = generateCstTable(handles,cst); - handles.State = 1; - cst = matRad_computeVoiContoursWrapper(cst,ct); - assignin('base','cst',cst); - - elseif ismember('ct',AllVarNames) && ~ismember('cst',AllVarNames) - handles = showError(handles,'GUI OpeningFunc: could not find cst file'); - elseif ~ismember('ct',AllVarNames) && ismember('cst',AllVarNames) - handles = showError(handles,'GUI OpeningFunc: could not find ct file'); - end -catch - handles = showError(handles,'GUI OpeningFunc: Could not load ct and cst file'); -end - -if ismember('ct',AllVarNames) && ismember('cst',AllVarNames) - handles = reloadGUI(hObject, handles, ct, cst); -else - handles = reloadGUI(hObject, handles); -end - -guidata(hObject, handles); - -%Validates the attributes for the command line Modes -function validateModeValue(x) -%When passed from OS terminal (or inline in Command Window) everything is a string -if isdeployed || ischar(x) || isstring(x) - x=str2double(x); -end -validateattributes(x,{'logical','numeric'},{'scalar','binary'}); - - - -function Callback_StructVisibilty(source,~) - -handles = guidata(findobj('Name','matRadGUI')); - -contextUiChildren = get(get(handles.figure1,'UIContextMenu'),'Children'); - -Idx = find(strcmp(get(contextUiChildren,'Label'),get(source,'Label'))); -if strcmp(get(source,'Checked'),'on') - set(contextUiChildren(Idx),'Checked','off'); -else - set(contextUiChildren(Idx),'Checked','on'); -end -%guidata(findobj('Name','matRadGUI'), handles); -UpdatePlot(handles); - -% --- Outputs from this function are returned to the command line. -function varargout = matRadGUI_OutputFcn(~, ~, handles) -% varargout cell array for returning output args (see VARARGOUT); -% hObject handle to figure -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Get default command line output from handles structure -varargout{1} = handles.output; - -% set focus on error dialog -if isfield(handles,'ErrorDlg') - figure(handles.ErrorDlg) -end - -% --- Executes on button press in btnLoadMat. -function btnLoadMat_Callback(hObject, ~, handles) -% hObject handle to btnLoadMat (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -[FileName, FilePath] = uigetfile('*.mat'); -if FileName == 0 % user pressed cancel --> do nothing. +if matRad_cfg.disableGUI + matRad_cfg.dispInfo('matRad GUI disabled in matRad_cfg!\n'); return; end -handles = resetGUI(hObject, handles); - -try - - % delete existing workspace - parse variables from base workspace - AllVarNames = evalin('base','who'); - RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; - - for i = 1:length(RefVarNames) - if sum(ismember(AllVarNames,RefVarNames{i}))>0 - evalin('base',['clear ', RefVarNames{i}]); - end - end - - % read new data - load([FilePath FileName]); - set(handles.legendTable,'String',{'no data loaded'}); - set(handles.popupDisplayOption,'String','no option available'); - -catch ME - handles = showError(handles,'LoadMatFileFnc: Could not load *.mat file',ME); - - guidata(hObject,handles); - UpdateState(handles); - UpdatePlot(handles); - return -end - -try - generateCstTable(handles,cst); - handles.TableChanged = false; - set(handles.popupTypeOfPlot,'Value',1); - cst = matRad_computeVoiContoursWrapper(cst,ct); - - assignin('base','ct',ct); - assignin('base','cst',cst); - handles.State = 1; -catch ME - handles = showError(handles,'LoadMatFileFnc: Could not load *.mat file',ME); -end - -% check if a optimized plan was loaded -if exist('stf','var') - assignin('base','stf',stf); -end -if exist('pln','var') - assignin('base','pln',pln); -end -if exist('dij','var') - assignin('base','dij',dij); -end -% if exist('stf','var') && exist('dij','var') -% handles.State = 2; -% end - -if exist('resultGUI','var') - assignin('base','resultGUI',resultGUI); - % handles.State = 3; - % handles.SelectedDisplayOption ='physicalDose'; -end - -% recheck current workspace variables -AllVarNames = evalin('base','who'); -handles.AllVarNames = AllVarNames; - -if ismember('ct',AllVarNames) && ismember('cst',AllVarNames) - handles = reloadGUI(hObject, handles, ct, cst); -else - handles = reloadGUI(hObject, handles); -end - -guidata(hObject,handles); - -% --- Executes on button press in btnLoadDicom. -function btnLoadDicom_Callback(hObject, ~, handles) -% hObject handle to btnLoadDicom (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -try - % delete existing workspace - parse variables from base workspace - set(handles.popupDisplayOption,'String','no option available'); - AllVarNames = evalin('base','who'); - RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; - for i = 1:length(RefVarNames) - if sum(ismember(AllVarNames,RefVarNames{i}))>0 - evalin('base',['clear ', RefVarNames{i}]); - end - end - handles.State = 0; - matRad_importDicomGUI; - -catch - handles = showError(handles,'DicomImport: Could not import data'); -end -UpdateState(handles); -guidata(hObject,handles); - - -% --- Executes on button press in btn_export. -function btn_export_Callback(hObject, eventdata, handles) -% hObject handle to btn_export (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) +handleValid = true; try - matRad_exportGUI; + handleValid = ishandle(hMatRadGUI.guiHandle); catch - handles = showError(handles,'Could not export data'); -end -UpdateState(handles); -guidata(hObject,handles); - -function editBixelWidth_Callback(hObject, ~, handles) -% hObject handle to editBixelWidth (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of editBixelWidth as text -% str2double(get(hObject,'String')) returns contents of editBixelWidth as a double -getPlnFromGUI(handles); -if handles.State > 0 - handles.State = 1; - UpdateState(handles); - guidata(hObject,handles); -end - -function editGantryAngle_Callback(hObject, ~, handles) -% hObject handle to editGantryAngle (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of editGantryAngle as text -% str2double(get(hObject,'String')) returns contents of editGantryAngle as a double -getPlnFromGUI(handles); -if handles.State > 0 - handles.State = 1; - UpdateState(handles); - UpdatePlot(handles); - guidata(hObject,handles); -end - -function editCouchAngle_Callback(hObject, ~, handles) -% hObject handle to editCouchAngle (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of editCouchAngle as text -% str2double(get(hObject,'String')) returns contents of editCouchAngle as a double -getPlnFromGUI(handles); -if handles.State > 0 - handles.State = 1; - UpdateState(handles); - guidata(hObject,handles); -end - -% --- Executes on selection change in popupRadMode. -function popupRadMode_Callback(hObject, eventdata, handles) -% hObject handle to popupRadMode (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - - -checkRadiationComposition(handles); -contents = cellstr(get(hObject,'String')); -RadIdentifier = contents{get(hObject,'Value')}; - -switch RadIdentifier - case 'photons' - set(handles.vmcFlag,'Value',0); - set(handles.vmcFlag,'Enable','on') - set(handles.btnSetTissue,'Enable','off'); - set(handles.btnRunSequencing,'Enable','on'); - set(handles.btnRunDAO,'Enable','on'); - set(handles.txtSequencing,'Enable','on'); - set(handles.editSequencingLevel,'Enable','on'); - - case {'carbon','protons'} - set(handles.vmcFlag,'Value',0); - set(handles.vmcFlag,'Enable','off') - - set(handles.btnSetTissue,'Enable','on'); - set(handles.btnRunSequencing,'Enable','off'); - set(handles.btnRunDAO,'Enable','off'); - set(handles.radiobutton3Dconf,'Enable','off'); - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); -end - -if handles.State > 0 - pln = evalin('base','pln'); - if handles.State > 0 && ~strcmp(contents(get(hObject,'Value')),pln.radiationMode) - - % new radiation modality is photons -> just keep physicalDose - if strcmp(contents(get(hObject,'Value')),'photons') - try - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); - if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha'); end - if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end - if isfield(resultGUI,'RBExD '); resultGUI = rmfield(resultGUI,'RBExD'); end - if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end - assignin('base','resultGUI',resultGUI); - handles = updateIsoDoseLineCache(handles); - end - catch - end - elseif strcmp(contents(get(hObject,'Value')),'protons') - try - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); - if isfield(resultGUI,'alpha'); resultGUI = rmfield(resultGUI,'alpha');end - if isfield(resultGUI,'beta'); resultGUI = rmfield(resultGUI,'beta'); end - if isfield(resultGUI,'RBE'); resultGUI = rmfield(resultGUI,'RBE'); end - assignin('base','resultGUI',resultGUI); - handles = updateIsoDoseLineCache(handles); - end - catch - end - end - - guidata(hObject,handles); - UpdatePlot(handles); - - handles.State = 1; - UpdateState(handles); - end - -getPlnFromGUI(handles); -setPln(handles); -guidata(hObject,handles); - -end - - -% --- Executes on button press in btnCalcDose. -function btnCalcDose_Callback(hObject, ~, handles) -% hObject handle to btnCalcDose (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% http://stackoverflow.com/questions/24703962/trigger-celleditcallback-before-button-callback -% http://www.mathworks.com/matlabcentral/newsreader/view_thread/332613 -% wait some time until the CallEditCallback is finished -% Callback triggers the cst saving mechanism from GUI -try - % indicate that matRad is busy - % change mouse pointer to hour glass - Figures = gcf;%findobj('type','figure'); - set(Figures, 'pointer', 'watch'); - drawnow; - % disable all active objects - InterfaceObj = findobj(Figures,'Enable','on'); - set(InterfaceObj,'Enable','off'); - - % read plan from gui and save it to workspace - % gets also IsoCenter from GUI if checkbox is not checked - getPlnFromGUI(handles); - - % get default iso center as center of gravity of all targets if not - % already defined - pln = evalin('base','pln'); - - if length(pln.propStf.gantryAngles) ~= length(pln.propStf.couchAngles) - handles = showWarning(handles,'number of gantryAngles != number of couchAngles'); - end - %% - if ~checkRadiationComposition(handles) - fileName = [pln.radiationMode '_' pln.machine]; - handles = showError(handles,errordlg(['Could not find the following machine file: ' fileName ])); - guidata(hObject,handles); - return; - end - - %% check if isocenter is already set - if ~isfield(pln.propStf,'isoCenter') - handles = showWarning(handles,'no iso center set - using center of gravity based on structures defined as TARGET'); - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(evalin('base','cst'),evalin('base','ct')); - assignin('base','pln',pln); - elseif ~get(handles.checkIsoCenter,'Value') - if ~strcmp(get(handles.editIsoCenter,'String'),'multiple isoCenter') - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1)*str2num(get(handles.editIsoCenter,'String')); - end - end - -catch ME - handles = showError(handles,'CalcDoseCallback: Error in preprocessing!',ME); - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - guidata(hObject,handles); - return; -end - -% generate steering file -try - currPln = evalin('base','pln'); - % if we run 3d conf opt -> hijack runDao to trigger computation of - % connected bixels - if strcmp(pln.radiationMode,'photons') && get(handles.radiobutton3Dconf,'Value') - currpln.propOpt.runDAO = true; - end - - stf = matRad_generateStf(evalin('base','ct'),... - evalin('base','cst'),... - evalin('base','pln')); - assignin('base','stf',stf); -catch ME - handles = showError(handles,'CalcDoseCallback: Error in steering file generation!',ME); - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - guidata(hObject,handles); - return; -end - -% carry out dose calculation -try - if strcmp(pln.radiationMode,'photons') - if get(handles.vmcFlag,'Value') == 0 - dij = matRad_calcPhotonDose(evalin('base','ct'),stf,pln,evalin('base','cst')); - elseif get(handles.vmcFlag,'Value') == 1 - if ~isdeployed - dij = matRad_calcPhotonDoseVmc(evalin('base','ct'),stf,pln,evalin('base','cst')); - else - showError('VMC++ not available in matRad standalone application'); - end - end - elseif strcmp(pln.radiationMode,'protons') || strcmp(pln.radiationMode,'carbon') || strcmp(pln.radiationMode,'helium') - dij = matRad_calcParticleDose(evalin('base','ct'),stf,pln,evalin('base','cst')); - end - - % assign results to base worksapce - assignin('base','dij',dij); - handles.State = 2; - handles.TableChanged = false; - UpdateState(handles); - UpdatePlot(handles); - guidata(hObject,handles); -catch ME - handles = showError(handles,'CalcDoseCallback: Error in dose calculation!',ME); - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - guidata(hObject,handles); - return; -end - -% change state from busy to normal -set(Figures, 'pointer', 'arrow'); -set(InterfaceObj,'Enable','on'); - -guidata(hObject,handles); - - -%% plots ct and distributions -function UpdatePlot(handles) - -%profile on; - -axes(handles.axesFig); - -% this is necessary to prevent multiple callbacks of update plot drawing on -% top of each other in matlab <2014 -drawnow; - -defaultFontSize = 8; -currAxes = axis(handles.axesFig); -AxesHandlesCT_Dose = gobjects(0); -AxesHandlesVOI = cell(0); -AxesHandlesIsoDose = gobjects(0); - -if handles.State == 0 - cla reset - return -elseif handles.State > 0 - ct = evalin('base','ct'); - cst = evalin('base','cst'); - pln = evalin('base','pln'); -end - -%% state 3 indicates that a optimization has been performed - AllVarNames = evalin('base','who'); -if ismember('resultGUI',AllVarNames) - Result = evalin('base','resultGUI'); -end - -if exist('Result','var') - if ~isempty(Result) && ~isempty(ct.cubeHU) && ~isfield(handles,'DispInfo') - - DispInfo = fieldnames(Result); - - for i = 1:size(DispInfo,1) - - % delete weight vectors in Result struct for plotting - if isstruct(Result.(DispInfo{i,1})) || isvector(Result.(DispInfo{i,1})) - Result = rmfield(Result,DispInfo{i,1}); - DispInfo{i,2}=false; - else - %second dimension indicates if it should be plotted - DispInfo{i,2} = true; - % determine units - if strfind(DispInfo{i,1},'physicalDose') - DispInfo{i,3} = '[Gy]'; - elseif strfind(DispInfo{i,1},'alpha') - DispInfo{i,3} = '[Gy^{-1}]'; - elseif strfind(DispInfo{i,1},'beta') - DispInfo{i,3} = '[Gy^{-2}]'; - elseif strfind(DispInfo{i,1},'RBExD') - DispInfo{i,3} = '[Gy(RBE)]'; - elseif strfind(DispInfo{i,1},'LET') - DispInfo{i,3} = '[keV/um]'; - else - DispInfo{i,3} = '[a.u.]'; - end - DispInfo{i,4} = []; % optional for the future: color range for plotting - DispInfo{i,5} = []; % optional for the future: min max values - end - end - - set(handles.popupDisplayOption,'String',fieldnames(Result)); - if sum(strcmp(handles.SelectedDisplayOption,fieldnames(Result))) == 0 - handles.SelectedDisplayOption = DispInfo{find([DispInfo{:,2}],1,'first'),1}; - end - set(handles.popupDisplayOption,'Value',find(strcmp(handles.SelectedDisplayOption,fieldnames(Result)))); - - end -end - -%% set and get required variables -plane = get(handles.popupPlane,'Value'); -slice = round(get(handles.sliderSlice,'Value')); -hold(handles.axesFig,'on'); -if get(handles.popupTypeOfPlot,'Value')==1 - set(handles.axesFig,'YDir','Reverse'); -end - -%% Remove colorbar? -plotColorbarSelection = get(handles.popupmenu_chooseColorData,'Value'); - -if get(handles.popupTypeOfPlot,'Value')==2 || plotColorbarSelection == 1 - if isfield(handles,'cBarHandel') - delete(handles.cBarHandel); - end - %The following seems to be necessary as MATLAB messes up some stuff - %with the handle storage - ch = findall(gcf,'tag','Colorbar'); - if ~isempty(ch) - delete(ch); - end -end - -selectIx = get(handles.popupmenu_chooseColorData,'Value'); - -cla(handles.axesFig); -%% plot ct - if a ct cube is available and type of plot is set to 1 and not 2; 1 indicate cube plotting and 2 profile plotting -if ~isempty(ct) && get(handles.popupTypeOfPlot,'Value')==1 - - if selectIx == 3 - ctIx = 2; - else - ctIx = selectIx; - end - - if handles.cubeHUavailable - plotCtCube = ct.cubeHU; - ctLabel = 'Hounsfield Units'; - else - plotCtCube = ct.cube; - ctLabel = 'Electron Density / WEQ'; - end - - ctMap = matRad_getColormap(handles.ctColorMap,handles.cMapSize); - - if isempty(handles.dispWindow{ctIx,2}) - handles.dispWindow{ctIx,2} = [min(reshape([ct.cubeHU{:}],[],1)) max(reshape([ct.cubeHU{:}],[],1))]; - end - - if get(handles.radiobtnCT,'Value') - - % retrieve number of dose cube and check if a corresponding CT is available - num = regexp(handles.SelectedDisplayOption, '\d+', 'match'); - - if isempty(num) - ctScenNum = 1; - else - ctScenNum = str2double(num{1}); - if ~isnumeric(ctScenNum) - ctScenNum = 1; - end - % check if there is a ct scneario available - if ct.numOfCtScen < ctScenNum - ctScenNum = 1; - end - end - - [AxesHandlesCT_Dose(end+1),~,handles.dispWindow{ctIx,1}] = matRad_plotCtSlice(handles.axesFig,plotCtCube,ctScenNum,plane,slice,ctMap,handles.dispWindow{ctIx,1}); - - % plot colorbar? If 1 the user asked for the CT - if plotColorbarSelection == 2 && handles.cBarChanged - %Plot the colorbar - handles.cBarHandel = matRad_plotColorbar(handles.axesFig,ctMap,handles.dispWindow{ctIx,1},'fontsize',defaultFontSize); - %adjust lables - set(get(handles.cBarHandel,'ylabel'),'String', ctLabel,'fontsize',defaultFontSize); - % do not interprete as tex syntax - set(get(handles.cBarHandel,'ylabel'),'interpreter','none'); - end - end -end - -%% plot dose cube -if handles.State >= 1 && get(handles.popupTypeOfPlot,'Value')== 1 && exist('Result','var') - doseMap = matRad_getColormap(handles.doseColorMap,handles.cMapSize); - doseIx = 3; - % if the selected display option doesn't exist then simply display - % the first cube of the Result struct - if ~isfield(Result,handles.SelectedDisplayOption) - CubeNames = fieldnames(Result); - handles.SelectedDisplayOption = CubeNames{1,1}; - end - - dose = Result.(handles.SelectedDisplayOption); - - % dose colorwash - if ~isempty(dose) && ~isvector(dose) - - if isempty(handles.dispWindow{doseIx,2}) - handles.dispWindow{doseIx,2} = [min(dose(:)) max(dose(:))]; % set min and max dose values - end - - if get(handles.radiobtnDose,'Value') - [doseHandle,~,handles.dispWindow{doseIx,1}] = matRad_plotDoseSlice(handles.axesFig,dose,plane,slice,handles.CutOffLevel,handles.doseOpacity,doseMap,handles.dispWindow{doseIx,1}); - AxesHandlesCT_Dose(end+1) = doseHandle; - end - - % plot colorbar? - if plotColorbarSelection > 2 && handles.cBarChanged - %Plot the colorbar - handles.cBarHandel = matRad_plotColorbar(handles.axesFig,doseMap,handles.dispWindow{selectIx,1},'fontsize',defaultFontSize); - %adjust lables - Idx = find(strcmp(handles.SelectedDisplayOption,DispInfo(:,1))); - set(get(handles.cBarHandel,'ylabel'),'String', [DispInfo{Idx,1} ' ' DispInfo{Idx,3} ],'fontsize',defaultFontSize); - % do not interprete as tex syntax - set(get(handles.cBarHandel,'ylabel'),'interpreter','none'); - end - end - - - %% plot iso dose lines - if get(handles.radiobtnIsoDoseLines,'Value') - plotLabels = get(handles.radiobtnIsoDoseLinesLabels,'Value') == 1; - - %Sanity Check for Contours, which actually should have been - %computed before calling UpdatePlot - if ~isfield(handles.IsoDose,'Contours') - try - handles.IsoDose.Contours = matRad_computeIsoDoseContours(dose,handles.IsoDose.Levels); - catch - %If the computation didn't work, we set the field to - %empty, which will force matRad_plotIsoDoseLines to use - %matlabs contour function instead of repeating the - %failing computation every time - handles.IsoDose.Contours = []; - warning('Could not compute isodose lines! Will try slower contour function!'); - end - end - AxesHandlesIsoDose = matRad_plotIsoDoseLines(handles.axesFig,dose,handles.IsoDose.Contours,handles.IsoDose.Levels,plotLabels,plane,slice,doseMap,handles.dispWindow{doseIx,1},'LineWidth',1.5); - end -end - -selectIx = get(handles.popupmenu_chooseColorData,'Value'); -set(handles.txtMinVal,'String',num2str(handles.dispWindow{selectIx,2}(1,1))); -set(handles.txtMaxVal,'String',num2str(handles.dispWindow{selectIx,2}(1,2))); - -%% plot VOIs -if get(handles.radiobtnContour,'Value') && get(handles.popupTypeOfPlot,'Value')==1 && handles.State>0 - AxesHandlesVOI = [AxesHandlesVOI matRad_plotVoiContourSlice(handles.axesFig,cst,ct,ctScenNum,handles.VOIPlotFlag,plane,slice,[],'LineWidth',2)]; -end - -%% Set axis labels and plot iso center -matRad_plotAxisLabels(handles.axesFig,ct,plane,slice,defaultFontSize); - -if get(handles.radioBtnIsoCenter,'Value') == 1 && get(handles.popupTypeOfPlot,'Value') == 1 && ~isempty(pln) - hIsoCenterCross = matRad_plotIsoCenterMarker(handles.axesFig,pln,ct,plane,slice); -end - -if get(handles.radiobtnPlan,'value') == 1 && ~isempty(pln) - matRad_plotProjectedGantryAngles(handles.axesFig,pln,ct,plane); -end - -% the following line ensures the plotting order (optional) -% set(gca,'Children',[AxesHandlesCT_Dose hIsoCenterCross AxesHandlesIsoDose AxesHandlesVOI ]); - -%set axis ratio -ratios = [1/ct.resolution.x 1/ct.resolution.y 1/ct.resolution.z]; -set(handles.axesFig,'DataAspectRatioMode','manual'); -if plane == 1 - res = [ratios(3) ratios(1)]./max([ratios(3) ratios(1)]); - set(handles.axesFig,'DataAspectRatio',[res 1]) -elseif plane == 2 % sagittal plane - res = [ratios(3) ratios(2)]./max([ratios(3) ratios(2)]); - set(handles.axesFig,'DataAspectRatio',[res 1]) -elseif plane == 3 % Axial plane - res = [ratios(1) ratios(2)]./max([ratios(1) ratios(2)]); - set(handles.axesFig,'DataAspectRatio',[res 1]) -end - - -%% profile plot -if get(handles.popupTypeOfPlot,'Value') == 2 && exist('Result','var') - % set SAD - fileName = [pln.radiationMode '_' pln.machine]; - try - load(['basedata' filesep fileName]); - SAD = machine.meta.SAD; - catch - error(['Could not find the following machine file: ' fileName ]); - end - - % clear view and initialize some values - cla(handles.axesFig,'reset') - set(gca,'YDir','normal'); - ylabel('{\color{black}dose [Gy]}') - cColor={'black','green','magenta','cyan','yellow','red','blue'}; - - % Rotate the system into the beam. - % passive rotation & row vector multiplication & inverted rotation requires triple matrix transpose - rotMat_system_T = transpose(matRad_getRotationMatrix(pln.propStf.gantryAngles(handles.selectedBeam),pln.propStf.couchAngles(handles.selectedBeam))); - - if strcmp(handles.ProfileType,'longitudinal') - sourcePointBEV = [handles.profileOffset -SAD 0]; - targetPointBEV = [handles.profileOffset SAD 0]; - elseif strcmp(handles.ProfileType,'lateral') - sourcePointBEV = [-SAD handles.profileOffset 0]; - targetPointBEV = [ SAD handles.profileOffset 0]; - end - - rotSourcePointBEV = sourcePointBEV * rotMat_system_T; - rotTargetPointBEV = targetPointBEV * rotMat_system_T; - - % perform raytracing on the central axis of the selected beam, use unit - % electron density for plotting against the geometrical depth - [~,l,rho,~,ix] = matRad_siddonRayTracer(pln.propStf.isoCenter(handles.selectedBeam,:),ct.resolution,rotSourcePointBEV,rotTargetPointBEV,{0*ct.cubeHU{1}+1}); - d = [0 l .* rho{1}]; - % Calculate accumulated d sum. - vX = cumsum(d(1:end-1)); - - % this step is necessary if visualization is set to profile plot - % and another optimization is carried out - set focus on GUI - figHandles = get(0,'Children'); - idxHandle = []; - if ~isempty(figHandles) - v=version; - if str2double(v(1:3))>= 8.5 - idxHandle = strcmp({figHandles(:).Name},'matRadGUI'); - else - idxHandle = strcmp(get(figHandles,'Name'),'matRadGUI'); - end - end - figure(figHandles(idxHandle)); - - % plot physical dose - Content = get(handles.popupDisplayOption,'String'); - SelectedCube = Content{get(handles.popupDisplayOption,'Value')}; - if sum(strcmp(SelectedCube,{'physicalDose','effect','RBExD','alpha','beta','RBE'})) > 0 - Suffix = ''; - else - Idx = find(SelectedCube == '_'); - Suffix = SelectedCube(Idx:end); - end - - mPhysDose = Result.(['physicalDose' Suffix]); - PlotHandles{1} = plot(handles.axesFig,vX,mPhysDose(ix),'color',cColor{1,1},'LineWidth',3); hold on; - PlotHandles{1,2} ='physicalDose'; - ylabel(handles.axesFig,'dose in [Gy]'); - set(handles.axesFig,'FontSize',defaultFontSize); - - % plot counter - Cnt=2; - - if isfield(Result,['RBE' Suffix]) - - %disbale specific plots - %DispInfo{6,2}=0; - %DispInfo{5,2}=0; - %DispInfo{2,2}=0; - - % generate two lines for ylabel - StringYLabel1 = '\fontsize{8}{\color{red}RBE x dose [Gy(RBE)] \color{black}dose [Gy] '; - StringYLabel2 = ''; - for i=1:1:size(DispInfo,1) - if DispInfo{i,2} && sum(strcmp(DispInfo{i,1},{['effect' Suffix],['alpha' Suffix],['beta' Suffix]})) > 0 - %physicalDose is already plotted and RBExD vs RBE is plotted later with plotyy - if ~strcmp(DispInfo{i,1},['RBExD' Suffix]) &&... - ~strcmp(DispInfo{i,1},['RBE' Suffix]) && ... - ~strcmp(DispInfo{i,1},['physicalDose' Suffix]) - - mCube = Result.([DispInfo{i,1}]); - PlotHandles{Cnt,1} = plot(handles.axesFig,vX,mCube(ix),'color',cColor{1,Cnt},'LineWidth',3);hold on; - PlotHandles{Cnt,2} = DispInfo{i,1}; - StringYLabel2 = [StringYLabel2 ' \color{' cColor{1,Cnt} '}' DispInfo{i,1} ' [' DispInfo{i,3} ']']; - Cnt = Cnt+1; - end - end - end - StringYLabel2 = [StringYLabel2 '}']; - % always plot RBExD against RBE - mRBExDose = Result.(['RBExD' Suffix]); - vBED = mRBExDose(ix); - mRBE = Result.(['RBE' Suffix]); - vRBE = mRBE(ix); - - % plot biological dose against RBE - [ax, PlotHandles{Cnt,1}, PlotHandles{Cnt+1,1}]=plotyy(handles.axesFig,vX,vBED,vX,vRBE,'plot');hold on; - PlotHandles{Cnt,2}='RBExD'; - PlotHandles{Cnt+1,2}='RBE'; - - % set plotyy properties - set(get(ax(2),'Ylabel'),'String','RBE [a.u.]','FontSize',8); - ylabel({StringYLabel1;StringYLabel2}) - set(PlotHandles{Cnt,1},'Linewidth',4,'color','r'); - set(PlotHandles{Cnt+1,1},'Linewidth',3,'color','b'); - set(ax(1),'ycolor','r') - set(ax(2),'ycolor','b') - set(ax,'FontSize',8); - Cnt=Cnt+2; - end - - % asses target coordinates - tmpPrior = intmax; - tmpSize = 0; - for i=1:size(cst,1) - if strcmp(cst{i,3},'TARGET') && tmpPrior >= cst{i,5}.Priority && tmpSize 0 - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - Result = evalin('base','resultGUI'); - end - - ct = evalin('base','ct'); - cst = evalin('base','cst'); - pln = evalin('base','pln'); - - if ismember('stf',AllVarNames) - stf = evalin('base','stf'); - - %validate stf with current pln settings - validStf = true; - gantryAngles = [stf.gantryAngle]; - validStf = isequal(gantryAngles,pln.propStf.gantryAngles) & validStf; - couchAngles = [stf.couchAngle]; - validStf = isequal(couchAngles,pln.propStf.couchAngles) & validStf; - isoCenter = vertcat(stf.isoCenter); - validStf = isequal(isoCenter,pln.propStf.isoCenter) & validStf; - - if ~validStf - matRad_cfg = MatRad_Config.instance(); - matRad_cfg.dispWarning('stf and pln are not consistent, using pln for geometry display!'); - stf = []; - end - - else - stf = []; - end - - + matRad_rc(false); + hMatRadGUI = matRad_MainGUI; end -oldView = get(axesFig3D,'View'); - -cla(axesFig3D); -%delete(allchild(axesFig3D)); - -%test = allchild(axesFig3D); - -plane = get(handles.popupPlane,'Value'); -slice = round(get(handles.sliderSlice,'Value')); -defaultFontSize = 8; - -%Check if we need to precompute the surface data -if size(cst,2) < 8 - cst = matRad_computeAllVoiSurfaces(ct,cst); - assignin('base','cst',cst); +if nargout > 0 + hGUI = hMatRadGUI; end -set(fig3D,'Color',0.5*[1 1 1]); -set(axesFig3D,'Color',1*[0 0 0]); -%% Plot 3D structures -hold(axesFig3D,'on'); -if get(handles.radiobtnContour,'Value') && handles.State>0 - voiPatches = matRad_plotVois3D(axesFig3D,ct,cst,handles.VOIPlotFlag,colorcube); end -%% plot the CT slice -if get(handles.radiobtnCT,'Value') - window = handles.dispWindow{2,1}; %(2 for ct) - ctMap = matRad_getColormap(handles.ctColorMap,handles.cMapSize); - ctHandle = matRad_plotCtSlice3D(axesFig3D,ct,1,plane,slice,ctMap,window); -end -%% plot the dose slice -if handles.State >= 1 && exist('Result','var') - doseMap = matRad_getColormap(handles.doseColorMap,handles.cMapSize); - doseIx = 3; - % if the selected display option doesn't exist then simply display - % the first cube of the Result struct - if ~isfield(Result,handles.SelectedDisplayOption) - CubeNames = fieldnames(Result); - handles.SelectedDisplayOption = CubeNames{1,1}; - end - - dose = Result.(handles.SelectedDisplayOption); - - % dose colorwash - if ~isempty(dose) && ~isvector(dose) - - if isempty(handles.dispWindow{doseIx,2}) - handles.dispWindow{doseIx,2} = [min(dose(:)) max(dose(:))+1e-3]; % set min and max dose values - end - - if get(handles.radiobtnDose,'Value') - [doseHandle,~,handles.dispWindow{doseIx,1}] = matRad_plotDoseSlice3D(axesFig3D,ct,dose,plane,slice,handles.CutOffLevel,handles.doseOpacity,doseMap,handles.dispWindow{doseIx,1}); - end - if get(handles.radiobtnIsoDoseLines,'Value') - matRad_plotIsoDoseLines3D(axesFig3D,ct,dose,handles.IsoDose.Contours,handles.IsoDose.Levels,plane,slice,doseMap,handles.dispWindow{doseIx,1},'LineWidth',1.5); - end - end -end +%Validates the attributes for the command line Modes +function validateModeValue(x) +%When passed from OS terminal (or inline in Command Window) everything is a string +if isdeployed || ischar(x) || isstring(x) + x=str2double(x); -if get(handles.radiobtnPlan,'Value') - matRad_plotPlan3D(axesFig3D,pln,stf); end - -%hLight = light('Parent',axesFig3D); -%camlight(hLight,'left'); -%lighting('gouraud'); - -xlabel(axesFig3D,'x [voxels]','FontSize',defaultFontSize) -ylabel(axesFig3D,'y [voxels]','FontSize',defaultFontSize) -zlabel(axesFig3D,'z [voxels]','FontSize',defaultFontSize) -title(axesFig3D,'matRad 3D view'); - -% set axis ratio -ratios = [1 1 1]; %[1/ct.resolution.x 1/ct.resolution.y 1/ct.resolution.z]; -ratios = ratios([2 1 3]); -set(axesFig3D,'DataAspectRatioMode','manual'); -set(axesFig3D,'DataAspectRatio',ratios./max(ratios)); - -set(axesFig3D,'Ydir','reverse'); - -set(axesFig3D,'view',oldView); - - -% --- Executes on selection change in popupPlane. -function popupPlane_Callback(hObject, ~, handles) -% hObject handle to popupPlane (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns popupPlane contents as cell array -% contents{get(hObject,'Value')} returns selected item from popupPlane - -% set slice slider -handles.plane = get(hObject,'value'); -initViewSliceSlider(handles); - -handles.rememberCurrAxes = false; -UpdatePlot(handles); -handles.rememberCurrAxes = true; -guidata(hObject,handles); - -% --- Executes on slider movement. -function sliderSlice_Callback(~, ~, handles) -% hObject handle to sliderSlice (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'Value') returns position of slider -% get(hObject,'Min') and get(hObject,'Max') to determine range of slider -UpdatePlot(handles) - -% --- Executes on button press in radiobtnContour. -function radiobtnContour_Callback(~, ~, handles) -% hObject handle to radiobtnContour (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of radiobtnContour -UpdatePlot(handles) - -% --- Executes on button press in radiobtnDose. -function radiobtnDose_Callback(~, ~, handles) -% hObject handle to radiobtnDose (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of radiobtnDose -UpdatePlot(handles) - -% --- Executes on button press in radiobtnIsoDoseLines. -function radiobtnIsoDoseLines_Callback(~, ~, handles) -% hObject handle to radiobtnIsoDoseLines (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of radiobtnIsoDoseLines -UpdatePlot(handles) - -% --- Executes on button press in btnOptimize. -function btnOptimize_Callback(hObject, eventdata, handles) -% hObject handle to btnOptimize (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -try - % indicate that matRad is busy - % change mouse pointer to hour glass - Figures = gcf;%findobj('type','figure'); - set(Figures, 'pointer', 'watch'); - drawnow; - % disable all active objects - InterfaceObj = findobj(Figures,'Enable','on'); - set(InterfaceObj,'Enable','off'); - % wait until the table is updated - btnTableSave_Callback([],[],handles); %We don't need it? - - - % if a critical change to the cst has been made which affects the dij matrix - if handles.DijCalcWarning == true - - choice = questdlg('Overlap priorites of OAR constraints have been edited, a new OAR VOI was added or a critical row constraint was deleted. A new Dij calculation might be necessary.', ... - 'Title','Cancel','Calculate Dij then Optimize','Optimze directly','Optimze directly'); - - switch choice - case 'Cancel' - return - case 'Calculate dij again and optimize' - handles.DijCalcWarning = false; - btnCalcDose_Callback(hObject, eventdata, handles) - case 'Optimze directly' - handles.DijCalcWarning = false; - end - end - - pln = evalin('base','pln'); - ct = evalin('base','ct'); - - % optimize - if get(handles.radiobutton3Dconf,'Value') && strcmp(handles.Modalities{get(handles.popupRadMode,'Value')},'photons') - % conformal plan if photons and 3d conformal - if ~matRad_checkForConnectedBixelRows(evalin('base','stf')) - error('disconnetced dose influence data in BEV - run dose calculation again with consistent settings'); - end - [resultGUIcurrentRun,usedOptimizer] = matRad_fluenceOptimization(matRad_collapseDij(evalin('base','dij')),evalin('base','cst'),pln); - resultGUIcurrentRun.w = resultGUIcurrentRun.w * ones(evalin('base','dij.totalNumOfBixels'),1); - resultGUIcurrentRun.wUnsequenced = resultGUIcurrentRun.w; - else - if pln.propOpt.runDAO - if ~matRad_checkForConnectedBixelRows(evalin('base','stf')) - error('disconnetced dose influence data in BEV - run dose calculation again with consistent settings'); - end - end - - [resultGUIcurrentRun,usedOptimizer] = matRad_fluenceOptimization(evalin('base','dij'),evalin('base','cst'),pln); - end - - %if resultGUI already exists then overwrite the "standard" fields - AllVarNames = evalin('base','who'); - if ismember('resultGUI',AllVarNames) - resultGUI = evalin('base','resultGUI'); - sNames = fieldnames(resultGUIcurrentRun); - oldNames = fieldnames(resultGUI); - if(length(oldNames) > length(sNames)) - for j = 1:length(oldNames) - if strfind(oldNames{j}, 'beam') - resultGUI = rmfield(resultGUI, oldNames{j}); - end - end - end - for j = 1:length(sNames) - resultGUI.(sNames{j}) = resultGUIcurrentRun.(sNames{j}); - end - else - resultGUI = resultGUIcurrentRun; - end - assignin('base','resultGUI',resultGUI); - - % set some values - if handles.plane == 1 - set(handles.sliderSlice,'Value',ceil(pln.propStf.isoCenter(1,2)/ct.resolution.x)); - elseif handles.plane == 2 - set(handles.sliderSlice,'Value',ceil(pln.propStf.isoCenter(1,1)/ct.resolution.y)); - elseif handles.plane == 3 - set(handles.sliderSlice,'Value',ceil(pln.propStf.isoCenter(1,3)/ct.resolution.z)); - end - - handles.State = 3; - handles.SelectedDisplayOptionIdx = 1; - handles.SelectedDisplayOption = pln.bioParam.quantityVis; - handles.selectedBeam = 1; - % check IPOPT status and return message for GUI user if no DAO or - % particles - if ~pln.propOpt.runDAO || ~strcmp(pln.radiationMode,'photons') - CheckOptimizerStatus(usedOptimizer,'Fluence') - end - -catch ME - handles = showError(handles,'OptimizeCallback: Could not optimize!',ME); - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - guidata(hObject,handles); - return; -end - -% perform sequencing and DAO -try - - %% sequencing - if strcmp(pln.radiationMode,'photons') && (pln.propOpt.runSequencing || pln.propOpt.runDAO) - % resultGUI = matRad_xiaLeafSequencing(resultGUI,evalin('base','stf'),evalin('base','dij')... - % ,get(handles.editSequencingLevel,'Value')); - % resultGUI = matRad_engelLeafSequencing(resultGUI,evalin('base','stf'),evalin('base','dij')... - % ,str2double(get(handles.editSequencingLevel,'String'))); - resultGUI = matRad_siochiLeafSequencing(resultGUI,evalin('base','stf'),evalin('base','dij')... - ,str2double(get(handles.editSequencingLevel,'String'))); - - assignin('base','resultGUI',resultGUI); - end - -catch ME - handles = showError(handles,'OptimizeCallback: Could not perform sequencing',ME); - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - guidata(hObject,handles); - return; -end - -try - %% DAO - if strcmp(pln.radiationMode,'photons') && pln.propOpt.runDAO - handles = showWarning(handles,['Observe: You are running direct aperture optimization' filesep 'This is experimental code that has not been thoroughly debugged - especially in combination with constrained optimization.']); - [resultGUI,usedOptimizer] = matRad_directApertureOptimization(evalin('base','dij'),evalin('base','cst'),... - resultGUI.apertureInfo,resultGUI,pln); - assignin('base','resultGUI',resultGUI); - % check IPOPT status and return message for GUI user - CheckOptimizerStatus(usedOptimizer,'DAO'); - end - - if strcmp(pln.radiationMode,'photons') && (pln.propOpt.runSequencing || pln.propOpt.runDAO) - matRad_visApertureInfo(resultGUI.apertureInfo); - end - -catch ME - handles = showError(handles,'OptimizeCallback: Could not perform direct aperture optimization',ME); - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - guidata(hObject,handles); - return; -end - -% change state from busy to normal -set(Figures, 'pointer', 'arrow'); -set(InterfaceObj,'Enable','on'); -handles.dispWindow{3,1} = []; % reset dose ranges -handles.dispWindow{3,2} = []; % reset min max dose values -handles.rememberCurrAxes = false; -handles.IsoDose.Levels = 0; % ensure to use default iso dose line spacing -handles.cBarChanged = true; - -guidata(hObject,handles); -handles = updateIsoDoseLineCache(handles); -UpdateState(handles); -UpdatePlot(handles); -handles.rememberCurrAxes = true; -guidata(hObject,handles); - - -% the function CheckValidityPln checks if the provided plan is valid so -% that it can be used further on for optimization -function FlagValid = CheckValidityPln(cst) - -FlagValid = true; -%check if mean constraint is always used in combination -for i = 1:size(cst,1) - if ~isempty(cst{i,6}) - if ~isempty(strfind([cst{i,6}.type],'mean')) && isempty(strfind([cst{i,6}.type],'square')) - FlagValid = false; - warndlg('mean constraint needs to be defined in addition to a second constraint (e.g. squared deviation)'); - break - end - end -end - - -% --- Executes on selection change in popupTypeOfPlot -function popupTypeOfPlot_Callback(hObject, ~, handles) - - % intensity plot -if get(hObject,'Value') == 1 - - set(handles.sliderBeamSelection,'Enable','off') - set(handles.sliderOffset,'Enable','off') - set(handles.popupDisplayOption,'Enable','on') - set(handles.btnProfileType,'Enable','off'); - set(handles.popupPlane,'Enable','on'); - set(handles.radiobtnCT,'Enable','on'); - set(handles.radiobtnContour,'Enable','on'); - set(handles.radiobtnDose,'Enable','on'); - set(handles.radiobtnIsoDoseLines,'Enable','on'); - set(handles.radiobtnIsoDoseLinesLabels,'Enable','on'); - set(handles.sliderSlice,'Enable','on'); - -% profile plot -elseif get(hObject,'Value') == 2 - - if handles.State > 0 - if length(parseStringAsNum(get(handles.editGantryAngle,'String'),true)) > 1 - - set(handles.sliderBeamSelection,'Enable','on'); - handles.selectedBeam = 1; - pln = evalin('base','pln'); - set(handles.sliderBeamSelection,'Min',handles.selectedBeam,'Max',pln.propStf.numOfBeams,... - 'Value',handles.selectedBeam,... - 'SliderStep',[1/(pln.propStf.numOfBeams-1) 1/(pln.propStf.numOfBeams-1)],... - 'Enable','on'); - - else - handles.selectedBeam = 1; - end - - handles.profileOffset = get(handles.sliderOffset,'Value'); - - vMinMax = [-100 100]; - vRange = sum(abs(vMinMax)); - - ct = evalin('base','ct'); - if strcmp(get(handles.btnProfileType,'String'),'lateral') - SliderStep = vRange/ct.resolution.x; - else - SliderStep = vRange/ct.resolution.y; - end - - set(handles.sliderOffset,'Min',vMinMax(1),'Max',vMinMax(2),... - 'Value',handles.profileOffset,... - 'SliderStep',[1/SliderStep 1/SliderStep],... - 'Enable','on'); - end - - - set(handles.popupDisplayOption,'Enable','on'); - set(handles.btnProfileType,'Enable','on'); - set(handles.popupPlane,'Enable','off'); - set(handles.radiobtnCT,'Enable','off'); - set(handles.radiobtnContour,'Enable','off'); - set(handles.radiobtnDose,'Enable','off'); - set(handles.radiobtnIsoDoseLines,'Enable','off'); - set(handles.sliderSlice,'Enable','off'); - set(handles.radiobtnIsoDoseLinesLabels,'Enable','off'); - - - set(handles.btnProfileType,'Enable','on') - - if strcmp(get(handles.btnProfileType,'String'),'lateral') - handles.ProfileType = 'longitudinal'; - else - handles.ProfileType = 'lateral'; - end -end - -handles.cBarChanged = true; - -handles.rememberCurrAxes = false; -cla(handles.axesFig,'reset'); -UpdatePlot(handles); -handles.rememberCurrAxes = true; -guidata(hObject, handles); - -% --- Executes on selection change in popupDisplayOption. -function popupDisplayOption_Callback(hObject, ~, handles) -content = get(hObject,'String'); -handles.SelectedDisplayOption = content{get(hObject,'Value'),1}; -handles.SelectedDisplayOptionIdx = get(hObject,'Value'); -%handles.dispWindow{3,1} = []; handles.dispWindow{3,2} = []; - -if ~isfield(handles,'colormapLocked') || ~handles.colormapLocked - handles.dispWindow{3,1} = []; handles.dispWindow{3,2} = []; -end - -handles = updateIsoDoseLineCache(handles); -handles.cBarChanged = true; -guidata(hObject, handles); -UpdatePlot(handles); -guidata(hObject, handles); - -% --- Executes on slider movement. -function sliderBeamSelection_Callback(hObject, ~, handles) -% hObject handle to sliderBeamSelection (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'Value') returns position of slider -% get(hObject,'Min') and get(hObject,'Max') to determine range of slider - - -handles.selectedBeam = round(get(hObject,'Value')); -set(hObject, 'Value', handles.selectedBeam); -handles.rememberCurrAxes = false; -UpdatePlot(handles); -handles.rememberCurrAxes = true; -guidata(hObject,handles); - -% --- Executes on button press in btnProfileType. -function btnProfileType_Callback(hObject, ~, handles) -% hObject handle to btnProfileType (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -if strcmp(get(hObject,'Enable') ,'on') - if strcmp(handles.ProfileType,'lateral') - handles.ProfileType = 'longitudinal'; - set(hObject,'String','lateral'); - else - handles.ProfileType = 'lateral'; - set(hObject,'String','longitudinal'); - end - - handles.rememberCurrAxes = false; - UpdatePlot(handles); - handles.rememberCurrAxes = true; - - guidata(hObject, handles); - -end - -% enables/ disables buttons according to the current state -function UpdateState(handles) - -if handles.State > 0 - pln = evalin('base','pln'); - - if pln.bioParam.bioOpt - set(handles.btnSetTissue,'Enable','on'); - else - set(handles.btnSetTissue,'Enable','off'); - end - - cMapControls = allchild(handles.uipanel_colormapOptions); - for runHandles = cMapControls - set(runHandles,'Enable','on'); - end -end - -if handles.cubeHUavailable - cMapOptionsSelectList = {'None','CT (HU)','Result (i.e. dose)'}; - set(handles.popupmenu_windowPreset,'Visible','on'); - set(handles.text_windowPreset,'String','Window Preset'); -else - cMapOptionsSelectList = {'None','CT (ED)','Result (i.e. dose)'}; - set(handles.popupmenu_windowPreset,'Visible','off'); - set(handles.text_windowPreset,'String','No available Window Presets'); -end -handles.cBarChanged = true; - - switch handles.State - - case 0 - - set(handles.txtInfo,'String','no data loaded'); - set(handles.btnCalcDose,'Enable','off'); - set(handles.btnOptimize ,'Enable','off'); - set(handles.pushbutton_recalc,'Enable','off'); - set(handles.btnSaveToGUI,'Enable','off'); - set(handles.btnDVH,'Enable','off'); - set(handles.importDoseButton,'Enable','off'); - set(handles.btn_export,'Enable','off'); - set(handles.btn3Dview,'Enable','off'); - - cMapControls = allchild(handles.uipanel_colormapOptions); - for runHandles = cMapControls - set(runHandles,'Enable','off'); - end - - set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList{1}) - set(handles.popupmenu_chooseColorData,'Value',1); - - case 1 - - set(handles.txtInfo,'String','ready for dose calculation'); - set(handles.btnCalcDose,'Enable','on'); - set(handles.btnOptimize ,'Enable','off'); - set(handles.pushbutton_recalc,'Enable','off'); - set(handles.btnSaveToGUI,'Enable','off'); - set(handles.btnDVH,'Enable','off'); - set(handles.importDoseButton,'Enable','off'); - set(handles.btn_export,'Enable','on'); - set(handles.btn3Dview,'Enable','on'); - - set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList(1:2)) - set(handles.popupmenu_chooseColorData,'Value',2); - AllVarNames = evalin('base','who'); - if ~isempty(AllVarNames) - if ismember('resultGUI',AllVarNames) - set(handles.pushbutton_recalc,'Enable','on'); - set(handles.btnSaveToGUI,'Enable','on'); - set(handles.btnDVH,'Enable','on'); - set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList(1:3)) - set(handles.popupmenu_chooseColorData,'Value',3); - end - end - - case 2 - - set(handles.txtInfo,'String','ready for optimization'); - set(handles.btnCalcDose,'Enable','on'); - set(handles.btnOptimize ,'Enable','on'); - set(handles.pushbutton_recalc,'Enable','off'); - set(handles.btnSaveToGUI,'Enable','off'); - set(handles.btnDVH,'Enable','off'); - set(handles.importDoseButton,'Enable','off'); - set(handles.btn_export,'Enable','on'); - set(handles.btn3Dview,'Enable','on'); - set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList(1:2)) - set(handles.popupmenu_chooseColorData,'Value',2); - AllVarNames = evalin('base','who'); - - if ~isempty(AllVarNames) - if ismember('resultGUI',AllVarNames) - set(handles.pushbutton_recalc,'Enable','on'); - set(handles.btnSaveToGUI,'Enable','on'); - set(handles.btnDVH,'Enable','on'); - set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList(1:3)) - set(handles.popupmenu_chooseColorData,'Value',3); - end - end - - case 3 - set(handles.txtInfo,'String','plan is optimized'); - set(handles.btnCalcDose,'Enable','on'); - set(handles.btnOptimize ,'Enable','on'); - set(handles.pushbutton_recalc,'Enable','on'); - set(handles.btnSaveToGUI,'Enable','on'); - set(handles.btnDVH,'Enable','on'); - set(handles.btn_export,'Enable','on'); - set(handles.btn3Dview,'Enable','on'); - % resultGUI struct needs to be available to import dose - % otherwise inconsistent states can be achieved - set(handles.importDoseButton,'Enable','on'); - set(handles.popupmenu_chooseColorData,'String',cMapOptionsSelectList(1:3)) - set(handles.popupmenu_chooseColorData,'Value',3); - end - -guidata(handles.figure1,handles); - -% fill GUI elements with plan information -function setPln(handles) - -matRad_cfg = MatRad_Config.instance(); - -pln = evalin('base','pln'); -% sanity check of isoCenter -if size(pln.propStf.isoCenter,1) ~= pln.propStf.numOfBeams && size(pln.propStf.isoCenter,1) == 1 - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * pln.propStf.isoCenter(1,:); -elseif size(pln.propStf.isoCenter,1) ~= pln.propStf.numOfBeams && size(pln.propStf.isoCenter,1) ~= 1 - matRad_cfg.dispError('Isocenter in plan file are incosistent.'); -end - -%Sanity check for the bixelWidth field -bixelWidth = pln.propStf.bixelWidth; - -if isnumeric(bixelWidth) && isscalar(bixelWidth) - bixelWidth = num2str(pln.propStf.bixelWidth); -elseif ~isnumeric(bixelWidth) && ~strcmp(bixelWidth,'field') - matRad_cfg.dispError('Invalid bixel width! Must be a scalar number or ''field'' for field-based dose calculation with shapes stored in stf!'); -end - -set(handles.editBixelWidth,'String',bixelWidth); -set(handles.editFraction,'String',num2str(pln.numOfFractions)); - -if isfield(pln.propStf,'isoCenter') - if size(unique(pln.propStf.isoCenter,'rows'),1) == 1 - set(handles.editIsoCenter,'String',regexprep(num2str((round(pln.propStf.isoCenter(1,:)*10))./10), '\s+', ' ')); - set(handles.editIsoCenter,'Enable','on'); - set(handles.checkIsoCenter,'Enable','on'); - else - set(handles.editIsoCenter,'String','multiple isoCenter'); - set(handles.editIsoCenter,'Enable','off'); - set(handles.checkIsoCenter,'Value',0); - set(handles.checkIsoCenter,'Enable','off'); - end -end -set(handles.editGantryAngle,'String',num2str((pln.propStf.gantryAngles))); -set(handles.editCouchAngle,'String',num2str((pln.propStf.couchAngles))); -set(handles.popupRadMode,'Value',find(strcmp(get(handles.popupRadMode,'String'),pln.radiationMode))); -set(handles.popUpMachine,'Value',find(strcmp(get(handles.popUpMachine,'String'),pln.machine))); - -cellBioModel = get(handles.popMenuBioOpt,'String'); -cellQuantOpt = get(handles.popMenuQuantOpt,'String'); -set(handles.popMenuBioOpt,'Value',find(strcmp(pln.bioParam.model,cellBioModel))); -set(handles.popMenuQuantOpt,'Value',find(strcmp(pln.bioParam.quantityOpt,cellQuantOpt))); - -if (pln.bioParam.bioOpt) - set(handles.btnSetTissue,'Enable','on'); -else - set(handles.btnSetTissue,'Enable','off'); -end - -multScenDummy = matRad_multScen([],pln.multScen.TYPE); -ix = find(strcmp(multScenDummy.AvailableScenCreationTYPE,pln.multScen.TYPE)); -set(handles.popupmenuScenGen,'Value',ix); -%% enable sequencing button if radiation mode is set to photons -if strcmp(pln.radiationMode,'photons') && pln.propOpt.runSequencing - set(handles.btnRunSequencing,'Enable','on'); - set(handles.btnRunSequencing,'Value',1); - set(handles.txtSequencing,'Enable','on'); - set(handles.editSequencingLevel,'Enable','on'); -elseif strcmp(pln.radiationMode,'photons') && ~pln.propOpt.runSequencing - set(handles.btnRunSequencing,'Enable','on'); - set(handles.btnRunSequencing,'Value',0); - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); -else - set(handles.btnRunSequencing,'Enable','off'); - set(handles.txtSequencing,'Enable','off'); - set(handles.editSequencingLevel,'Enable','off'); -end -%% enable DAO button if radiation mode is set to photons -if strcmp(pln.radiationMode,'photons') && pln.propOpt.runDAO - set(handles.btnRunDAO,'Enable','on'); - set(handles.btnRunDAO,'Value',1); -elseif strcmp(pln.radiationMode,'photons') && ~pln.propOpt.runDAO - set(handles.btnRunDAO,'Enable','on'); - set(handles.btnRunDAO,'Value',0); -else - set(handles.btnRunDAO,'Enable','off'); -end - - -% --- Executes on button press in btnTableSave. -function btnTableSave_Callback(~, ~, handles) -% hObject handle to btnTableSave (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -if get(handles.checkIsoCenter,'Value') - pln = evalin('base','pln'); - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(evalin('base','cst'),evalin('base','ct')); - set(handles.editIsoCenter,'String',regexprep(num2str((round(pln.propStf.isoCenter(1,:) * 10))./10), '\s+', ' ')); - assignin('base','pln',pln); -end -getPlnFromGUI(handles); - -% --- Executes on selection change in listBoxCmd. -function listBoxCmd_Callback(hObject, ~, ~) -numLines = size(get(hObject,'String'),1); -set(hObject, 'ListboxTop', numLines); - -% --- Executes on slider movement. -function sliderOffset_Callback(hObject, ~, handles) -handles.profileOffset = get(hObject,'Value'); -UpdatePlot(handles); - - -%% HELPER FUNCTIONS -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% check validity of input for cst -function flagValidity = CheckValidity(Val) - -flagValidity = true; - -if ischar(Val) - Val = str2num(Val); -end - -if length(Val) > 2 - warndlg('invalid input!'); -end - -if isempty(Val) - warndlg('Input not a number!'); - flagValidity = false; -end - -if any(Val < 0) - warndlg('Input not a positive number!'); - flagValidity = false; -end - -% return IPOPT status as message box -function CheckOptimizerStatus(usedOptimizer,OptCase) - -[statusmsg,statusflag] = usedOptimizer.GetStatus(); - -if statusflag == 0 || statusflag == 1 - status = 'none'; -else - status = 'warn'; -end - -msgbox(['Optimizer finished with status ' num2str(statusflag) ' (' statusmsg ')'],'Optimizer',status,'modal'); - -% get pln file form GUI -function getPlnFromGUI(handles) - -% evalin pln (if existant) in order to decide whether isoCenter should be calculated -% automatically -if evalin('base','exist(''pln'',''var'')') - pln = evalin('base','pln'); -end - -% Special parsing of bixelWidth (since it can also be "field") for imported -% shapes -bixelWidth = get(handles.editBixelWidth,'String'); % [mm] / also corresponds to lateral spot spacing for particles -if strcmp(bixelWidth,'field') - pln.propStf.bixelWidth = bixelWidth; -else - pln.propStf.bixelWidth = parseStringAsNum(bixelWidth,false); - if isnan(pln.propStf.bixelWidth) - warndlg('Invalid bixel width! Use standard bixel width of 5mm!'); - pln.propStf.bixelWidth = 5; - set(handles.editBixelWidth,'String','5'); - end -end - -pln.propStf.gantryAngles = parseStringAsNum(get(handles.editGantryAngle,'String'),true); % [???] - -if handles.eduMode - set(handles.editCouchAngle,'String',num2str(zeros(size(pln.propStf.gantryAngles)))); -end - -pln.propStf.couchAngles = parseStringAsNum(get(handles.editCouchAngle,'String'),true); % [???] -pln.propStf.numOfBeams = numel(pln.propStf.gantryAngles); -try - ct = evalin('base','ct'); - pln.numOfVoxels = prod(ct.cubeDim); - pln.voxelDimensions = ct.cubeDim; -catch -end -pln.numOfFractions = parseStringAsNum(get(handles.editFraction,'String'),false); -contents = get(handles.popupRadMode,'String'); -pln.radiationMode = contents{get(handles.popupRadMode,'Value')}; % either photons / protons / carbon -contents = get(handles.popUpMachine,'String'); -pln.machine = contents{get(handles.popUpMachine,'Value')}; - -cellBioModel = get(handles.popMenuBioOpt,'String'); -cellQuantOpt = get(handles.popMenuQuantOpt,'String'); - -pln.bioParam = matRad_bioModel(pln.radiationMode, cellQuantOpt{get(handles.popMenuQuantOpt,'Value'),1}, cellBioModel{get(handles.popMenuBioOpt,'Value'),1}); -pln.bioOptimization = pln.bioParam.identifier; - -scenGenTypes = get(handles.popupmenuScenGen,'String'); -scenGenSelect = get(handles.popupmenuScenGen,'Value'); - -if evalin('base','exist(''ct'',''var'')') - pln.multScen = matRad_multScen(evalin('base','ct'),scenGenTypes{scenGenSelect,1}); -else - pln.multScen = matRad_multScen([],scenGenTypes{scenGenSelect,1}); -end - -pln.propOpt.runSequencing = logical(get(handles.btnRunSequencing,'Value')); -pln.propOpt.runDAO = logical(get(handles.btnRunDAO,'Value')); - -try - cst = evalin('base','cst'); - if (sum(strcmp('TARGET',cst(:,3))) > 0 && get(handles.checkIsoCenter,'Value')) || ... - (sum(strcmp('TARGET',cst(:,3))) > 0 && ~isfield(pln.propStf,'isoCenter')) - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct); - set(handles.checkIsoCenter,'Value',1); - else - if ~strcmp(get(handles.editIsoCenter,'String'),'multiple isoCenter') - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1) * str2num(get(handles.editIsoCenter,'String')); - end - end -catch - warning('couldnt set isocenter in getPln function') -end - -handles.pln = pln; -assignin('base','pln',pln); - -% parsing a string as number array -function number = parseStringAsNum(stringIn,isVector) -if isnumeric(stringIn) - number = stringIn; -else - number = str2num(stringIn); - if isempty(number) || length(number) > 1 && ~isVector - warndlg(['could not parse all parameters (pln, optimization parameter)']); - number = NaN; - elseif isVector && iscolumn(number) - number = number'; - end -end - - -% show error -function handles = showError(handles,Message,ME) - -if nargin == 3 - %Add exception message - if isfield(handles,'devMode') && handles.devMode - meType = 'extended'; - else - meType = 'basic'; - end - Message = {Message,ME.getReport(meType,'hyperlinks','off')}; -end - -if isfield(handles,'ErrorDlg') - if ishandle(handles.ErrorDlg) - close(handles.ErrorDlg); - end -end -handles.ErrorDlg = errordlg(Message); - -% show warning -function handles = showWarning(handles,Message,ME) - -if nargin == 3 - %Add exception message - if isfield(handles,'devMode') && handles.devMode - meType = 'extended'; - else - meType = 'basic'; - end - Message = {Message,ME.getReport(meType,'hyperlinks','off')}; -end - -if isfield(handles,'WarnDlg') - if ishandle(handles.WarnDlg) - close(handles.WarnDlg); - end -end -handles.WarnDlg = warndlg(Message); - -% check for valid machine data input file -function flag = checkRadiationComposition(handles) -flag = true; -contents = cellstr(get(handles.popUpMachine,'String')); -Machine = contents{get(handles.popUpMachine,'Value')}; -contents = cellstr(get(handles.popupRadMode,'String')); -radMod = contents{get(handles.popupRadMode,'Value')}; - -if isdeployed - baseroot = [ctfroot filesep 'matRad']; -else - baseroot = fileparts(mfilename('fullpath')); -end -FoundFile = dir([baseroot filesep 'basedata' filesep radMod '_' Machine '.mat']); - - -if isempty(FoundFile) - warndlg(['No base data available for machine: ' Machine]); - flag = false; -end - -function matRadScrollWheelFcn(src,event) - -% get handles -handles = guidata(src); - -% compute new slice -currSlice = round(get(handles.sliderSlice,'Value')); -newSlice = currSlice - event.VerticalScrollCount; - -% project to allowed set -newSlice = min(newSlice,get(handles.sliderSlice,'Max')); -newSlice = max(newSlice,get(handles.sliderSlice,'Min')); - -% update slider -set(handles.sliderSlice,'Value',newSlice); - -% update handles object -guidata(src,handles); - -% update plot -UpdatePlot(handles); - - - - -%% CALLBACKS -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% button: show DVH -function btnDVH_Callback(~, ~, handles) - -resultGUI = evalin('base','resultGUI'); -Content = get(handles.popupDisplayOption,'String'); -SelectedCube = Content{get(handles.popupDisplayOption,'Value')}; - -pln = evalin('base','pln'); -resultGUI_SelectedCube.physicalDose = resultGUI.(SelectedCube); - -if pln.bioParam.bioOpt - %check if one of the default fields is selected - if sum(strcmp(SelectedCube,{'physicalDose','effect','RBE,','RBExD','alpha','beta'})) > 0 - resultGUI_SelectedCube.physicalDose = resultGUI.physicalDose; - resultGUI_SelectedCube.RBExD = resultGUI.RBExD; - else - Idx = find(SelectedCube == '_'); - SelectedSuffix = SelectedCube(Idx(1):end); - resultGUI_SelectedCube.physicalDose = resultGUI.(['physicalDose' SelectedSuffix]); - resultGUI_SelectedCube.RBExD = resultGUI.(['RBExD' SelectedSuffix]); - end -end - -%adapt visibilty -cst = evalin('base','cst'); -for i = 1:size(cst,1) - cst{i,5}.Visible = handles.VOIPlotFlag(i); -end - -matRad_indicatorWrapper(cst,pln,resultGUI_SelectedCube); - -assignin('base','cst',cst); - -% radio button: plot isolines labels -function radiobtnIsoDoseLinesLabels_Callback(~, ~, handles) -UpdatePlot(handles); - -% button: refresh -function btnRefresh_Callback(hObject, ~, handles) - -handles = resetGUI(hObject, handles); - -%% parse variables from base workspace -AllVarNames = evalin('base','who'); -handles.AllVarNames = AllVarNames; -try - if ismember('ct',AllVarNames) && ismember('cst',AllVarNames) - ct = evalin('base','ct'); - cst = evalin('base','cst'); - %cst = setCstTable(handles,cst); - cst = generateCstTable(handles,cst); - handles.State = 1; - cst = matRad_computeVoiContoursWrapper(cst,ct); - assignin('base','cst',cst); - handles = reloadGUI(hObject, handles, ct, cst); - elseif ismember('ct',AllVarNames) && ~ismember('cst',AllVarNames) - handles = showError(handles,'GUI OpeningFunc: could not find cst file'); - ct = evalin('base','ct'); - handles = reloadGUI(hObject,handles,ct); - elseif ~ismember('ct',AllVarNames) && ismember('cst',AllVarNames) - handles = showError(handles,'GUI OpeningFunc: could not find ct file'); - handles = reloadGUI(hObject, handles); - else - handles = reloadGUI(hObject, handles); - end -catch - handles = showError(handles,'GUI OpeningFunc: Could not load ct and cst file'); - handles = reloadGUI(hObject, handles); -end - -guidata(hObject, handles); - - -% text box: # fractions -function editFraction_Callback(hObject, ~, handles) -getPlnFromGUI(handles); -guidata(hObject,handles); - -% text box: stratification levels -function editSequencingLevel_Callback(~, ~, ~) - -% text box: isoCenter in [mm] -function editIsoCenter_Callback(hObject, ~, handles) - -pln = evalin('base','pln'); -tmpIsoCenter = str2num(get(hObject,'String')); - -if length(tmpIsoCenter) == 3 - if sum(any(unique(pln.propStf.isoCenter,'rows')~=tmpIsoCenter)) - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1)*tmpIsoCenter; - handles.State = 1; - UpdateState(handles); - end -else - handles = showError(handles,'EditIsoCenterCallback: Could not set iso center'); -end - -assignin('base','pln',pln); -guidata(hObject,handles); - -% check box: iso center auto -function checkIsoCenter_Callback(hObject, ~, handles) - -W = evalin('base','whos'); -doesPlnExist = ismember('pln',{W(:).name}); - -if get(hObject,'Value') && doesPlnExist - pln = evalin('base','pln'); - if ~isfield(pln.propStf,'isoCenter') - pln.propStf.isoCenter = NaN; - end - tmpIsoCenter = matRad_getIsoCenter(evalin('base','cst'),evalin('base','ct')); - if ~isequal(tmpIsoCenter,pln.propStf.isoCenter) - pln.propStf.isoCenter = ones(pln.propStf.numOfBeams,1)*tmpIsoCenter; - handles.State = 1; - UpdateState(handles); - end - set(handles.editIsoCenter,'String',regexprep(num2str((round(tmpIsoCenter*10))./10), '\s+', ' ')); - set(handles.editIsoCenter,'Enable','off') - assignin('base','pln',pln); -else - set(handles.editIsoCenter,'Enable','on') -end - -% radio button: run sequencing -function btnRunSequencing_Callback(~, ~, handles) -getPlnFromGUI(handles); - -% radio button: run direct aperture optimization -function btnRunDAO_Callback(~, ~, handles) -getPlnFromGUI(handles); - -% button: set iso dose levels -function btnSetIsoDoseLevels_Callback(hObject, ~, handles) -prompt = {['Enter iso dose levels in [Gy]. Enter space-separated numbers, e.g. 1.5 2 3 4.98. Enter 0 to use default values']}; -if isequal(handles.IsoDose.Levels,0) || ~isvector(handles.IsoDose.Levels) || any(~isnumeric(handles.IsoDose.Levels)) || any(isnan(handles.IsoDose.Levels)) - defaultLine = {'1 2 3 '}; -else - if isrow(handles.IsoDose.Levels) - defaultLine = cellstr(num2str(handles.IsoDose.Levels,'%.2g ')); - else - defaultLine = cellstr(num2str(handles.IsoDose.Levels','%.2g ')); - end -end - -try - Input = inputdlg(prompt,'Set iso dose levels ', [1 70],defaultLine); - if ~isempty(Input) - handles.IsoDose.Levels = (sort(str2num(Input{1}))); - if length(handles.IsoDose.Levels) == 1 && (handles.IsoDose.Levels(1) ~= 0) - handles.IsoDose.Levels = [handles.IsoDose.Levels handles.IsoDose.Levels]; - end - handles.IsoDose.NewIsoDoseFlag = true; - end -catch - warning('Couldnt parse iso dose levels - using default values'); - handles.IsoDose.Levels = 0; -end -handles = updateIsoDoseLineCache(handles); -handles.IsoDose.NewIsoDoseFlag = false; -UpdatePlot(handles); -guidata(hObject,handles); - - -% popup menu: machine -function popUpMachine_Callback(hObject, ~, handles) -contents = cellstr(get(hObject,'String')); -checkRadiationComposition(handles); -if handles.State > 0 - pln = evalin('base','pln'); - if handles.State > 0 && ~strcmp(contents(get(hObject,'Value')),pln.machine) - handles.State = 1; - UpdateState(handles); - guidata(hObject,handles); - end - getPlnFromGUI(handles); -end - -% toolbar load button -function toolbarLoad_ClickedCallback(hObject, eventdata, handles) -btnLoadMat_Callback(hObject, eventdata, handles); - -% toolbar save button -function toolbarSave_ClickedCallback(hObject, eventdata, handles) - -btnTableSave_Callback(hObject, eventdata, handles); - -try - - if handles.State > 0 - ct = evalin('base','ct'); - cst = evalin('base','cst'); - pln = evalin('base','pln'); - end - - if handles.State > 1 - stf = evalin('base','stf'); - dij = evalin('base','dij'); - end - - if handles.State > 2 - resultGUI = evalin('base','resultGUI'); - end - - switch handles.State - case 1 - uisave({'cst','ct','pln'}); - case 2 - uisave({'cst','ct','pln','stf','dij'}); - case 3 - uisave({'cst','ct','pln','stf','dij','resultGUI'}); - end - -catch - handles = showWarning(handles,'Could not save files'); -end -guidata(hObject,handles); - -% button: about -function btnAbout_Callback(hObject, eventdata, handles) - -[~,matRadVer] = matRad_version; - -msg{1} = ['matRad ''' matRadVer.name '''']; %Name -if handles.eduMode - msg{1} = [msg{1} ' Educational']; -end -msg{end+1} = sprintf('v%d.%d.%d',matRadVer.major,matRadVer.minor,matRadVer.patch); %Version Number -if isdeployed - msg{end+1} = 'Standalone Version'; -elseif ~isempty(matRadVer.branch) && ~isempty(matRadVer.commitID) - msg{end+1} = sprintf('Git: Branch %s, commit %s',matRadVer.branch,matRadVer.commitID(1:8)); -end - -[env,envver] = matRad_getEnvironment(); -msg{end+1} = sprintf('Environment: %s v%s %s',env,envver,version('-release')); - -msg{end+1} = 'Web: www.matrad.org'; -msg{end+1} = 'E-Mail: contact@matrad.org'; - -msgbox(msg,'About matRad'); - -% button: close -function figure1_CloseRequestFcn(hObject, ~, ~) -set(0,'DefaultUicontrolBackgroundColor',[0.5 0.5 0.5]); -selection = questdlg('Do you really want to close matRad?',... - 'Close matRad',... - 'Yes','No','Yes'); - -%BackgroundColor',[0.5 0.5 0.5] - switch selection - case 'Yes' - delete(hObject); - case 'No' - return - end - -% --- Executes on button press in pushbutton_recalc. -function pushbutton_recalc_Callback(hObject, ~, handles) - -% recalculation only makes sense if ... -if evalin('base','exist(''pln'',''var'')') && ... - evalin('base','exist(''stf'',''var'')') && ... - evalin('base','exist(''ct'',''var'')') && ... - evalin('base','exist(''cst'',''var'')') && ... - evalin('base','exist(''resultGUI'',''var'')') - -try - - % indicate that matRad is busy - % change mouse pointer to hour glass - Figures = gcf;%findobj('type','figure'); - set(Figures, 'pointer', 'watch'); - drawnow; - % disable all active objects - InterfaceObj = findobj(Figures,'Enable','on'); - set(InterfaceObj,'Enable','off'); - - % get all data from workspace - pln = evalin('base','pln'); - stf = evalin('base','stf'); - ct = evalin('base','ct'); - cst = evalin('base','cst'); - resultGUI = evalin('base','resultGUI'); - - % get weights of the selected cube - Content = get(handles.popupDisplayOption,'String'); - SelectedCube = Content{get(handles.popupDisplayOption,'Value')}; - Suffix = strsplit(SelectedCube,'_'); - if length(Suffix)>1 - Suffix = ['_' Suffix{2}]; - else - Suffix = ''; - end - - wField = ['w' Suffix]; - - if ~isfield(resultGUI,wField) - warndlg(['No exact match found for weight vector ''' wField ''' with selected dose insance. Trying common weight vector ''w'' instead!']); - wField = 'w'; - end - - %Second sanity check to exclude case with no 'w' present - if ~isfield(resultGUI,wField) - errordlg('No weight vector found for forward dose recalculation!'); - return; - end - - if sum([stf.totalNumOfBixels]) ~= length(resultGUI.(wField)) - errordlg('Selected weight vector does not correspond to current steering file (wrong number of entries/bixels!)!'); - return; - end - - % change isocenter if that was changed and do _not_ recreate steering - % information - for i = 1:numel(pln.propStf.gantryAngles) - stf(i).isoCenter = pln.propStf.isoCenter(i,:); - end - - % recalculate influence matrix - if strcmp(pln.radiationMode,'photons') - dij = matRad_calcPhotonDose(ct,stf,pln,cst); - elseif strcmp(pln.radiationMode,'protons') || strcmp(pln.radiationMode,'carbon') - dij = matRad_calcParticleDose(ct,stf,pln,cst); - end - - % recalculate cubes in resultGUI - resultGUIreCalc = matRad_calcCubes(resultGUI.(wField),dij); - - % delete old variables to avoid confusion - if isfield(resultGUI,'effect') - resultGUI = rmfield(resultGUI,'effect'); - resultGUI = rmfield(resultGUI,'RBExD'); - resultGUI = rmfield(resultGUI,'RBE'); - resultGUI = rmfield(resultGUI,'alpha'); - resultGUI = rmfield(resultGUI,'beta'); - end - - % overwrite the "standard" fields - sNames = fieldnames(resultGUIreCalc); - for j = 1:length(sNames) - resultGUI.(sNames{j}) = resultGUIreCalc.(sNames{j}); - end - - % assign results to base worksapce - assignin('base','dij',dij); - assignin('base','resultGUI',resultGUI); - - handles.State = 3; - - % show physicalDose of newly computed state - handles.SelectedDisplayOption = 'physicalDose'; - set(handles.popupDisplayOption,'Value',find(strcmp('physicalDose',Content))); - - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - - handles.cBarChanged = true; - - handles = updateIsoDoseLineCache(handles); - - UpdateState(handles); - - handles.rememberCurrAxes = false; - UpdatePlot(handles); - handles.rememberCurrAxes = true; - - guidata(hObject,handles); - -catch ME - handles = showError(handles,'CalcDoseCallback: Error in dose recalculation!',ME); - - % change state from busy to normal - set(Figures, 'pointer', 'arrow'); - set(InterfaceObj,'Enable','on'); - - guidata(hObject,handles); - return; - -end - -end - - -% --- Executes on button press in btnSetTissue. -function btnSetTissue_Callback(hObject, ~, handles) - -%check if patient is loaded -if handles.State == 0 - return -end - -%parse variables from base-workspace -cst = evalin('base','cst'); -pln = evalin('base','pln'); - -fileName = [pln.radiationMode '_' pln.machine]; -load(['basedata' filesep fileName]); - -% check for available cell types characterized by alphaX and betaX -if strcmp(pln.radiationMode,'carbon') - % check for available cell types characterized by alphaX and betaX - for i = 1:size(machine.data(1).alphaX,2) - CellType{i} = [num2str(machine.data(1).alphaX(i)) ' ' num2str(machine.data(1).betaX(i))]; - end -else - for i = 1:size(pln.bioParam.AvailableAlphaXBetaX,1) - CellType{i} = num2str(pln.bioParam.AvailableAlphaXBetaX{i,1}); - end -end - -%fill table data array -for i = 1:size(cst,1) - data{i,1} = cst{i,2}; - data{i,2} = [num2str(cst{i,5}.alphaX) ' ' num2str(cst{i,5}.betaX)]; - data{i,3} = (cst{i,5}.alphaX / cst{i,5}.betaX ); -end - -Width = 400; -Height = 200 + 20*size(data,1); -ScreenSize = get(0,'ScreenSize'); -% show "set tissue parameter" window -figHandles = get(0,'Children'); -if ~isempty(figHandles) - IdxHandle = strcmp(get(figHandles,'Name'),'Set Tissue Parameters'); -else - IdxHandle = []; -end - -%check if window is already exists -if any(IdxHandle) - IdxTable = find(strcmp({figHandles(IdxHandle).Children.Type},'uitable')); - set(figHandles(IdxHandle).Children(IdxTable), 'Data', []); - figTissue = figHandles(IdxHandle); - %set focus - figure(figTissue); -else - figTissue = figure('Name','Set Tissue Parameters','Color',[.5 .5 .5],'NumberTitle','off','Position',... - [ceil(ScreenSize(3)/2) ceil(ScreenSize(4)/2) Width Height]); -end - -% define the tissue parameter table -cNames = {'VOI','alphaX betaX','alpha beta ratio'}; -columnformat = {'char',CellType,'numeric'}; - -tissueTable = uitable('Parent', figTissue,'Data', data,'ColumnEditable',[false true false],... - 'ColumnName',cNames, 'ColumnFormat',columnformat,'Position',[50 150 5 10]); -set(tissueTable,'CellEditCallback',@tissueTable_CellEditCallback); -% set width and height -currTablePos = get(tissueTable,'Position'); -currTableExt = get(tissueTable,'Extent'); -currTablePos(3) = currTableExt(3); -currTablePos(4) = currTableExt(4); -set(tissueTable,'Position',currTablePos); - -% define two buttons with callbacks -uicontrol('Parent', figTissue,'Style', 'pushbutton', 'String', 'Save&Close',... - 'Position', [Width-(0.25*Width) 0.1 * Height 70 30],... - 'Callback', @(hpb,eventdata)SaveTissueParameters(hpb,eventdata,handles,tissueTable)); - -uicontrol('Parent', figTissue,'Style', 'pushbutton', 'String', 'Cancel&Close',... - 'Position', [Width-(0.5*Width) 0.1 * Height 80 30],... - 'Callback', 'close'); - -guidata(hObject,handles); -UpdateState(handles); - - -function SaveTissueParameters(~, ~, handles,tissueTable) -cst = evalin('base','cst'); -% get handle to uiTable - -% retrieve data from uitable -data = get(tissueTable,'data'); - -for i = 1:size(cst,1) - for j = 1:size(data,1) - if strcmp(cst{i,2},data{j,1}) - alphaXBetaX = str2num(data{j,2}); - cst{i,5}.alphaX = alphaXBetaX(1); - cst{i,5}.betaX = alphaXBetaX(2); - end - end -end -assignin('base','cst',cst); -close(get(tissueTable,'Parent')); -handles.State = 2; -UpdateState(handles); - - -function tissueTable_CellEditCallback(hObject, eventdata, ~) -if eventdata.Indices(2) == 2 - alphaXBetaX = str2num(eventdata.NewData); - data = get(hObject,'Data'); - data{eventdata.Indices(1),3} = alphaXBetaX(1)/alphaXBetaX(2); - set(hObject,'Data',data); -end - -% --- Executes on button press in btnSaveToGUI. -function btnSaveToGUI_Callback(hObject, ~, handles) - -Width = 400; -Height = 200; -ScreenSize = get(0,'ScreenSize'); - -% show "Provide result name" window -figHandles = get(0,'Children'); -if ~isempty(figHandles) - IdxHandle = strcmp(get(figHandles,'Name'),'Provide result name'); -else - IdxHandle = []; -end - -%check if window is already exists -if any(IdxHandle) - figDialog = figHandles(IdxHandle); - %set focus - figure(figDialog); -else - figDialog = dialog('Position',[ceil(ScreenSize(3)/2) ceil(ScreenSize(4)/2) Width Height],'Name','Provide result name','Color',[0.5 0.5 0.5]); - - uicontrol('Parent',figDialog,... - 'Style','text',... - 'Position',[20 Height - (0.35*Height) 350 60],... - 'String','Please provide a decriptive name for your optimization result:','FontSize',10,'BackgroundColor',[0.5 0.5 0.5]); - - uicontrol('Parent',figDialog,... - 'Style','edit',... - 'Position',[30 60 350 60],... - 'String','Please enter name here...','FontSize',10,'BackgroundColor',[0.55 0.55 0.55]); - - uicontrol('Parent', figDialog,'Style', 'pushbutton', 'String', 'Save','FontSize',10,... - 'Position', [0.42*Width 0.1 * Height 70 30],... - 'Callback', @(hpb,eventdata)SaveResultToGUI(hpb,eventdata,guidata(hpb))); -end - -uiwait(figDialog); -guidata(hObject, handles); -UpdateState(handles) -UpdatePlot(handles) - - -function SaveResultToGUI(~, ~, ~) -AllFigHandles = get(0,'Children'); -ixHandle = strcmp(get(AllFigHandles,'Name'),'Provide result name'); -uiEdit = get(AllFigHandles(ixHandle),'Children'); - -if strcmp(get(uiEdit(2),'String'),'Please enter name here...') - - formatOut = 'mmddyyHHMM'; - Suffix = ['_' datestr(now,formatOut)]; -else - % delete special characters - Suffix = get(uiEdit(2),'String'); - logIx = isstrprop(Suffix,'alphanum'); - Suffix = ['_' Suffix(logIx)]; -end - -pln = evalin('base','pln'); -resultGUI = evalin('base','resultGUI'); - -if isfield(resultGUI,'physicalDose') - resultGUI.(['physicalDose' Suffix]) = resultGUI.physicalDose; -end -if isfield(resultGUI,'w') - resultGUI.(['w' Suffix]) = resultGUI.w; -end - -if pln.bioParam.bioOpt - - if isfield(resultGUI,'RBExD') - resultGUI.(['RBExD' Suffix]) = resultGUI.RBExD; - end - - if strcmp(pln.radiationMode,'carbon') == 1 - if isfield(resultGUI,'effect') - resultGUI.(['effect' Suffix])= resultGUI.effect; - end - - if isfield(resultGUI,'RBE') - resultGUI.(['RBE' Suffix]) = resultGUI.RBE; - end - if isfield(resultGUI,'alpha') - resultGUI.(['alpha' Suffix]) = resultGUI.alpha; - end - if isfield(resultGUI,'beta') - resultGUI.(['beta' Suffix]) = resultGUI.beta; - end - end -end - -close(AllFigHandles(ixHandle)); -assignin('base','resultGUI',resultGUI); - -%Update IsodoseLines -function handles = updateIsoDoseLineCache(handles) -resultGUI = evalin('base','resultGUI'); -% select first cube if selected option does not exist -if ~isfield(resultGUI,handles.SelectedDisplayOption) - CubeNames = fieldnames(resultGUI); - dose = resultGUI.(CubeNames{1,1}); -else - dose = resultGUI.(handles.SelectedDisplayOption); -end - -%if function is called for the first time then set display parameters -if isempty(handles.dispWindow{3,2}) - handles.dispWindow{3,1} = [min(dose(:)) max(dose(:))]; % set default dose range - handles.dispWindow{3,2} = [min(dose(:)) max(dose(:))]; % set min max values -end - -minMaxRange = handles.dispWindow{3,1}; -% if upper colorrange is defined then use it otherwise 120% iso dose - upperMargin = 1; -if abs((max(dose(:)) - handles.dispWindow{3,1}(1,2))) < 0.01 * max(dose(:)) - upperMargin = 1.2; -end - -if (length(handles.IsoDose.Levels) == 1 && handles.IsoDose.Levels(1,1) == 0) || ~handles.IsoDose.NewIsoDoseFlag - vLevels = [0.1:0.1:0.9 0.95:0.05:upperMargin]; - referenceDose = (minMaxRange(1,2))/(upperMargin); - handles.IsoDose.Levels = minMaxRange(1,1) + (referenceDose-minMaxRange(1,1)) * vLevels; -end -handles.IsoDose.Contours = matRad_computeIsoDoseContours(dose,handles.IsoDose.Levels); - - - -%% CREATE FUNCTIONS -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% popup menu: machine -function popUpMachine_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% text box: max value -function txtMaxVal_CreateFcn(hObject, ~, ~) - -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% text box: edit iso center -function editIsoCenter_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% text box: stratification levels -function editSequencingLevel_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function slicerPrecision_CreateFcn(hObject, ~, ~) -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -function editBixelWidth_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function editGantryAngle_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function editCouchAngle_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function popupRadMode_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function editFraction_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function popupPlane_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function sliderSlice_CreateFcn(hObject, ~, ~) -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -function popupTypeOfPlot_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function popupDisplayOption_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function sliderBeamSelection_CreateFcn(hObject, ~, ~) -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -function listBoxCmd_CreateFcn(hObject, ~, ~) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -function sliderOffset_CreateFcn(hObject, ~, ~) -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -% --- Executes on selection change in legendTable. -function legendTable_Callback(hObject, eventdata, handles) -% hObject handle to legendTable (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns legendTable contents as cell array -% contents{get(hObject,'Value')} returns selected item from legendTable -cst = evalin('base','cst'); - -idx = get(hObject,'Value'); -clr = dec2hex(round(cst{idx,5}.visibleColor(:)*255),2)'; -clr = ['#';clr(:)]'; - -%Get the string entries -tmpString = get(handles.legendTable,'String'); - -if handles.VOIPlotFlag(idx) - handles.VOIPlotFlag(idx) = false; - cst{idx,5}.Visible = false; - tmpString{idx} = ['
',cst{idx,2},'
']; -elseif ~handles.VOIPlotFlag(idx) - handles.VOIPlotFlag(idx) = true; - cst{idx,5}.Visible = true; - tmpString{idx} = ['
',cst{idx,2},'
']; -end -set(handles.legendTable,'String',tmpString); - -% update cst in workspace accordingly -assignin('base','cst',cst) - -guidata(hObject, handles); -UpdatePlot(handles) - -% --- Executes during object creation, after setting all properties. -function legendTable_CreateFcn(hObject, eventdata, handles) -% hObject handle to legendTable (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: listbox controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in importDoseButton. -function importDoseButton_Callback(hObject,eventdata, handles) -% hObject handle to importDoseButton (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -extensions{1} = '*.nrrd'; -[filenames,filepath,~] = uigetfile(extensions,'MultiSelect','on'); - -if ~iscell(filenames) - tmp = filenames; - filenames = cell(1); - filenames{1} = tmp; -end - -ct = evalin('base','ct'); -resultGUI = evalin('base','resultGUI'); - -for filename = filenames - [~,name,~] = fileparts(filename{1}); - [cube,~] = matRad_readCube(fullfile(filepath,filename{1})); - if ~isequal(ct.cubeDim, size(cube)) - errordlg('Dimensions of the imported cube do not match with ct','Import failed!','modal'); - continue; - end - - fieldname = ['import_' matlab.lang.makeValidName(name, 'ReplacementStyle','delete')]; - resultGUI.(fieldname) = cube; -end - -assignin('base','resultGUI',resultGUI); -btnRefresh_Callback(hObject, eventdata, handles) - -% --- Executes on button press in pushbutton_importFromBinary. -function pushbutton_importFromBinary_Callback(hObject, eventdata, handles) -% hObject handle to pushbutton_importFromBinary (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -try - % delete existing workspace - parse variables from base workspace - set(handles.popupDisplayOption,'String','no option available'); - AllVarNames = evalin('base','who'); - RefVarNames = {'ct','cst','pln','stf','dij','resultGUI'}; - for i = 1:length(RefVarNames) - if sum(ismember(AllVarNames,RefVarNames{i}))>0 - evalin('base',['clear ', RefVarNames{i}]); - end - end - handles.State = 0; - - %call the gui - uiwait(matRad_importGUI); - - %Check if we have the variables in the workspace - if evalin('base','exist(''cst'',''var'')') == 1 && evalin('base','exist(''ct'',''var'')') == 1 - cst = evalin('base','cst'); - ct = evalin('base','ct'); - %setCstTable(handles,cst); - generateCstTable(handles,cst); - handles.TableChanged = false; - set(handles.popupTypeOfPlot,'Value',1); - - % compute HU values - if ~isfield(ct, 'cubeHU') - ct = matRad_electronDensitiesToHU(ct); - assignin('base','ct',ct); - end - if ~isfield(ct, 'cubeHU') - handles.cubeHUavailable = false; - else - handles.cubeHUavailable = true; - end - - % precompute contours - cst = matRad_computeVoiContoursWrapper(ct,cst); - - assignin('base','ct',ct); - assignin('base','cst',cst); - - if evalin('base','exist(''pln'',''var'')') - assignin('base','pln',pln); - setPln(handles); - else - getPlnFromGUI(handles); - setPln(handles); - end - handles.State = 1; - end - - % set slice slider - handles.plane = get(handles.popupPlane,'value'); - if handles.State >0 - set(handles.sliderSlice,'Min',1,'Max',ct.cubeDim(handles.plane),... - 'Value',round(ct.cubeDim(handles.plane)/2),... - 'SliderStep',[1/(ct.cubeDim(handles.plane)-1) 1/(ct.cubeDim(handles.plane)-1)]); - end - - if handles.State > 0 - % define context menu for structures - for i = 1:size(cst,1) - if cst{i,5}.Visible - handles.VOIPlotFlag(i) = true; - else - handles.VOIPlotFlag(i) = false; - end - end - end - - handles.dispWindow = cell(3,2); - handles.cBarChanged = true; - - UpdateState(handles); - handles.rememberCurrAxes = false; - UpdatePlot(handles); - handles.rememberCurrAxes = true; -catch - handles = showError(handles,'Binary Patient Import: Could not import data'); - UpdateState(handles); -end - -guidata(hObject,handles); - -% --- Executes on button press in radioBtnIsoCenter. -function radioBtnIsoCenter_Callback(hObject, eventdata, handles) -% hObject handle to radioBtnIsoCenter (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -UpdatePlot(handles) -% Hint: get(hObject,'Value') returns toggle state of radioBtnIsoCenter - -% -------------------------------------------------------------------- -function uipushtool_screenshot_ClickedCallback(hObject, eventdata, handles) -% hObject handle to uipushtool_screenshot (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - - -tmpFig = figure('position',[100 100 700 600],'Visible','off','name','Current View'); -cBarHandle = findobj(handles.figure1,'Type','colorbar'); -if ~isempty(cBarHandle) - new_handle = copyobj([handles.axesFig cBarHandle],tmpFig); -else - new_handle = copyobj(handles.axesFig,tmpFig); -end - -oldPos = get(handles.axesFig,'Position'); -set(new_handle(1),'units','normalized', 'Position',oldPos); - -if ~isfield(handles,'lastStoragePath') || exist(handles.lastStoragePath,'dir') ~= 7 - lastStoragePath = []; -else - lastStoragePath = handles.lastStoragePath; -end - -[filename, pathname] = uiputfile({'*.jpg;*.tif;*.png;*.gif','All Image Files'; '*.fig','MATLAB figure file'},'Save current view',[lastStoragePath 'screenshot.png']); - - - -if ~isequal(filename,0) && ~isequal(pathname,0) - handles.lastStoragePath = pathname; - set(gcf, 'pointer', 'watch'); - saveas(tmpFig,fullfile(pathname,filename)); - set(gcf, 'pointer', 'arrow'); - close(tmpFig); - uiwait(msgbox('Current view has been succesfully saved!')); -else - uiwait(msgbox('Aborted saving, showing figure instead!')); - set(tmpFig,'Visible','on'); -end - -guidata(hObject,handles); - - -%% Callbacks & Functions for color setting -function UpdateColormapOptions(handles) - -if isfield(handles,'colormapLocked') && handles.colormapLocked - return; -end - -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); - -cMapSelectionIndex = get(handles.popupmenu_chooseColormap,'Value'); -cMapStrings = get(handles.popupmenu_chooseColormap,'String'); - -if selectionIndex > 1 - set(handles.uitoggletool8,'State','on'); -else - set(handles.uitoggletool8,'State','off'); -end - -try - if selectionIndex == 2 - ct = evalin('base','ct'); - currentMap = handles.ctColorMap; - window = handles.dispWindow{selectionIndex,1}; - if handles.cubeHUavailable - minMax = [min(ct.cubeHU{1}(:)) max(ct.cubeHU{1}(:))]; - else - minMax = [min(ct.cube{1}(:)) max(ct.cube{1}(:))]; - end - % adjust value for custom window to current - handles.windowPresets(1).width = max(window) - min(window); - handles.windowPresets(1).center = mean(window); - % update full window information - handles.windowPresets(2).width = minMax(2) - minMax(1); - handles.windowPresets(2).center = mean(minMax); - elseif selectionIndex == 3 - result = evalin('base','resultGUI'); - dose = result.(handles.SelectedDisplayOption); - currentMap = handles.doseColorMap; - minMax = [min(dose(:)) max(dose(:))]; - window = handles.dispWindow{selectionIndex,1}; - else - window = [0 1]; - minMax = window; - currentMap = 'bone'; - end -catch - window = [0 1]; - minMax = window; - currentMap = 'bone'; -end - -valueRange = minMax(2) - minMax(1); - -windowWidth = window(2) - window(1); -windowCenter = mean(window); - -%This are some arbritrary settings to configure the sliders -sliderCenterMinMax = [minMax(1)-valueRange/2 minMax(2)+valueRange/2]; -sliderWidthMinMax = [0 valueRange*2]; - -%if we have selected a value outside this range, we adapt the slider -%windows -if windowCenter < sliderCenterMinMax(1) - sliderCenterMinMax(1) = windowCenter; -end -if windowCenter > sliderCenterMinMax(2) - sliderCenterMinMax(2) = windowCenter; -end -if windowWidth < sliderWidthMinMax(1) - sliderWidthMinMax(1) = windowWidth; -end -if windowCenter > sliderCenterMinMax(2) - sliderWidthMinMax(2) = windowWidth; -end - - -set(handles.edit_windowCenter,'String',num2str(windowCenter,3)); -set(handles.edit_windowWidth,'String',num2str(windowWidth,3)); -set(handles.edit_windowRange,'String',num2str(window,4)); -set(handles.slider_windowCenter,'Min',sliderCenterMinMax(1),'Max',sliderCenterMinMax(2),'Value',windowCenter); -set(handles.slider_windowWidth,'Min',sliderWidthMinMax(1),'Max',sliderWidthMinMax(2),'Value',windowWidth); - -cMapPopupIndex = find(strcmp(currentMap,cMapStrings)); -set(handles.popupmenu_chooseColormap,'Value',cMapPopupIndex); - -guidata(gcf,handles); - -% --- Executes on selection change in popupmenu_chooseColorData. -function popupmenu_chooseColorData_Callback(hObject, eventdata, handles) -% hObject handle to popupmenu_chooseColorData (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_chooseColorData contents as cell array -% contents{get(hObject,'Value')} returns selected item from popupmenu_chooseColorData - -%index = get(hObject,'Value') - 1; - -handles.cBarChanged = true; - -guidata(hObject,handles); -UpdatePlot(handles); - - -% --- Executes during object creation, after setting all properties. -function popupmenu_chooseColorData_CreateFcn(hObject, eventdata, handles) -% hObject handle to popupmenu_chooseColorData (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: popupmenu controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on slider movement. -function slider_windowCenter_Callback(hObject, eventdata, handles) -% hObject handle to slider_windowCenter (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'Value') returns position of slider -% get(hObject,'Min') and get(hObject,'Max') to determine range of slider - -newCenter = get(hObject,'Value'); -range = get(handles.slider_windowWidth,'Value'); -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); - -handles.dispWindow{selectionIndex,1} = [newCenter-range/2 newCenter+range/2]; -handles.cBarChanged = true; - -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes during object creation, after setting all properties. -function slider_windowCenter_CreateFcn(hObject, eventdata, handles) -% hObject handle to slider_windowCenter (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: slider controls usually have a light gray background. -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -set(hObject,'Value',0.5); - -% --- Executes on slider movement. -function slider_windowWidth_Callback(hObject, eventdata, handles) -% hObject handle to slider_windowWidth (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'Value') returns position of slider -% get(hObject,'Min') and get(hObject,'Max') to determine range of slider - -newWidth = get(hObject,'Value'); -center = get(handles.slider_windowCenter,'Value'); -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); -handles.dispWindow{selectionIndex,1} = [center-newWidth/2 center+newWidth/2]; -handles.cBarChanged = true; - -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes during object creation, after setting all properties. -function slider_windowWidth_CreateFcn(hObject, eventdata, handles) -% hObject handle to slider_windowWidth (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: slider controls usually have a light gray background. -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -set(hObject,'Value',1.0); - - -% --- Executes on selection change in popupmenu_chooseColormap. -function popupmenu_chooseColormap_Callback(hObject, eventdata, handles) -% hObject handle to popupmenu_chooseColormap (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_chooseColormap contents as cell array -% contents{get(hObject,'Value')} returns selected item from popupmenu_chooseColormap - -index = get(hObject,'Value'); -strings = get(hObject,'String'); - -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); - -switch selectionIndex - case 2 - handles.ctColorMap = strings{index}; - case 3 - handles.doseColorMap = strings{index}; - otherwise -end - -handles.cBarChanged = true; - -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes during object creation, after setting all properties. -function popupmenu_chooseColormap_CreateFcn(hObject, eventdata, handles) -% hObject handle to popupmenu_chooseColormap (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: popupmenu controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -function edit_windowRange_Callback(hObject, eventdata, handles) -% hObject handle to edit_windowRange (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of edit_windowRange as text -% str2double(get(hObject,'String')) returns contents of edit_windowRange as a double - -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); -vRange = str2num(get(hObject,'String')); -% matlab adds a zero in the beginning when text field is changed -if numel(vRange) == 3 - vRange = vRange(vRange~=0); -end - -handles.dispWindow{selectionIndex,1} = sort(vRange); - -handles.cBarChanged = true; - - % compute new iso dose lines -if selectionIndex > 2 - guidata(hObject,handles); - handles = updateIsoDoseLineCache(handles); -end - -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes during object creation, after setting all properties. -function edit_windowRange_CreateFcn(hObject, eventdata, handles) -% hObject handle to edit_windowRange (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -set(hObject,'String','0 1'); - - -function edit_windowCenter_Callback(hObject, eventdata, handles) -% hObject handle to edit_windowCenter (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of edit_windowCenter as text -% str2double(get(hObject,'String')) returns contents of edit_windowCenter as a double - -newCenter = str2double(get(hObject,'String')); -width = get(handles.slider_windowWidth,'Value'); -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); -handles.dispWindow{selectionIndex,1} = [newCenter-width/2 newCenter+width/2]; -handles.cBarChanged = true; -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes during object creation, after setting all properties. -function edit_windowCenter_CreateFcn(hObject, eventdata, handles) -% hObject handle to edit_windowCenter (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - - -function edit_windowWidth_Callback(hObject, eventdata, handles) -% hObject handle to edit_windowWidth (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of edit_windowWidth as text -% str2double(get(hObject,'String')) returns contents of edit_windowWidth as a double - -newWidth = str2double(get(hObject,'String')); -center = get(handles.slider_windowCenter,'Value'); -selectionIndex = get(handles.popupmenu_chooseColorData,'Value'); -handles.dispWindow{selectionIndex,1} = [center-newWidth/2 center+newWidth/2]; -handles.cBarChanged = true; -guidata(hObject,handles); -UpdatePlot(handles); - - -% --- Executes during object creation, after setting all properties. -function edit_windowWidth_CreateFcn(hObject, eventdata, handles) -% hObject handle to edit_windowWidth (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% -------------------------------------------------------------------- -function uitoggletool8_ClickedCallback(hObject, eventdata, handles) -% hObject handle to uitoggletool8 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -%Check if on or off -val = strcmp(get(hObject,'State'),'on'); - -%Now we have to apply the new selection to our colormap options panel -if ~val - newSelection = 1; -else - %Chooses the selection from the highest state - selections = get(handles.popupmenu_chooseColorData,'String'); - newSelection = numel(selections); -end -set(handles.popupmenu_chooseColorData,'Value',newSelection); - -handles.cBarChanged = true; -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes on slider movement. -function sliderOpacity_Callback(hObject, eventdata, handles) -% hObject handle to sliderOpacity (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -handles.doseOpacity = get(hObject,'Value'); -guidata(hObject,handles); -UpdatePlot(handles); - -% --- Executes during object creation, after setting all properties. -function sliderOpacity_CreateFcn(hObject, eventdata, handles) -% hObject handle to sliderOpacity (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: slider controls usually have a light gray background. -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); -end - -%% Data Cursors -function cursorText = dataCursorUpdateFunction(obj,event_obj) -% Display the position of the data cursor -% obj Currently not used (empty) -% event_obj Handle to event object -% output_txt Data cursor text string (string or cell array of strings). - -target = findall(0,'Name','matRadGUI'); - -% Get GUI data (maybe there is another way?) -handles = guidata(target); - -% position of the data point to label -pos = get(event_obj,'Position'); - -%Different behavior for image and profile plot -if get(handles.popupTypeOfPlot,'Value')==1 %Image view - cursorText = cell(0,1); - try - if handles.State >= 1 - plane = get(handles.popupPlane,'Value'); - slice = round(get(handles.sliderSlice,'Value')); - - %Get the CT values - ct = evalin('base','ct'); - - %We differentiate between pos and ix, since the user may put - %the datatip on an isoline which returns a continous position - cubePos = zeros(1,3); - cubePos(plane) = slice; - cubePos(1:end ~= plane) = fliplr(pos); - cubeIx = round(cubePos); - - %Here comes the index permutation stuff - %Cube Index - cursorText{end+1,1} = ['Cube Index: ' mat2str(cubeIx)]; - %Space Coordinates - coords = zeros(1,3); - coords(1) = cubePos(2)*ct.resolution.x; - coords(2) = cubePos(1)*ct.resolution.y; - coords(3) = cubePos(3)*ct.resolution.z; - cursorText{end+1,1} = ['Space Coordinates: ' mat2str(coords,5) ' mm']; - - ctVal = ct.cubeHU{1}(cubeIx(1),cubeIx(2),cubeIx(3)); - cursorText{end+1,1} = ['HU Value: ' num2str(ctVal,3)]; - end - - %Add dose information if available - if handles.State == 3 - %get result structure - result = evalin('base','resultGUI'); - - %Get all result names from popup - resultNames = get(handles.popupDisplayOption,'String'); - - %Display all values of fields found in the resultGUI struct - for runResult = 1:numel(resultNames) - name = resultNames{runResult}; - if isfield(result,name) - field = result.(name); - val = field(cubeIx(1),cubeIx(2),cubeIx(3)); - cursorText{end+1,1} = [name ': ' num2str(val,3)]; - end - end - end - catch - cursorText{end+1,1} = 'Error while retreiving Data!'; - end -else %Profile view - cursorText = cell(2,1); - cursorText{1} = ['Radiological Depth: ' num2str(pos(1),3) ' mm']; - cursorText{2} = [get(target,'DisplayName') ': ' num2str(pos(2),3)]; -end - - - -% --- Executes on selection change in popMenuBioOpt. -function popMenuBioOpt_Callback(hObject, ~, handles) - -if handles.State > 0 - - pln = evalin('base','pln'); - cellBioOpt = get(handles.popMenuBioOpt,'String'); - cellQuantOpt = get(handles.popMenuQuantOpt,'String'); - - NewBioOptimization = cellBioOpt(get(handles.popMenuBioOpt,'Value'),:); - OldBioOptimization = pln.bioParam.model; - - NewQuantityOpt = cellQuantOpt(get(handles.popMenuQuantOpt,'Value'),:); - - % switch from biological opt to physical optimization - if ~strcmp(NewBioOptimization,'none') && strcmp(OldBioOptimization,'none') - NewQuantityOpt = 'RBExD'; - set(handles.popMenuQuantOpt,'Value',find(strcmp(cellQuantOpt,NewQuantityOpt))) - end - - % switch from biological opt to physical optimization - if strcmp(NewBioOptimization,'none') && ~strcmp(OldBioOptimization,'none') - NewQuantityOpt = 'physicalDose'; - set(handles.popMenuQuantOpt,'Value',find(strcmp(cellQuantOpt,NewQuantityOpt))) - end - - - if ~strcmp(NewBioOptimization,pln.bioParam.model) || ~strcmp(NewQuantityOpt,pln.bioParam.quantityOpt) - handles.State = 1; - end - - %obtain updated bio model - getPlnFromGUI(handles); - setPln(handles); - UpdateState(handles); -end - -guidata(hObject,handles); - -% --- Executes during object creation, after setting all properties. -function popMenuBioOpt_CreateFcn(hObject, eventdata, handles) -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end -bioParam = matRad_bioModel('photons','physicalDose','none'); -set(hObject,'String',bioParam.AvailableModels); - - -% --- Executes during object creation, after setting all properties. -function popMenuQuantOpt_CreateFcn(hObject, eventdata, handles) -% Hint: popupmenu controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end -bioParam = matRad_bioModel('photons','physicalDose','none'); -set(hObject,'String',bioParam.AvailableQuantitiesForOpt); - -% --- Executes on selection change in popMenuQuantOpt. -function popMenuQuantOpt_Callback(hObject, eventdata, handles) - - getPlnFromGUI(handles); - setPln(handles); - UpdateState(handles); - -guidata(hObject,handles); - -% --- Executes on button press in btn3Dview. -function btn3Dview_Callback(hObject, eventdata, handles) -% hObject handle to btn3Dview (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -if ~isfield(handles,'axesFig3D') || ~isfield(handles,'axesFig3D') || ~isgraphics(handles.axesFig3D) - handles.fig3D = figure('Name','matRad 3D View'); - handles.axesFig3D = axes('Parent',handles.fig3D); - view(handles.axesFig3D,3); - try - ct = evalin('base','ct'); - - xlim(handles.axesFig3D,[0 ct.resolution.x*ct.cubeDim(2)]); - ylim(handles.axesFig3D,[0 ct.resolution.y*ct.cubeDim(1)]); - zlim(handles.axesFig3D,[0 ct.resolution.z*ct.cubeDim(3)]); - catch - end -end -%end - -UpdatePlot(handles); - -guidata(hObject,handles); - -% --- Executes on button press in radiobtnCT. -function radiobtnCT_Callback(hObject, eventdata, handles) -% hObject handle to radiobtnCT (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of radiobtnCT -UpdatePlot(handles) - -% --- Executes on button press in radiobtnPlan. -function radiobtnPlan_Callback(hObject, eventdata, handles) -% hObject handle to radiobtnPlan (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of radiobtnPlan -UpdatePlot(handles) - - -% --- Executes on selection change in popupmenu_windowPreset. -function popupmenu_windowPreset_Callback(hObject, eventdata, handles) -% hObject handle to popupmenu_windowPreset (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_windowPreset contents as cell array -% contents{get(hObject,'Value')} returns selected item from popupmenu_windowPreset - -selectionIndexCube = 2; % working on ct only -selectionIndexWindow = get(handles.popupmenu_windowPreset,'Value'); -newCenter = handles.windowPresets(selectionIndexWindow).center; -newWidth = handles.windowPresets(selectionIndexWindow).width; - -handles.dispWindow{selectionIndexCube,1} = [newCenter - newWidth/2 newCenter + newWidth/2]; -handles.cBarChanged = true; -guidata(hObject,handles); -UpdatePlot(handles); -UpdateColormapOptions(handles); - - -% --- Executes during object creation, after setting all properties. -function popupmenu_windowPreset_CreateFcn(hObject, eventdata, handles) -% hObject handle to popupmenu_windowPreset (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: popupmenu controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% setup ct window list -% data and values from CERR https://github.com/adityaapte/CERR -windowNames = {'Custom','Full','Abd/Med', 'Head', 'Liver', 'Lung', 'Spine', 'Vrt/Bone'}; -windowCenter = {NaN, NaN, -10, 45, 80, -500, 30, 400}; -windowWidth = {NaN, NaN, 330, 125, 305, 1500, 300, 1500}; -windowPresets = cell2struct([windowNames', windowCenter', windowWidth'], {'name', 'center', 'width'},2); - - -handles.windowPresets = windowPresets; - -selectionList = {windowPresets(:).name}; -set(hObject,'String',selectionList(:)); -set(hObject,'Value',1); - - -guidata(hObject,handles); - -% --- Executes on button press in checkbox_lockColormap. -function checkbox_lockColormap_Callback(hObject, eventdata, handles) -% hObject handle to checkbox_lockColormap (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of checkbox_lockColormap -handles.colormapLocked = get(hObject,'Value'); - -if handles.colormapLocked - state = 'Off'; %'Inactive'; -else - state = 'On'; -end - -set(handles.popupmenu_chooseColorData,'Enable',state); -set(handles.popupmenu_windowPreset,'Enable',state); -set(handles.slider_windowWidth,'Enable',state); -set(handles.slider_windowCenter,'Enable',state); -set(handles.edit_windowWidth,'Enable',state); -set(handles.edit_windowCenter,'Enable',state); -set(handles.edit_windowRange,'Enable',state); -set(handles.popupmenu_chooseColormap,'Enable',state); - - -guidata(hObject,handles); - - -function cst = updateStructureTable(handles,cst) -colorAssigned = true; - -% check whether all structures have an assigned color -for i = 1:size(cst,1) - if ~isfield(cst{i,5},'visibleColor') - colorAssigned = false; - break; - elseif isempty(cst{i,5}.visibleColor) - colorAssigned = false; - break; - end -end - -% assign color if color assignment is not already present or inconsistent -if colorAssigned == false - m = 64; - colorStep = ceil(m/size(cst,1)); - colors = colorcube(colorStep*size(cst,1)); - % spread individual VOI colors in the colorcube color palette - colors = colors(1:colorStep:end,:); - - for i = 1:size(cst,1) - cst{i,5}.visibleColor = colors(i,:); - end -end - -for s = 1:size(cst,1) - handles.VOIPlotFlag(s) = cst{s,5}.Visible; - clr = dec2hex(round(cst{s,5}.visibleColor(:)*255),2)'; - clr = ['#';clr(:)]'; - if handles.VOIPlotFlag(s) - tmpString{s} = ['
',cst{s,2},'
']; - else - tmpString{s} = ['
',cst{s,2},'
']; - end -end -set(handles.legendTable,'String',tmpString); - -%-- generates the CST table -function cst = generateCstTable(handles,cst) - -cst = updateStructureTable(handles,cst); - -cstPanel = handles.uipanel3; - -%Get units in pixels to to calculate aspect ratio -cstPanelPosUnit = get(cstPanel,'Units'); -set(cstPanel,'Units','pixels'); -cstPanelPosPix = get(cstPanel,'Position'); -set(cstPanel,'Units',cstPanelPosUnit); - -aspectRatio = cstPanelPosPix(3) / cstPanelPosPix(4); - -%Parameters for line height -objHeight = 0.095;% 22; -lineHeight = 0.1; %25; %Height of a table line -yTopSep = 0.12;%40; %Separation of the first line from the top -tableViewHeight = 1 - yTopSep; - -%Define the widths of the fields relatively -buttonW = objHeight / aspectRatio; % Make button squared -nameW = 3.5*buttonW; -typeW = 3*buttonW; -opW = buttonW; -functionW = 6*buttonW; -robustnessW = 0.5*functionW; -penaltyW = 2*buttonW; -paramTitleW = 4*buttonW; -paramW = 2*buttonW; -fieldSep = 0.25*buttonW; %Separation between fields horizontally - - -%Scrollbar -%Check if a scrollbar already exists to get possible position of slider -cstPanelChildren = get(cstPanel,'Children'); -cstVertTableScroll = findobj(cstPanelChildren,'Style','slider'); -if isempty(cstVertTableScroll) - sliderPos = 0; -else - sliderPos = get(cstVertTableScroll,'Max') - get(cstVertTableScroll,'Value'); -end - -ypos = @(c) tableViewHeight - c*lineHeight + sliderPos; - -%Clean the whole panel for new setup -delete(cstPanelChildren); - -%Creates a dummy axis to allow for the use of textboxes instead of uicontrol to be able to use the (la)tex interpreter -tmpAxes = axes('Parent',cstPanel,'units','normalized','position',[0 0 1 1],'visible','off'); - -organTypes = {'OAR', 'TARGET'}; - -%columnname = {'VOI name','VOI type','priority','obj. / const.'};%,'penalty','dose', 'EUD','volume','robustness'}; - -%Get all Classes & classNames -mpkgObjectives = meta.package.fromName('DoseObjectives'); -mpkgConstraints = meta.package.fromName('DoseConstraints'); -classList = [mpkgObjectives.ClassList; mpkgConstraints.ClassList]; - -classList = classList(not([classList.Abstract])); - -%Now get the "internal" name from the properties -classNames = cell(2,numel(classList)); -for clIx = 1:numel(classList) - cl = classList(clIx); - pList = cl.PropertyList; %Get List of all properties - pNameIx = arrayfun(@(p) strcmp(p.Name,'name'),pList); %get index of the "name" property - p = pList(pNameIx); %select name property - pName = p.DefaultValue; % get value / name - classNames(:,clIx) = {cl.Name; pName}; %Store class name and display name -end - -if size(cst,2) < 6 - numOfObjectives = 0; -else - numOfObjectives = sum(cellfun(@numel,cst(:,6))); -end - -cnt = 0; - -newline = '\n'; - -%Setup Headlines -xPos = 0.01; %5 - -h = uicontrol(cstPanel,'Style','text','String','+/-','Units','normalized','Position',[xPos ypos(cnt) buttonW objHeight],'TooltipString','Remove or add Constraint or Objective'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','VOI name','Units','normalized','Position',[xPos ypos(cnt) nameW objHeight],'TooltipString','Name of the structure with objective/constraint'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','VOI type','Units','normalized','Position',[xPos ypos(cnt) typeW objHeight],'TooltipString','Segmentation Classification'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','OP','Units','normalized','Position',[xPos ypos(cnt) opW objHeight],'TooltipString',['Overlap Priority' char(10) '(Smaller number overlaps higher number)']); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','Robustness','Units','normalized','Position',[xPos ypos(cnt) robustnessW objHeight],'TooltipString','Robustness Setting'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','Function','Units','normalized','Position',[xPos ypos(cnt) functionW objHeight],'TooltipString','Objective/Constraint function type'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','p','Units','normalized','Position',[xPos ypos(cnt) penaltyW objHeight],'TooltipString','Optimization penalty'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','text','String','| Parameters','Units','normalized','Position',[xPos ypos(cnt) paramTitleW objHeight],'TooltipString','List of parameters','HorizontalAlignment','left'); -tmp_pos = get(h,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -cnt = cnt + 1; - -%Create Objectives / Constraints controls -for i = 1:size(cst,1) - %Safety break in case the 6th column is empty, because it allows us to - %run less checks afterwards - if numOfObjectives == 0 - break; - end - if strcmp(cst(i,3),'IGNORED')~=1 - %Compatibility Layer for old objective format - if isstruct(cst{i,6}) - cst{i,6} = arrayfun(@matRad_DoseOptimizationFunction.convertOldOptimizationStruct,cst{i,6},'UniformOutput',false); - end - for j=1:numel(cst{i,6}) - - obj = cst{i,6}{j}; - - %Convert to class if not - if ~isa(obj,'matRad_DoseOptimizationFunction') - try - obj = matRad_DoseOptimizationFunction.createInstanceFromStruct(obj); - catch ME - warning('Objective/Constraint not valid!\n%s',ME.message) - continue; - end - end - - - - - %VOI - xPos = 0.01;%5; - - h = uicontrol(cstPanel,'Style','pushbutton','String','-','Units','normalized','Position',[xPos ypos(cnt) buttonW objHeight],'TooltipString','Remove Objective/Constraint','Callback',{@btObjRemove_Callback,handles},... - 'UserData',[i,j]); - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - h = uicontrol(cstPanel','Style','edit','String',cst{i,2},'Units','normalized','Position',[xPos ypos(cnt) nameW objHeight],'TooltipString','Name',... - 'Enable','inactive',... %Disable editing of name atm - 'UserData',[i,2],'Callback',{@editCstParams_Callback,handles}); %Callback added, however, editing is disabled atm - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - h = uicontrol(cstPanel,'Style','popupmenu','String',organTypes','Value',find(strcmp(cst{i,3},organTypes)),'Units','normalized','Position',[xPos ypos(cnt) typeW objHeight],'TooltipString','Segmentation Classification',... - 'UserData',[i,3],'Callback',{@editCstParams_Callback,handles}); - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - h = uicontrol(cstPanel,'Style','edit','String',num2str(cst{i,5}.Priority),'Units','normalized','Position',[xPos ypos(cnt) opW objHeight],'TooltipString',['Overlap Priority' newline '(Smaller number overlaps higher number)'],... - 'UserData',[i,5],'Callback',{@editCstParams_Callback,handles}); - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - - robAvail = obj.availableRobustness(); - robSetting = obj.robustness; - robIx = find(strcmp(robSetting,robAvail)); - h = uicontrol(cstPanel,'Style','popupmenu','String',robAvail','Value',robIx,'Units','normalized','Position',[xPos ypos(cnt) robustnessW objHeight],'TooltipString','Select Robustness',... - 'UserData',{i,j,'robustness'},'Callback',{@editObjParam_Callback,handles}); - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - - h = uicontrol(cstPanel,'Style','popupmenu','String',classNames(2,:)','Value',find(strcmp(obj.name,classNames(2,:))),'Units','normalized','Position',[xPos ypos(cnt) functionW objHeight],'TooltipString','Select Objective/Constraint',... - 'UserData',{[i,j],classNames(1,:)},'Callback',{@changeObjFunction_Callback,handles}); - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - - %Check if we have an objective to display penalty - if isa(obj,'DoseObjectives.matRad_DoseObjective') - h = uicontrol(cstPanel,'Style','edit','String',num2str(obj.penalty),'Units','normalized','Position',[xPos ypos(cnt) penaltyW objHeight],'TooltipString','Objective Penalty','UserData',{i,j,'penalty'},'Callback',{@editObjParam_Callback,handles}); - else - h = uicontrol(cstPanel,'Style','edit','String','----','Units','normalized','Position',[xPos ypos(cnt) penaltyW objHeight],'Enable','off'); - end - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - - %Iterate through parameters - for p = 1:numel(obj.parameterNames) - h = text('Parent',tmpAxes,'String',['| ' obj.parameterNames{p} ':'],'VerticalAlignment','middle','Units','normalized','Position',[xPos ypos(cnt)+lineHeight/2],'Interpreter','tex','FontWeight','normal',... - 'FontSize',get(cstPanel,'FontSize'),'FontName',get(cstPanel,'FontName'),'FontUnits',get(cstPanel,'FontUnits'),'FontWeight','normal'); - tmp_pos = get(h,'Extent'); - xPos = xPos + tmp_pos(3) + fieldSep; - - %Check if we have a cell and therefore a parameter list - if iscell(obj.parameterTypes{p}) - h = uicontrol(cstPanel,'Style','popupmenu','String',obj.parameterTypes{p}','Value',obj.parameters{p},'TooltipString',obj.parameterNames{p},'Units','normalized','Position',[xPos ypos(cnt) paramW*2 objHeight],'UserData',{i,j,p},'Callback',{@editObjParam_Callback,handles}); - else - h = uicontrol(cstPanel,'Style','edit','String',num2str(obj.parameters{p}),'TooltipString',obj.parameterNames{p},'Units','normalized','Position',[xPos ypos(cnt) paramW objHeight],'UserData',{i,j,p},'Callback',{@editObjParam_Callback,handles}); - end - - tmp_pos = get(h,'Position'); - xPos = xPos + tmp_pos(3) + fieldSep; - end - - cnt = cnt +1; - end - end -end -xPos = 0.01; %5 -hAdd = uicontrol(cstPanel,'Style','pushbutton','String','+','Units','normalized','Position',[xPos ypos(cnt) buttonW objHeight],'TooltipString','Add Objective/Constraint','Callback',{@btObjAdd_Callback,handles}); -tmp_pos = get(hAdd,'Position'); -xPos = xPos + tmp_pos(3) + fieldSep; -h = uicontrol(cstPanel,'Style','popupmenu','String',cst(:,2)','Units','normalized','Position',[xPos ypos(cnt) nameW objHeight]); -set(hAdd,'UserData',h); - -%Calculate Scrollbar / Slider Position -lastPos = ypos(cnt); -firstPos = ypos(0); -tableHeight = abs(firstPos - lastPos); - -exceedFac = tableHeight / tableViewHeight; %How much the current umber of rows exceeds the window height capacity -if exceedFac > 1 - sliderFac = exceedFac - 1; - uicontrol(cstPanel,'Style','slider','Units','normalized','Position',[0.975 0 0.025 1],'Min',0,'Max',ceil(sliderFac)*tableViewHeight,'SliderStep',[lineHeight tableViewHeight] ./ (ceil(sliderFac)*tableViewHeight),'Value',ceil(sliderFac)*tableViewHeight - sliderPos,'Callback',{@cstTableSlider_Callback,handles}); -end - - -% --- Executes when uipanel3 is resized. -function uipanel3_SizeChangedFcn(hObject, eventdata, handles) -% hObject handle to uipanel3 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -try - generateCstTable(handles,evalin('base','cst')); -catch -end - -function btObjAdd_Callback(hObject, ~, handles) -% hObject handle to btObjAdd -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -popupHandle = hObject.UserData; -cstIndex = popupHandle.Value; - -cst = evalin('base','cst'); -%Add Standard Objective -if strcmp(cst{cstIndex,3},'TARGET') - cst{cstIndex,6}{end+1} = struct(DoseObjectives.matRad_SquaredDeviation); -else - cst{cstIndex,6}{end+1} = struct(DoseObjectives.matRad_SquaredOverdosing); -end - -assignin('base','cst',cst); - -generateCstTable(handles,cst); - -function btObjRemove_Callback(hObject, ~, handles) -% hObject handle to btObjRemove (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -ix = hObject.UserData; - -cst = evalin('base','cst'); -%Add Standard Objective - -cst{ix(1),6}(ix(2)) = []; - -assignin('base','cst',cst); - -generateCstTable(handles,cst); - -function editObjParam_Callback(hObject, ~, handles) -% hObject handle to current objective parameter -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -ix = hObject.UserData; - -cst = evalin('base','cst'); -%Add Standard Objective - -%if the third index is 0 we changed the penalty -%if we have a popupmenu selection we use value -%otherwise we use the edit string - -if ~isnumeric(ix{3}) - switch ix{3} - case 'robustness' - v = hObject.Value; - cst{ix{1},6}{ix{2}}.robustness = hObject.String{v}; - case 'penalty' - cst{ix{1},6}{ix{2}}.penalty = str2double(hObject.String); - otherwise - matRad_cfg = MatRad_Config.instance(); - matRad_cfg.dispError('Unknown objective/constraint property %s',ix{3}); - end - -elseif isequal(hObject.Style,'popupmenu') - cst{ix{1},6}{ix{2}}.parameters{ix{3}} = hObject.Value; -else - cst{ix{1},6}{ix{2}}.parameters{ix{3}} = str2double(hObject.String); -end - -assignin('base','cst',cst); - -generateCstTable(handles,cst); - -function changeObjFunction_Callback(hObject, ~, handles) -% hObject handle to objective popup -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -data = hObject.UserData; -ix = data{1}; -classNames = data{2}; -classToCreate = classNames{hObject.Value}; - -cst = evalin('base','cst'); -%Add Standard Objective - -%We just check if the user really wanted to change the objective to be -%user-friendly -currentObj = cst{ix(1),6}{ix(2)}; -currentClass = class(currentObj); -if ~strcmp(currentClass,classToCreate) - newObj = eval(classToCreate); - - % Only if we have a penalty value for optimization, apply the new one - % Maybe this check should be more exact? - if (isfield(currentObj,'penalty') || isprop(currentObj,'penalty')) && isprop(newObj,'penalty') - newObj.penalty = currentObj.penalty; - end - - - - cst{ix(1),6}{ix(2)} = struct(newObj); - - assignin('base','cst',cst); - - generateCstTable(handles,cst); -end - -function editCstParams_Callback(hObject,~,handles) -data = hObject.UserData; -ix = data(1); -col = data(2); - -cst = evalin('base','cst'); - -switch col - case 2 - cst{ix,col} = hObject.String; - case 3 - cst{ix,col} = hObject.String{hObject.Value}; - case 5 - cst{ix,col}.Priority = uint32(str2double(hObject.String)); - otherwise - warning('Wrong column assignment in GUI based cst setting'); -end - -assignin('base','cst',cst); - -generateCstTable(handles,cst); - -% --- Executes on selection change in popupmenuScenGen. -function popupmenuScenGen_Callback(hObject, eventdata, handles) -% hObject handle to popupmenuScenGen (see GCBO) -contents = cellstr(get(hObject,'String')); - -if handles.State >= 1 - ct = evalin('base','ct'); - pln = evalin('base','pln'); - pln.scenGenType = contents{get(hObject,'Value')}; - pln.multScen = matRad_multScen(ct,pln.scenGenType); - assignin('base','pln',pln); -end - -% --- Executes on button press in radiobutton3Dconf. -function radiobutton3Dconf_Callback(hObject, eventdata, handles) -% hObject handle to radiobutton3Dconf (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: contents = cellstr(get(hObject,'String')) returns popupmenuScenGen contents as cell array -% contents{get(hObject,'Value')} returns selected item from popupmenuScenGen - - - -% --- Executes during object creation, after setting all properties. -function popupmenuScenGen_CreateFcn(hObject, eventdata, handles) -% hObject handle to popupmenuScenGen (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: popupmenu controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - -% --- Executes on slider movement. -function cstTableSlider_Callback(hObject, eventdata, handles) -% hObject handle to cstTableSlider (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'Value') returns position of slider -% get(hObject,'Min') and get(hObject,'Max') to determine range of slider -generateCstTable(handles,evalin('base','cst')); - -% --- Executes during object creation, after setting all properties. -function cstTableSlider_CreateFcn(hObject, eventdata, handles) -% hObject handle to cstTableSlider (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: slider controls usually have a light gray background. -if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor',[.9 .9 .9]); +validateattributes(x,{'logical','numeric'},{'scalar','binary'}); end diff --git a/matRad_comparePlnDijStf.m b/matRad_comparePlnDijStf.m new file mode 100644 index 000000000..58914f3b8 --- /dev/null +++ b/matRad_comparePlnDijStf.m @@ -0,0 +1,84 @@ +function [allMatch, msg] = matRad_comparePlnDijStf(pln,stf,dij) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% call +% matching = matRad_comparePlnDijStf(pln,stf,dij) +% +% input +% dij: matRad dij struct +% stf: matRad steering information struct +% pln: matRad plan meta information struct +% +% output +% +% allMatch: flag is true if they all match +% matching: message to display +% +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +allMatch=true; + + +%% compare gantry angles in dij, stf and pln +stf_gantryAngles=[stf.gantryAngle]; +if dij.numOfBeams ~= numel(stf_gantryAngles) || dij.numOfBeams ~= numel(pln.propStf.gantryAngles) ... % different size + || ~isempty(find(stf_gantryAngles-pln.propStf.gantryAngles, 1)) % values in stf and pln do not match + allMatch=false; + msg= 'Gantry angles do not match'; + return +end + +%% compare couch angles in stf and pln +stf_couchAngles=[stf.couchAngle]; +if numel(stf_couchAngles) ~= numel(pln.propStf.couchAngles) ... % different size + || ~isempty(find(stf_couchAngles-pln.propStf.couchAngles, 1)) % values in stf and pln do not match + allMatch=false; + msg= 'Couch angles do not match'; + return +end + +%% compare Bixel width in stf and pln +if stf(1).bixelWidth ~= pln.propStf.bixelWidth + allMatch=false; + msg= 'Bixel width does not match'; + return +end + +%% compare radiation mode in stf and pln +if ~strcmp(stf(1).radiationMode, pln.radiationMode) + allMatch=false; + msg= 'Radiation mode does not match'; + return +end + +%% compare isocenter in stf and pln for each gantry angle +for i = 1:numel(pln.propStf.gantryAngles) + if ~isempty(find(stf(i).isoCenter - pln.propStf.isoCenter(i,:) ,1)) + allMatch=false; + msg= 'Isocenter does not match'; + return + end +end + +%% compare number of rays per beam in dij and stf +stf_RaysPerBeam=[stf.numOfRays]; +if numel(stf_RaysPerBeam) ~= numel(dij.numOfRaysPerBeam) ... % different size + || ~isempty(find(stf_RaysPerBeam-dij.numOfRaysPerBeam,1)) % different values + msg= 'Number of rays does not match'; + allMatch=false; + return +end + +end \ No newline at end of file diff --git a/matRad_fluenceOptimization.m b/matRad_fluenceOptimization.m old mode 100644 new mode 100755 diff --git a/matRad_indicatorWrapper.m b/matRad_indicatorWrapper.m index 0442fa28d..718c119b8 100644 --- a/matRad_indicatorWrapper.m +++ b/matRad_indicatorWrapper.m @@ -53,13 +53,16 @@ dvh = matRad_calcDVH(cst,doseCube,'cum'); qi = matRad_calcQualityIndicators(cst,pln,doseCube,refGy,refVol); -figure,set(gcf,'Color',[1 1 1]); -subplot(2,1,1) -matRad_showDVH(dvh,cst,pln); -subplot(2,1,2) + +matRad_cfg = MatRad_Config.instance(); +hF = figure('Color',matRad_cfg.gui.backgroundColor); + +subplotHandle1 = subplot(2,1,1); +matRad_showDVH(subplotHandle1,dvh,cst,pln); +subplotHandle2 = subplot(2,1,2); ixVoi = cellfun(@(c) c.Visible == 1,cst(:,5)); qi = qi(ixVoi); -matRad_showQualityIndicators(qi); +matRad_showQualityIndicators(subplotHandle2,qi); diff --git a/matRad_rc.m b/matRad_rc.m index 69e5b73a1..a98da5e3c 100644 --- a/matRad_rc.m +++ b/matRad_rc.m @@ -1,3 +1,4 @@ +function matRad_rc(clearWindow) % matRad rc script % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -13,6 +14,10 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if nargin < 1 + clearWindow = true; +end + % Initialize matRad matRad_cfg = MatRad_Config.instance(); if ~strcmp(matRad_cfg.matRadRoot,fileparts(mfilename("fullpath"))) @@ -22,9 +27,12 @@ matRad_cfg = MatRad_Config.instance(); end -%clear command window and close all figures -clc; -close all; +% clear command window and close all figures +if clearWindow + + clc; + close all; +end % clear workspace and command prompt, close all figures [env,envver] = matRad_getEnvironment(); @@ -34,3 +42,4 @@ clear env envver vString; +end \ No newline at end of file diff --git a/matRad_sequencing.m b/matRad_sequencing.m new file mode 100644 index 000000000..f9f13873f --- /dev/null +++ b/matRad_sequencing.m @@ -0,0 +1,67 @@ +function resultGUI = matRad_sequencing(resultGUI,stf,dij,pln,visBool) +% matRad inverse planning wrapper function +% +% call +% resultGUI = matRad_sequencing(resultGUI,stf,dij,pln) +% +% input +% dij: matRad dij struct +% stf: matRad stf struct +% pln: matRad pln struct +% resultGUI: struct containing optimized fluence vector, dose, and (for +% biological optimization) RBE-weighted dose etc. +% +% output +% resultGUI: struct containing optimized fluence vector, dose, and (for +% biological optimization) RBE-weighted dose etc. +% +% References +% - +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2016 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +matRad_cfg = MatRad_Config.instance(); + +if nargin < 5 + visBool = 0; +end + +if strcmp(pln.radiationMode,'photons') && (pln.propSeq.runSequencing || pln.propOpt.runDAO) + + if ~isfield(pln.propSeq, 'sequencer') + pln.propSeq.sequencer = 'siochi'; % default: siochi sequencing algorithm + matRad_cfg.dispWarning ('pln.propSeq.sequencer not specified. Using siochi leaf sequencing (default).') + end + + if ~isfield(pln.propSeq, 'sequencingLevel') + pln.propSeq.sequencingLevel = 5; + matRad_cfg.dispWarning ('pln.propSeq.sequencingLevel not specified. Using 5 sequencing levels (default).') + end + + switch pln.propSeq.sequencer + case 'xia' + resultGUI = matRad_xiaLeafSequencing(resultGUI,stf,dij,pln.propSeq.sequencingLevel,visBool); + case 'engel' + resultGUI = matRad_engelLeafSequencing(resultGUI,stf,dij,pln.propSeq.sequencingLevel,visBool); + case 'siochi' + resultGUI = matRad_siochiLeafSequencing(resultGUI,stf,dij,pln.propSeq.sequencingLevel,visBool); + otherwise + matRad_cfg.dispError('Could not find specified sequencing algorithm'); + end +elseif (pln.propSeq.runSequencing || pln.propOpt.runDAO) && ~strcmp(pln.radiationMode,'photons') + matRad_cfg.dispWarning('Sequencing is only specified for pln.radiationMode = "photons". Continuing with out sequencing ... ') +end +end + + diff --git a/matRad_showDVH.m b/matRad_showDVH.m index 8d2443ef5..f61094a5e 100644 --- a/matRad_showDVH.m +++ b/matRad_showDVH.m @@ -1,4 +1,4 @@ -function matRad_showDVH(dvh,cst,pln,lineStyleIndicator) +function matRad_showDVH(axesHandle,dvh,cst,pln,lineStyleIndicator) % matRad dvh visualizaion % % call @@ -41,7 +41,7 @@ function matRad_showDVH(dvh,cst,pln,lineStyleIndicator) % create new figure and set default line style indicator if not explictly % specified -hold on; +hold(axesHandle,'on'); %reduce cst visibleIx = cellfun(@(c) c.Visible == 1,cst(:,5)); @@ -73,7 +73,7 @@ function matRad_showDVH(dvh,cst,pln,lineStyleIndicator) ix = max([1 find(dvh(i).volumePoints>0,1,'last')]); currDvh = [dvh(i).doseGrid(1:ix);dvh(i).volumePoints(1:ix)]; - plot(currDvh(1,:),currDvh(2,:),'LineWidth',4,'Color',colorMx(i,:), ... + plot(axesHandle,currDvh(1,:),currDvh(2,:),'LineWidth',4,'Color',colorMx(i,:), ... 'LineStyle',lineStyles{lineStyleIndicator},'DisplayName',cstNames{i}) maxDVHvol = max(maxDVHvol,max(currDvh(2,:))); @@ -81,24 +81,27 @@ function matRad_showDVH(dvh,cst,pln,lineStyleIndicator) end fontSizeValue = 14; -myLegend = legend('show','location','NorthEast'); +myLegend = legend(axesHandle,'show','location','NorthEast'); set(myLegend,'FontSize',10,'Interpreter','none'); -legend boxoff +legend(axesHandle,'boxoff') -ylim([0 1.1*maxDVHvol]); -xlim([0 1.2*maxDVHdose]); +ylim(axesHandle,[0 1.1*maxDVHvol]); +xlim(axesHandle,[0 1.2*maxDVHdose]); -grid on,grid minor -box(gca,'on'); -set(gca,'LineWidth',1.5,'FontSize',fontSizeValue); -ylabel('Volume [%]','FontSize',fontSizeValue) +grid(axesHandle,'on'),grid(axesHandle,'minor') +box(axesHandle,'on'); %box(gca,'on'); +set(axesHandle,'LineWidth',1.5,'FontSize',fontSizeValue); %set(gca,'LineWidth',1.5,'FontSize',fontSizeValue); +ylabel(axesHandle,'Volume [%]','FontSize',fontSizeValue) if exist('pln','var') && ~isempty(pln) + if strcmp(pln.bioParam.model,'none') xlabel('Dose [Gy]','FontSize',fontSizeValue); else - xlabel('RBE x Dose [Gy(RBE)]','FontSize',fontSizeValue); + xlabel(axesHandle,'RBE x Dose [Gy(RBE)]','FontSize',fontSizeValue); end else xlabel('Dose [Gy]','FontSize',fontSizeValue); + end +hold(axesHandle,'off'); diff --git a/matRad_showQualityIndicators.m b/matRad_showQualityIndicators.m index 32a6162e0..0fbf333c1 100644 --- a/matRad_showQualityIndicators.m +++ b/matRad_showQualityIndicators.m @@ -1,10 +1,11 @@ -function matRad_showQualityIndicators(qi) +function matRad_showQualityIndicators(figHandle,qi) % matRad display of quality indicators as table % % call % matRad_showQualityIndicators(qi) % % input +% figHandle: handle to figure to display the Quality Indicators in % qi: result struct from matRad_calcQualityIndicators % % output @@ -45,17 +46,29 @@ function matRad_showQualityIndicators(qi) qiEmpty = cellfun(@isempty,qi); qi(qiEmpty) = {'-'}; +%Layout depending on axes type + +hType = get(figHandle,'Type'); + +if ~strcmp(hType,'figure') + pos = get(figHandle,'position'); + if strcmp(hType,'axes') + axis(figHandle,'off'); + end + hF = ancestor(figHandle,'figure'); +else + pos = get(figHandle,'position'); + hF = figHandle; +end + %since uitable is only available in newer octave versions, we try and catch -try +try % Create the uitable - table = uitable(gcf,'Data',qi,... + table = uitable(hF,'Data',qi,... 'ColumnName',cnames,... - 'RowName',rnames,'ColumnWidth',{70}); - - % Layout - pos = get(gca,'position'); - set(table,'units','normalized','position',pos) - axis off + 'RowName',rnames,'ColumnWidth',{70},... + 'units','normalized',... + 'position',pos); catch ME matRad_cfg.dispWarning('The uitable function is not implemented in %s v%s.',env,vStr); end diff --git a/optimization/matRad_getObjectivesAndConstraints.m b/optimization/matRad_getObjectivesAndConstraints.m new file mode 100644 index 000000000..9cc9734eb --- /dev/null +++ b/optimization/matRad_getObjectivesAndConstraints.m @@ -0,0 +1,96 @@ +function classNames = matRad_getObjectivesAndConstraints() +% matRad steering information generation +% +% call +% classNames = matRad_getObjectivesAndConstraints() +% +% +% output +% classNames: contains class names (row 1) and display descriptions +% (row 2) of all available objectives +% +% References +% - +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2015 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +env = matRad_getEnvironment(); + +if strcmp(env,'MATLAB') %use the package methodology for Matlab (stable) + mpkgObjectives = meta.package.fromName('DoseObjectives'); + mpkgConstraints = meta.package.fromName('DoseConstraints'); + classList = [mpkgObjectives.ClassList; mpkgConstraints.ClassList]; + + classList = classList(not([classList.Abstract])); + + %Now get the "internal" name from the properties + classNames = cell(2,numel(classList)); + for clIx = 1:numel(classList) + cl = classList(clIx); + pList = cl.PropertyList; %Get List of all properties + pNameIx = arrayfun(@(p) strcmp(p.Name,'name'),pList); %get index of the "name" property + p = pList(pNameIx); %select name property + pName = p.DefaultValue; % get value / name + classNames(:,clIx) = {cl.Name; pName}; %Store class name and display name + end +else + currDir = fileparts(mfilename('fullpath')); + objectiveDir = [currDir filesep '+DoseObjectives']; + constraintDir = [currDir filesep '+DoseConstraints']; + + objFiles = dir(objectiveDir); + for i=1:numel(objFiles) + objFiles(i).pkgName = 'DoseObjectives'; + end + constrFiles = dir(constraintDir); + for i=1:numel(objFiles) + constrFiles(i).pkgName = 'DoseConstraints'; + end + + allFiles = [objFiles; constrFiles]; + + [defNames,dispNames] = arrayfun(@testForClass,allFiles,'UniformOutput',false); + + classNames(1,:) = defNames; + classNames(2,:) = dispNames; + + selectIx = cellfun(@isempty,classNames); + classNames = classNames(:,~selectIx(1,:)); +end +end + +function [defName,dispName] = testForClass(potentialClassFile) + + + try +% fPath = potentialClassFile.folder; + [~,fName,~] = fileparts(potentialClassFile.name); + fType = potentialClassFile.pkgName; + + clTmp = meta.class.fromName([fType '.' fName]); + defName = clTmp.Name; + + pList = clTmp.PropertyList; + + pNameIx = cellfun(@(p) strcmp(p.Name,'name'),pList); + p = pList(pNameIx); + dispName = p{1}.DefaultValue ; + + + catch + defName = ''; + dispName = ''; + end +end + diff --git a/plotting/matRad_plotColorbar.m b/plotting/matRad_plotColorbar.m index 2534b3d6c..e0cc66d67 100644 --- a/plotting/matRad_plotColorbar.m +++ b/plotting/matRad_plotColorbar.m @@ -32,17 +32,19 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + [env,envver] = matRad_getEnvironment(); colormap(axesHandle,cMap); - +caxis(axesHandle,window); %sanity check to avoid a crash -if window(1) < window(2) - caxis(window); -else - matRad_cfg = MatRad_Config.instance(); - matRad_cfg.dispWarning('Unsuitable display window [%d,%d] for color axis!',window(1),window(2)); -end +% if window(1) < window(2) +% +% else +% +% matRad_cfg = MatRad_Config.instance(); +% matRad_cfg.dispWarning('Unsuitable display window [%d,%d] for color axis!',window(1),window(2)); +% end switch env diff --git a/plotting/matRad_plotIsoDoseLines.m b/plotting/matRad_plotIsoDoseLines.m index 43e819235..81e07dbe5 100644 --- a/plotting/matRad_plotIsoDoseLines.m +++ b/plotting/matRad_plotIsoDoseLines.m @@ -4,10 +4,8 @@ % itself % % call -% isoLineHandles = matRad_plotIsoDoseLines(axesHandle,doseCube,isoContours,isoLevels,plotLabels,plane,slice,cMap) -% isoLineHandles = matRad_plotIsoDoseLines(axesHandle,doseCube,isoContours,isoLevels,plotLabels,plane,slice,window) -% isoLineHandles = matRad_plotIsoDoseLines(axesHandle,doseCube,isoContours,isoLevels,plotLabels,plane,slice,cMap,window) -% isoLineHandles = matRad_plotIsoDoseLines(axesHandle,doseCube,isoContours,isoLevels,plotLabels,plane,slice,cMap,window, ...) +% isoLineHandles = +% matRad_plotIsoDoseLines(axesHandle,doseCube,isoContours,isoLevels,plotLabels,plane,slice,...) % % input % axesHandle handle to axes the slice should be displayed in @@ -86,7 +84,7 @@ colors = squeeze(ind2rgb(isoColorLevel,cMap)); axes(axesHandle); -hold on; +hold(axesHandle,'on'); %Check if there is a contour in the plane if any(isoContours{slice,plane}(:)) @@ -100,7 +98,7 @@ else color = unique(colors,'rows'); end - isoLineHandles(end+1) = line(isoContours{slice,plane}(1,lower+1:lower+steps),... + isoLineHandles{end+1} = line(isoContours{slice,plane}(1,lower+1:lower+steps),... isoContours{slice,plane}(2,lower+1:lower+steps),... 'Color',color,'Parent',axesHandle,varargin{:}); if plotLabels @@ -113,6 +111,6 @@ end end -hold off; +hold(axesHandle,'off'); end diff --git a/plotting/matRad_plotIsoDoseLines3D.m b/plotting/matRad_plotIsoDoseLines3D.m index d88f773ff..8991d7476 100644 --- a/plotting/matRad_plotIsoDoseLines3D.m +++ b/plotting/matRad_plotIsoDoseLines3D.m @@ -94,7 +94,7 @@ isoLineHandles = contourslice(axesHandle,xMesh,yMesh,zMesh,doseCube,slices{[1 2 3]},isoLevels); else axes(axesHandle); - hold on; + hold(axesHandle,'on'); for s = 1:numel(sliceIndices) currSlice = sliceIndices(s); @@ -157,7 +157,7 @@ end end - hold off; + hold(axesHandle,'off'); end end diff --git a/plotting/matRad_plotProjectedGantryAngles.m b/plotting/matRad_plotProjectedGantryAngles.m index a82e377bf..a543f1e66 100644 --- a/plotting/matRad_plotProjectedGantryAngles.m +++ b/plotting/matRad_plotProjectedGantryAngles.m @@ -47,16 +47,17 @@ function matRad_plotProjectedGantryAngles(axesHandle,pln,ct,plane) gantryAngleVisColor = 'w'; + hold(axesHandle,'on'); plot(axesHandle,x,y,'LineWidth',1,'Color',gantryAngleVisColor) % add text - txt = '180°'; + txt = '180°'; text(axesHandle,1.1*r*sind(0)+xOffset,1.1*r*cosd(0)+yOffset,txt,'Color',gantryAngleVisColor) - txt = '90°'; + txt = '90°'; text(axesHandle,1.1*r*sind(90)+xOffset,1.1*r*cosd(90)+yOffset,txt,'Color',gantryAngleVisColor) - txt = '0°'; + txt = '0°'; text(axesHandle,1.1*r*sind(180)+xOffset,1.1*r*cosd(180)+yOffset,txt,'Color',gantryAngleVisColor) - txt = '270°'; + txt = '270°'; text(axesHandle,1.22*r*sind(270)+xOffset,1.22*r*cosd(270)+yOffset,txt,'Color',gantryAngleVisColor) % plot gantry angles diff --git a/plotting/matRad_plotVoiContourSlice.m b/plotting/matRad_plotVoiContourSlice.m index 69b14ec13..8f2a5437f 100644 --- a/plotting/matRad_plotVoiContourSlice.m +++ b/plotting/matRad_plotVoiContourSlice.m @@ -1,4 +1,4 @@ -function [voiContourHandles] = matRad_plotVoiContourSlice(axesHandle,cst,ct,ctIndex,selection,plane,slice,cMap,varargin) +function [voiContourHandles, visibleOnSlice] = matRad_plotVoiContourSlice(axesHandle,cst,ct,ctIndex,selection,plane,slice,cMap,varargin) % matRad function that plots the contours of the segmentations given in cst % % call @@ -61,7 +61,7 @@ if isempty(selection) || numel(selection) ~= size(cst,1) selection = true(size(cst,1),1); end - +visibleOnSlice = false(size(cst,1),1); voiContourHandles = cell(0); switch env case 'MATLAB' @@ -104,7 +104,7 @@ if any(C(:)) lower = 1; % lower marks the beginning of a section while lower-1 ~= size(C,2) - hold on + hold(axesHandle,'on') %hold on steps = C(2,lower); % number of elements of current line section tmpLineHandle(end+1) = line(C(1,lower+1:lower+steps),... C(2,lower+1:lower+steps),... @@ -113,6 +113,7 @@ lower = lower+steps+1; end voiContourHandles{end+1} = tmpLineHandle; + visibleOnSlice(s) = true; else % create empty line object voiContourHandles{end+1} = {}; diff --git a/plotting/matRad_plotVois3D.m b/plotting/matRad_plotVois3D.m index 56eaa5ada..6e85c05d7 100644 --- a/plotting/matRad_plotVois3D.m +++ b/plotting/matRad_plotVois3D.m @@ -57,7 +57,7 @@ cMapScale = size(cMap,1)-1; axes(axesHandle); -wasHold = ishold(); +wasHold = ishold(axesHandle); hold(axesHandle,'on'); diff --git a/tools/matRad_compareDose.m b/tools/matRad_compareDose.m index 591311750..876f1d3a1 100644 --- a/tools/matRad_compareDose.m +++ b/tools/matRad_compareDose.m @@ -291,10 +291,10 @@ disp('Plotting DVH...'); hfig.dvh.fig = figure('Renderer', 'painters', 'Position', [10 100 1000 700]); - set(gcf,'Color',[1 1 1]); - matRad_showDVH(dvh1,cst,pln); + set(gcf,'Color',matRad_cfg.gui.backgroundColor); + matRad_showDVH(axes(gcf),dvh1,cst,pln); hold on - matRad_showDVH(dvh2,cst,pln,2); + matRad_showDVH(gca,dvh2,cst,pln,2); xlim([0 dvhWindow*1.2]) title('Dose Volume Histrogram, Dose 1: solid, Dose 2: dashed') end diff --git a/tools/matRad_fixExportedGUI.m b/tools/matRad_fixExportedGUI.m new file mode 100644 index 000000000..e3f073fec --- /dev/null +++ b/tools/matRad_fixExportedGUI.m @@ -0,0 +1,155 @@ +function matRad_fixExportedGUI(guiFile,replaceOnly) +% matRad function to postprocess figure files created from GUI. Removes +% unnecessary and compatability problem causing code and extracts the +% layout and main function storing them in separate files. Backups of the +% figure & m files are created +% +% call +% matRad_fixExportedGUI(guiFile,replaceOnly) +% +% input +% guiFile: m-file exported from guide +% replaceOnly: optional, default false. when set to true, only +% unnecessary and incompatible code is removed and no +% external files (i.e. layout / main function) or backups +% are created +% +% +% References +% - +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Copyright 2019 the matRad development team. +% +% This file is part of the matRad project. It is subject to the license +% terms in the LICENSE file found in the top-level directory of this +% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part +% of the matRad project, including this file, may be copied, modified, +% propagated, or distributed except according to the terms contained in the +% LICENSE file. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +if nargin < 2 + replaceOnly = false; +end + +[filepath, name, ext] = fileparts(which(guiFile)); + +if ~replaceOnly + copyfile(guiFile,[filepath filesep name ext '.bak']); + copyfile([filepath filesep name '.fig'],[filepath filesep name '.fig.bak']); +end + +fid = fopen(guiFile,'r'); +f=fread(fid,'*char')'; +fclose(fid); + + +%fix strange newlines after string attributes +f = regexprep(f,'(String'',''\w+?)\r+?('')','$1$2'); +f = regexprep(f,'''String'',\[\]?','''String'','''''); + + +f = regexprep(f,'\r?\n''Alphamap'',\[[\s.;0-9]*?\](,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''DecorationContainer'',\[[\s.;0-9]*?\](,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''Layer'',''(back|middle|front)''(,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''DisplayName'',blanks\(0\)(,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''HelpTopicKey'',blanks\(0\)(,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''DimensionNames'',{[\s\w'']*?}(,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''Description'',''[\w\s]*?''(,\.\.\.|\))',''); +f = regexprep(f,'\r?\n''Tooltip'',''[\w\s]*?''(,\.\.\.|\))',''); + +%uitable problems +expr = {'Units','FontUnits','RearrangeableColumns','RowStriping','ForegroundColor','Enable','HandleVisibility','FontSize','FontName','FontAngle','FontWeight'}; +expr = strjoin(expr,'|'); +expr = ['\r?\n''(' expr ')'',get\(0,''defaultuitable(' expr ')''\)(,\.\.\.|\))']; +f = regexprep(f,expr,''); + +%On/Off properties +expr = {'FontSmoothing','CLimInclude','ALimInclude','IncludeRenderer','IsContainer','IsContainer','Serializable','TransformForPrintFcnImplicitInvoke'}; +expr = strjoin(expr,'|'); +expr = ['\r?\n''(' expr ')'',''(on|off)''(,\.\.\.|\))']; +f = regexprep(f,expr,''); + +%Mode sets +modestrings = {'ScreenPixelsPerInch','DecorationContainer','Color','FontSize','Layer','FontSmoothing','IsContainer','PickableParts','DimensionNames','Description','TransformForPrintFcnImplicitInvoke','CurrentAxes','CurrentObject','CurrentPoint','SelectionType'}; +modestrings = strjoin(modestrings,'|'); +expr = ['\r?\n''(' modestrings ')Mode'',''\w*?''(,\.\.\.|\))']; +f = regexprep(f,expr,''); + + +%Axes Mode gets +modestrings = {'Colormap','Alphamap','Camera','DataSpace','ColorSpace','FontSize','DecorationContainer','ChildContainer','XRuler','YRuler','ZRuler','AmbientLightSource','ActivePositionProperty'}; +modestrings = strjoin(modestrings,'|'); +expr = ['\r?\n''(' modestrings ')Mode'',get\(0,''defaultaxes(' modestrings ')Mode''\)(,\.\.\.|\))']; +f = regexprep(f,expr,''); + +%Figure Mode gets +modestrings = {'PaperSize','PaperType','PaperUnits'}; +modestrings = strjoin(modestrings,'|'); +expr = ['\r?\n''(' modestrings ')Mode'',get\(0,''defaultfigure(' modestrings ')Mode''\)(,\.\.\.|\))']; +f = regexprep(f,expr,''); + +%Tooltip property +f = regexprep(f,'\r?\n''TooltipMode'',get\(0,''default(uipushtool|uitoggletool|uicontrol)TooltipMode''\)(,\.\.\.|\))',''); + +%ui element mode gets +modestrings = {'BackgroundColor','Value'}; +modestrings = strjoin(modestrings,'|'); +f = regexprep(f,['\r?\n''(' modestrings ')Mode'',get\(0,''default(uipushtool|uitoggletool|uicontrol)(' modestrings ')Mode''\)(,\.\.\.|\))'],''); + +%Fix remaining whitespaces +f = regexprep(f,'(,\.\.\.)\s*?;',');'); + +%Fix Titles +f = regexprep(f,'''Title'',{(.*?)}','''Title'',strtrim(strjoin({$1}))'); + +%Octave doesn't handle ishghandle +f = regexprep(f,'ishghandle','isgraphics'); + +%Octave doesn't know guidemfile +f = regexprep(f,'guidemfile','%guidemfile'); + +%rename the MainFcn +f = regexprep(f,'gui_mainfcn',[name '_gui_mainFcn']); + +if ~replaceOnly + %now extract the layout and mainfcn, which are added to the end of the file + %by the export + expr = '(% --- Creates and returns a handle to the GUI figure\..*?)(% --- Handles default GUIDE GUI creation and callback dispatch.*)'; + out = regexp(f,expr,'tokens'); + layoutFcn = out{1}{1}; + guiMainFcn = out{1}{2}; + %remove the functions + f = regexprep(f,expr,''); + %write the functions to files + [~,~] = mkdir([filepath filesep 'gui']); + addpath([filepath filesep 'gui']); + fLayoutId = fopen([filepath filesep 'gui' filesep name '_LayoutFcn.m'],'w'); + fprintf(fLayoutId,'%s',layoutFcn); + fclose(fLayoutId); + fGuiMainFcnId = fopen([filepath filesep 'gui' filesep name '_gui_mainFcn.m'],'w'); + fprintf(fGuiMainFcnId,'%s',guiMainFcn); + fclose(fGuiMainFcnId); +end + +fid = fopen(guiFile,'w'); +fprintf(fid,'%s',f); +fclose(fid); + +if ~replaceOnly + try + mat = load([filepath filesep name '.mat']); + mat = mat.mat; + save([filepath filesep name '.mat'],'mat','-v7'); + catch + fprintf('No .mat file was exported with GUI\n'); + end + + h1 = feval([name '_LayoutFcn'],'reuse'); + savefig(h1,[filepath filesep name '.fig']); + close(h1); +end +end \ No newline at end of file diff --git a/tools/matRad_plotSliceWrapper.m b/tools/matRad_plotSliceWrapper.m index 698e84dd4..deab0f402 100644 --- a/tools/matRad_plotSliceWrapper.m +++ b/tools/matRad_plotSliceWrapper.m @@ -117,7 +117,8 @@ %plot VOI contours if ~isempty(cst) - hContour = matRad_plotVoiContourSlice(axesHandle,cst,ct,cubeIdx,voiSelection,plane,slice,contourColorMap,varargin{:}); + + [hContour,~] = matRad_plotVoiContourSlice(axesHandle,cst,ct,cubeIdx,voiSelection,plane,slice,contourColorMap,varargin{:}); if boolPlotLegend visibleOnSlice = (~cellfun(@isempty,hContour));