Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New function for Slice plotting added. Bug fixing in iso lines plotter. #818

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
6 changes: 3 additions & 3 deletions matRad/plotting/matRad_plotIsoDoseLines.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@
%fly
if isempty(isoContours)
if plane == 1
C = contourc(doseCube(slice,:,:),isoLevels);
C = contourc(squeeze(doseCube(slice,:,:)),isoLevels);

Check warning on line 61 in matRad/plotting/matRad_plotIsoDoseLines.m

View check run for this annotation

Codecov / codecov/patch

matRad/plotting/matRad_plotIsoDoseLines.m#L61

Added line #L61 was not covered by tests
elseif plane == 2
C = contourc(doseCube(:,slice,:),isoLevels);
C = contourc(squeeze(doseCube(:,slice,:)),isoLevels);

Check warning on line 63 in matRad/plotting/matRad_plotIsoDoseLines.m

View check run for this annotation

Codecov / codecov/patch

matRad/plotting/matRad_plotIsoDoseLines.m#L63

Added line #L63 was not covered by tests
elseif plane == 3
C = contourc(doseCube(:,:,slice),isoLevels);
C = contourc(squeeze(doseCube(:,:,slice)),isoLevels);

Check warning on line 65 in matRad/plotting/matRad_plotIsoDoseLines.m

View check run for this annotation

Codecov / codecov/patch

matRad/plotting/matRad_plotIsoDoseLines.m#L65

Added line #L65 was not covered by tests
end
isoContours{slice,plane} = C;
end
Expand Down
212 changes: 212 additions & 0 deletions matRad/util/matRad_plotSlice.m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice. I guess by calling matRad_plotSlice from the old matRad_plotSliceWrapper you also cover most of the file with tests now due to the examples.

You would just need to add a few tests for, e.g., leaving out certain arguments.

Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
function [hCMap,hDose,hCt,hContour,hIsoDose] = matRad_plotSlice(ct, varargin)
% matRad tool function to directly plot a complete slice of a ct with dose
% optionally including contours and isolines
%
% call
% [] = matRad_plotSlice(ct, dose, varargin)
%
% input (required)
% ct matRad ct struct
% dose dose cube
%
% input (optional/empty) to be called as Name-value pair arguments:
% axesHandle handle to axes the slice should be displayed in
% cst matRad cst struct
% cubeIdx Index of the desired cube in the ct struct
% plane plane view (coronal=1,sagittal=2,axial=3)
% slice slice in the selected plane of the 3D cube
% thresh threshold for display of dose values
% alpha alpha value for the dose overlay
% contourColorMap colormap for the VOI contours
% doseColorMap colormap for the dose
% doseWindow dose value window
% doseIsoLevels levels defining the isodose contours
% voiSelection logicals defining the current selection of contours
% that should be plotted. Can be set to [] to plot
% all non-ignored contours.
% colorBarLabel string defining the yLabel of the colorBar
% boolPlotLegend boolean if legend should be plottet or not
% varargin Additional MATLAB Line or Text Properties (e.g. 'LineWidth', 'FontSize', etc.)
%
%
% References
% -
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright 2025 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/LICENSE.md. 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.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

defaultDose = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe leave a comment here before the argument handling.

defaultCst = [];
defaultSlice = floor(min(ct.cubeDim)./2);
defaultAxesHandle = gca;
defaultCubeIdx = 1;
defaultPlane = 1;
defaultDoseWindow = [];
defaultThresh = [];
defaultAlpha = [];
defaultDoseColorMap = jet;
defaultDoseIsoLevels = [];
defaultVOIselection = [];
defaultContourColorMap = [];
defaultBoolPlotLegend = false;
defaultColorBarLabel = [];
defaultShowCt = true;

