916 lines
31 KiB
Matlab
916 lines
31 KiB
Matlab
function spider_plot(P, varargin)
|
|
%spider_plot Create a spider or radar plot with individual axes.
|
|
%
|
|
% Syntax:
|
|
% spider_plot(P)
|
|
% spider_plot(P, Name, Value, ...)
|
|
%
|
|
% Input Arguments:
|
|
% (Required)
|
|
% P - The data points used to plot the spider chart. The
|
|
% rows are the groups of data and the columns are the
|
|
% data points. The axes labels and axes limits are
|
|
% automatically generated if not specified.
|
|
% [vector | matrix]
|
|
%
|
|
% Name-Value Pair Arguments:
|
|
% (Optional)
|
|
% AxesLabels - Used to specify the label each of the axes.
|
|
% [auto-generated (default) | cell array of character vectors | 'none']
|
|
%
|
|
% AxesInterval - Used to change the number of intervals displayed
|
|
% between the webs.
|
|
% [3 (default) | integer]
|
|
%
|
|
% AxesPrecision - Used to change the precision level on the value
|
|
% displayed on the axes.
|
|
% [1 (default) | integer | vector]
|
|
%
|
|
% AxesDisplay - Used to change the number of axes in which the
|
|
% axes text are displayed. 'None' or 'one' can be used
|
|
% to simplify the plot appearance for normalized data.
|
|
% ['all' (default) | 'none' | 'one']
|
|
%
|
|
% AxesLimits - Used to manually set the axes limits. A matrix of
|
|
% 2 x size(P, 2). The top row is the minimum axes
|
|
% limits and the bottow row is the maximum axes limits.
|
|
% [auto-scaled (default) | matrix]
|
|
%
|
|
% FillOption - Used to toggle color fill option.
|
|
% ['off' (default) | 'on']
|
|
%
|
|
% FillTransparency - Used to set color fill transparency.
|
|
% [0.1 (default) | scalar in range (0, 1)]
|
|
%
|
|
% Color - Used to specify the line color, specified as an RGB
|
|
% triplet. The intensities must be in the range (0, 1).
|
|
% [MATLAB colors (default) | RGB triplet | hexadecimal color code]
|
|
%
|
|
% LineStyle - Used to change the line style of the plots.
|
|
% ['-' (default) | '--' | ':' | '-.' | 'none' | cell array of character vectors]
|
|
%
|
|
% LineWidth - Used to change the line width, where 1 point is
|
|
% 1/72 of an inch.
|
|
% [0.5 (default) | positive value | vector]
|
|
%
|
|
% Marker - Used to change the marker symbol of the plots.
|
|
% ['o' (default) | 'none' | '*' | 's' | 'd' | ... | cell array of character vectors]
|
|
%
|
|
% MarkerSize - Used to change the marker size, where 1 point is
|
|
% 1/72 of an inch.
|
|
% [8 (default) | positive value | vector]
|
|
%
|
|
% AxesFont - Used to change the font type of the values
|
|
% displayed on the axes.
|
|
% [Helvetica (default) | supported font name]
|
|
%
|
|
% LabelFont - Used to change the font type of the labels.
|
|
% [Helvetica (default) | supported font name]
|
|
%
|
|
% AxesFontSize - Used to change the font size of the values
|
|
% displayed on the axes.
|
|
% [10 (default) | scalar value greater than zero]
|
|
%
|
|
% LabelFontSize - Used to change the font size of the labels.
|
|
% [10 (default) | scalar value greater than zero]
|
|
%
|
|
% Direction - Used to change the direction of rotation of the
|
|
% plotted data and axis labels.
|
|
% ['clockwise' (default) | 'counterclockwise']
|
|
%
|
|
% AxesDirection - Used to change the direction of axes.
|
|
% ['normal' (default) | 'reverse' | cell array of character vectors]
|
|
%
|
|
% AxesLabelsOffset - Used to adjust the position offset of the axes
|
|
% labels.
|
|
% [0.1 (default) | positive value]
|
|
%
|
|
% AxesScaling - Used to change the scaling of the axes.
|
|
% ['linear' (default) | 'log' | cell array of character vectors]
|
|
%
|
|
% AxesColor - Used to change the color of the spider axes.
|
|
% [grey (default) | RGB triplet | hexadecimal color code]
|
|
%
|
|
% AxesLabelsEdge - Used to change the edge color of the axes labels.
|
|
% [black (default) | RGB triplet | hexadecimal color code | 'none']
|
|
%
|
|
% AxesOffset - Used to change to axes offset from the origin.
|
|
% [1 (default) | any integer less than the axes interval]
|
|
%
|
|
% Examples:
|
|
% % Example 1: Minimal number of arguments. All non-specified, optional
|
|
% arguments are set to their default values. Axes labels
|
|
% and limits are automatically generated and set.
|
|
%
|
|
% D1 = [5 3 9 1 2];
|
|
% D2 = [5 8 7 2 9];
|
|
% D3 = [8 2 1 4 6];
|
|
% P = [D1; D2; D3];
|
|
% spider_plot(P);
|
|
% legend('D1', 'D2', 'D3', 'Location', 'southoutside');
|
|
%
|
|
% % Example 2: Manually setting the axes limits and axes precision.
|
|
% All non-specified, optional arguments are set to their
|
|
% default values.
|
|
%
|
|
% D1 = [5 3 9 1 2];
|
|
% D2 = [5 8 7 2 9];
|
|
% D3 = [8 2 1 4 6];
|
|
% P = [D1; D2; D3];
|
|
% spider_plot(P,...
|
|
% 'AxesLimits', [1, 2, 1, 1, 1; 10, 8, 9, 5, 10],... % [min axes limits; max axes limits]
|
|
% 'AxesPrecision', [0, 1, 1, 1, 1]);
|
|
%
|
|
% % Example 3: Set fill option on. The fill transparency can be adjusted.
|
|
%
|
|
% D1 = [5 3 9 1 2];
|
|
% D2 = [5 8 7 2 9];
|
|
% D3 = [8 2 1 4 6];
|
|
% P = [D1; D2; D3];
|
|
% spider_plot(P,...
|
|
% 'AxesLabels', {'S1', 'S2', 'S3', 'S4', 'S5'},...
|
|
% 'AxesInterval', 2,...
|
|
% 'FillOption', 'on',...
|
|
% 'FillTransparency', 0.1);
|
|
%
|
|
% % Example 4: Maximum number of arguments.
|
|
%
|
|
% D1 = [5 3 9 1 2];
|
|
% D2 = [5 8 7 2 9];
|
|
% D3 = [8 2 1 4 6];
|
|
% P = [D1; D2; D3];
|
|
% spider_plot(P,...
|
|
% 'AxesLabels', {'S1', 'S2', 'S3', 'S4', 'S5'},...
|
|
% 'AxesInterval', 4,...
|
|
% 'AxesPrecision', 0,...
|
|
% 'AxesDisplay', 'one',...
|
|
% 'AxesLimits', [1, 2, 1, 1, 1; 10, 8, 9, 5, 10],...
|
|
% 'FillOption', 'on',...
|
|
% 'FillTransparency', 0.2,...
|
|
% 'Color', [1, 0, 0; 0, 1, 0; 0, 0, 1],...
|
|
% 'LineStyle', {'--', '-', '--'},...
|
|
% 'LineWidth', [1, 2, 3],...
|
|
% 'Marker', {'o', 'd', 's'},...
|
|
% 'MarkerSize', [8, 10, 12],...
|
|
% 'AxesFont', 'Times New Roman',...
|
|
% 'LabelFont', 'Times New Roman',...
|
|
% 'AxesFontSize', 12,...
|
|
% 'LabelFontSize', 10,...
|
|
% 'Direction', 'clockwise',...
|
|
% 'AxesDirection', {'reverse', 'normal', 'normal', 'normal', 'normal'},...
|
|
% 'AxesLabelsOffset', 0.1,...
|
|
% 'AxesScaling', 'linear',...
|
|
% 'AxesColor', [0.6, 0.6, 0.6],...
|
|
% 'AxesLabelsEdge', 'none',...
|
|
% 'AxesOffset', 1);
|
|
%
|
|
% % Example 5: Excel-like radar charts.
|
|
%
|
|
% D1 = [5 0 3 4 4];
|
|
% D2 = [2 1 5 5 4];
|
|
% P = [D1; D2];
|
|
% spider_plot(P,...
|
|
% 'AxesInterval', 5,...
|
|
% 'AxesPrecision', 0,...
|
|
% 'AxesDisplay', 'one',...
|
|
% 'AxesLimits', [0, 0, 0, 0, 0; 5, 5, 5, 5, 5],...
|
|
% 'FillOption', 'on',...
|
|
% 'FillTransparency', 0.1,...
|
|
% 'Color', [139, 0, 0; 240, 128, 128]/255,...
|
|
% 'LineWidth', 4,...
|
|
% 'Marker', 'none',...
|
|
% 'AxesFontSize', 14,...
|
|
% 'LabelFontSize', 10,...
|
|
% 'AxesColor', [0.8, 0.8, 0.8],...
|
|
% 'AxesLabelsEdge', 'none');
|
|
% title('Excel-like Radar Chart',...
|
|
% 'FontSize', 14);
|
|
% legend_str = {'D1', 'D2'};
|
|
% legend(legend_str, 'Location', 'southoutside');
|
|
%
|
|
% % Example 6: Logarithimic scale on specified axes. Axes limits and axes
|
|
% intervals can be individually set as well.
|
|
%
|
|
% D1 = [5 3 9 1 1];
|
|
% D2 = [5 8 7 2 10];
|
|
% D3 = [8 2 1 4 100];
|
|
% P = [D1; D2; D3];
|
|
% spider_plot(P,...
|
|
% 'AxesInterval', 2,...
|
|
% 'AxesPrecision', 0,...
|
|
% 'AxesFontSize', 10,...
|
|
% 'AxesLabels', {'Linear Scale', 'Linear Scale', 'Linear Scale', 'Linear Scale', 'Logarithimic Scale'},...
|
|
% 'AxesScaling', {'linear', 'linear', 'linear', 'linear', 'log'},...
|
|
% 'AxesLimits', [1, 1, 1, 1, 1; 10, 10, 10, 10, 100]);
|
|
% legend('D1', 'D2', 'D3', 'Location', 'northeast');
|
|
%
|
|
% % Example 7: Spider plot with subplot feature.
|
|
%
|
|
% D1 = [5 3 9 1 2];
|
|
% D2 = [5 8 7 2 9];
|
|
% D3 = [8 2 1 4 6];
|
|
% P = [D1; D2; D3];
|
|
% subplot(1, 2, 1)
|
|
% spider_plot(P,...
|
|
% 'AxesInterval', 1,...
|
|
% 'AxesPrecision', 0);
|
|
% subplot(1, 2, 2)
|
|
% spider_plot(P,...
|
|
% 'AxesInterval', 1,...
|
|
% 'AxesPrecision', 0);
|
|
%
|
|
% Author:
|
|
% Moses Yoo, (jyoo at hatci dot com)
|
|
% 2020-12-01: Added support for adjust the axes offset from origin.
|
|
% 2020-11-30: Allow for one data group without specified axes limits.
|
|
% 2020-11-30: Added support for changing axes and label font type.
|
|
% 2020-11-06: Fix bug in reverse axes direction feature.
|
|
% 2020-10-08: Adjust axes precision to be set to one or more axis.
|
|
% 2020-09-30: Updated examples and added ability to reverse axes direction.
|
|
% 2020-07-05: Added feature to change spider axes and axes labels edge color.
|
|
% 2020-06-17: Allow logarithmic scale to be set to one or more axis.
|
|
% 2020-03-26: Added feature to allow different line styles, line width,
|
|
% marker type, and marker sizes for the data groups.
|
|
% 2020-02-12: Fixed condition and added error checking for when only one
|
|
% data group is plotted.
|
|
% 2020-01-27: Corrected bug where only 7 entries were allowed in legend.
|
|
% 2020-01-06: Added support for subplot feature.
|
|
% 2019-11-27: Add option to change axes to logarithmic scale.
|
|
% 2019-11-15: Add feature to customize the plot rotational direction and
|
|
% the offset position of the axis labels.
|
|
% 2019-10-23: Minor revision to set starting axes as the vertical line.
|
|
% Add customization option for font sizes and axes display.
|
|
% 2019-10-16: Minor revision to add name-value pairs for customizing
|
|
% color, marker, and line settings.
|
|
% 2019-10-08: Another major revision to convert to name-value pairs and
|
|
% add color fill option.
|
|
% 2019-09-17: Major revision to improve speed, clarity, and functionality
|
|
%
|
|
% Special Thanks:
|
|
% Special thanks to Gabriela Andrade, Andrés Garcia, Alex Grenyer,
|
|
% Tobias Kern, Zafar Ali, Christophe Hurlin, Roman, Mariusz Sepczuk,
|
|
% Mohamed Abubakr, Nicolai, Jingwei Too, Cedric Jamet & Richard Ruff
|
|
% for their feature recommendations and suggested bug fixes.
|
|
|
|
%%% Data Properties %%%
|
|
% Point properties
|
|
[num_data_groups, num_data_points] = size(P);
|
|
|
|
% Number of optional arguments
|
|
numvarargs = length(varargin);
|
|
|
|
% Check for even number of name-value pair argments
|
|
if mod(numvarargs, 2) == 1
|
|
error('Error: Please check name-value pair arguments');
|
|
end
|
|
|
|
% Create default labels
|
|
axes_labels = cell(1, num_data_points);
|
|
|
|
% Iterate through number of data points
|
|
for ii = 1:num_data_points
|
|
% Default axes labels
|
|
axes_labels{ii} = sprintf('Label %i', ii);
|
|
end
|
|
|
|
% Default arguments
|
|
axes_interval = 3;
|
|
axes_precision = 1;
|
|
axes_display = 'all';
|
|
axes_limits = [];
|
|
fill_option = 'off';
|
|
fill_transparency = 0.2;
|
|
colors = lines(num_data_groups);
|
|
line_style = '-';
|
|
line_width = 2;
|
|
marker_type = 'o';
|
|
marker_size = 8;
|
|
axes_font = 'Helvetica';
|
|
label_font = 'Helvetica';
|
|
axes_font_size = 10;
|
|
label_font_size = 10;
|
|
direction = 'clockwise';
|
|
axes_direction = 'normal';
|
|
axes_labels_offset = 0.1;
|
|
axes_scaling = 'linear';
|
|
axes_color = [0.6, 0.6, 0.6];
|
|
axes_labels_edge = 'k';
|
|
axes_offset = 1;
|
|
|
|
% Check if optional arguments were specified
|
|
if numvarargs > 1
|
|
% Initialze name-value arguments
|
|
name_arguments = varargin(1:2:end);
|
|
value_arguments = varargin(2:2:end);
|
|
|
|
% Iterate through name-value arguments
|
|
for ii = 1:length(name_arguments)
|
|
% Set value arguments depending on name
|
|
switch lower(name_arguments{ii})
|
|
case 'axeslabels'
|
|
axes_labels = value_arguments{ii};
|
|
case 'axesinterval'
|
|
axes_interval = value_arguments{ii};
|
|
case 'axesprecision'
|
|
axes_precision = value_arguments{ii};
|
|
case 'axesdisplay'
|
|
axes_display = value_arguments{ii};
|
|
case 'axeslimits'
|
|
axes_limits = value_arguments{ii};
|
|
case 'filloption'
|
|
fill_option = value_arguments{ii};
|
|
case 'filltransparency'
|
|
fill_transparency = value_arguments{ii};
|
|
case 'color'
|
|
colors = value_arguments{ii};
|
|
case 'linestyle'
|
|
line_style = value_arguments{ii};
|
|
case 'linewidth'
|
|
line_width = value_arguments{ii};
|
|
case 'marker'
|
|
marker_type = value_arguments{ii};
|
|
case 'markersize'
|
|
marker_size = value_arguments{ii};
|
|
case 'axesfont'
|
|
axes_font = value_arguments{ii};
|
|
case 'labelfont'
|
|
label_font = value_arguments{ii};
|
|
case 'axesfontsize'
|
|
axes_font_size = value_arguments{ii};
|
|
case 'labelfontsize'
|
|
label_font_size = value_arguments{ii};
|
|
case 'direction'
|
|
direction = value_arguments{ii};
|
|
case 'axesdirection'
|
|
axes_direction = value_arguments{ii};
|
|
case 'axeslabelsoffset'
|
|
axes_labels_offset = value_arguments{ii};
|
|
case 'axesscaling'
|
|
axes_scaling = value_arguments{ii};
|
|
case 'axescolor'
|
|
axes_color = value_arguments{ii};
|
|
case 'axeslabelsedge'
|
|
axes_labels_edge = value_arguments{ii};
|
|
case 'axesoffset'
|
|
axes_offset = value_arguments{ii};
|
|
otherwise
|
|
error('Error: Please enter in a valid name-value pair.');
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
%%% Error Check %%%
|
|
% Check if axes labels is a cell
|
|
if iscell(axes_labels)
|
|
% Check if the axes labels are the same number as the number of points
|
|
if length(axes_labels) ~= num_data_points
|
|
error('Error: Please make sure the number of labels is the same as the number of points.');
|
|
end
|
|
else
|
|
% Check if valid char entry
|
|
if ~contains(axes_labels, 'none')
|
|
error('Error: Please enter in valid labels or "none" to remove labels.');
|
|
end
|
|
end
|
|
|
|
% Check if axes limits is not empty
|
|
if ~isempty(axes_limits)
|
|
% Check if the axes limits same length as the number of points
|
|
if size(axes_limits, 1) ~= 2 || size(axes_limits, 2) ~= num_data_points
|
|
error('Error: Please make sure the min and max axes limits match the number of data points.');
|
|
end
|
|
|
|
% Lower and upper limits
|
|
lower_limits = axes_limits(1, :);
|
|
upper_limits = axes_limits(2, :);
|
|
|
|
% Difference in upper and lower limits
|
|
diff_limits = upper_limits - lower_limits;
|
|
|
|
% Check to make sure upper limit is greater than lower limit
|
|
if any(diff_limits < 0)
|
|
error('Error: Please make sure max axes limits are greater than the min axes limits.');
|
|
end
|
|
|
|
% Check the range of axes limits
|
|
if any(diff_limits == 0)
|
|
error('Error: Please make sure the min and max axes limits are different.');
|
|
end
|
|
end
|
|
|
|
% Check if axes precision is numeric
|
|
if isnumeric(axes_precision)
|
|
% Check is length is one
|
|
if length(axes_precision) == 1
|
|
% Repeat array to number of data points
|
|
axes_precision = repmat(axes_precision, num_data_points, 1);
|
|
elseif length(axes_precision) ~= num_data_points
|
|
error('Error: Please specify the same number of axes precision as number of data points.');
|
|
end
|
|
else
|
|
error('Error: Please make sure the axes precision is a numeric value.');
|
|
end
|
|
|
|
% Check if axes properties are an integer
|
|
if floor(axes_interval) ~= axes_interval || any(floor(axes_precision) ~= axes_precision)
|
|
error('Error: Please enter in an integer for the axes properties.');
|
|
end
|
|
|
|
% Check if axes properties are positive
|
|
if axes_interval < 1 || any(axes_precision < 0)
|
|
error('Error: Please enter a positive value for the axes properties.');
|
|
end
|
|
|
|
% Check if axes display is valid char entry
|
|
if ~ismember(axes_display, {'all', 'none', 'one'})
|
|
error('Error: Invalid axes display entry. Please enter in "all", "none", or "one" to set axes text.');
|
|
end
|
|
|
|
% Check if not a valid fill option arguement
|
|
if ~ismember(fill_option, {'off', 'on'})
|
|
error('Error: Please enter either "off" or "on" for fill option.');
|
|
end
|
|
|
|
% Check if fill transparency is valid
|
|
if fill_transparency < 0 || fill_transparency > 1
|
|
error('Error: Please enter a transparency value between [0, 1].');
|
|
end
|
|
|
|
% Check if font size is greater than zero
|
|
if axes_font_size <= 0 || label_font_size <= 0
|
|
error('Error: Please enter a font size greater than zero.');
|
|
end
|
|
|
|
% Check if direction is valid char entry
|
|
if ~ismember(direction, {'counterclockwise', 'clockwise'})
|
|
error('Error: Invalid direction entry. Please enter in "counterclockwise" or "clockwise" to set direction of rotation.');
|
|
end
|
|
|
|
% Check if axes direction is valid char entry
|
|
if ~ismember(axes_direction, {'normal', 'reverse'})
|
|
error('Error: Invalid axes direction entry. Please enter in "normal" or "reverse" to set axes direction.');
|
|
end
|
|
|
|
% Check if axes labels offset is positive
|
|
if axes_labels_offset < 0
|
|
error('Error: Please enter a positive for the axes labels offset.');
|
|
end
|
|
|
|
% Check if axes scaling is valid
|
|
if any(~ismember(axes_scaling, {'linear', 'log'}))
|
|
error('Error: Invalid axes scaling entry. Please enter in "linear" or "log" to set axes scaling.');
|
|
end
|
|
|
|
% Check if axes offset is valid
|
|
if floor(axes_offset)~=axes_offset || axes_offset < 0 || axes_offset > axes_interval
|
|
error('Error: Invalid axes offset entry. Please enter in an integer value that is between [0, axes_interval].');
|
|
end
|
|
|
|
% Check if axes scaling is a cell
|
|
if iscell(axes_scaling)
|
|
% Check is length is one
|
|
if length(axes_scaling) == 1
|
|
% Repeat array to number of data groups
|
|
axes_scaling = repmat(axes_scaling, num_data_points, 1);
|
|
elseif length(axes_scaling) ~= num_data_points
|
|
error('Error: Please specify the same number of axes scaling as number of data points.');
|
|
end
|
|
else
|
|
% Repeat array to number of data groups
|
|
axes_scaling = repmat({axes_scaling}, num_data_points, 1);
|
|
end
|
|
|
|
% Check if line style is a char
|
|
if ischar(line_style)
|
|
% Convert to cell array of char
|
|
line_style = cellstr(line_style);
|
|
|
|
% Repeat cell to number of data groups
|
|
line_style = repmat(line_style, num_data_groups, 1);
|
|
elseif iscellstr(line_style)
|
|
% Check is length is one
|
|
if length(line_style) == 1
|
|
% Repeat cell to number of data groups
|
|
line_style = repmat(line_style, num_data_groups, 1);
|
|
elseif length(line_style) ~= num_data_groups
|
|
error('Error: Please specify the same number of line styles as number of data groups.');
|
|
end
|
|
else
|
|
error('Error: Please make sure the line style is a char or a cell array of char.');
|
|
end
|
|
|
|
% Check if line width is numeric
|
|
if isnumeric(line_width)
|
|
% Check is length is one
|
|
if length(line_width) == 1
|
|
% Repeat array to number of data groups
|
|
line_width = repmat(line_width, num_data_groups, 1);
|
|
elseif length(line_width) ~= num_data_groups
|
|
error('Error: Please specify the same number of line width as number of data groups.');
|
|
end
|
|
else
|
|
error('Error: Please make sure the line width is a numeric value.');
|
|
end
|
|
|
|
% Check if marker type is a char
|
|
if ischar(marker_type)
|
|
% Convert to cell array of char
|
|
marker_type = cellstr(marker_type);
|
|
|
|
% Repeat cell to number of data groups
|
|
marker_type = repmat(marker_type, num_data_groups, 1);
|
|
elseif iscellstr(marker_type)
|
|
% Check is length is one
|
|
if length(marker_type) == 1
|
|
% Repeat cell to number of data groups
|
|
marker_type = repmat(marker_type, num_data_groups, 1);
|
|
elseif length(marker_type) ~= num_data_groups
|
|
error('Error: Please specify the same number of line styles as number of data groups.');
|
|
end
|
|
else
|
|
error('Error: Please make sure the line style is a char or a cell array of char.');
|
|
end
|
|
|
|
% Check if line width is numeric
|
|
if isnumeric(marker_size)
|
|
if length(marker_size) == 1
|
|
% Repeat array to number of data groups
|
|
marker_size = repmat(marker_size, num_data_groups, 1);
|
|
elseif length(marker_size) ~= num_data_groups
|
|
error('Error: Please specify the same number of line width as number of data groups.');
|
|
end
|
|
else
|
|
error('Error: Please make sure the line width is numeric.');
|
|
end
|
|
|
|
% Check if axes direction is a cell
|
|
if iscell(axes_direction)
|
|
% Check is length is one
|
|
if length(axes_direction) == 1
|
|
% Repeat array to number of data groups
|
|
axes_direction = repmat(axes_direction, num_data_points, 1);
|
|
elseif length(axes_direction) ~= num_data_points
|
|
error('Error: Please specify the same number of axes direction as number of data points.');
|
|
end
|
|
else
|
|
% Repeat array to number of data groups
|
|
axes_direction = repmat({axes_direction}, num_data_points, 1);
|
|
end
|
|
|
|
%%% Axes Scaling Properties %%%
|
|
% Check axes scaling option
|
|
log_index = strcmp(axes_scaling, 'log');
|
|
|
|
% If any log scaling is specified
|
|
if any(log_index)
|
|
% Initialize copy
|
|
P_log = P(:, log_index);
|
|
|
|
% Logarithm of base 10, account for numbers less than 1
|
|
P_log = sign(P_log) .* log10(abs(P_log));
|
|
|
|
% Minimum and maximun log limits
|
|
min_limit = min(min(fix(P_log)));
|
|
max_limit = max(max(ceil(P_log)));
|
|
recommended_axes_interval = max_limit - min_limit;
|
|
|
|
% Warning message
|
|
warning('For the log scale values, recommended axes limit is [%i, %i] with an axes interval of %i.',...
|
|
10^min_limit, 10^max_limit, recommended_axes_interval);
|
|
|
|
% Replace original
|
|
P(:, log_index) = P_log;
|
|
end
|
|
|
|
%%% Figure Properties %%%
|
|
% Grab current figure
|
|
fig = gcf;
|
|
|
|
% Set figure background
|
|
fig.Color = 'white';
|
|
|
|
% Reset axes
|
|
cla reset;
|
|
|
|
% Current axes handle
|
|
ax = gca;
|
|
|
|
% Axis limits
|
|
hold on;
|
|
axis square;
|
|
axis([-1, 1, -1, 1] * 1.3);
|
|
|
|
% Axis properties
|
|
ax.XTickLabel = [];
|
|
ax.YTickLabel = [];
|
|
ax.XColor = 'none';
|
|
ax.YColor = 'none';
|
|
|
|
% Polar increments
|
|
theta_increment = 2*pi/num_data_points;
|
|
full_interval = axes_interval + 1;
|
|
rho_offset = axes_offset/full_interval;
|
|
|
|
%%% Scale Data %%%
|
|
% Pre-allocation
|
|
P_scaled = zeros(size(P));
|
|
axes_range = zeros(3, num_data_points);
|
|
|
|
% Check axes scaling option
|
|
axes_direction_index = strcmp(axes_direction, 'reverse');
|
|
|
|
% Iterate through number of data points
|
|
for ii = 1:num_data_points
|
|
% Check for one data group and no axes limits
|
|
if num_data_groups == 1 && isempty(axes_limits)
|
|
% Group of points
|
|
group_points = P(:, :);
|
|
else
|
|
% Group of points
|
|
group_points = P(:, ii);
|
|
end
|
|
|
|
% Check for log axes scaling option
|
|
if log_index(ii)
|
|
% Minimum and maximun log limits
|
|
min_value = min(fix(group_points));
|
|
max_value = max(ceil(group_points));
|
|
else
|
|
% Automatically the range of each group
|
|
min_value = min(group_points);
|
|
max_value = max(group_points);
|
|
end
|
|
|
|
% Range of min and max values
|
|
range = max_value - min_value;
|
|
|
|
% Check if axes_limits is not empty
|
|
if ~isempty(axes_limits)
|
|
% Check for log axes scaling option
|
|
if log_index(ii)
|
|
% Logarithm of base 10, account for numbers less than 1
|
|
axes_limits(:, ii) = sign(axes_limits(:, ii)) .* log10(abs(axes_limits(:, ii))); %#ok<AGROW>
|
|
end
|
|
|
|
% Manually set the range of each group
|
|
min_value = axes_limits(1, ii);
|
|
max_value = axes_limits(2, ii);
|
|
range = max_value - min_value;
|
|
|
|
% Check if the axes limits are within range of points
|
|
if min_value > min(group_points) || max_value < max(group_points)
|
|
error('Error: Please make the manually specified axes limits are within range of the data points.');
|
|
end
|
|
end
|
|
|
|
% Scale points to range from [0, 1]
|
|
P_scaled(:, ii) = ((P(:, ii) - min_value) / range);
|
|
|
|
% If reverse axes direction is specified
|
|
if axes_direction_index(ii)
|
|
% Store to array
|
|
axes_range(:, ii) = [max_value; min_value; range];
|
|
P_scaled(:, ii) = -(P_scaled(:, ii) - 1);
|
|
else
|
|
% Store to array
|
|
axes_range(:, ii) = [min_value; max_value; range];
|
|
end
|
|
|
|
% Add offset of [rho_offset] and scaling factor of [1 - rho_offset]
|
|
P_scaled(:, ii) = P_scaled(:, ii) * (1 - rho_offset) + rho_offset;
|
|
end
|
|
|
|
%%% Polar Axes %%%
|
|
% Polar coordinates
|
|
rho_increment = 1/full_interval;
|
|
rho = 0:rho_increment:1;
|
|
|
|
% Check rotational direction
|
|
switch direction
|
|
case 'counterclockwise'
|
|
% Shift by pi/2 to set starting axis the vertical line
|
|
theta = (0:theta_increment:2*pi) + (pi/2);
|
|
case 'clockwise'
|
|
% Shift by pi/2 to set starting axis the vertical line
|
|
theta = (0:-theta_increment:-2*pi) + (pi/2);
|
|
end
|
|
|
|
% Remainder after using a modulus of 2*pi
|
|
theta = mod(theta, 2*pi);
|
|
|
|
% Iterate through each theta
|
|
for ii = 1:length(theta)-1
|
|
% Convert polar to cartesian coordinates
|
|
[x_axes, y_axes] = pol2cart(theta(ii), rho);
|
|
|
|
% Plot webs
|
|
h = plot(x_axes, y_axes,...
|
|
'LineWidth', 1.5,...
|
|
'Color', axes_color);
|
|
|
|
% Turn off legend annotation
|
|
h.Annotation.LegendInformation.IconDisplayStyle = 'off';
|
|
end
|
|
|
|
% Iterate through each rho
|
|
for ii = 2:length(rho)
|
|
% Convert polar to cartesian coordinates
|
|
[x_axes, y_axes] = pol2cart(theta, rho(ii));
|
|
|
|
% Plot axes
|
|
h = plot(x_axes, y_axes,...
|
|
'Color', axes_color);
|
|
|
|
% Turn off legend annotation
|
|
h.Annotation.LegendInformation.IconDisplayStyle = 'off';
|
|
end
|
|
|
|
% Set end index depending on axes display argument
|
|
switch axes_display
|
|
case 'all'
|
|
theta_end_index = length(theta)-1;
|
|
case 'one'
|
|
theta_end_index = 1;
|
|
case 'none'
|
|
theta_end_index = 0;
|
|
end
|
|
|
|
% Rho start index and offset interval
|
|
rho_start_index = axes_offset+1;
|
|
offset_interval = full_interval - axes_offset;
|
|
|
|
% Iterate through each theta
|
|
for ii = 1:theta_end_index
|
|
% Convert polar to cartesian coordinates
|
|
[x_axes, y_axes] = pol2cart(theta(ii), rho);
|
|
|
|
% Iterate through points on isocurve
|
|
for jj = rho_start_index:length(rho)
|
|
% Axes increment range
|
|
min_value = axes_range(1, ii);
|
|
range = axes_range(3, ii);
|
|
|
|
% If reverse axes direction is specified
|
|
if axes_direction_index(ii)
|
|
% Axes increment value
|
|
axes_value = min_value - (range/offset_interval) * (jj-rho_start_index);
|
|
else
|
|
% Axes increment value
|
|
axes_value = min_value + (range/offset_interval) * (jj-rho_start_index);
|
|
end
|
|
|
|
% Check for log axes scaling option
|
|
if log_index(ii)
|
|
% Exponent to the tenth power
|
|
axes_value = 10^axes_value;
|
|
end
|
|
|
|
% Display axes text
|
|
text_str = sprintf(sprintf('%%.%if', axes_precision(ii)), axes_value);
|
|
text(x_axes(jj), y_axes(jj), text_str,...
|
|
'Units', 'Data',...
|
|
'Color', 'k',...
|
|
'FontName', axes_font,...
|
|
'FontSize', axes_font_size,...
|
|
'HorizontalAlignment', 'center',...
|
|
'VerticalAlignment', 'middle');
|
|
end
|
|
end
|
|
|
|
%%% Plot %%%
|
|
% Iterate through number of data groups
|
|
for ii = 1:num_data_groups
|
|
% Convert polar to cartesian coordinates
|
|
[x_points, y_points] = pol2cart(theta(1:end-1), P_scaled(ii, :));
|
|
|
|
% Make points circular
|
|
x_circular = [x_points, x_points(1)];
|
|
y_circular = [y_points, y_points(1)];
|
|
|
|
% Plot data points
|
|
plot(x_circular, y_circular,...
|
|
'LineStyle', line_style{ii},...
|
|
'Marker', marker_type{ii},...
|
|
'Color', colors(ii, :),...
|
|
'LineWidth', line_width(ii),...
|
|
'MarkerSize', marker_size(ii),...
|
|
'MarkerFaceColor', colors(ii, :));
|
|
|
|
% Check if fill option is toggled on
|
|
if strcmp(fill_option, 'on')
|
|
% Fill area within polygon
|
|
h = patch(x_circular, y_circular, colors(ii, :),...
|
|
'EdgeColor', 'none',...
|
|
'FaceAlpha', fill_transparency);
|
|
|
|
% Turn off legend annotation
|
|
h.Annotation.LegendInformation.IconDisplayStyle = 'off';
|
|
end
|
|
end
|
|
|
|
% Find object handles
|
|
text_handles = findobj(ax.Children,...
|
|
'Type', 'Text');
|
|
patch_handles = findobj(ax.Children,...
|
|
'Type', 'Patch');
|
|
isocurve_handles = findobj(ax.Children,...
|
|
'Color', axes_color,...
|
|
'-and', 'Type', 'Line');
|
|
plot_handles = findobj(ax.Children, '-not',...
|
|
'Color', axes_color,...
|
|
'-and', 'Type', 'Line');
|
|
|
|
% Manually set the stack order
|
|
uistack(text_handles, 'bottom');
|
|
uistack(plot_handles, 'bottom');
|
|
uistack(patch_handles, 'bottom');
|
|
uistack(isocurve_handles, 'bottom');
|
|
|
|
%%% Labels %%%
|
|
% Check labels argument
|
|
if ~strcmp(axes_labels, 'none')
|
|
% Convert polar to cartesian coordinates
|
|
[x_axes, y_axes] = pol2cart(theta, rho(end));
|
|
|
|
% Iterate through number of data points
|
|
for ii = 1:length(axes_labels)
|
|
% Angle of point in radians
|
|
theta_point = theta(ii);
|
|
|
|
% Find out which quadrant the point is in
|
|
if theta_point == 0
|
|
quadrant = 0;
|
|
elseif theta_point == pi/2
|
|
quadrant = 1.5;
|
|
elseif theta_point == pi
|
|
quadrant = 2.5;
|
|
elseif theta_point == 3*pi/2
|
|
quadrant = 3.5;
|
|
elseif theta_point == 2*pi
|
|
quadrant = 0;
|
|
elseif theta_point > 0 && theta_point < pi/2
|
|
quadrant = 1;
|
|
elseif theta_point > pi/2 && theta_point < pi
|
|
quadrant = 2;
|
|
elseif theta_point > pi && theta_point < 3*pi/2
|
|
quadrant = 3;
|
|
elseif theta_point > 3*pi/2 && theta_point < 2*pi
|
|
quadrant = 4;
|
|
end
|
|
|
|
% Adjust label alignment depending on quadrant
|
|
switch quadrant
|
|
case 0
|
|
horz_align = 'left';
|
|
vert_align = 'middle';
|
|
x_pos = axes_labels_offset;
|
|
y_pos = 0;
|
|
case 1
|
|
horz_align = 'left';
|
|
vert_align = 'bottom';
|
|
x_pos = axes_labels_offset;
|
|
y_pos = axes_labels_offset;
|
|
case 1.5
|
|
horz_align = 'center';
|
|
vert_align = 'bottom';
|
|
x_pos = 0;
|
|
y_pos = axes_labels_offset;
|
|
case 2
|
|
horz_align = 'right';
|
|
vert_align = 'bottom';
|
|
x_pos = -axes_labels_offset;
|
|
y_pos = axes_labels_offset;
|
|
case 2.5
|
|
horz_align = 'right';
|
|
vert_align = 'middle';
|
|
x_pos = -axes_labels_offset;
|
|
y_pos = 0;
|
|
case 3
|
|
horz_align = 'right';
|
|
vert_align = 'top';
|
|
x_pos = -axes_labels_offset;
|
|
y_pos = -axes_labels_offset;
|
|
case 3.5
|
|
horz_align = 'center';
|
|
vert_align = 'top';
|
|
x_pos = 0;
|
|
y_pos = -axes_labels_offset;
|
|
case 4
|
|
horz_align = 'left';
|
|
vert_align = 'top';
|
|
x_pos = axes_labels_offset;
|
|
y_pos = -axes_labels_offset;
|
|
end
|
|
|
|
% Display text label
|
|
text(x_axes(ii)+x_pos, y_axes(ii)+y_pos, axes_labels{ii},...
|
|
'Units', 'Data',...
|
|
'HorizontalAlignment', horz_align,...
|
|
'VerticalAlignment', vert_align,...
|
|
'EdgeColor', axes_labels_edge,...
|
|
'BackgroundColor', 'w',...
|
|
'FontName', label_font,...
|
|
'FontSize', label_font_size);
|
|
end
|
|
end |