isDose = @(x) isnumeric(x) && all(size(x) == ct.cubeDim);
isSlice = @(x) x>=1 && x<=max(ct.cubeDim) && floor(x)==x;
isAxes = @(x) strcmp(get(gca, 'type'), 'axes');
isCubeIdx = @(x) isscalar(x);
isPlane = @(x) isscalar(x) && (sum(x==[1, 2, 3])==1);
isDoseWindow = @(x) (length(x) == 2 && isvector(x));
isThresh = @(x) (isscalar(x) && (x>=0) && (x<=1)) || isempty(x);
isAlpha = @(x) isscalar(x) && (x>=0) && (x<=1) || isempty(x);
isDoseColorMap = @(x) isnumeric(x) && (size(x, 2)==3) && all(x(:) >= 0) && all(x(:) <= 1);
isDoseIsoLevels = @(x) isnumeric(x) && isvector(x)|| isempty(x);
isVOIselection = @(x) isnumeric(x) || isempty(x); %all(x(:)==1 | x(:)==0) || isempty(x);
isContourColorMap = @(x) isnumeric(x) && (size(x, 2)==3) && size(x, 1)>=2 && all(x(:) >= 0) && all(x(:) <= 1);
isBoolPlotLegend = @(x) x==0 || x ==1;
isColorBarLabel = @(x) isstring(x) || ischar(x) || isempty(x);
isShowCt = @(x) isscalar(x) && (x==0) || (x==1);

p = inputParser;
p.KeepUnmatched = true;
addRequired(p, 'ct')

addParameter(p, 'dose', defaultDose, isDose)
addParameter(p, 'cst', defaultCst)
addParameter(p, 'slice', defaultSlice, isSlice)
addParameter(p, 'axesHandle', defaultAxesHandle, isAxes)
addParameter(p, 'cubeIdx', defaultCubeIdx, isCubeIdx)
addParameter(p, 'plane', defaultPlane, isPlane)
addParameter(p, 'doseWindow', defaultDoseWindow, isDoseWindow)
addParameter(p, 'thresh', defaultThresh, isThresh)
addParameter(p, 'alpha', defaultAlpha, isAlpha)
addParameter(p, 'doseColorMap', defaultDoseColorMap, isDoseColorMap)
addParameter(p, 'doseIsoLevels', defaultDoseIsoLevels, isDoseIsoLevels)
addParameter(p, 'voiSelection', defaultVOIselection, isVOIselection)
addParameter(p, 'contourColorMap', defaultContourColorMap, isContourColorMap)
addParameter(p, 'boolPlotLegend', defaultBoolPlotLegend, isBoolPlotLegend)
addParameter(p, 'colorBarLabel', defaultColorBarLabel, isColorBarLabel)
addParameter(p, 'showCt', defaultShowCt, isShowCt)

parse(p, ct, varargin{:});

%% Unmatched properties
% General properties
lineFieldNames = fieldnames(set(line));
textFieldNames = fieldnames(set(text));
% Filter line properties from Unmatched
unmParamNames = fieldnames(p.Unmatched);
lineFields = unmParamNames(ismember(unmParamNames, lineFieldNames));
lineValues = struct2cell(p.Unmatched);
lineValues = lineValues(ismember(unmParamNames, lineFieldNames));
lineVarargin = reshape([lineFields, lineValues]', 1, []);
% Filter text properites from Unmatched
textFields = unmParamNames(ismember(unmParamNames, textFieldNames));
textValues = struct2cell(p.Unmatched);
textValues = textValues(ismember(unmParamNames, textFieldNames));
textVarargin = reshape([textFields, textValues]', 1, []);

%% Plot ct slice
matRad_cfg = MatRad_Config.instance();

% Flip axes direction
set(p.Results.axesHandle,'YDir','Reverse');
% plot ct slice
if p.Results.showCt
hCt = matRad_plotCtSlice(p.Results.axesHandle,p.Results.ct.cubeHU,p.Results.cubeIdx,p.Results.plane,p.Results.slice, [], []);
else
%figure()
end
hold on;

%% Plot dose
if ~isempty(p.Results.dose)
doseWindow = [min(min(min(p.Results.dose))) max(max(max(p.Results.dose)))];
if ~isempty(p.Results.doseWindow) && p.Results.doseWindow(2) - p.Results.doseWindow(1) <= 0
doseWindow = p.Results.doseWindow;

Check warning on line 137 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L137

Added line #L137 was not covered by tests
end
[hDose,doseColorMap,doseWindow] = matRad_plotDoseSlice(p.Results.axesHandle, p.Results.dose, p.Results.plane, p.Results.slice, p.Results.thresh, p.Results.alpha, p.Results.doseColorMap, doseWindow);
hold on;

%% Plot iso dose lines
hIsoDose = [];
if ~isempty(p.Results.doseIsoLevels)
hIsoDose = matRad_plotIsoDoseLines(p.Results.axesHandle,p.Results.dose,[],p.Results.doseIsoLevels,false,p.Results.plane,p.Results.slice,p.Results.doseColorMap,p.Results.doseWindow, lineVarargin{:});

Check warning on line 145 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L145

Added line #L145 was not covered by tests
end

%% Set Colorbar
hCMap = matRad_plotColorbar(p.Results.axesHandle,doseColorMap,doseWindow,'Location','EastOutside');
set(hCMap,'Color',matRad_cfg.gui.textColor);
if ~isempty(p.Results.colorBarLabel)
set(get(hCMap,'YLabel'),'String', p.Results.colorBarLabel,'FontSize',matRad_cfg.gui.fontSize);

Check warning on line 152 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L152

Added line #L152 was not covered by tests
end
set(get(hCMap,'YLabel'),'String', p.Results.colorBarLabel, textVarargin{:});
end
%% Plot VOI contours & Legend

if ~isempty(p.Results.cst)
[hContour,~] = matRad_plotVoiContourSlice(p.Results.axesHandle, p.Results.cst, p.Results.ct, p.Results.cubeIdx, p.Results.voiSelection, p.Results.plane, p.Results.slice, p.Results.contourColorMap, lineVarargin{:});

Check warning on line 159 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L159

Added line #L159 was not covered by tests

if p.Results.boolPlotLegend
visibleOnSlice = (~cellfun(@isempty,hContour));
hContourTmp = cellfun(@(X) X(1),hContour(visibleOnSlice),'UniformOutput',false);
voiSelection = visibleOnSlice;
if ~isempty(p.Results.voiSelection)
voiSelection = visibleOnSlice(find(p.Results.voiSelection));

Check warning on line 166 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L161-L166

Added lines #L161 - L166 were not covered by tests
end
hLegend = legend(p.Results.axesHandle,[hContourTmp{:}],[p.Results.cst(voiSelection,2)],'AutoUpdate','off','TextColor',matRad_cfg.gui.textColor);
set(hLegend,'Box','On');
set(hLegend,'TextColor',matRad_cfg.gui.textColor);
if ~isempty(textVarargin)
set(hLegend, textVarargin{:});

Check warning on line 172 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L168-L172

Added lines #L168 - L172 were not covered by tests
else
set(hLegend,'FontSize',matRad_cfg.gui.fontSize);

Check warning on line 174 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L174

Added line #L174 was not covered by tests
end
end
else
hContour = [];
end

%% Adjust axes
axis(p.Results.axesHandle,'tight');
set(p.Results.axesHandle,'xtick',[],'ytick',[]);
colormap(p.Results.axesHandle,p.Results.doseColorMap);
fontSize = [];
if isfield(p.Unmatched, 'FontSize')
fontSize = p.Unmatched.FontSize;

Check warning on line 187 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L187

Added line #L187 was not covered by tests
end
matRad_plotAxisLabels(p.Results.axesHandle,p.Results.ct,p.Results.plane,p.Results.slice, fontSize, [])

% Set axis ratio.
ratios = [1/p.Results.ct.resolution.x 1/p.Results.ct.resolution.y 1/p.Results.ct.resolution.z];

set(p.Results.axesHandle,'DataAspectRatioMode','manual');
if p.Results.plane == 1
res = [ratios(3) ratios(2)]./max([ratios(3) ratios(2)]);
set(p.Results.axesHandle,'DataAspectRatio',[res 1])
elseif p.Results.plane == 2 % sagittal plane
res = [ratios(3) ratios(1)]./max([ratios(3) ratios(1)]);
set(p.Results.axesHandle,'DataAspectRatio',[res 1])
elseif p.Results.plane == 3 % Axial plane
res = [ratios(2) ratios(1)]./max([ratios(2) ratios(1)]);
set(p.Results.axesHandle,'DataAspectRatio',[res 1])
end

%% Set text properties
if ~isempty(textVarargin)
set(p.Results.axesHandle, textVarargin{:})
set(p.Results.axesHandle.Title, textVarargin{:})

Check warning on line 209 in matRad/util/matRad_plotSlice.m

View check run for this annotation

Codecov / codecov/patch

matRad/util/matRad_plotSlice.m#L208-L209

Added lines #L208 - L209 were not covered by tests
end

end
73 changes: 4 additions & 69 deletions matRad/util/matRad_plotSliceWrapper.m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the commented code. We don't need it anymore.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

warning('Deprecation warning: matRad_plotSliceWrapper is deprecated. Using matRad_plot_Slice instead');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a dedicated matRad_cfg.dispDeprecationWarning(...) for this


% Handle the argument list
if ~exist('thresh','var') || isempty(thresh)
thresh = [];
Expand Down Expand Up @@ -101,76 +103,9 @@

matRad_cfg = MatRad_Config.instance();

set(axesHandle,'YDir','Reverse');
% plot ct slice
hCt = matRad_plotCtSlice(axesHandle,ct.cubeHU,cubeIdx,plane,slice);
hold on;

% plot dose
if ~isempty(doseWindow) && doseWindow(2) - doseWindow(1) <= 0
doseWindow = [0 2];
end

[hDose,doseColorMap,doseWindow] = matRad_plotDoseSlice(axesHandle,dose,plane,slice,thresh,alpha,doseColorMap,doseWindow);

% plot iso dose lines
if ~isempty(doseIsoLevels)
hIsoDose = matRad_plotIsoDoseLines(axesHandle,dose,[],doseIsoLevels,false,plane,slice,doseColorMap,doseWindow,varargin{:});
hold on;
else
hIsoDose = [];
end

%plot VOI contours
if ~isempty(cst)

[hContour,~] = matRad_plotVoiContourSlice(axesHandle,cst,ct,cubeIdx,voiSelection,plane,slice,contourColorMap,varargin{:});

if boolPlotLegend
visibleOnSlice = (~cellfun(@isempty,hContour));
ixLegend = find(voiSelection);
hContourTmp = cellfun(@(X) X(1),hContour(visibleOnSlice),'UniformOutput',false);
if ~isempty(voiSelection)
hLegend = legend(axesHandle,[hContourTmp{:}],[cst(ixLegend(visibleOnSlice),2)],'AutoUpdate','off','TextColor',matRad_cfg.gui.textColor);
else
hLegend = legend(axesHandle,[hContourTmp{:}],[cst(visibleOnSlice,2)],'AutoUpdate','off','TextColor',matRad_cfg.gui.textColor);
end
set(hLegend,'Box','Off');
set(hLegend,'TextColor',matRad_cfg.gui.textColor);
set(hLegend,'FontSize',matRad_cfg.gui.fontSize);
matRad_cfg.dispDeprecationWarning('Deprecation warning: matRad_plotSliceWrapper is deprecated. Using matRad_plot_Slice instead');

end
else
hContour = [];
end

axis(axesHandle,'tight');
set(axesHandle,'xtick',[],'ytick',[]);
colormap(axesHandle,doseColorMap);

matRad_plotAxisLabels(axesHandle,ct,plane,slice,[])

% set axis ratio

ratios = [1/ct.resolution.x 1/ct.resolution.y 1/ct.resolution.z];

set(axesHandle,'DataAspectRatioMode','manual');
if plane == 1
res = [ratios(3) ratios(2)]./max([ratios(3) ratios(2)]);
set(axesHandle,'DataAspectRatio',[res 1])
elseif plane == 2 % sagittal plane
res = [ratios(3) ratios(1)]./max([ratios(3) ratios(1)]);
set(axesHandle,'DataAspectRatio',[res 1])
elseif plane == 3 % Axial plane
res = [ratios(2) ratios(1)]./max([ratios(2) ratios(1)]);
set(axesHandle,'DataAspectRatio',[res 1])
end

hCMap = matRad_plotColorbar(axesHandle,doseColorMap,doseWindow,'Location','EastOutside');
set(hCMap,'Color',matRad_cfg.gui.textColor);
if ~isempty(colorBarLabel)
set(get(hCMap,'YLabel'),'String', colorBarLabel,'FontSize',matRad_cfg.gui.fontSize);
end
[hCMap,hDose,hCt,hContour,hIsoDose] = matRad_plotSlice(ct, 'axesHandle', axesHandle, 'cst', cst, 'cubeIdx', cubeIdx, 'dose', dose, 'plane', plane, 'slice', slice,'thresh', thresh, 'alpha', alpha, 'contourColorMap', contourColorMap, 'doseColorMap', doseColorMap, 'doseWindow', doseWindow, 'doseIsoLevels', doseIsoLevels, 'voiSelection', voiSelection, 'colorBarLabel', colorBarLabel, 'boolPlotLegend', boolPlotLegend, 'others', varargin);

end

Loading