├── LICENSE.txt
├── README.md
├── screenshot
├── example1.PNG
├── example10.PNG
├── example11.png
├── example12.png
├── example13.PNG
├── example14.PNG
├── example15.PNG
├── example16.PNG
├── example17.PNG
├── example18.PNG
├── example2.png
├── example3.PNG
├── example4.png
├── example5.png
├── example6.png
├── example7.PNG
├── example7b.PNG
├── example8.PNG
└── example9.png
├── spider_plot.m
├── spider_plot_R2019b.m
├── spider_plot_R2019b_examples.mlx
├── spider_plot_class.m
├── spider_plot_class_examples.mlx
└── spider_plot_examples.mlx
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020-2023, Moses
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the distribution
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.mathworks.com/matlabcentral/fileexchange/59561-spider_plot)
2 | [](https://matlab.mathworks.com/open/github/v1?repo=NewGuy012/spider_plot)
3 |
4 | # spider_plot
5 | Create a spider or radar plot with customizable individual axes.
6 |
7 | The three functions included have the same functionality but with 3 different implementation:
8 | - **spider_plot()** is compatible with most MATLAB versions.
9 | - **spider_plot_R2019b()** is compatible with R2019b and above. It uses the new argument validation feature.
10 | - **spider_plot_class()** is compatible with R2019b and above. It uses the new chart class feature.
11 |
12 | ## Syntax:
13 | **spider_plot(P)**
14 |
15 | **spider_plot(P, Name, Value, ...)**
16 |
17 | **h = spider_plot(_)**
18 |
19 | ## Input Arguments:
20 | *(Required)*
21 |
22 | - **P** - The data points used to plot the spider chart. The rows are the groups of data and the columns are the data points.
23 | The axes labels and axes limits are automatically generated if not specified.
24 | [vector | matrix]
25 |
26 | ## Output Arguments:
27 | *(Optional)*
28 | - **h** - Figure handle of spider plot.
29 | [figure object]
30 |
31 | ## Name-Value Pair Arguments:
32 | *(Optional)*
33 |
34 | - **AxesLabels** - Used to specify the label each of the axes.
35 | [auto-generated (default) | cell of strings | 'none']
36 |
37 | - **AxesInterval** - Used to change the number of intervals displayed between the webs.
38 | [3 (default) | integer]
39 |
40 | - **AxesPrecision** - Used to change the precision level on the value displayed on the axes.
41 | [1 (default) | integer | vector]
42 |
43 | - **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.
44 | ['all' (default) | 'none' | 'one' | 'data' | 'data-percent']
45 |
46 | - **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.
47 | [auto-scaled (default) | matrix]
48 |
49 | - **FillOption** - Used to toggle fill color option.
50 | ['off' (default) | 'on' | cell array of character vectors]
51 |
52 | - **FillTransparency** - Used to set fill color transparency.
53 | [0.1 (default) | scalar in range (0, 1) | vector]
54 |
55 | - **Color** - Used to specify the line color, specified as an RGB triplet. The intensities must be in the range (0, 1).
56 | [MATLAB colors (default) | RGB triplet]
57 |
58 | - **LineStyle** - Used to change the line style of the plots.
59 | ['-' (default) | '--' | ':' | '-.' | 'none' | cell array of character vectors]
60 |
61 | - **LineWidth** - Used to change the line width, where 1 point is 1/72 of an inch.
62 | [0.5 (default) | positive value | vector]
63 |
64 | - **LineTransparency** - Used to set the line color transparency.
65 | [1 (default) | scalar in range (0, 1) | vector]
66 |
67 | - **Marker** - Used to change the marker symbol of the plots.
68 | ['o' (default) | '*' | 's' | 'd' | '+' | '.' | 'x' | '^' | 'v' | 'h' | 'none' | cell array of character vectors]
69 |
70 | - **MarkerSize** - Used to change the marker size, where 1 point is 1/72 of an inch.
71 | [36 (default) | positive value | vector]
72 |
73 | - **MarkerTransparency** - Used to set the marker color transparency.
74 | [1 (default) | scalar in range (0, 1) | vector]
75 |
76 | - **AxesFont** - Used to change the font type of the values displayed on the axes.
77 | [Helvetica (default) | supported font name]
78 |
79 | - **LabelFont** - Used to change the font type of the labels.
80 | [Helvetica (default) | supported font name]
81 |
82 | - **AxesFontSize** - Used to change the font size of the values displayed on the axes.
83 | [10 (default) | scalar value greater than zero]
84 |
85 | - **AxesFontColor** - Used to change the font color of the values displayed on the axes.
86 | [black (default) | RGB triplet]
87 |
88 | - **LabelFontSize** - Used to change the font size of the labels.
89 | [10 (default) | scalar value greater than zero]
90 |
91 | - **Direction** - Used to change the direction of rotation of the plotted data and axis labels.
92 | ['clockwise' (default) | 'counterclockwise']
93 |
94 | - **AxesDirection** - Used to change the direction of axes.
95 | ['normal' (default) | 'reverse' | cell array of character vectors]
96 |
97 | - **AxesLabelsOffset** - Used to adjust the position offset of the axes labels.
98 | [0.2 (default) | positive value]
99 |
100 | - **AxesDataOffset** - Used to adjust the position offset of the data labels when AxesDisplay is set to 'data'.
101 | [0.1 (default) | positive value]
102 |
103 | - **AxesScaling** - Used to change the scaling of the axes.
104 | ['linear' (default) | 'log' | cell array of character vectors]
105 |
106 | - **AxesColor** - Used to change the color of the spider axes.
107 | [grey (default) | RGB triplet | hexadecimal color code]
108 |
109 | - **AxesLabelsEdge** - Used to change the edge color of the axes labels.
110 | [black (default) | RGB triplet | hexadecimal color code | 'none']
111 |
112 | - **AxesOffset** - Used to change to axes offset from the origin.
113 | [1 (default) | any integer less than the axes interval]
114 |
115 | - **AxesZoom** - Used to change zoom of axes.
116 | [0.7 (default) | scalar in range (0, 1)]
117 |
118 | - **AxesHorzAlign** - Used to change the horizontal alignment of axes tick labels.
119 | ['center' (default) | 'left' | 'right' | 'quadrant']
120 |
121 | - **AxesVertAlign** - Used to change the vertical aligment of axes tick labels.
122 | ['middle' (default) | 'top' | 'cap' | 'bottom' | 'baseline' | 'quadrant']
123 |
124 | - **PlotVisible** - Used to change the visibility of the plotted lines and markers.
125 | ['on' (default) | 'off']
126 |
127 | - **AxesTickLabels** - Used to change the axes tick labels.
128 | ['data' (default) | cell array of character vectors]
129 |
130 | - **AxesInterpreter** - Used to change the text interpreter of axes labels and axes tick labels.
131 | ['tex' (default) | 'latex' | 'none' | cell array of character vectors]
132 |
133 | - **BackgroundColor** - Used to change the color of the background.
134 | ['white' (default) | RGB triplet | hexadecimal color code | 'r' | 'g' | 'b' | ...]
135 |
136 | - **MinorGrid** - Used to toggle the minor grid.
137 | ['off' (default) | 'on']
138 |
139 | - **MinorGridInterval**- Used to change number of minor grid lines in between the major grid lines.
140 | [2 (default) | integer value greater than zero]
141 |
142 | - **AxesZero** - Used to add a reference axes at value zero.
143 | ['off' (default) | 'on']
144 |
145 | - **AxesZeroColor** - Used to change the color of the zero reference axes.
146 | ['black' (default) | RGB triplet | hexadecimal color code | 'r' | 'g' | 'b' | ...]
147 |
148 | - **AxesZeroWidth** - Used to change the line width of the zero reference axes.
149 | [2 (default) | positive value]
150 |
151 | - **AxesRadial** - Used to toggle radial axes.
152 | ['on' (default) | 'off']
153 |
154 | - **AxesWeb** - Used to toggle web axes.
155 | ['on' (default) | 'off']
156 |
157 | - **AxesShaded** - Used to toggle shaded area around axes.
158 | ['off' (default) | 'on']
159 |
160 | - **AxesShadedLimits** - Used to set the limits of the shaded area. 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.
161 | [AxesLimits (default) | cell array]
162 |
163 | - **AxesShadedColor** - Used to change the color of the shaded area.
164 | ['green' (default) | RGB triplet | hexadecimal color code | 'r' | 'g' | 'b' | cell array]
165 |
166 | - **AxesShadedTransparency**- Used to the shaded area transparency.
167 | [0.2 (default) | vector in range (0, 1)]
168 |
169 | - **AxesLabelsRotate** - Used to rotate the axes labels to be aligned with axes.
170 | ['off' (default) | 'on']
171 |
172 | - **AxesHandle** - Used to specify the axes to plot in.
173 | [new axes (default) | Axes object]
174 |
175 | - **ErrorBars** - Used to toggle error bars mode with +/- standard deviation end points.
176 | ['off' (default) | 'on']
177 |
178 | - **AxesWebType** - Used to set the type of web drawn.
179 | ['web' (default) | 'circular']
180 |
181 | - **AxesTickFormat** - Used to format the axes tick text. Overrides 'AxesPrecision' value.
182 | ['%.2f' (default) | cell array of character vectors]
183 |
184 | - **FillCData** - Used to set the fill colors when fill option is set to 'interp'.
185 | [empty (default) | vector]
186 |
187 | - **ErrorPositive** - Used to set the error bar length in positive direction when 'ErrorBars' is set to 'on'.
188 | [empty (default) | vector]
189 |
190 | - **ErrorNegative** - Used to set the error bar length in negative direction when 'ErrorBars' is set to 'on'.
191 | [empty (default) | vector]
192 |
193 | - **AxesStart** - Used to set the initial starting point of axes. Specify a value between [0, 2pi].
194 | [pi/2 (default) | scalar]
195 |
196 | - **AxesRadialLineWidth** - Used to set the line width of the radial axes.
197 | [1.5 | scalar]
198 |
199 | - **AxesRadialLineStyle** - Used to set the line style of the radial axes.
200 | ['-' (default) | '--' | ':' | '-.' | 'none' | cell array of character vectors]
201 |
202 | - **AxesWebLineWidth** - Used to set the line width of the axes web.
203 | [1 | scalar]
204 |
205 | - **AxesWebLineStyle** - Used to set the line style of the axes web.
206 | ['-' (default) | '--' | ':' | '-.' | 'none' | cell array of character vectors]
207 |
208 | *(spider_plot_class only properties)*
209 | - **LegendLabels** - Used to add the labels to the legend.
210 | [cell array of character vectors]
211 |
212 | - **LegendHandle** - Used to customize legend settings.
213 | [legend handle object]
214 |
215 | - **TiledLayoutHandle** - Used to customize tiled layout settings.
216 | [tiled chart layout handle object]
217 |
218 | - **TiledLegendHandle** - Used to customize tiled legend settings.
219 | [legend handle object of tiled layout]
220 |
221 | - **NextTileIter** - Iterates with consecutive tile plots.
222 | [1 (default)]
223 |
224 | ## Examples:
225 | ### Example 1: Minimal number of arguments. All optional arguments are set to their default values. Axes labels and limits are automatically set.
226 | ```matlab
227 | % Initialize data points
228 | D1 = [5 3 9 1 2];
229 | D2 = [5 8 7 2 9];
230 | D3 = [8 2 1 4 6];
231 | P = [D1; D2; D3];
232 |
233 | % Spider plot
234 | spider_plot(P);
235 |
236 | % Legend settings
237 | legend('D1', 'D2', 'D3', 'Location', 'southoutside');
238 | ```
239 |
240 |
241 |
242 |
243 |
244 | ### Example 2: Manually setting the axes limits and axes precision. All other optional arguments are set to their default values.
245 | ```matlab
246 | % Initialize data points
247 | D1 = [5 3 9 1 2];
248 | D2 = [5 8 7 2 9];
249 | D3 = [8 2 1 4 6];
250 | P = [D1; D2; D3];
251 |
252 | % Spider plot
253 | spider_plot(P,...
254 | 'AxesLimits', [1, 2, 1, 1, 1; 10, 8, 9, 5, 10],... % [min axes limits; max axes limits]
255 | 'AxesPrecision', [0, 1, 1, 1, 1]);
256 | ```
257 |
258 |
259 |
260 |
261 |
262 | ### Example 3: Set fill option on. The fill transparency can be adjusted.
263 | ```matlab
264 | % Initialize data points
265 | D1 = [5 3 9 1 2];
266 | D2 = [5 8 7 2 9];
267 | D3 = [8 2 1 4 6];
268 | P = [D1; D2; D3];
269 |
270 | % Spider plot
271 | spider_plot(P,...
272 | 'AxesLabels', {'S1', 'S2', 'S3', 'S4', 'S5'},...
273 | 'AxesInterval', 2,...
274 | 'FillOption', {'on', 'on', 'off'},...
275 | 'FillTransparency', [0.2, 0.1, 0.1]);
276 | ```
277 |
278 |
279 |
280 |
281 |
282 | ### Example 4: Maximum number of arguments.
283 | ```matlab
284 | % Initialize data points
285 | D1 = [5 3 9 1 2];
286 | D2 = [5 8 7 2 9];
287 | D3 = [8 2 1 4 6];
288 | P = [D1; D2; D3];
289 |
290 | % Spider plot
291 | spider_plot(P,...
292 | 'AxesLabels', {'S1', 'S2', 'S3', 'S4', 'S5'},...
293 | 'AxesInterval', 4,...
294 | 'AxesPrecision', 0,...
295 | 'AxesDisplay', 'one',...
296 | 'AxesLimits', [1, 2, 1, 1, 1; 10, 8, 9, 5, 10],...
297 | 'FillOption', 'on',...
298 | 'FillTransparency', 0.2,...
299 | 'Color', [1, 0, 0; 0, 1, 0; 0, 0, 1],...
300 | 'LineStyle', {'--', '-', '--'},...
301 | 'LineWidth', [1, 2, 3],...
302 | 'LineTransparency', 1,...
303 | 'Marker', {'o', 'd', 's'},...
304 | 'MarkerSize', [8, 10, 12],...
305 | 'MarkerTransparency', 1,...
306 | 'AxesFont', 'Times New Roman',...
307 | 'LabelFont', 'Times New Roman',...
308 | 'AxesFontSize', 12,...
309 | 'LabelFontSize', 10,...
310 | 'Direction', 'clockwise',...
311 | 'AxesDirection', {'reverse', 'normal', 'normal', 'normal', 'normal'},...
312 | 'AxesLabelsOffset', 0.2,...
313 | 'AxesDataOffset', 0.1,...
314 | 'AxesScaling', 'linear',...
315 | 'AxesColor', [0.6, 0.6, 0.6],...
316 | 'AxesLabelsEdge', 'none',...
317 | 'AxesOffset', 1,...
318 | 'AxesZoom', 1,...
319 | 'AxesHorzAlign', 'quadrant',...
320 | 'AxesVertAlign', 'quadrant',...
321 | 'PlotVisible', 'on',...
322 | 'AxesTickLabels', 'data',...
323 | 'AxesInterpreter', 'tex',...
324 | 'BackgroundColor' , 'w',...
325 | 'MinorGrid', 'off',...
326 | 'MinorGridInterval', 2,...
327 | 'AxesZero', 'off',...
328 | 'AxesZeroColor', 'k',...
329 | 'AxesZeroWidth', 2,...
330 | 'AxesRadial', 'on',...
331 | 'AxesWeb', 'on',...
332 | 'AxesShaded', 'off',...
333 | 'AxesShadedLimits', [],...
334 | 'AxesShadedColor', 'g',...
335 | 'AxesShadedTransparency', 0.2,...
336 | 'AxesLabelsRotate', 'off');
337 | ```
338 |
339 |
340 |
341 |
342 |
343 | ### Example 5: Excel-like radar charts.
344 | ```matlab
345 | % Initialize data points
346 | D1 = [5 0 3 4 4];
347 | D2 = [2 1 5 5 4];
348 | P = [D1; D2];
349 |
350 | % Spider plot
351 | spider_plot(P,...
352 | 'AxesInterval', 5,...
353 | 'AxesPrecision', 0,...
354 | 'AxesDisplay', 'one',...
355 | 'AxesLimits', [0, 0, 0, 0, 0; 5, 5, 5, 5, 5],...
356 | 'FillOption', 'on',...
357 | 'FillTransparency', 0.1,...
358 | 'Color', [139, 0, 0; 240, 128, 128]/255,...
359 | 'LineWidth', 4,...
360 | 'Marker', 'none',...
361 | 'AxesFontSize', 14,...
362 | 'LabelFontSize', 10,...
363 | 'AxesColor', [0.8, 0.8, 0.8],...
364 | 'AxesLabelsEdge', 'none',...
365 | 'AxesRadial', 'off');
366 |
367 | % Title and legend settings
368 | title(sprintf('Excel-like Radar Chart'),...
369 | 'FontSize', 14);
370 | legend_str = {'D1', 'D2'};
371 | legend(legend_str, 'Location', 'southoutside');
372 | ```
373 |
374 |
375 |
376 |
377 |
378 | ### Example 6: Logarithimic scale on specified axes. Axes limits and axes intervals can be individually set as well.
379 | ```matlab
380 | % Initialize data points
381 | D1 = [5 3 9 1 1];
382 | D2 = [5 8 7 2 10];
383 | D3 = [8 2 1 4 100];
384 | P = [D1; D2; D3];
385 |
386 | % Spider plot
387 | spider_plot(P,...
388 | 'AxesInterval', 2,...
389 | 'AxesPrecision', 0,...
390 | 'AxesFontSize', 10,...
391 | 'AxesLabels', {'Linear Scale', 'Linear Scale', 'Linear Scale', 'Linear Scale', 'Logarithimic Scale'},...
392 | 'AxesScaling', {'linear', 'linear', 'linear', 'linear', 'log'},...
393 | 'AxesLimits', [1, 1, 1, 1, 1; 10, 10, 10, 10, 100]);
394 |
395 | % Legend properties
396 | legend('D1', 'D2', 'D3', 'Location', 'northeast');
397 | ```
398 |
399 |
400 |
401 |
402 |
403 | ### Example 7a: Spider plot with tiledlayout feature in R2019b.
404 | ```matlab
405 | % Initialize data points
406 | D1 = [5 3 9 1 2];
407 | D2 = [5 8 7 2 9];
408 | D3 = [8 2 1 4 6];
409 | P = [D1; D2; D3];
410 |
411 | % Titled layout
412 | t = tiledlayout(2, 2);
413 |
414 | % Tile 1
415 | nexttile;
416 | spider_plot_R2019b(P,...
417 | 'AxesInterval', 1,...
418 | 'AxesPrecision', 0);
419 |
420 | % Tile 2
421 | nexttile;
422 | spider_plot_R2019b(P,...
423 | 'AxesInterval', 1,...
424 | 'AxesPrecision', 0);
425 |
426 | % Tile 3
427 | nexttile(3, [1, 2]);
428 | spider_plot_R2019b(P,...
429 | 'AxesInterval', 1,...
430 | 'AxesPrecision', 0);
431 |
432 | % Tile properties
433 | t.TileSpacing = 'compact';
434 | t.Padding = 'compact';
435 | title(t, 'Spider Plots');
436 | ```
437 |
438 |
439 |
440 |
441 | ### Example 7b: Spider plot class with tiledlayout feature.
442 | ```matlab
443 | % Initialize data points
444 | D1 = [5 3 9 1 2];
445 | D2 = [5 8 7 2 9];
446 | D3 = [8 2 1 4 6];
447 | P = [D1; D2; D3];
448 | close all;
449 | clc;
450 |
451 | % Individual spider plots
452 | figure;
453 | s1 = spider_plot_class(P);
454 | s1.LegendLabels = {'Data1a', 'Data1b', 'Data1c'};
455 | s1.AxesZoom = 1;
456 | s1.AxesHorzAlign = 'quadrant';
457 | s1.AxesVertAlign = 'quadrant';
458 |
459 | figure;
460 | s2 = spider_plot_class(P);
461 | s2.LegendLabels = {'Data2a', 'Data2b', 'Data2c'};
462 | s2.AxesZoom = 1;
463 | s2.AxesHorzAlign = 'center';
464 | s2.AxesVertAlign = 'top';
465 |
466 | figure;
467 | s3 = spider_plot_class(P);
468 | s3.LegendLabels = {'Data3a', 'Data3b', 'Data3c'};
469 | s3.AxesZoom = 1;
470 | s3.AxesHorzAlign = 'left';
471 | s3.AxesVertAlign = 'middle';
472 |
473 | % Tiled layout
474 | s1.tiledlayout(2, 2);
475 |
476 | % Next tiles
477 | s1.nexttile(s1);
478 | s1.nexttile(s2);
479 | s1.nexttile(s3, 3, [1, 2]);
480 |
481 | % Tiled layout settings
482 | s1.TiledLayoutHandle.TileSpacing = 'none';
483 | s1.TiledLayoutHandle.Padding = 'compact';
484 | title(s1.TiledLayoutHandle, "Spider Plots");
485 |
486 | % Legend settings
487 | s1.tiledlegend('FontSize', 8);
488 | s1.TiledLegendHandle.Layout.TileSpan = [1, 2];
489 | s1.TiledLegendHandle.Layout.Tile = 1;
490 | ```
491 |
492 |
493 |
494 |
495 | ### Example 8: Spider plot with values only on data points.
496 | ```matlab
497 | % Initialize data points
498 | D1 = [1 3 4 1 2];
499 | D2 = [5 8 7 5 9];
500 | P = [D1; D2];
501 |
502 | % Spider plot
503 | s = spider_plot_class(P);
504 | s.AxesLimits = [1, 1, 1, 1, 1; 10, 10, 10, 10, 10];
505 | s.AxesDisplay = 'data';
506 | s.AxesLabelsOffset = 0.2;
507 | s.AxesDataOffset = 0.1;
508 | s.AxesFontColor = [0, 0, 1; 1, 0, 0];
509 |
510 | % Legend properties
511 | s.LegendLabels = {'D1', 'D2'};
512 | s.LegendHandle.Location = 'northeastoutside';
513 | ```
514 |
515 |
516 |
517 |
518 | ### Example 9: Spider plot with text as axes tick labels.
519 | ```matlab
520 | % Initialize data points
521 | D1 = [5 3 9 1 2];
522 | D2 = [5 8 7 2 9];
523 | D3 = [8 2 1 4 6];
524 | P = [D1; D2; D3];
525 |
526 | % Spider plot
527 | spider_plot(P,...
528 | 'AxesTickLabels', {'first', 'second', 'third', 'fourth'});
529 | ```
530 |
531 |
532 |
533 |
534 | ### Example 10: Spider plot with interpreter setting customized for each axes label.
535 | ```matlab
536 | % Initialize data points
537 | D1 = [5 3 9 1 2];
538 | D2 = [5 8 7 2 9];
539 | D3 = [8 2 1 4 6];
540 | P = [D1; D2; D3];
541 |
542 | % Spider plot
543 | spider_plot(P,...
544 | 'AxesLabels', {'\beta=1', '$\int_1^{20} x^2 dx$', '$\mathcal{O}$', 'Normal', 'Normal'},...
545 | 'AxesInterpreter', {'tex', 'latex', 'latex', 'none', 'none'},...
546 | 'AxesTickInterpreter', {'tex', 'latex', 'latex', 'none', 'none'});
547 | ```
548 |
549 |
550 |
551 |
552 | ### Example 11: Spider plot with multiple shaded area around axes.
553 | ```matlab
554 | %% Initialize data points
555 | D1 = [5 3 9 1 2];
556 | D2 = [5 8 7 2 9];
557 | D3 = [8 2 1 4 6];
558 | P = [D1; D2; D3];
559 |
560 | % Multiple shaded regions
561 | axes_shaded_limits = {...
562 | [5.5, 4, 3, 2, 4; 7, 6.5, 6, 3.5, 6],... % [min axes limits; max axes limits]
563 | [5.5, 4, 3, 2, 4; 6, 7.0, 8, 3.0, 6]};
564 |
565 | % Spider plot
566 | spider_plot(P,...
567 | 'AxesShaded', 'on',...
568 | 'AxesShadedLimits', axes_shaded_limits,...
569 | 'AxesShadedColor', {'b', 'r'},...
570 | 'AxesShadedTransparency', 0.1);
571 | ```
572 |
573 |
574 |
575 |
576 | ### Example 12: Spider plot with rotated axes labels. This is useful when you have many labels that would otherwise overlap.
577 | ```matlab
578 | % Initialize data points
579 | D1 = [5 3 9 1 2 2 9 3 1 9 8 7 2 3 6];
580 | D2 = [5 8 7 2 9 7 6 4 8 9 2 1 8 2 4];
581 | D3 = [8 2 1 4 6 1 8 4 2 3 7 5 6 1 6];
582 | P = [D1; D2; D3];
583 |
584 | % Spider plot
585 | spider_plot(P,...
586 | 'AxesLimits', [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;...
587 | 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10],...
588 | 'AxesInterval', 5,...
589 | 'AxesDisplay', 'one',...
590 | 'AxesPrecision', 0,...
591 | 'AxesLabelsRotate', 'on',...
592 | 'AxesLabelsOffset', 0.1,...
593 | 'AxesRadial', 'off');
594 | ```
595 |
596 |
597 |
598 |
599 | ### Example 13: Spider plot in appdesigner.
600 | ```matlab
601 | % Initialize data points
602 | D1 = [5 3 9 1 2];
603 | D2 = [5 8 7 2 9];
604 | D3 = [8 2 1 4 6];
605 | P = [D1; D2; D3];
606 |
607 | % Spider plot
608 | spider_plot(P, 'AxesHandle', app.UIAxes); % <-- Replace with UIAxes component name
609 | ```
610 |
611 |
612 |
613 |
614 | ### Example 14: Leave out plotting missing values specified by "Inf".
615 | ```matlab
616 | % Initialize data points
617 | D1 = [5 3 9 1 2];
618 | D2 = [5 8 7 2 9];
619 | D3 = [8 2 1 4 inf];
620 | P = [D1; D2; D3];
621 |
622 | % Axes limits
623 | axes_limits = [1 1 1 1 1; 10 10 10 10 10];
624 |
625 | % Spider plot
626 | spider_plot(P,...
627 | 'AxesLimits', axes_limits,...
628 | 'FillOption', 'on');
629 | ```
630 |
631 |
632 |
633 |
634 | ### Example 15: Plot error bars using +/- standard deviation.
635 | ```matlab
636 | % Initialize data points
637 | D1 = [5 3 9 1 2];
638 | D2 = [5 8 7 2 9];
639 | D3 = [8 2 1 4 6];
640 | P = [D1; D2; D3];
641 |
642 | % Spider plot
643 | spider_plot(P,...
644 | 'AxesLimits', [1, 1, 1, 1, 1; 10, 10, 10, 10, 10],... % [min axes limits; max axes limits]
645 | 'ErrorBars', 'on');
646 | ```
647 |
648 |
649 |
650 |
651 | ### Example 16: Plot error bars using user specified +/- values.
652 | ```matlab
653 | % Initialize data points
654 | D1 = [3, 5, 7, 2, 8];
655 | P = D1;
656 | axes_limits = [...
657 | 1 1 1 1 1;...
658 | 10 10 10 10 10];
659 | error_positive = [2 3 2 5 1];
660 | error_negative = [1 3 3 1 6];
661 |
662 | % Spider plot
663 | spider_plot(P,...
664 | 'AxesLimits', axes_limits,...
665 | 'AxesDisplay', 'one',...
666 | 'ErrorBars', 'on',...
667 | 'ErrorPositive', error_positive,...
668 | 'ErrorNegative', error_negative);
669 | ```
670 |
671 |
672 |
673 |
674 | ### Example 17: Spider plot with circular web type.
675 | ```matlab
676 | % Initialize data points
677 | D1 = [5 3 9 1 2];
678 | D2 = [5 8 7 2 9];
679 | D3 = [8 2 1 4 6];
680 | P = [D1; D2; D3];
681 |
682 | % Spider plot
683 | spider_plot(P,...
684 | 'AxesLimits', [1, 1, 1, 1, 1; 10, 10, 10, 10, 10],...
685 | 'AxesWebType', 'circular');
686 | ```
687 |
688 |
689 |
690 |
691 | ### Example 18: Spider plot a gradient fill color.
692 | ```matlab
693 | % Initialize data points
694 | D1 = [4, 3, 4, 2, 4, 3, 3, 1, 5];
695 | P = D1;
696 | % Axes limits
697 | axes_limits = [...
698 | 1 1 1 1 1 1 1 1 1;...
699 | 5 5 5 5 5 5 5 5 5];
700 |
701 | % Spider plot
702 | colormap(flipud(hot));
703 | spider_plot(P,...
704 | 'AxesInterval', 4,...
705 | 'AxesPrecision', 0,...
706 | 'AxesLimits', axes_limits,...
707 | 'AxesDisplay', 'one',...
708 | 'AxesLabelsEdge', 'none',...
709 | 'AxesWebType', 'circular',...
710 | 'AxesStart', 0,...
711 | 'Direction', 'counterclockwise',...
712 | 'Color', 'r',...
713 | 'FillOption', 'interp',...
714 | 'FillTransparency', 0.9,...
715 | 'FillCData', D1);
716 | ```
717 |
718 |
719 |
720 |
721 | ## Author:
722 | Moses Yoo, (juyoung.m.yoo at gmail dot com)
723 | - 2024-12-06: Take into account reverse axes direction for shaded limits.
724 |
725 | - 2024-02-01: Added in option to change the axes line style and line width.
726 |
727 | - 2024-01-10: Fixed bug when combining shading with lower and upper boundaries with logarithmic scaling.
728 |
729 | - 2022-09-13: Added "AxesTickInterpreter" name-value pair option.
730 |
731 | - 2022-06-14: Added ability to manually set the error bar lengths. Add in support for gradient fills.
732 |
733 | - 2022-02-22: Added option to format axes tick text.
734 |
735 | - 2022-02-19: Add in option to change axes web type.
736 |
737 | - 2022-02-18: Implement feature to display error bars with +/- standard deviation.
738 |
739 | - 2022-12-21: Add ability to display data points in percentage.
740 |
741 | - 2022-11-25: Remove connecting line for missing values specified by Inf.
742 |
743 | - 2022-10-24: Leave out plotting missing values specified by Inf.
744 |
745 | - 2022-10-19: Explicitly specify handles to allow plotting in appdesigner.
746 |
747 | - 2022-10-08: Allow for multiple shaded regions.
748 |
749 | - 2022-09-07: Fix bug for specified axes handle not be respected.
750 |
751 | - 2022-08-02: Added in name-value pair to use specified axes handle.
752 |
753 | - 2022-07-27: Corrected bug where only 7 entries were allowed in spider_plot_class.
754 |
755 | - 2022-03-23: Adjust rotated axes label alignment to be closer to axes.
756 |
757 | - 2022-03-21: Allow axes labels to be rotated to be aligned with axes.
758 |
759 | - 2022-03-17: Allow a shaded band to be plotted around the axes.
760 |
761 | - 2022-02-14: Add support for reference axes at value zero. Allow for toggling radial and angular axes on or off.
762 |
763 | - 2022-01-23: Add ability to change figure/axes background color. Allow for toggling minor grid lines.
764 |
765 | - 2022-01-03: Fix legend to include line and marker attributes.
766 |
767 | - 2021-11-24: Fix axes labels misalignment. Add option to set offset for data display values.
768 |
769 | - 2021-11-09: Add option to change the text interpreter of axes labels and axes tick labels.
770 |
771 | - 2021-11-01: Allow for plot lines and markers to be hidden. Allow for custom text of axes tick labels.
772 |
773 | - 2021-04-17: Fix data display values when log scale is set.
774 |
775 | - 2021-04-13: Add option to adjust line and marker transparency.
776 |
777 | - 2021-04-08: Add option for data values to be displayed on axes. Add support to adjust axes font colors.
778 |
779 | - 2021-03-19: Allow legend to be global in tiledlayout in spider_plot_class. Allow axes values to be shifted. Allow axes zoom level to be adjusted.
780 |
781 | - 2021-03-17: Implement tiledlayout and nexttile compatibility in spider_plot_class.
782 |
783 | - 2020-12-09: Allow fill option and fill transparency for each data group.
784 |
785 | - 2020-12-01: Added support for adjust the axes offset from origin.
786 |
787 | - 2020-11-30: Allow for one data group without specified axes limits.
788 |
789 | - 2020-11-30: Added support for changing axes and label font type.
790 |
791 | - 2020-10-08: Adjust axes precision to be set to one or more axis.
792 |
793 | - 2020-09-30: Updated examples and added ability to reverse axes direction.
794 |
795 | - 2020-07-05: Added feature to change spider axes and axes labels edge color.
796 |
797 | - 2020-06-17: Allow logarithmic scale to be set to one or more axis.
798 |
799 | - 2020-03-26: Added feature to allow different line styles, line width, marker type, and marker sizes for the data groups.
800 |
801 | - 2020-02-17: Major revision in converting the function into a custom chart class. New feature introduced in R2019b.
802 |
803 | - 2020-02-12: Fixed condition and added error checking for when only one data group is plotted.
804 |
805 | - 2020-01-27: Corrected bug where only 7 entries were allowed in legend.
806 |
807 | - 2020-01-06: Added support for subplot and tiledlayout feature (tiledlayout introduced in R2019b).
808 |
809 | - 2019-11-27: Add option to change axes to logarithmic scale.
810 |
811 | - 2019-11-15: Add feature to customize the plot rotational direction and the offset position of the axis labels.
812 |
813 | - 2019-10-28: Major revision in implementing the new function argument validation feature introduced in R2019b. Replaced previous method of error checking and setting of default values.
814 |
815 | - 2019-10-23: Minor revision to set starting axes as the vertical line. Add customization option for font sizes and axes display.
816 |
817 | - 2019-10-16: Minor revision to add name-value pairs for customizing color, marker, and line settings.
818 |
819 | - 2019-10-08: Another major revision to convert to name-value pairs and add color fill option.
820 |
821 | - 2019-09-17: Major revision to improve speed, clarity, and functionality
822 |
823 | ## Special Thanks:
824 | Special thanks to the following people for their feature recommendations and bug finds.
825 | - Gabriela Andrade
826 | - Andrés Garcia
827 | - Jiro Doke
828 | - Alex Grenyer
829 | - Tobias Kern
830 | - Omar Hadri
831 | - Zafar Ali
832 | - Christophe Hurlin
833 | - Sean de Wolski
834 | - Roman
835 | - Mariusz Sepczuk
836 | - Mohamed Abubakr
837 | - Maruis Mueller
838 | - Nicolai
839 | - Jingwei Too
840 | - Cedric Jamet
841 | - Richard Ruff
842 | - Marie-Kristin Schreiber
843 | - Jean-Baptise Billaud
844 | - Juan Carlos Vargas Rubio
845 | - Anthony Wang
846 | - Hanting Zhu
847 | - Pauline Oeuvray
848 | - Oliver Nicholls
849 | - Yu-Chi Chen
850 | - Fabrizio De Caro
851 | - Waqas Ahmad
852 | - Mario Di Siena
853 | - Rebecca
854 | - Nikolaos Koutsouleris
855 | - Sergi Torres
856 | - Clara Vetter
857 | - schkorf1
858 | - Philipp
859 | - Alexis
860 | - John Wills
861 | - Nian
862 | - Rebecca
863 | - Gonzalo Santos Perodia
864 | - Sam Crameri
865 | - Sébastien Mailfert
866 | - Gustavo Mellior
867 | - Alawode Basit
868 | - Andrea Quintarelli
869 |
870 | [](https://www.mathworks.com/matlabcentral/fileexchange/59561-spider_plot)
871 | [](https://matlab.mathworks.com/open/github/v1?repo=NewGuy012/spider_plot)
--------------------------------------------------------------------------------
/screenshot/example1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example1.PNG
--------------------------------------------------------------------------------
/screenshot/example10.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example10.PNG
--------------------------------------------------------------------------------
/screenshot/example11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example11.png
--------------------------------------------------------------------------------
/screenshot/example12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example12.png
--------------------------------------------------------------------------------
/screenshot/example13.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example13.PNG
--------------------------------------------------------------------------------
/screenshot/example14.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example14.PNG
--------------------------------------------------------------------------------
/screenshot/example15.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example15.PNG
--------------------------------------------------------------------------------
/screenshot/example16.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example16.PNG
--------------------------------------------------------------------------------
/screenshot/example17.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example17.PNG
--------------------------------------------------------------------------------
/screenshot/example18.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example18.PNG
--------------------------------------------------------------------------------
/screenshot/example2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example2.png
--------------------------------------------------------------------------------
/screenshot/example3.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example3.PNG
--------------------------------------------------------------------------------
/screenshot/example4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example4.png
--------------------------------------------------------------------------------
/screenshot/example5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example5.png
--------------------------------------------------------------------------------
/screenshot/example6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example6.png
--------------------------------------------------------------------------------
/screenshot/example7.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example7.PNG
--------------------------------------------------------------------------------
/screenshot/example7b.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example7b.PNG
--------------------------------------------------------------------------------
/screenshot/example8.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example8.PNG
--------------------------------------------------------------------------------
/screenshot/example9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/screenshot/example9.png
--------------------------------------------------------------------------------
/spider_plot.m:
--------------------------------------------------------------------------------
1 | function varargout = spider_plot(P, varargin)
2 | %spider_plot Create a spider or radar plot with individual axes.
3 | %
4 | % Syntax:
5 | % spider_plot(P)
6 | % spider_plot(P, Name, Value, ...)
7 | % h = spider_plot(_)
8 | %
9 | % Documentation:
10 | % Please refer to the MathWorks File Exchange or GitHub page for the
11 | % detailed documentation and examples.
12 |
13 | %%% Data Properties %%%
14 | % Point properties
15 | [num_data_groups, num_data_points] = size(P);
16 |
17 | % Number of optional arguments
18 | numvarargs = length(varargin);
19 |
20 | % Check for even number of name-value pair argments
21 | if mod(numvarargs, 2) == 1
22 | error('Error: Please check name-value pair arguments');
23 | end
24 |
25 | % Create default labels
26 | axes_labels = cell(1, num_data_points);
27 |
28 | % Iterate through number of data points
29 | for ii = 1:num_data_points
30 | % Default axes labels
31 | axes_labels{ii} = sprintf('Label %i', ii);
32 | end
33 |
34 | % Replace Inf with NaN
35 | P_limits = P;
36 | inf_index = isinf(P_limits);
37 | P_limits(inf_index) = nan;
38 |
39 | % Default arguments
40 | axes_interval = 3;
41 | axes_precision = 1;
42 | axes_display = 'all';
43 | axes_limits = [min(P_limits, [], 1); max(P_limits, [], 1)];
44 | fill_option = 'off';
45 | fill_transparency = 0.2;
46 | fill_cdata = [];
47 | colors = lines(num_data_groups);
48 | line_style = '-';
49 | line_width = 2;
50 | line_transparency = 1;
51 | marker_type = 'o';
52 | marker_size = 36;
53 | marker_transparency = 1;
54 | axes_font = 'Helvetica';
55 | label_font = 'Helvetica';
56 | axes_font_size = 10;
57 | axes_font_color = [0, 0, 0];
58 | label_font_size = 10;
59 | direction = 'clockwise';
60 | axes_direction = 'normal';
61 | axes_labels_offset = 0.2;
62 | axes_data_offset = 0.1;
63 | axes_scaling = 'linear';
64 | axes_color = [0.6, 0.6, 0.6];
65 | axes_labels_edge = 'k';
66 | axes_offset = 1;
67 | axes_zoom = 0.7;
68 | axes_horz_align = 'center';
69 | axes_vert_align = 'middle';
70 | plot_visible = 'on';
71 | axes_tick_labels = 'data';
72 | axes_interpreter = 'tex';
73 | axes_tick_interpreter = 'tex';
74 | background_color = 'w';
75 | minor_grid = 'off';
76 | minor_grid_interval = 2;
77 | axes_zero = 'off';
78 | axes_zero_color = [0.6, 0.6, 0.6];
79 | axes_zero_width = 2;
80 | axes_radial = 'on';
81 | axes_web = 'on';
82 | axes_shaded = 'off';
83 | axes_shaded_limits = {axes_limits};
84 | axes_shaded_color = 'g';
85 | axes_shaded_transparency = 0.2;
86 | axes_labels_rotate = 'off';
87 | axes_handle = gobjects;
88 | error_bars = 'off';
89 | error_positive = [];
90 | error_negative = [];
91 | axes_web_type = 'web';
92 | axes_tick_format = 'default';
93 | axes_start = pi/2;
94 | axes_radial_line_width = 1.5;
95 | axes_radial_line_style = '-';
96 | axes_web_line_width = 1;
97 | axes_web_line_style = '-';
98 |
99 | % Check if optional arguments were specified
100 | if numvarargs > 1
101 | % Initialze name-value arguments
102 | name_arguments = varargin(1:2:end);
103 | value_arguments = varargin(2:2:end);
104 |
105 | % Iterate through name-value arguments
106 | for ii = 1:length(name_arguments)
107 | % Set value arguments depending on name
108 | switch lower(name_arguments{ii})
109 | case 'axeslabels'
110 | axes_labels = value_arguments{ii};
111 | case 'axesinterval'
112 | axes_interval = value_arguments{ii};
113 | case 'axesprecision'
114 | axes_precision = value_arguments{ii};
115 | case 'axesdisplay'
116 | axes_display = value_arguments{ii};
117 | case 'axeslimits'
118 | axes_limits = value_arguments{ii};
119 | case 'filloption'
120 | fill_option = value_arguments{ii};
121 | case 'filltransparency'
122 | fill_transparency = value_arguments{ii};
123 | case 'fillcdata'
124 | fill_cdata = value_arguments{ii};
125 | case 'color'
126 | colors = value_arguments{ii};
127 | case 'linestyle'
128 | line_style = value_arguments{ii};
129 | case 'linewidth'
130 | line_width = value_arguments{ii};
131 | case 'linetransparency'
132 | line_transparency = value_arguments{ii};
133 | case 'marker'
134 | marker_type = value_arguments{ii};
135 | case 'markersize'
136 | marker_size = value_arguments{ii};
137 | case 'markertransparency'
138 | marker_transparency = value_arguments{ii};
139 | case 'axesfont'
140 | axes_font = value_arguments{ii};
141 | case 'labelfont'
142 | label_font = value_arguments{ii};
143 | case 'axesfontsize'
144 | axes_font_size = value_arguments{ii};
145 | case 'axesfontcolor'
146 | axes_font_color = value_arguments{ii};
147 | case 'labelfontsize'
148 | label_font_size = value_arguments{ii};
149 | case 'direction'
150 | direction = value_arguments{ii};
151 | case 'axesdirection'
152 | axes_direction = value_arguments{ii};
153 | case 'axeslabelsoffset'
154 | axes_labels_offset = value_arguments{ii};
155 | case 'axesdataoffset'
156 | axes_data_offset = value_arguments{ii};
157 | case 'axesscaling'
158 | axes_scaling = value_arguments{ii};
159 | case 'axescolor'
160 | axes_color = value_arguments{ii};
161 | case 'axeslabelsedge'
162 | axes_labels_edge = value_arguments{ii};
163 | case 'axesoffset'
164 | axes_offset = value_arguments{ii};
165 | case 'axeszoom'
166 | axes_zoom = value_arguments{ii};
167 | case 'axeshorzalign'
168 | axes_horz_align = value_arguments{ii};
169 | case 'axesvertalign'
170 | axes_vert_align = value_arguments{ii};
171 | case 'plotvisible'
172 | plot_visible = value_arguments{ii};
173 | case 'axesticklabels'
174 | axes_tick_labels = value_arguments{ii};
175 | case 'axestickinterpreter'
176 | axes_tick_interpreter = value_arguments{ii};
177 | case 'axesinterpreter'
178 | axes_interpreter = value_arguments{ii};
179 | case 'backgroundcolor'
180 | background_color = value_arguments{ii};
181 | case 'minorgrid'
182 | minor_grid = value_arguments{ii};
183 | case 'minorgridinterval'
184 | minor_grid_interval = value_arguments{ii};
185 | case 'axeszero'
186 | axes_zero = value_arguments{ii};
187 | case 'axeszerowidth'
188 | axes_zero_width = value_arguments{ii};
189 | case 'axeszerocolor'
190 | axes_zero_color = value_arguments{ii};
191 | case 'axesradial'
192 | axes_radial = value_arguments{ii};
193 | case 'axesweb'
194 | axes_web = value_arguments{ii};
195 | case 'axesshaded'
196 | axes_shaded = value_arguments{ii};
197 | case 'axesshadedlimits'
198 | axes_shaded_limits = value_arguments{ii};
199 | case 'axesshadedcolor'
200 | axes_shaded_color = value_arguments{ii};
201 | case 'axesshadedtransparency'
202 | axes_shaded_transparency = value_arguments{ii};
203 | case 'axeslabelsrotate'
204 | axes_labels_rotate = value_arguments{ii};
205 | case 'axeshandle'
206 | axes_handle = value_arguments{ii};
207 | case 'errorbars'
208 | error_bars = value_arguments{ii};
209 | case 'errorpositive'
210 | error_positive = value_arguments{ii};
211 | case 'errornegative'
212 | error_negative = value_arguments{ii};
213 | case 'axeswebtype'
214 | axes_web_type = value_arguments{ii};
215 | case 'axestickformat'
216 | axes_tick_format = value_arguments{ii};
217 | case 'axesstart'
218 | axes_start = value_arguments{ii};
219 | case 'axesradiallinewidth'
220 | axes_radial_line_width = value_arguments{ii};
221 | case 'axesradiallinestyle'
222 | axes_radial_line_style = value_arguments{ii};
223 | case 'axesweblinewidth'
224 | axes_web_line_width = value_arguments{ii};
225 | case 'axesweblinestyle'
226 | axes_web_line_style = value_arguments{ii};
227 | otherwise
228 | error('Error: Please enter in a valid name-value pair.');
229 | end
230 | end
231 |
232 | end
233 |
234 | %%% Error Check %%%
235 | % Check if axes labels is a cell
236 | if iscell(axes_labels)
237 | % Check if the axes labels are the same number as the number of points
238 | if length(axes_labels) ~= num_data_points
239 | error('Error: Please make sure the number of labels is the same as the number of points.');
240 | end
241 | else
242 | % Check if valid char entry
243 | if ~contains(axes_labels, 'none')
244 | error('Error: Please enter in valid labels or "none" to remove labels.');
245 | end
246 | end
247 |
248 | % Check if the axes limits same length as the number of points
249 | if size(axes_limits, 1) ~= 2 || size(axes_limits, 2) ~= num_data_points
250 | error('Error: Please make sure the min and max axes limits match the number of data points.');
251 | end
252 |
253 | % Lower and upper limits
254 | lower_limits = axes_limits(1, :);
255 | upper_limits = axes_limits(2, :);
256 |
257 | % Difference in upper and lower limits
258 | diff_limits = upper_limits - lower_limits;
259 |
260 | % Check to make sure upper limit is greater than lower limit
261 | if any(diff_limits < 0)
262 | error('Error: Please make sure max axes limits are greater than the min axes limits.');
263 | end
264 |
265 | % Check the range of axes limits
266 | if any(diff_limits == 0)
267 | error('Error: Please set the axes limits and make sure the min and max axes limits are different.');
268 | end
269 |
270 | % Check if axes precision is numeric
271 | if isnumeric(axes_precision)
272 | % Check is length is one
273 | if length(axes_precision) == 1
274 | % Repeat array to number of data points
275 | axes_precision = repmat(axes_precision, num_data_points, 1);
276 | elseif length(axes_precision) ~= num_data_points
277 | error('Error: Please specify the same number of axes precision as number of data points.');
278 | end
279 | else
280 | error('Error: Please make sure the axes precision is a numeric value.');
281 | end
282 |
283 | % Check if axes properties are an integer
284 | if floor(axes_interval) ~= axes_interval || any(floor(axes_precision) ~= axes_precision)
285 | error('Error: Please enter in an integer for the axes properties.');
286 | end
287 |
288 | % Check if axes properties are positive
289 | if axes_interval < 1 || any(axes_precision < 0)
290 | error('Error: Please enter a positive value for the axes properties.');
291 | end
292 |
293 | % Check if axes display is valid char entry
294 | if ~ismember(axes_display, {'all', 'none', 'one', 'data', 'data-percent'})
295 | error('Error: Invalid axes display entry. Please enter in "all", "none", "one", "data", or "data-percent" to set axes text.');
296 | end
297 |
298 | % Check if fill option is valid
299 | if any(~ismember(fill_option, {'off', 'on', 'interp'}))
300 | error('Error: Please enter either "off", "on" or "interp" for fill option.');
301 | end
302 |
303 | % Check if fill transparency is valid
304 | if any(fill_transparency < 0) || any(fill_transparency > 1)
305 | error('Error: Please enter a transparency value between [0, 1].');
306 | end
307 |
308 | % Check if line transparency is valid
309 | if any(line_transparency < 0) || any(line_transparency > 1)
310 | error('Error: Please enter a transparency value between [0, 1].');
311 | end
312 |
313 | % Check if marker transparency is valid
314 | if any(marker_transparency < 0) || any(marker_transparency > 1)
315 | error('Error: Please enter a transparency value between [0, 1].');
316 | end
317 |
318 | % Check if font size is greater than zero
319 | if axes_font_size <= 0 || label_font_size <= 0
320 | error('Error: Please enter a font size greater than zero.');
321 | end
322 |
323 | % Check if direction is valid char entry
324 | if ~ismember(direction, {'counterclockwise', 'clockwise'})
325 | error('Error: Invalid direction entry. Please enter in "counterclockwise" or "clockwise" to set direction of rotation.');
326 | end
327 |
328 | % Check if axes direction is valid char entry
329 | if ~ismember(axes_direction, {'normal', 'reverse'})
330 | error('Error: Invalid axes direction entry. Please enter in "normal" or "reverse" to set axes direction.');
331 | end
332 |
333 | % Check if axes labels offset is positive
334 | if axes_labels_offset < 0
335 | error('Error: Please enter a positive for the axes labels offset.');
336 | end
337 |
338 | % Check if axes data offset is positive
339 | if axes_data_offset < 0
340 | error('Error: Please enter a positive for the axes data offset.');
341 | end
342 |
343 | % Check if axes scaling is valid
344 | if any(~ismember(axes_scaling, {'linear', 'log'}))
345 | error('Error: Invalid axes scaling entry. Please enter in "linear" or "log" to set axes scaling.');
346 | end
347 |
348 | % Check if axes offset is valid
349 | if floor(axes_offset)~=axes_offset || axes_offset < 0 || axes_offset > axes_interval
350 | error('Error: Invalid axes offset entry. Please enter in an integer value that is between [0, axes_interval].');
351 | end
352 |
353 | % Check if axes zoom value is valid
354 | if ~isnumeric(axes_zoom) || length(axes_zoom) ~= 1 || axes_zoom < 0 || axes_zoom > 1
355 | error('Error: Please enter an axes zoom value between [0, 1].');
356 | end
357 |
358 | % Check if axes horizontal alignment is valid
359 | if any(~ismember(axes_horz_align, {'center', 'left', 'right', 'quadrant'}))
360 | error('Error: Invalid axes horizontal alignment entry.');
361 | end
362 |
363 | % Check if axes vertical alignment is valid
364 | if any(~ismember(axes_vert_align, {'middle', 'top', 'cap', 'bottom', 'baseline', 'quadrant'}))
365 | error('Error: Invalid axes vertical alignment entry.');
366 | end
367 |
368 | % Check if plot visible is valid
369 | if ~ismember(plot_visible, {'on', 'off'})
370 | error('Error: Invalid plot visible entry. Please enter in "on" or "off" to set plot visiblity.');
371 | end
372 |
373 | % Check if axes interpreter is valid
374 | if any(~ismember(axes_interpreter, {'tex', 'latex', 'none'}))
375 | error('Error: Please enter either "tex", "latex", or "none" for axes interpreter option.');
376 | end
377 |
378 | % Check if axes tick interpreter is valid
379 | if any(~ismember(axes_tick_interpreter, {'tex', 'latex', 'none'}))
380 | error('Error: Please enter either "tex", "latex", or "none" for axes tick interpreter option.');
381 | end
382 |
383 | % Check if minor grid is valid
384 | if ~ismember(minor_grid, {'on', 'off'})
385 | error('Error: Invalid minor grid entry. Please enter in "on" or "off" to toggle minor grid.');
386 | end
387 |
388 | % Check if axes zero is valid
389 | if ~ismember(axes_zero, {'on', 'off'})
390 | error('Error: Invalid axes zero entry. Please enter in "on" or "off" to set axes visiblity.');
391 | end
392 |
393 | % Check if axes radial is valid
394 | if ~ismember(axes_radial, {'on', 'off'})
395 | error('Error: Invalid axes radial entry. Please enter in "on" or "off" to set axes visiblity.');
396 | end
397 |
398 | % Check if axes web is valid
399 | if ~ismember(axes_web, {'on', 'off'})
400 | error('Error: Invalid axes web entry. Please enter in "on" or "off" to set axes visiblity.');
401 | end
402 |
403 | % Check if axes zero line width is numeric
404 | if ~isnumeric(axes_zero_width)
405 | error('Error: Please make sure the axes zero width is a numeric value.');
406 | end
407 |
408 | % Check if minor grid interval is valid
409 | if floor(minor_grid_interval)~=minor_grid_interval || minor_grid_interval < 0
410 | error('Error: Invalid minor grid interval entry. Please enter in an integer value that is positive.');
411 | end
412 |
413 | % Check if axes shaded is valid
414 | if any(~ismember(axes_shaded, {'off', 'on'}))
415 | error('Error: Please enter either "off" or "on" for axes shaded option.');
416 | end
417 |
418 | % Check if axes shaded transparency is valid
419 | if any(axes_shaded_transparency < 0) || any(axes_shaded_transparency > 1)
420 | error('Error: Please enter a transparency value between [0, 1].');
421 | end
422 |
423 | % Check if error bars is valid
424 | if any(~ismember(error_bars, {'off', 'on'}))
425 | error('Error: Please enter either "off" or "on" for error bars option.');
426 | end
427 |
428 | % Check if error positive and error negative are valid
429 | if strcmp(error_bars, 'on') &&...
430 | ~isempty(error_positive) &&...
431 | ~isempty(error_negative)
432 | % Check that the length match the data points
433 | if length(error_positive) ~= num_data_points
434 | error('Error: Please make sure the number of error positive elements equal the data points');
435 | end
436 |
437 | % Check that the length match the data points
438 | if length(error_negative) ~= num_data_points
439 | error('Error: Please make sure the number of error negative elements equal the data points');
440 | end
441 | end
442 |
443 | % Check if axes web type is valid
444 | if any(~ismember(axes_web_type, {'web', 'circular'}))
445 | error('Error: Please enter either "web" or "circular" for axes web type option.');
446 | end
447 |
448 | % Check if axes start is valid
449 | if ~(axes_start >= 0 && axes_start <= 2*pi)
450 | error('Error: Please select an axes start value between [0, 2pi].')
451 | end
452 |
453 | % Check if axes shaded limits is empty
454 | if isempty(axes_shaded_limits)
455 | axes_shaded_limits = {axes_limits};
456 | elseif iscell(axes_shaded_limits)
457 | % Iterate through axes shaded limits
458 | for ii = 1:length(axes_shaded_limits)
459 | % Initialize
460 | axes_shaded_limit = axes_shaded_limits{ii};
461 |
462 | % Check if the axes shaded limits same length as the number of points
463 | if size(axes_shaded_limit, 1) ~= 2 || size(axes_shaded_limit, 2) ~= num_data_points
464 | error('Error: Please make sure the min and max axes shaded limits match the number of data points.');
465 | end
466 |
467 | % Check if within min and max axes limits
468 | if any(axes_shaded_limit(1, :) < axes_limits(1, :)) ||...
469 | any(axes_shaded_limit(2, :) > axes_limits(2, :))
470 | error('Error: Please make sure the axes shaded limits are within the min and max axes limits.');
471 | end
472 | end
473 | else
474 | error('Error: Please make sure the axes shaded limits are in a cell array.');
475 | end
476 |
477 | % Check if axes shaded color is a char
478 | if ischar(axes_shaded_color)
479 | % Convert to cell array of char
480 | axes_shaded_color = cellstr(axes_shaded_color);
481 |
482 | % Repeat cell to number of axes shaded limits groups
483 | axes_shaded_color = repmat(axes_shaded_color, length(axes_shaded_limits), 1);
484 | elseif iscellstr(axes_shaded_color)
485 | % Check is length is one
486 | if length(axes_shaded_color) == 1
487 | % Repeat cell to number of axes shaded limits groups
488 | axes_shaded_color = repmat(axes_shaded_color, length(axes_shaded_limits), 1);
489 | end
490 | end
491 |
492 | % Check if axes shaded color is valid
493 | if length(axes_shaded_color) ~= length(axes_shaded_limits)
494 | error('Error: Please specify the same number of axes shaded color as number of axes shaded limits groups.');
495 | end
496 |
497 | % Check if axes shaded transparency is numeric
498 | if isnumeric(axes_shaded_transparency)
499 | % Check is length is one
500 | if length(axes_shaded_transparency) == 1
501 | % Repeat array to number of axes shaded limits groups
502 | axes_shaded_transparency = repmat(axes_shaded_transparency, length(axes_shaded_limits), 1);
503 | elseif length(axes_shaded_transparency) ~= length(axes_shaded_limits)
504 | error('Error: Please specify the same number of axes shaded transparency as number axes shaded limits groups.');
505 | end
506 | else
507 | error('Error: Please make sure the axes shaded transparency is a numeric value.');
508 | end
509 |
510 | % Check if axes labels rotate is valid
511 | if any(~ismember(axes_labels_rotate, {'off', 'on'}))
512 | error('Error: Please enter either "off" or "on" for axes labels rotate option.');
513 | end
514 |
515 | % Check if axes interpreter is a char
516 | if ischar(axes_interpreter)
517 | % Convert to cell array of char
518 | axes_interpreter = cellstr(axes_interpreter);
519 |
520 | % Repeat cell to number of data groups
521 | axes_interpreter = repmat(axes_interpreter, length(axes_labels), 1);
522 | elseif iscellstr(axes_interpreter)
523 | % Check is length is one
524 | if length(axes_interpreter) == 1
525 | % Repeat cell to number of data groups
526 | axes_interpreter = repmat(axes_interpreter, length(axes_labels), 1);
527 | elseif length(axes_interpreter) ~= length(axes_labels)
528 | error('Error: Please specify the same number of axes interpreters as axes labels.');
529 | end
530 | else
531 | error('Error: Please make sure the axes interpreter is a char or a cell array of char.');
532 | end
533 |
534 | % Check if axes tick interpreter is a char
535 | if ischar(axes_tick_interpreter)
536 | % Convert to cell array of char
537 | axes_tick_interpreter = cellstr(axes_tick_interpreter);
538 |
539 | % Repeat cell to number of data groups
540 | axes_tick_interpreter = repmat(axes_tick_interpreter, length(axes_labels), 1);
541 | elseif iscellstr(axes_tick_interpreter)
542 | % Check is length is one
543 | if length(axes_tick_interpreter) == 1
544 | % Repeat cell to number of data groups
545 | axes_tick_interpreter = repmat(axes_tick_interpreter, length(axes_labels), 1);
546 | elseif length(axes_tick_interpreter) ~= length(axes_labels)
547 | error('Error: Please specify the same number of axes tick interpreters as axes labels.');
548 | end
549 | else
550 | error('Error: Please make sure the axes tick interpreter is a char or a cell array of char.');
551 | end
552 |
553 | % Check if axes tick labels is valid
554 | if iscell(axes_tick_labels)
555 | if length(axes_tick_labels) ~= axes_interval+1
556 | error('Error: Invalid axes tick labels entry. Please enter in a cell array with the same length of axes interval + 1.');
557 | end
558 | else
559 | if ~strcmp(axes_tick_labels, 'data')
560 | error('Error: Invalid axes tick labels entry. Please enter in "data" or a cell array of desired tick labels.');
561 | end
562 |
563 | end
564 |
565 | % Check if axes scaling is a cell
566 | if iscell(axes_scaling)
567 | % Check is length is one
568 | if length(axes_scaling) == 1
569 | % Repeat array to number of data groups
570 | axes_scaling = repmat(axes_scaling, num_data_points, 1);
571 | elseif length(axes_scaling) ~= num_data_points
572 | error('Error: Please specify the same number of axes scaling as number of data points.');
573 | end
574 | else
575 | % Repeat array to number of data groups
576 | axes_scaling = repmat({axes_scaling}, num_data_points, 1);
577 | end
578 |
579 | % Check if line style is a char
580 | if ischar(line_style)
581 | % Convert to cell array of char
582 | line_style = cellstr(line_style);
583 |
584 | % Repeat cell to number of data groups
585 | line_style = repmat(line_style, num_data_groups, 1);
586 | elseif iscellstr(line_style)
587 | % Check is length is one
588 | if length(line_style) == 1
589 | % Repeat cell to number of data groups
590 | line_style = repmat(line_style, num_data_groups, 1);
591 | elseif length(line_style) ~= num_data_groups
592 | error('Error: Please specify the same number of line styles as number of data groups.');
593 | end
594 | else
595 | error('Error: Please make sure the line style is a char or a cell array of char.');
596 | end
597 |
598 | % Check if line width is numeric
599 | if isnumeric(line_width)
600 | % Check is length is one
601 | if length(line_width) == 1
602 | % Repeat array to number of data groups
603 | line_width = repmat(line_width, num_data_groups, 1);
604 | elseif length(line_width) ~= num_data_groups
605 | error('Error: Please specify the same number of line width as number of data groups.');
606 | end
607 | else
608 | error('Error: Please make sure the line width is a numeric value.');
609 | end
610 |
611 | % Check if marker type is a char
612 | if ischar(marker_type)
613 | % Convert to cell array of char
614 | marker_type = cellstr(marker_type);
615 |
616 | % Repeat cell to number of data groups
617 | marker_type = repmat(marker_type, num_data_groups, 1);
618 | elseif iscellstr(marker_type)
619 | % Check is length is one
620 | if length(marker_type) == 1
621 | % Repeat cell to number of data groups
622 | marker_type = repmat(marker_type, num_data_groups, 1);
623 | elseif length(marker_type) ~= num_data_groups
624 | error('Error: Please specify the same number of line styles as number of data groups.');
625 | end
626 | else
627 | error('Error: Please make sure the line style is a char or a cell array of char.');
628 | end
629 |
630 | % Check if line width is numeric
631 | if isnumeric(marker_size)
632 | if length(marker_size) == 1
633 | % Repeat array to number of data groups
634 | marker_size = repmat(marker_size, num_data_groups, 1);
635 | elseif length(marker_size) ~= num_data_groups
636 | error('Error: Please specify the same number of line width as number of data groups.');
637 | end
638 | else
639 | error('Error: Please make sure the line width is numeric.');
640 | end
641 |
642 | % Check if axes direction is a cell
643 | if iscell(axes_direction)
644 | % Check is length is one
645 | if length(axes_direction) == 1
646 | % Repeat array to number of data points
647 | axes_direction = repmat(axes_direction, num_data_points, 1);
648 | elseif length(axes_direction) ~= num_data_points
649 | error('Error: Please specify the same number of axes direction as number of data points.');
650 | end
651 | else
652 | % Repeat array to number of data points
653 | axes_direction = repmat({axes_direction}, num_data_points, 1);
654 | end
655 |
656 | % Check if fill option is a cell
657 | if iscell(fill_option)
658 | % Check is length is one
659 | if length(fill_option) == 1
660 | % Repeat array to number of data groups
661 | fill_option = repmat(fill_option, num_data_groups, 1);
662 | elseif length(fill_option) ~= num_data_groups
663 | error('Error: Please specify the same number of fill option as number of data groups.');
664 | end
665 | else
666 | % Repeat array to number of data groups
667 | fill_option = repmat({fill_option}, num_data_groups, 1);
668 | end
669 |
670 | % Check fill data
671 | if all(strcmp(fill_option, 'interp'))
672 | if isempty(fill_cdata)
673 | error('Error: Please enter in a valid fill cdata.');
674 | else
675 | if length(fill_cdata) ~= num_data_points
676 | error('Error: Please make sure that fill cdata matches the number of data points.');
677 | end
678 | end
679 | end
680 |
681 | % Check if fill transparency is numeric
682 | if isnumeric(fill_transparency)
683 | % Check is length is one
684 | if length(fill_transparency) == 1
685 | % Repeat array to number of data groups
686 | fill_transparency = repmat(fill_transparency, num_data_groups, 1);
687 | elseif length(fill_transparency) ~= num_data_groups
688 | error('Error: Please specify the same number of fill transparency as number of data groups.');
689 | end
690 | else
691 | error('Error: Please make sure the transparency is a numeric value.');
692 | end
693 |
694 | % Check if line transparency is numeric
695 | if isnumeric(line_transparency)
696 | % Check is length is one
697 | if length(line_transparency) == 1
698 | % Repeat array to number of data groups
699 | line_transparency = repmat(line_transparency, num_data_groups, 1);
700 | elseif length(line_transparency) ~= num_data_groups
701 | error('Error: Please specify the same number of line transparency as number of data groups.');
702 | end
703 | else
704 | error('Error: Please make sure the transparency is a numeric value.');
705 | end
706 |
707 | % Check if marker transparency is numeric
708 | if isnumeric(marker_transparency)
709 | % Check is length is one
710 | if length(marker_transparency) == 1
711 | % Repeat array to number of data groups
712 | marker_transparency = repmat(marker_transparency, num_data_groups, 1);
713 | elseif length(marker_transparency) ~= num_data_groups
714 | error('Error: Please specify the same number of marker transparency as number of data groups.');
715 | end
716 | else
717 | error('Error: Please make sure the transparency is a numeric value.');
718 | end
719 |
720 | % Check if axes display is data
721 | if ismember(axes_display, {'data', 'data-percent'})
722 | if size(axes_font_color, 1) ~= num_data_groups
723 | % Check axes font color dimensions
724 | if size(axes_font_color, 1) == 1 && size(axes_font_color, 2) == 3
725 | axes_font_color = repmat(axes_font_color, num_data_groups, 1);
726 | else
727 | error('Error: Please specify axes font color as a RGB triplet normalized to 1.');
728 | end
729 | end
730 | end
731 |
732 | % Check if axes tick format is a char
733 | if ischar(axes_tick_format)
734 | % Convert to cell array of char
735 | axes_tick_format = cellstr(axes_tick_format);
736 |
737 | % Repeat cell to number of axes shaded limits groups
738 | axes_tick_format = repmat(axes_tick_format, num_data_points, 1);
739 | elseif iscellstr(axes_tick_format)
740 | % Check is length is one
741 | if length(axes_tick_format) == 1
742 | % Repeat cell to number of axes shaded limits groups
743 | axes_tick_format = repmat(axes_tick_format, num_data_points, 1);
744 | end
745 | else
746 | error('Error: Please a character array or cell of character array for axes tick format.');
747 | end
748 |
749 | %%% Axes Scaling Properties %%%
750 | % Selected data
751 | P_selected = P;
752 |
753 | % Check axes scaling option
754 | log_index = strcmp(axes_scaling, 'log');
755 |
756 | % If any log scaling is specified
757 | if any(log_index)
758 | % Initialize copy
759 | P_log = P_selected(:, log_index);
760 |
761 | % Logarithm of base 10, account for numbers less than 1
762 | P_log = sign(P_log) .* log10(abs(P_log));
763 |
764 | % Minimum and maximun log limits
765 | min_limit = min(min(fix(P_log)));
766 | max_limit = max(max(ceil(P_log)));
767 | recommended_axes_interval = max_limit - min_limit;
768 |
769 | % Warning message
770 | warning('For the log scale values, recommended axes limit is [%i, %i] with an axes interval of %i.',...
771 | 10^min_limit, 10^max_limit, recommended_axes_interval);
772 |
773 | % Replace original
774 | P_selected(:, log_index) = P_log;
775 | end
776 |
777 | %%% Figure Properties %%%
778 | % Check if axes handle is specified
779 | if isempty(properties(axes_handle))
780 | % Grab current figure
781 | fig = gcf;
782 |
783 | % Reset axes
784 | cla reset;
785 |
786 | % Current axes handle
787 | ax = gca;
788 | else
789 | % Use specified axes handle
790 | ax = axes_handle;
791 | axes(ax);
792 |
793 | % Grab figure handle of specified axes handle
794 | fig = ax.Parent;
795 | end
796 |
797 | % Check for output arguments
798 | if nargout > 1
799 | error('Error: Too many output arguments assigned.');
800 | end
801 | varargout{1} = fig;
802 |
803 | % Set figure and axes background
804 | if isprop(fig, "Color")
805 | fig.Color = background_color;
806 | end
807 |
808 | ax.Color = background_color;
809 |
810 | % Axis limits
811 | hold(ax, 'on');
812 | axis(ax, 'square');
813 | scaling_factor = 1 + (1 - axes_zoom);
814 | axis(ax, [-1, 1, -1, 1] * scaling_factor);
815 |
816 | % Axis properties
817 | ax.XTickLabel = [];
818 | ax.YTickLabel = [];
819 | ax.XColor = 'none';
820 | ax.YColor = 'none';
821 |
822 | % Polar increments
823 | theta_increment = 2*pi/num_data_points;
824 | full_interval = axes_interval + 1;
825 | rho_offset = axes_offset/full_interval;
826 |
827 | %%% Scale Data %%%
828 | % Number of shaded rows
829 | num_shaded = length(axes_shaded_limits);
830 | shaded_rows = num_shaded * 2;
831 |
832 | % Check if axes shaded is on
833 | if strcmp(axes_shaded, 'on')
834 | % Concatenate
835 | all_shaded_limits = vertcat(axes_shaded_limits{:});
836 |
837 | % If any log scaling is specified
838 | if any(log_index)
839 | % Initialize copy
840 | A_log = all_shaded_limits(:, log_index);
841 |
842 | % Logarithm of base 10, account for numbers less than 1
843 | A_log = sign(A_log) .* log10(abs(A_log));
844 |
845 | % Update
846 | all_shaded_limits(:, log_index) = A_log;
847 | end
848 |
849 | % Append
850 | P_selected = [P_selected; all_shaded_limits];
851 | end
852 |
853 | % Pre-allocation
854 | P_scaled = zeros(size(P_selected));
855 | axes_range = zeros(3, num_data_points);
856 |
857 | % Check axes scaling option
858 | axes_direction_index = strcmp(axes_direction, 'reverse');
859 |
860 | % Iterate through number of data points
861 | for ii = 1:num_data_points
862 | % Check for one data group and no axes limits
863 | if num_data_groups == 1 && isempty(axes_limits)
864 | % Group of points
865 | group_points = P_selected(:, :);
866 | else
867 | % Group of points
868 | group_points = P_selected(:, ii);
869 | end
870 |
871 | % Replace Inf with NaN
872 | inf_index = isinf(group_points);
873 | group_points(inf_index) = nan;
874 |
875 | % Check for log axes scaling option
876 | if log_index(ii)
877 | % Minimum and maximun log limits
878 | min_value = min(fix(group_points));
879 | max_value = max(ceil(group_points));
880 | else
881 | % Automatically the range of each group
882 | min_value = min(group_points);
883 | max_value = max(group_points);
884 | end
885 |
886 | % Range of min and max values
887 | range = max_value - min_value;
888 |
889 | % Check if axes_limits is not empty
890 | if ~isempty(axes_limits)
891 | % Check for log axes scaling option
892 | if log_index(ii)
893 | % Logarithm of base 10, account for numbers less than 1
894 | axes_limits(:, ii) = sign(axes_limits(:, ii)) .* log10(abs(axes_limits(:, ii)));
895 | end
896 |
897 | % Manually set the range of each group
898 | min_value = axes_limits(1, ii);
899 | max_value = axes_limits(2, ii);
900 | range = max_value - min_value;
901 |
902 | % Check if the axes limits are within range of points
903 | if min_value > min(group_points) || max_value < max(group_points)
904 | error('Error: Please make sure the manually specified axes limits are within range of the data points.');
905 | end
906 | end
907 |
908 | % Check if range is valid
909 | if range == 0
910 | error('Error: Range of data values is not valid. Please specify the axes limits.');
911 | end
912 |
913 | % Scale points to range from [0, 1]
914 | P_scaled(:, ii) = ((P_selected(:, ii) - min_value) / range);
915 |
916 | % If reverse axes direction is specified
917 | if axes_direction_index(ii)
918 | % Store to array
919 | axes_range(:, ii) = [max_value; min_value; range];
920 | P_scaled(:, ii) = -(P_scaled(:, ii) - 1);
921 | else
922 | % Store to array
923 | axes_range(:, ii) = [min_value; max_value; range];
924 | end
925 |
926 | % Add offset of [rho_offset] and scaling factor of [1 - rho_offset]
927 | P_scaled(:, ii) = P_scaled(:, ii) * (1 - rho_offset) + rho_offset;
928 | end
929 |
930 | % Check if axes shaded is on
931 | if strcmp(axes_shaded, 'on')
932 | P_shaded = P_scaled(end-shaded_rows+1:end, :);
933 | P_scaled = P_scaled(1:end-shaded_rows, :);
934 | end
935 |
936 | %%% Polar Axes %%%
937 | % Polar coordinates
938 | rho_increment = 1/full_interval;
939 | rho = 0:rho_increment:1;
940 |
941 | % Check rotational direction
942 | switch direction
943 | case 'counterclockwise'
944 | % Shift the starting axis
945 | theta = (0:theta_increment:2*pi) + axes_start;
946 | case 'clockwise'
947 | % Shift the starting axes
948 | theta = (0:-theta_increment:-2*pi) + axes_start;
949 | end
950 |
951 | % Remainder after using a modulus of 2*pi
952 | theta = mod(theta, 2*pi);
953 |
954 | % Check if axes radial is toggled on
955 | if strcmp(axes_radial, 'on')
956 | % Iterate through each theta
957 | for ii = 1:length(theta)-1
958 | % Convert polar to cartesian coordinates
959 | [x_axes, y_axes] = pol2cart(theta(ii), rho);
960 |
961 | % Plot webs
962 | h = plot(ax, x_axes, y_axes,...
963 | 'LineStyle', axes_radial_line_style,...
964 | 'LineWidth', axes_radial_line_width,...
965 | 'Color', axes_color);
966 |
967 | % Turn off legend annotation
968 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
969 | end
970 | end
971 |
972 | % Check axes web type
973 | if strcmp(axes_web_type, 'web')
974 | theta_web = theta;
975 | elseif strcmp(axes_web_type, 'circular')
976 | theta_web = 0:((2*pi)/(2^7)):2*pi;
977 | end
978 |
979 | % Check if axes web is toggled on
980 | if strcmp(axes_web, 'on')
981 | % Iterate through each rho
982 | for ii = 2:length(rho)
983 | % Convert polar to cartesian coordinates
984 | [x_axes, y_axes] = pol2cart(theta_web, rho(ii));
985 |
986 | % Plot axes
987 | h = plot(ax, x_axes, y_axes,...
988 | 'LineStyle', axes_web_line_style,...
989 | 'LineWidth', axes_web_line_width,...
990 | 'Color', axes_color);
991 |
992 | % Turn off legend annotation
993 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
994 | end
995 | end
996 |
997 | % Check if minor grid is toggled on
998 | if strcmp(minor_grid, 'on')
999 | % Polar coordinates
1000 | rho_minor_increment = 1/(full_interval*minor_grid_interval);
1001 | rho_minor = rho(2):rho_minor_increment:1;
1002 | rho_minor = setdiff(rho_minor, rho(2:end));
1003 |
1004 | % Iterate through each rho minor
1005 | for ii = 1:length(rho_minor)
1006 | % Convert polar to cartesian coordinates
1007 | [x_axes, y_axes] = pol2cart(theta, rho_minor(ii));
1008 |
1009 | % Plot axes
1010 | h = plot(ax, x_axes, y_axes, '--',...
1011 | 'Color', axes_color,...
1012 | 'LineWidth', 0.5);
1013 |
1014 | % Turn off legend annotation
1015 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1016 | end
1017 | end
1018 |
1019 | % Check if axes zero is toggled on
1020 | if strcmp(axes_zero, 'on') && strcmp(axes_display, 'one')
1021 | % Scale points to range from [0, 1]
1022 | zero_scaled = (0 - min_value) / range;
1023 |
1024 | % If reverse axes direction is specified
1025 | if strcmp(axes_direction, 'reverse')
1026 | zero_scaled = -zero_scaled - 1;
1027 | end
1028 |
1029 | % Add offset of [rho_offset] and scaling factor of [1 - rho_offset]
1030 | zero_scaled = zero_scaled * (1 - rho_offset) + rho_offset;
1031 |
1032 | % Convert polar to cartesian coordinates
1033 | [x_axes, y_axes] = pol2cart(theta, zero_scaled);
1034 |
1035 | % Plot webs
1036 | h = plot(ax, x_axes, y_axes,...
1037 | 'LineWidth', axes_zero_width,...
1038 | 'Color', axes_zero_color);
1039 |
1040 | % Turn off legend annotation
1041 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1042 | end
1043 |
1044 | % Set end index depending on axes display argument
1045 | switch axes_display
1046 | case 'all'
1047 | theta_end_index = length(theta)-1;
1048 | case 'one'
1049 | theta_end_index = 1;
1050 | case 'none'
1051 | theta_end_index = 0;
1052 | case 'data'
1053 | theta_end_index = 0;
1054 | case 'data-percent'
1055 | theta_end_index = 0;
1056 | end
1057 |
1058 | % Rho start index and offset interval
1059 | rho_start_index = axes_offset+1;
1060 | offset_interval = full_interval - axes_offset;
1061 |
1062 | % Alignment for axes labels
1063 | horz_align = axes_horz_align;
1064 | vert_align = axes_vert_align;
1065 |
1066 | % Iterate through each theta
1067 | for ii = 1:theta_end_index
1068 | % Convert polar to cartesian coordinates
1069 | [x_axes, y_axes] = pol2cart(theta(ii), rho);
1070 |
1071 | % Check if horizontal alignment is quadrant based
1072 | if strcmp(axes_horz_align, 'quadrant')
1073 | % Alignment based on quadrant
1074 | [horz_align, ~] = quadrant_position(theta(ii));
1075 | end
1076 |
1077 | % Check if vertical alignment is quadrant based
1078 | if strcmp(axes_vert_align, 'quadrant')
1079 | % Alignment based on quadrant
1080 | [~, vert_align] = quadrant_position(theta(ii));
1081 | end
1082 |
1083 | % Iterate through points on isocurve
1084 | for jj = rho_start_index:length(rho)
1085 | % Axes increment range
1086 | min_value = axes_range(1, ii);
1087 | range = axes_range(3, ii);
1088 |
1089 | % If reverse axes direction is specified
1090 | if axes_direction_index(ii)
1091 | % Axes increment value
1092 | axes_value = min_value - (range/offset_interval) * (jj-rho_start_index);
1093 | else
1094 | % Axes increment value
1095 | axes_value = min_value + (range/offset_interval) * (jj-rho_start_index);
1096 | end
1097 |
1098 | % Check for log axes scaling option
1099 | if log_index(ii)
1100 | % Exponent to the tenth power
1101 | axes_value = 10^axes_value;
1102 | end
1103 |
1104 | % Display axes text
1105 | if strcmp(axes_tick_labels, 'data')
1106 | % Check axes tick format option
1107 | if strcmp(axes_tick_format{ii}, 'default')
1108 | text_format = sprintf('%%.%if', axes_precision(ii));
1109 | else
1110 | text_format = axes_tick_format{ii};
1111 | end
1112 |
1113 | % Formatted axes tick text
1114 | text_str = sprintf(text_format, axes_value);
1115 | else
1116 | text_str = axes_tick_labels{jj-axes_offset};
1117 | end
1118 |
1119 | t = text(ax, x_axes(jj), y_axes(jj), text_str,...
1120 | 'Units', 'Data',...
1121 | 'Color', axes_font_color,...
1122 | 'FontName', axes_font,...
1123 | 'FontSize', axes_font_size,...
1124 | 'HorizontalAlignment', horz_align,...
1125 | 'VerticalAlignment', vert_align,...
1126 | 'Interpreter', axes_tick_interpreter{ii});
1127 | end
1128 | end
1129 |
1130 | %%% Plot %%%
1131 | % Check if any NaNs detected
1132 | if any(isnan(P_scaled), 'all')
1133 | % Set value to zero
1134 | nan_index = isnan(P_scaled);
1135 | P_scaled(nan_index) = 0;
1136 | end
1137 |
1138 | % Check if error bars are desired
1139 | if strcmp(error_bars, 'on')
1140 | % Calculate mean and standard deviation
1141 | P_mean = mean(P, 1);
1142 | P_std = std(P, 0, 1);
1143 |
1144 | % Check if plus or minus error is specified
1145 | if isempty(error_positive) ||...
1146 | isempty(error_negative)
1147 | % Default values
1148 | error_positive = P_std;
1149 | error_negative = P_std;
1150 | end
1151 |
1152 | % Display to command window
1153 | fprintf("Error Bar Properties\n");
1154 | fprintf("--------------------\n")
1155 | format_str = repmat('%.2f ', 1, length(P_mean));
1156 | fprintf(" Average values: " + format_str + "\n", P_mean);
1157 | fprintf("Standard deviation: " + format_str + "\n", P_std);
1158 |
1159 | % Mean +/- standard deviation
1160 | P_mean = [P_mean; P_mean + error_positive; P_mean - error_negative];
1161 |
1162 | % Scale points to range from [0, 1] and apply offset
1163 | P_mean = (P_mean - axes_range(1, :)) ./ axes_range(3, :);
1164 | P_mean = P_mean * (1 - rho_offset) + rho_offset;
1165 |
1166 | % Convert from polar to cartesian
1167 | A_theta = theta(1:end-1);
1168 | A_scaled = P_mean;
1169 | [x_points, y_points] = pol2cart(A_theta, A_scaled);
1170 |
1171 | % Scatter plot mean values
1172 | scatter(ax, x_points(1, :), y_points(1, :),...
1173 | 'Marker', marker_type{1},...
1174 | 'SizeData', marker_size(1),...
1175 | 'MarkerFaceColor', colors(1, :),...
1176 | 'MarkerEdgeColor', colors(1, :),...
1177 | 'MarkerFaceAlpha', marker_transparency(1),...
1178 | 'MarkerEdgeAlpha', marker_transparency(1),...
1179 | 'Visible', plot_visible);
1180 |
1181 | % Iterate through each group
1182 | for ii = 1:size(x_points, 2)
1183 | % Mean +- standard deviation
1184 | X = x_points(2:3, ii)';
1185 | Y = y_points(2:3, ii)';
1186 |
1187 | % Plot error bar
1188 | h = plot(ax, X, Y,...
1189 | 'LineStyle', line_style{1},...
1190 | 'Color', colors(1, :),...
1191 | 'LineWidth', line_width(1),...
1192 | 'Visible', plot_visible);
1193 |
1194 | % Turn off legend annotation
1195 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1196 |
1197 | % Perpendicular line
1198 | v = [diff(X); diff(Y)];
1199 | v = 0.05 * v / vecnorm(v);
1200 |
1201 | % Top and bottom
1202 | for jj = 1:length(X)
1203 | % Plot end tip
1204 | x_endtip = [X(jj)+v(2), X(jj)-v(2)];
1205 | y_endtip = [Y(jj)-v(1), Y(jj)+v(1)];
1206 | h = plot(x_endtip, y_endtip,...
1207 | 'LineStyle', line_style{1},...
1208 | 'Color', colors(1, :),...
1209 | 'LineWidth', line_width(1),...
1210 | 'Visible', plot_visible);
1211 |
1212 | % Turn off legend annotation
1213 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1214 | end
1215 | end
1216 |
1217 | % Plot empty line with combined attributes for legend
1218 | plot(ax, nan, nan,...
1219 | 'Marker', marker_type{1},...
1220 | 'MarkerSize', marker_size(1)/6,...
1221 | 'MarkerFaceColor', colors(1, :),...
1222 | 'MarkerEdgeColor', colors(1, :),...
1223 | 'LineStyle', line_style{1},...
1224 | 'Color', colors(1, :),...
1225 | 'LineWidth', line_width(1),...
1226 | 'Visible', plot_visible);
1227 | end
1228 |
1229 | % Pre-allocation
1230 | S_info = struct(...
1231 | 'data_group_num', nan(num_data_groups, 1),...
1232 | 'perimeter', nan(num_data_groups, 1),...
1233 | 'area', nan(num_data_groups, 1));
1234 |
1235 | % Iterate through number of data groups
1236 | for ii = 1:num_data_groups
1237 | % Check if error bars are desired
1238 | if strcmp(error_bars, 'off')
1239 | % Initialize
1240 | A_scaled = P_scaled(ii, :);
1241 | A_theta = theta(1:end-1);
1242 |
1243 | % Find the index of Inf values
1244 | inf_index = isinf(A_scaled);
1245 | noninf_index = find(~inf_index);
1246 |
1247 | % Check if any Inf values detected
1248 | if any(inf_index)
1249 | % Do not plot Inf values
1250 | A_scaled(inf_index) = nan;
1251 | A_theta(inf_index) = nan;
1252 | end
1253 |
1254 | % Convert polar to cartesian coordinates
1255 | [x_points, y_points] = pol2cart(A_theta, A_scaled);
1256 |
1257 | % Calculate the perimeter and area
1258 | pgon = polyshape(x_points, y_points);
1259 | S_info(ii).data_group_num = "D" + ii;
1260 | S_info(ii).perimeter = perimeter(pgon);
1261 | S_info(ii).area = area(pgon);
1262 |
1263 | % Make points circular
1264 | x_circular = [x_points, x_points(1)];
1265 | y_circular = [y_points, y_points(1)];
1266 |
1267 | % Plot data points
1268 | h = plot(ax, x_circular, y_circular,...
1269 | 'LineStyle', line_style{ii},...
1270 | 'Color', colors(ii, :),...
1271 | 'LineWidth', line_width(ii),...
1272 | 'Visible', plot_visible);
1273 | h.Color(4) = line_transparency(ii);
1274 |
1275 | % Turn off legend annotation
1276 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1277 |
1278 | h = scatter(ax, x_points, y_points,...
1279 | 'Marker', marker_type{ii},...
1280 | 'SizeData', marker_size(ii),...
1281 | 'MarkerFaceColor', colors(ii, :),...
1282 | 'MarkerEdgeColor', colors(ii, :),...
1283 | 'MarkerFaceAlpha', marker_transparency(ii),...
1284 | 'MarkerEdgeAlpha', marker_transparency(ii),...
1285 | 'Visible', plot_visible);
1286 |
1287 | % Turn off legend annotation
1288 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1289 |
1290 | % Plot empty line with combined attributes for legend
1291 | plot(ax, nan, nan,...
1292 | 'Marker', marker_type{ii},...
1293 | 'MarkerSize', marker_size(ii)/6,...
1294 | 'MarkerFaceColor', colors(ii, :),...
1295 | 'MarkerEdgeColor', colors(ii, :),...
1296 | 'LineStyle', line_style{ii},...
1297 | 'Color', colors(ii, :),...
1298 | 'LineWidth', line_width(ii),...
1299 | 'Visible', plot_visible);
1300 | end
1301 |
1302 | % Iterate through number of data points
1303 | if ismember(axes_display, {'data', 'data-percent'})
1304 | for jj = noninf_index
1305 | % Convert polar to cartesian coordinates
1306 | [current_theta, current_rho] = cart2pol(x_points(jj), y_points(jj));
1307 | [x_pos, y_pos] = pol2cart(current_theta, current_rho+axes_data_offset);
1308 |
1309 | % Data value
1310 | data_value = P(ii, jj);
1311 |
1312 | % Format data value
1313 | if strcmp(axes_display, 'data-percent')
1314 | text_str = sprintf(sprintf('%%.%if%%%%', axes_precision(jj)), data_value*100);
1315 | else
1316 | text_str = sprintf(sprintf('%%.%if', axes_precision(jj)), data_value);
1317 | end
1318 |
1319 | % Display axes text
1320 | text(ax, x_pos, y_pos, text_str,...
1321 | 'Units', 'Data',...
1322 | 'Color', axes_font_color(ii, :),...
1323 | 'FontName', axes_font,...
1324 | 'FontSize', axes_font_size,...
1325 | 'HorizontalAlignment', 'center',...
1326 | 'VerticalAlignment', 'middle');
1327 | end
1328 | end
1329 |
1330 | switch fill_option{ii}
1331 | case 'on'
1332 | % Fill area within polygon
1333 | h = patch(ax, x_points, y_points, colors(ii, :),...
1334 | 'EdgeColor', 'none',...
1335 | 'FaceAlpha', fill_transparency(ii));
1336 |
1337 | % Turn off legend annotation
1338 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1339 |
1340 | case 'interp'
1341 | % Fill area within polygon
1342 | c_data = reshape(fill_cdata, [], 1);
1343 | h = patch(ax, x_points, y_points, c_data,...
1344 | 'EdgeColor', 'none',...
1345 | 'FaceAlpha', fill_transparency(ii));
1346 |
1347 | % Turn off legend annotation
1348 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1349 | end
1350 | end
1351 |
1352 | % Save to figure handle
1353 | fig.UserData = S_info;
1354 |
1355 | % Check if axes shaded is on
1356 | if strcmp(axes_shaded, 'on')
1357 | % Check if any axes direction was flipped
1358 | if any(axes_direction_index)
1359 | % Flip shaded limits
1360 | A_flip = P_shaded(:, axes_direction_index);
1361 | A_flip = flipud(A_flip);
1362 | P_shaded(:, axes_direction_index) = A_flip;
1363 | end
1364 |
1365 | % Iterate through number of shaded groups
1366 | for ii = 1:num_shaded
1367 | % Initialize
1368 | P_shaded_lower = P_shaded(2*ii-1, :);
1369 | P_shaded_upper = P_shaded(2*ii, :);
1370 | P_temp = [P_shaded_lower; P_shaded_upper];
1371 |
1372 | % Polar verticies of patch
1373 | P_temp = [P_temp, P_temp(:, 1)];
1374 | P_temp = [P_temp(1, :), P_temp(2, :)];
1375 | patch_rho = reshape(P_temp, 1, []);
1376 | patch_theta = [theta, theta];
1377 |
1378 | % Convert polar to cartesian coordinates
1379 | [x_points, y_points] = pol2cart(patch_theta, patch_rho);
1380 |
1381 | % Interweave lower and upper limits
1382 | x_points = reshape(x_points, [], 2)';
1383 | y_points = reshape(y_points, [], 2)';
1384 |
1385 | x_points = x_points(:);
1386 | y_points = y_points(:);
1387 |
1388 | % Increment through groups of four vertices at a time
1389 | for jj = 1:2:length(x_points)-2
1390 | % Verticies and face points
1391 | v = [x_points(jj:jj+3), y_points(jj:jj+3)];
1392 | f = [1 2 4 3];
1393 |
1394 | % Patch polygon
1395 | h = patch(ax, 'Faces', f, 'Vertices', v,...
1396 | 'FaceColor', axes_shaded_color{ii},...
1397 | 'EdgeColor', 'none',...
1398 | 'FaceAlpha', axes_shaded_transparency(ii));
1399 |
1400 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1401 | end
1402 | end
1403 | end
1404 |
1405 | % Find object handles
1406 | text_handles = findobj(ax.Children,...
1407 | 'Type', 'Text');
1408 | patch_handles = findobj(ax.Children,...
1409 | 'Type', 'Patch');
1410 | isocurve_handles = findobj(ax.Children,...
1411 | 'Color', axes_color,...
1412 | '-and', 'Type', 'Line');
1413 | plot_handles = findobj(ax.Children, '-not',...
1414 | 'Color', axes_color,...
1415 | '-and', 'Type', 'Line');
1416 |
1417 | % Manually set the stack order
1418 | uistack(plot_handles, 'bottom');
1419 | uistack(patch_handles, 'bottom');
1420 | uistack(isocurve_handles, 'bottom');
1421 | uistack(text_handles, 'top');
1422 |
1423 | %%% Labels %%%
1424 | % Check if axes labels rotate is on
1425 | if strcmp(axes_labels_rotate, 'on')
1426 | % Find number of degrees to rotate text by
1427 | rotate_deg = rad2deg(text_rotation(theta));
1428 |
1429 | % Find the horizontal alignments to align closer to axes
1430 | horz_aligns = text_alignment(theta);
1431 | else
1432 | % No rotation
1433 | rotate_deg = zeros(1, length(theta));
1434 | end
1435 |
1436 | % Check labels argument
1437 | if ~strcmp(axes_labels, 'none')
1438 | % Iterate through number of data points
1439 | for ii = 1:length(axes_labels)
1440 | % Horizontal text alignment by quadrant
1441 | [horz_align, ~] = quadrant_position(theta(ii));
1442 |
1443 | % Check if axes labels rotate is on
1444 | if strcmp(axes_labels_rotate, 'on')
1445 | % Adjust horizontal text alignment
1446 | horz_align = horz_aligns{ii};
1447 | end
1448 |
1449 | % Convert polar to cartesian coordinates
1450 | [x_pos, y_pos] = pol2cart(theta(ii), rho(end)+axes_labels_offset);
1451 |
1452 | % Display text label
1453 | text(ax, x_pos, y_pos, axes_labels{ii},...
1454 | 'Units', 'Data',...
1455 | 'HorizontalAlignment', horz_align,...
1456 | 'VerticalAlignment', 'middle',...
1457 | 'EdgeColor', axes_labels_edge,...
1458 | 'BackgroundColor', background_color,...
1459 | 'FontName', label_font,...
1460 | 'FontSize', label_font_size,...
1461 | 'Interpreter', axes_interpreter{ii},...
1462 | 'Rotation', rotate_deg(ii));
1463 | end
1464 | end
1465 |
1466 | function horz_aligns = text_alignment(theta)
1467 | % Pre-allocate cell
1468 | horz_aligns = cell(length(theta), 1);
1469 | horz_aligns(:) = {'center'};
1470 |
1471 | % Iterate through each theta
1472 | for kk = 1:length(theta)
1473 | % Adjust horizontal alignment accordingly
1474 | if theta(kk) <= pi/2
1475 | horz_aligns{kk} = 'left';
1476 | elseif theta(kk) < 3*pi/2
1477 | horz_aligns{kk} = 'right';
1478 | elseif theta(kk) <= 2*pi
1479 | horz_aligns{kk} = 'left';
1480 | end
1481 | end
1482 | end
1483 |
1484 | function rotate_deg = text_rotation(theta)
1485 | % Find how much to rotate text
1486 | rotate_deg = theta;
1487 |
1488 | % Iterate through each theta
1489 | for kk = 1:length(theta)
1490 | % Adjust sign and rotation accordingly
1491 | if theta(kk) == 0
1492 | rotate_deg(kk) = 0;
1493 | elseif theta(kk) > 0 && theta(kk) <= pi/2
1494 | rotate_deg(kk) = theta(kk);
1495 | elseif theta(kk) > pi/2 && theta(kk) < pi
1496 | rotate_deg(kk) = -(pi - theta(kk));
1497 | elseif theta(kk) == pi
1498 | rotate_deg(kk) = 0;
1499 | elseif theta(kk) > pi && theta(kk) < 3*pi/2
1500 | rotate_deg(kk) = -(pi - theta(kk));
1501 | elseif theta(kk) >= 3*pi/2
1502 | rotate_deg(kk) = -(2*pi - theta(kk));
1503 | end
1504 | end
1505 | end
1506 |
1507 | function [horz_align, vert_align] = quadrant_position(theta_point)
1508 | % Find out which quadrant the point is in
1509 | if theta_point == 0
1510 | quadrant = 0;
1511 | elseif theta_point == pi/2
1512 | quadrant = 1.5;
1513 | elseif theta_point == pi
1514 | quadrant = 2.5;
1515 | elseif theta_point == 3*pi/2
1516 | quadrant = 3.5;
1517 | elseif theta_point == 2*pi
1518 | quadrant = 0;
1519 | elseif theta_point > 0 && theta_point < pi/2
1520 | quadrant = 1;
1521 | elseif theta_point > pi/2 && theta_point < pi
1522 | quadrant = 2;
1523 | elseif theta_point > pi && theta_point < 3*pi/2
1524 | quadrant = 3;
1525 | elseif theta_point > 3*pi/2 && theta_point < 2*pi
1526 | quadrant = 4;
1527 | end
1528 |
1529 | % Adjust label alignment depending on quadrant
1530 | switch quadrant
1531 | case 0
1532 | horz_align = 'left';
1533 | vert_align = 'middle';
1534 | case 1
1535 | horz_align = 'left';
1536 | vert_align = 'bottom';
1537 | case 1.5
1538 | horz_align = 'center';
1539 | vert_align = 'bottom';
1540 | case 2
1541 | horz_align = 'right';
1542 | vert_align = 'bottom';
1543 | case 2.5
1544 | horz_align = 'right';
1545 | vert_align = 'middle';
1546 | case 3
1547 | horz_align = 'right';
1548 | vert_align = 'top';
1549 | case 3.5
1550 | horz_align = 'center';
1551 | vert_align = 'top';
1552 | case 4
1553 | horz_align = 'left';
1554 | vert_align = 'top';
1555 | end
1556 | end
1557 | end
--------------------------------------------------------------------------------
/spider_plot_R2019b.m:
--------------------------------------------------------------------------------
1 | function varargout = spider_plot_R2019b(P, options)
2 | %spider_plot_R2019b Create a spider or radar plot with individual axes.
3 | %
4 | % Syntax:
5 | % spider_plot_R2019b(P)
6 | % spider_plot_R2019b(P, Name, Value, ...)
7 | % h = spider_plot_R2019b(_)
8 | %
9 | % Documentation:
10 | % Please refer to the MathWorks File Exchange or GitHub page for the
11 | % detailed documentation and examples.
12 |
13 | %%% Argument Validation %%%
14 | arguments
15 | P (:, :) double
16 | options.AxesLabels {validateAxesLabels(options.AxesLabels, P)} = cellstr("Label " + (1:size(P, 2)))
17 | options.AxesInterval (1, 1) double {mustBeInteger, mustBePositive} = 3
18 | options.AxesPrecision (:, :) double {mustBeInteger, mustBeNonnegative} = 1
19 | options.AxesDisplay char {mustBeMember(options.AxesDisplay, {'all', 'none', 'one', 'data', 'data-percent'})} = 'all'
20 | options.AxesLimits double {validateAxesLimits(options.AxesLimits, P)} = [];
21 | options.FillOption {mustBeMember(options.FillOption, {'off', 'on', 'interp'})} = 'off'
22 | options.FillTransparency double {mustBeGreaterThanOrEqual(options.FillTransparency, 0), mustBeLessThanOrEqual(options.FillTransparency, 1)} = 0.2
23 | options.Color = get(groot,'defaultAxesColorOrder')
24 | options.LineStyle = '-'
25 | options.LineWidth (:, :) double {mustBePositive} = 2
26 | options.LineTransparency double {mustBeGreaterThanOrEqual(options.LineTransparency, 0), mustBeLessThanOrEqual(options.LineTransparency, 1)} = 1
27 | options.Marker = 'o'
28 | options.MarkerSize (:, :) double {mustBePositive} = 36
29 | options.MarkerTransparency double {mustBeGreaterThanOrEqual(options.MarkerTransparency, 0), mustBeLessThanOrEqual(options.MarkerTransparency, 1)} = 1
30 | options.AxesFont char = 'Helvetica'
31 | options.LabelFont char = 'Helvetica'
32 | options.AxesFontSize (1, 1) double {mustBePositive} = 10
33 | options.AxesFontColor = [0, 0, 0]
34 | options.LabelFontSize (1, 1) double {mustBePositive} = 10
35 | options.Direction char {mustBeMember(options.Direction, {'counterclockwise', 'clockwise'})} = 'clockwise'
36 | options.AxesDirection = 'normal'
37 | options.AxesLabelsOffset (1, 1) double {mustBeNonnegative} = 0.2
38 | options.AxesDataOffset (1, 1) double {mustBeNonnegative} = 0.1
39 | options.AxesScaling = 'linear'
40 | options.AxesColor = [0.6, 0.6, 0.6]
41 | options.AxesLabelsEdge = 'k'
42 | options.AxesOffset (1, 1) double {mustBeNonnegative, mustBeInteger} = 1
43 | options.AxesZoom double {mustBeGreaterThanOrEqual(options.AxesZoom, 0), mustBeLessThanOrEqual(options.AxesZoom, 1)} = 0.7 % Axes scale
44 | options.AxesHorzAlign char {mustBeMember(options.AxesHorzAlign, {'center', 'left', 'right', 'quadrant'})} = 'center' % Horizontal alignment of axes labels
45 | options.AxesVertAlign char {mustBeMember(options.AxesVertAlign, {'middle', 'top', 'cap', 'bottom', 'baseline', 'quadrant'})} = 'middle' % Vertical alignment of axes labels
46 | options.PlotVisible {mustBeMember(options.PlotVisible, {'off', 'on'})} = 'on'
47 | options.AxesTickLabels {mustBeText} = 'data'
48 | options.AxesInterpreter {mustBeText} = 'tex'
49 | options.AxesTickInterpreter {mustBeText} = 'tex'
50 | options.BackgroundColor = [1, 1, 1];
51 | options.MinorGrid {mustBeMember(options.MinorGrid, {'off', 'on'})} = 'off'
52 | options.MinorGridInterval (1, 1) double {mustBePositive, mustBeInteger} = 2
53 | options.AxesZero {mustBeMember(options.AxesZero, {'off', 'on'})} = 'off'
54 | options.AxesZeroColor = [0, 0, 0];
55 | options.AxesZeroWidth (1, 1) double {mustBePositive} = 2
56 | options.AxesRadial {mustBeMember(options.AxesRadial, {'off', 'on'})} = 'on'
57 | options.AxesWeb {mustBeMember(options.AxesWeb, {'off', 'on'})} = 'on'
58 | options.AxesShaded {mustBeMember(options.AxesShaded, {'off', 'on'})} = 'off'
59 | options.AxesShadedLimits cell = {}
60 | options.AxesShadedColor = 'g'
61 | options.AxesShadedTransparency double {mustBeGreaterThanOrEqual(options.AxesShadedTransparency, 0), mustBeLessThanOrEqual(options.AxesShadedTransparency, 1)} = 0.2 % Shading alpha
62 | options.AxesLabelsRotate {mustBeMember(options.AxesLabelsRotate, {'off', 'on'})} = 'off'
63 | options.AxesHandle = gobjects
64 | options.ErrorBars {mustBeMember(options.ErrorBars, {'off', 'on'})} = 'off'
65 | options.AxesWebType {mustBeMember(options.AxesWebType, {'web', 'circular'})} = 'web'
66 | options.AxesTickFormat {mustBeText} = 'default'
67 | options.FillCData = []
68 | options.ErrorPositive = []
69 | options.ErrorNegative = []
70 | options.AxesStart = pi/2
71 | options.AxesRadialLineStyle = '-';
72 | options.AxesRadialLineWidth = 1.5;
73 | options.AxesWebLineStyle = '-';
74 | options.AxesWebLineWidth = 1;
75 | end
76 |
77 | %%% Data Properties %%%
78 | % Point properties
79 | [num_data_groups, num_data_points] = size(P);
80 |
81 | %%% Validate Axes Limits
82 | if isempty(options.AxesLimits)
83 | % Replace Inf with NaN
84 | P_limits = P;
85 | inf_index = isinf(P_limits);
86 | P_limits(inf_index) = nan;
87 |
88 | % Default arguments
89 | options.AxesLimits = [min(P_limits, [], 1); max(P_limits, [], 1)];
90 | end
91 |
92 | %%% Validate Colors
93 | % Check if there is enough colors
94 | if size(options.Color, 1) < num_data_groups
95 | warning('Warning: Default colors have been applied to match the number of data group. Please enter in "Color" option if specific colors are desired.');
96 | options.Color = lines(num_data_groups);
97 | end
98 |
99 | %%% Validate Properties
100 | % Check if axes offset is valid
101 | if options.AxesOffset > options.AxesInterval
102 | error('Error: Invalid axes offset entry. Please enter in an integer value that is between [0, axes_interval].');
103 | end
104 |
105 | % Check if axes start is valid
106 | if ~(options.AxesStart >= 0 && options.AxesStart <= 2*pi)
107 | error('Error: Please select an axes start value between [0, 2pi].')
108 | end
109 |
110 | % Check if axes shaded limits is empty
111 | if isempty(options.AxesShadedLimits)
112 | options.AxesShadedLimits = {options.AxesLimits};
113 | end
114 |
115 | %%% Validate Axes Precision
116 | % Check if axes precision is numeric
117 | if isnumeric(options.AxesPrecision)
118 | % Check is length is one
119 | if length(options.AxesPrecision) == 1
120 | % Repeat array to number of data points
121 | options.AxesPrecision = repmat(options.AxesPrecision, num_data_points, 1);
122 | elseif length(options.AxesPrecision) ~= num_data_points
123 | error('Error: Please specify the same number of axes precision as number of data points.');
124 | end
125 | else
126 | error('Error: Please make sure the axes precision is a numeric value.');
127 | end
128 |
129 | %%% Validate Line Style
130 | % Check if line style is a char
131 | if ischar(options.LineStyle)
132 | % Convert to cell array of char
133 | options.LineStyle = cellstr(options.LineStyle);
134 |
135 | % Repeat cell to number of data groups
136 | options.LineStyle = repmat(options.LineStyle, num_data_groups, 1);
137 | elseif iscellstr(options.LineStyle) %#ok<*ISCLSTR>
138 | % Check is length is one
139 | if length(options.LineStyle) == 1
140 | % Repeat cell to number of data groups
141 | options.LineStyle = repmat(options.LineStyle, num_data_groups, 1);
142 | elseif length(options.LineStyle) ~= num_data_groups
143 | error('Error: Please specify the same number of line styles as number of data groups.');
144 | end
145 | else
146 | error('Error: Please make sure the line style is a char or a cell array of char.');
147 | end
148 |
149 | %%% Validate Line Width
150 | % Check if line width is numeric
151 | if isnumeric(options.LineWidth)
152 | % Check is length is one
153 | if length(options.LineWidth) == 1
154 | % Repeat array to number of data groups
155 | options.LineWidth = repmat(options.LineWidth, num_data_groups, 1);
156 | elseif length(options.LineWidth) ~= num_data_groups
157 | error('Error: Please specify the same number of line width as number of data groups.');
158 | end
159 | else
160 | error('Error: Please make sure the line width is a numeric value.');
161 | end
162 |
163 | %%% Validate Marker
164 | % Check if marker type is a char
165 | if ischar(options.Marker)
166 | % Convert to cell array of char
167 | options.Marker = cellstr(options.Marker);
168 |
169 | % Repeat cell to number of data groups
170 | options.Marker = repmat(options.Marker, num_data_groups, 1);
171 | elseif iscellstr(options.Marker)
172 | % Check is length is one
173 | if length(options.Marker) == 1
174 | % Repeat cell to number of data groups
175 | options.Marker = repmat(options.Marker, num_data_groups, 1);
176 | elseif length(options.Marker) ~= num_data_groups
177 | error('Error: Please specify the same number of line styles as number of data groups.');
178 | end
179 | else
180 | error('Error: Please make sure the line style is a char or a cell array of char.');
181 | end
182 |
183 | %%% Validate Marker Size
184 | % Check if line width is numeric
185 | if isnumeric(options.MarkerSize)
186 | if length(options.MarkerSize) == 1
187 | % Repeat array to number of data groups
188 | options.MarkerSize = repmat(options.MarkerSize, num_data_groups, 1);
189 | elseif length(options.MarkerSize) ~= num_data_groups
190 | error('Error: Please specify the same number of line width as number of data groups.');
191 | end
192 | else
193 | error('Error: Please make sure the line width is numeric.');
194 | end
195 |
196 | %%% Validate Axes Scaling
197 | % Check if axes scaling is valid
198 | if any(~ismember(options.AxesScaling, {'linear', 'log'}))
199 | error('Error: Invalid axes scaling entry. Please enter in "linear" or "log" to set axes scaling.');
200 | end
201 |
202 | % Check if axes scaling is a cell
203 | if iscell(options.AxesScaling)
204 | % Check is length is one
205 | if length(options.AxesScaling) == 1
206 | % Repeat array to number of data points
207 | options.AxesScaling = repmat(options.AxesScaling, num_data_points, 1);
208 | elseif length(options.AxesScaling) ~= num_data_points
209 | error('Error: Please specify the same number of axes scaling as number of data points.');
210 | end
211 | else
212 | % Repeat array to number of data points
213 | options.AxesScaling = repmat({options.AxesScaling}, num_data_points, 1);
214 | end
215 |
216 | %%% Validate Axes Direction
217 | % Check if axes direction is a cell
218 | if iscell(options.AxesDirection)
219 | % Check is length is one
220 | if length(options.AxesDirection) == 1
221 | % Repeat array to number of data points
222 | options.AxesDirection = repmat(options.AxesDirection, num_data_points, 1);
223 | elseif length(options.AxesDirection) ~= num_data_points
224 | error('Error: Please specify the same number of axes direction as number of data points.');
225 | end
226 | else
227 | % Repeat array to number of data points
228 | options.AxesDirection = repmat({options.AxesDirection}, num_data_points, 1);
229 | end
230 |
231 | %%% Validate Fill Option
232 | % Check if fill option is a cell
233 | if iscell(options.FillOption)
234 | % Check is length is one
235 | if length(options.FillOption) == 1
236 | % Repeat array to number of data groups
237 | options.FillOption = repmat(options.FillOption, num_data_groups, 1);
238 | elseif length(options.FillOption) ~= num_data_groups
239 | error('Error: Please specify the same number of fill option as number of data groups.');
240 | end
241 | else
242 | % Repeat array to number of data groups
243 | options.FillOption = repmat({options.FillOption}, num_data_groups, 1);
244 | end
245 |
246 | % Check fill data
247 | if all(strcmp(options.FillOption, 'interp'))
248 | if isempty(options.FillCData)
249 | error('Error: Please enter in a valid fill cdata.');
250 | else
251 | if length(options.FillCData) ~= num_data_points
252 | error('Error: Please make sure that fill cdata matches the number of data points.');
253 | end
254 | end
255 | end
256 |
257 | %%% Validate Fill Transparency
258 | % Check if fill transparency is numeric
259 | if isnumeric(options.FillTransparency)
260 | % Check is length is one
261 | if length(options.FillTransparency) == 1
262 | % Repeat array to number of data groups
263 | options.FillTransparency = repmat(options.FillTransparency, num_data_groups, 1);
264 | elseif length(options.FillTransparency) ~= num_data_groups
265 | error('Error: Please specify the same number of fill transparency as number of data groups.');
266 | end
267 | else
268 | error('Error: Please make sure the transparency is a numeric value.');
269 | end
270 |
271 | %%% Validate Line Transparency
272 | % Check if line transparency is numeric
273 | if isnumeric(options.LineTransparency)
274 | % Check is length is one
275 | if length(options.LineTransparency) == 1
276 | % Repeat array to number of data groups
277 | options.LineTransparency = repmat(options.LineTransparency, num_data_groups, 1);
278 | elseif length(options.LineTransparency) ~= num_data_groups
279 | error('Error: Please specify the same number of line transparency as number of data groups.');
280 | end
281 | else
282 | error('Error: Please make sure the transparency is a numeric value.');
283 | end
284 |
285 | %%% Validate Marker Transparency
286 | % Check if marker transparency is numeric
287 | if isnumeric(options.MarkerTransparency)
288 | % Check is length is one
289 | if length(options.MarkerTransparency) == 1
290 | % Repeat array to number of data groups
291 | options.MarkerTransparency = repmat(options.MarkerTransparency, num_data_groups, 1);
292 | elseif length(options.MarkerTransparency) ~= num_data_groups
293 | error('Error: Please specify the same number of marker transparency as number of data groups.');
294 | end
295 | else
296 | error('Error: Please make sure the transparency is a numeric value.');
297 | end
298 |
299 | %%% Validate Axes Font Color
300 | % Check if axes display is data
301 | if ismember(options.AxesDisplay, {'data', 'data-percent'})
302 | if size(options.AxesFontColor, 1) ~= num_data_groups
303 | % Check axes font color dimensions
304 | if size(options.AxesFontColor, 1) == 1 && size(options.AxesFontColor, 2) == 3
305 | options.AxesFontColor = repmat(options.AxesFontColor, num_data_groups, 1);
306 | else
307 | error('Error: Please specify axes font color as a RGB triplet normalized to 1.');
308 | end
309 | end
310 | end
311 |
312 | %%% Validate Axes Tick Labels
313 | % Check if axes tick labels is valid
314 | if iscell(options.AxesTickLabels)
315 | if length(options.AxesTickLabels) ~= options.AxesInterval+1
316 | error('Error: Invalid axes tick labels entry. Please enter in a cell array with the same length of axes interval + 1.');
317 | end
318 | else
319 | if ~strcmp(options.AxesTickLabels, 'data')
320 | error('Error: Invalid axes tick labels entry. Please enter in "data" or a cell array of desired tick labels.');
321 | end
322 | end
323 |
324 | %%% Validate Axes Interpreter
325 | % Check if axes interpreter is valid
326 | if any(~ismember(options.AxesInterpreter, {'tex', 'latex', 'none'}))
327 | error('Error: Please enter either "tex", "latex", or "none" for axes interpreter option.');
328 | end
329 |
330 | % Check if axes tick interpreter is valid
331 | if any(~ismember(options.AxesTickInterpreter, {'tex', 'latex', 'none'}))
332 | error('Error: Please enter either "tex", "latex", or "none" for axes tick interpreter option.');
333 | end
334 |
335 | % Check if axes interpreter is a char
336 | if ischar(options.AxesInterpreter)
337 | % Convert to cell array of char
338 | options.AxesInterpreter = cellstr(options.AxesInterpreter);
339 |
340 | % Repeat cell to number of axes labels
341 | options.AxesInterpreter = repmat(options.AxesInterpreter, length(options.AxesLabels), 1);
342 | elseif iscellstr(options.AxesInterpreter)
343 | % Check is length is one
344 | if length(options.AxesInterpreter) == 1
345 | % Repeat cell to number of axes labels
346 | options.AxesInterpreter = repmat(options.AxesInterpreter, length(options.AxesLabels), 1);
347 | elseif length(options.AxesInterpreter) ~= length(options.AxesLabels)
348 | error('Error: Please specify the same number of axes interpreters as axes labels.');
349 | end
350 | else
351 | error('Error: Please make sure the axes interpreter is a char or a cell array of char.');
352 | end
353 |
354 | % Check if axes tick interpreter is a char
355 | if ischar(options.AxesTickInterpreter)
356 | % Convert to cell array of char
357 | options.AxesTickInterpreter = cellstr(options.AxesTickInterpreter);
358 |
359 | % Repeat cell to number of axes labels
360 | options.AxesTickInterpreter = repmat(options.AxesTickInterpreter, length(options.AxesLabels), 1);
361 | elseif iscellstr(options.AxesTickInterpreter)
362 | % Check is length is one
363 | if length(options.AxesTickInterpreter) == 1
364 | % Repeat cell to number of axes labels
365 | options.AxesTickInterpreter = repmat(options.AxesTickInterpreter, length(options.AxesLabels), 1);
366 | elseif length(options.AxesTickInterpreter) ~= length(options.AxesLabels)
367 | error('Error: Please specify the same number of axes tick interpreters as axes labels.');
368 | end
369 | else
370 | error('Error: Please make sure the axes tick interpreter is a char or a cell array of char.');
371 | end
372 |
373 | %%% Validate Axes Shaded Limits
374 | % Iterate through axes shaded limits
375 | for ii = 1:length(options.AxesShadedLimits)
376 | % Initialize
377 | axes_shaded_limit = options.AxesShadedLimits{ii};
378 |
379 | % Check if the axes shaded limits same length as the number of points
380 | if size(axes_shaded_limit, 1) ~= 2 || size(axes_shaded_limit, 2) ~= num_data_points
381 | error('Error: Please make sure the min and max axes shaded limits match the number of data points.');
382 | end
383 |
384 | % Check if within min and max axes limits
385 | if any(axes_shaded_limit(1, :) < options.AxesLimits(1, :)) ||...
386 | any(axes_shaded_limit(2, :) > options.AxesLimits(2, :))
387 | error('Error: Please make sure the axes shaded limits are within the min and max axes limits.');
388 | end
389 | end
390 |
391 | % Check if axes shaded color is a char
392 | if ischar(options.AxesShadedColor)
393 | % Convert to cell array of char
394 | options.AxesShadedColor = cellstr(options.AxesShadedColor);
395 |
396 | % Repeat cell to number of axes shaded limits groups
397 | options.AxesShadedColor = repmat(options.AxesShadedColor, length(options.AxesShadedLimits), 1);
398 | elseif iscellstr(options.AxesShadedColor)
399 | % Check is length is one
400 | if length(options.AxesShadedColor) == 1
401 | % Repeat cell to number of axes shaded limits groups
402 | options.AxesShadedColor = repmat(options.AxesShadedColor, length(options.AxesShadedLimits), 1);
403 | end
404 | end
405 |
406 | % Check if axes shaded color is valid
407 | if length(options.AxesShadedColor) ~= length(options.AxesShadedLimits)
408 | error('Error: Please specify the same number of axes shaded color as number of axes shaded limits groups.');
409 | end
410 |
411 | % Check is length is one
412 | if length(options.AxesShadedTransparency) == 1
413 | % Repeat array to number of axes shaded limits groups
414 | options.AxesShadedTransparency = repmat(options.AxesShadedTransparency, length(options.AxesShadedLimits), 1);
415 | elseif length(options.AxesShadedTransparency) ~= length(options.AxesShadedLimits)
416 | error('Error: Please specify the same number of axes shaded transparency as number axes shaded limits groups.');
417 | end
418 |
419 | %%% Validate Axes Tick Format
420 | % Check if axes tick format is a char
421 | if ischar(options.AxesTickFormat)
422 | % Convert to cell array of char
423 | options.AxesTickFormat = cellstr(options.AxesTickFormat);
424 |
425 | % Repeat cell to number of axes shaded limits groups
426 | options.AxesTickFormat = repmat(options.AxesTickFormat, num_data_points, 1);
427 | elseif iscellstr(options.AxesTickFormat)
428 | % Check is length is one
429 | if length(options.AxesTickFormat) == 1
430 | % Repeat cell to number of axes shaded limits groups
431 | options.AxesTickFormat = repmat(options.AxesTickFormat, num_data_points, 1);
432 | end
433 | else
434 | error('Error: Please a character array or cell of character array for axes tick format.');
435 | end
436 |
437 | % Check if error positive and error negative are valid
438 | if strcmp(options.ErrorBars, 'on') &&...
439 | ~isempty(options.ErrorPositive) &&...
440 | ~isempty(options.ErrorNegative)
441 | % Check that the length match the data points
442 | if length(options.ErrorPositive) ~= num_data_points
443 | error('Error: Please make sure the number of error positive elements equal the data points');
444 | end
445 |
446 | % Check that the length match the data points
447 | if length(options.ErrorNegative) ~= num_data_points
448 | error('Error: Please make sure the number of error negative elements equal the data points');
449 | end
450 | end
451 |
452 | %%% Axes Scaling Properties %%%
453 | % Selected data
454 | P_selected = P;
455 |
456 | % Check axes scaling option
457 | log_index = strcmp(options.AxesScaling, 'log');
458 |
459 | % If any log scaling is specified
460 | if any(log_index)
461 | % Initialize copy
462 | P_log = P_selected(:, log_index);
463 |
464 | % Logarithm of base 10, account for numbers less than 1
465 | P_log = sign(P_log) .* log10(abs(P_log));
466 |
467 | % Minimum and maximun log limits
468 | min_limit = min(min(fix(P_log)));
469 | max_limit = max(max(ceil(P_log)));
470 | recommended_axes_interval = max_limit - min_limit;
471 |
472 | % Warning message
473 | warning('For the log scale values, recommended axes limit is [%i, %i] with an axes interval of %i.',...
474 | 10^min_limit, 10^max_limit, recommended_axes_interval);
475 |
476 | % Replace original
477 | P_selected(:, log_index) = P_log;
478 | end
479 |
480 | %%% Figure Properties %%%
481 | % Check if axes handle is specified
482 | if isempty(properties(options.AxesHandle))
483 | % Grab current figure
484 | fig = gcf;
485 |
486 | % Reset axes
487 | cla reset;
488 |
489 | % Current axes handle
490 | ax = gca;
491 | else
492 | % Use specified axes handle
493 | ax = options.AxesHandle;
494 | axes(ax);
495 |
496 | % Grab figure handle of specified axes handle
497 | fig = ax.Parent;
498 | end
499 |
500 | % Check for output arguments
501 | if nargout > 1
502 | error('Error: Too many output arguments assigned.');
503 | end
504 | varargout{1} = fig;
505 |
506 | % Set figure and axes background
507 | if isprop(fig, "Color")
508 | fig.Color = options.BackgroundColor;
509 | end
510 |
511 | ax.Color = options.BackgroundColor;
512 |
513 | % Axis limits
514 | scaling_factor = 1 + (1 - options.AxesZoom);
515 | hold(ax, 'on');
516 | axis(ax, 'square');
517 | axis(ax, [-1, 1, -1, 1] * scaling_factor);
518 |
519 | % Axis properties
520 | ax.XTickLabel = [];
521 | ax.YTickLabel = [];
522 | ax.XColor = 'none';
523 | ax.YColor = 'none';
524 |
525 | % Polar increments
526 | theta_increment = 2*pi/num_data_points;
527 | full_interval = options.AxesInterval+1;
528 | rho_offset = options.AxesOffset/full_interval;
529 |
530 | %%% Scale Data %%%
531 | % Number of shaded rows
532 | num_shaded = length(options.AxesShadedLimits);
533 | shaded_rows = num_shaded * 2;
534 |
535 | % Check if axes shaded is on
536 | if strcmp(options.AxesShaded, 'on')
537 | % Concatenate
538 | all_shaded_limits = vertcat(options.AxesShadedLimits{:});
539 |
540 | % If any log scaling is specified
541 | if any(log_index)
542 | % Initialize copy
543 | A_log = all_shaded_limits(:, log_index);
544 |
545 | % Logarithm of base 10, account for numbers less than 1
546 | A_log = sign(A_log) .* log10(abs(A_log));
547 |
548 | % Update
549 | all_shaded_limits(:, log_index) = A_log;
550 | end
551 |
552 | % Append
553 | P_selected = [P_selected; all_shaded_limits];
554 | end
555 |
556 | % Pre-allocation
557 | P_scaled = zeros(size(P_selected));
558 | axes_range = zeros(3, num_data_points);
559 |
560 | % Check axes scaling option
561 | axes_direction_index = strcmp(options.AxesDirection, 'reverse');
562 |
563 | % Iterate through number of data points
564 | for ii = 1:num_data_points
565 | % Check for one data group and no axes limits
566 | if num_data_groups == 1 && isempty(options.AxesLimits)
567 | % Group of points
568 | group_points = P_selected(:, :);
569 | else
570 | % Group of points
571 | group_points = P_selected(:, ii);
572 | end
573 |
574 | % Replace Inf with NaN
575 | inf_index = isinf(group_points);
576 | group_points(inf_index) = nan;
577 |
578 | % Check for log axes scaling option
579 | if log_index(ii)
580 | % Minimum and maximun log limits
581 | min_value = min(fix(group_points));
582 | max_value = max(ceil(group_points));
583 | else
584 | % Automatically the range of each group
585 | min_value = min(group_points);
586 | max_value = max(group_points);
587 | end
588 |
589 | % Range of min and max values
590 | range = max_value - min_value;
591 |
592 | % Check if options.AxesLimits is not empty
593 | if ~isempty(options.AxesLimits)
594 | % Check for log axes scaling option
595 | if log_index(ii)
596 | % Logarithm of base 10, account for numbers less than 1
597 | options.AxesLimits(:, ii) = sign(options.AxesLimits(:, ii)) .* log10(abs(options.AxesLimits(:, ii)));
598 | end
599 |
600 | % Manually set the range of each group
601 | min_value = options.AxesLimits(1, ii);
602 | max_value = options.AxesLimits(2, ii);
603 | range = max_value - min_value;
604 |
605 | % Check if the axes limits are within range of points
606 | if min_value > min(group_points) || max_value < max(group_points)
607 | error('Error: Please make sure the manually specified axes limits are within range of the data points.');
608 | end
609 | end
610 |
611 | % Check if range is valid
612 | if range == 0
613 | error('Error: Range of data values is not valid. Please specify the axes limits.');
614 | end
615 |
616 | % Scale points to range from [0, 1]
617 | P_scaled(:, ii) = ((P_selected(:, ii) - min_value) / range);
618 |
619 | % If reverse axes direction is specified
620 | if axes_direction_index(ii)
621 | % Store to array
622 | axes_range(:, ii) = [max_value; min_value; range];
623 | P_scaled(:, ii) = -(P_scaled(:, ii) - 1);
624 | else
625 | % Store to array
626 | axes_range(:, ii) = [min_value; max_value; range];
627 | end
628 |
629 | % Add offset of [rho_offset] and scaling factor of [1 - rho_offset]
630 | P_scaled(:, ii) = P_scaled(:, ii) * (1 - rho_offset) + rho_offset;
631 | end
632 |
633 | % Check if axes shaded is on
634 | if strcmp(options.AxesShaded, 'on')
635 | P_shaded = P_scaled(end-shaded_rows+1:end, :);
636 | P_scaled = P_scaled(1:end-shaded_rows, :);
637 | end
638 |
639 | %%% Polar Axes %%%
640 | % Polar coordinates
641 | rho_increment = 1/full_interval;
642 | rho = 0:rho_increment:1;
643 |
644 | % Check specified direction of rotation
645 | switch options.Direction
646 | case 'counterclockwise'
647 | % Shift the starting axis
648 | theta = (0:theta_increment:2*pi) + options.AxesStart;
649 | case 'clockwise'
650 | % Shift the starting axis
651 | theta = (0:-theta_increment:-2*pi) + options.AxesStart;
652 | end
653 |
654 | % Remainder after using a modulus of 2*pi
655 | theta = mod(theta, 2*pi);
656 |
657 | % Check if axes radial is toggled on
658 | if strcmp(options.AxesRadial, 'on')
659 | % Iterate through each theta
660 | for ii = 1:length(theta)-1
661 | % Convert polar to cartesian coordinates
662 | [x_axes, y_axes] = pol2cart(theta(ii), rho);
663 |
664 | % Plot webs
665 | h = plot(ax, x_axes, y_axes,...
666 | 'LineStyle', options.AxesRadialLineStyle,...
667 | 'LineWidth', options.AxesRadialLineWidth,...
668 | 'Color', options.AxesColor);
669 |
670 | % Turn off legend annotation
671 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
672 | end
673 | end
674 |
675 | % Check axes web type
676 | if strcmp(options.AxesWebType, 'web')
677 | theta_web = theta;
678 | elseif strcmp(options.AxesWebType, 'circular')
679 | theta_web = 0:((2*pi)/(2^7)):2*pi;
680 | end
681 |
682 | % Check if axes web is toggled on
683 | if strcmp(options.AxesWeb, 'on')
684 | % Iterate through each rho
685 | for ii = 2:length(rho)
686 | % Convert polar to cartesian coordinates
687 | [x_axes, y_axes] = pol2cart(theta_web, rho(ii));
688 |
689 | % Plot axes
690 | h = plot(ax, x_axes, y_axes,...
691 | 'LineStyle', options.AxesWebLineStyle,...
692 | 'LineWidth', options.AxesWebLineWidth,...
693 | 'Color', options.AxesColor);
694 |
695 | % Turn off legend annotation
696 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
697 | end
698 | end
699 |
700 | % Check if minor grid is toggled on
701 | if strcmp(options.MinorGrid, 'on')
702 | % Polar coordinates
703 | rho_minor_increment = 1/(full_interval*options.MinorGridInterval);
704 | rho_minor = rho(2):rho_minor_increment:1;
705 | rho_minor = setdiff(rho_minor, rho(2:end));
706 |
707 | % Iterate through each rho minor
708 | for ii = 1:length(rho_minor)
709 | % Convert polar to cartesian coordinates
710 | [x_axes, y_axes] = pol2cart(theta, rho_minor(ii));
711 |
712 | % Plot axes
713 | h = plot(ax, x_axes, y_axes, '--',...
714 | 'Color', options.AxesColor,...
715 | 'LineWidth', 0.5);
716 |
717 | % Turn off legend annotation
718 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
719 | end
720 | end
721 |
722 | % Check if axes zero is toggled on
723 | if strcmp(options.AxesZero, 'on') && strcmp(options.AxesDisplay, 'one')
724 | % Scale points to range from [0, 1]
725 | zero_scaled = (0 - min_value) / range;
726 |
727 | % If reverse axes direction is specified
728 | if strcmp(options.AxesDirection, 'reverse')
729 | zero_scaled = -zero_scaled - 1;
730 | end
731 |
732 | % Add offset of [rho_offset] and scaling factor of [1 - rho_offset]
733 | zero_scaled = zero_scaled * (1 - rho_offset) + rho_offset;
734 |
735 | % Convert polar to cartesian coordinates
736 | [x_axes, y_axes] = pol2cart(theta, zero_scaled);
737 |
738 | % Plot webs
739 | h = plot(ax, x_axes, y_axes,...
740 | 'LineWidth', options.AxesZeroWidth,...
741 | 'Color', options.AxesZeroColor);
742 |
743 | % Turn off legend annotation
744 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
745 | end
746 |
747 | % Set end index depending on axes display argument
748 | switch options.AxesDisplay
749 | case 'all'
750 | theta_end_index = length(theta)-1;
751 | case 'one'
752 | theta_end_index = 1;
753 | case 'none'
754 | theta_end_index = 0;
755 | case 'data'
756 | theta_end_index = 0;
757 | case 'data-percent'
758 | theta_end_index = 0;
759 | end
760 |
761 | % Rho start index and offset interval
762 | rho_start_index = options.AxesOffset+1;
763 | offset_interval = full_interval - options.AxesOffset;
764 |
765 | % Alignment for axes labels
766 | horz_align = options.AxesHorzAlign;
767 | vert_align = options.AxesVertAlign;
768 |
769 | % Iterate through each theta
770 | for ii = 1:theta_end_index
771 | % Convert polar to cartesian coordinates
772 | [x_axes, y_axes] = pol2cart(theta(ii), rho);
773 |
774 | % Check if horizontal alignment is quadrant based
775 | if strcmp(options.AxesHorzAlign, 'quadrant')
776 | % Alignment based on quadrant
777 | [horz_align, ~] = quadrant_position(theta(ii));
778 | end
779 |
780 | % Check if vertical alignment is quadrant based
781 | if strcmp(options.AxesVertAlign, 'quadrant')
782 | % Alignment based on quadrant
783 | [~, vert_align] = quadrant_position(theta(ii));
784 | end
785 |
786 | % Iterate through points on isocurve
787 | for jj = rho_start_index:length(rho)
788 | % Axes increment range
789 | min_value = axes_range(1, ii);
790 | range = axes_range(3, ii);
791 |
792 | % If reverse axes direction is specified
793 | if axes_direction_index(ii)
794 | % Axes increment value
795 | axes_value = min_value - (range/offset_interval) * (jj-rho_start_index);
796 | else
797 | % Axes increment value
798 | axes_value = min_value + (range/offset_interval) * (jj-rho_start_index);
799 | end
800 |
801 | % Check for log axes scaling option
802 | if log_index(ii)
803 | % Exponent to the tenth power
804 | axes_value = 10^axes_value;
805 | end
806 |
807 | % Display axes text
808 | if strcmp(options.AxesTickLabels, 'data')
809 | % Check axes tick format option
810 | if strcmp(options.AxesTickFormat{ii}, 'default')
811 | text_format = sprintf('%%.%if', options.AxesPrecision(ii));
812 | else
813 | text_format = options.AxesTickFormat{ii};
814 | end
815 |
816 | % Formatted axes tick text
817 | text_str = sprintf(text_format, axes_value);
818 | else
819 | text_str = options.AxesTickLabels{jj-options.AxesOffset};
820 | end
821 |
822 | t = text(ax, x_axes(jj), y_axes(jj), text_str,...
823 | 'Units', 'Data',...
824 | 'Color', options.AxesFontColor,...
825 | 'FontName', options.AxesFont,...
826 | 'FontSize', options.AxesFontSize,...
827 | 'HorizontalAlignment', horz_align,...
828 | 'VerticalAlignment', vert_align,...
829 | 'Interpreter', options.AxesTickInterpreter{ii});
830 | end
831 | end
832 |
833 | %%% Plot %%%
834 | % Check if any NaNs detected
835 | if any(isnan(P_scaled), 'all')
836 | % Set value to zero
837 | nan_index = isnan(P_scaled);
838 | P_scaled(nan_index) = 0;
839 | end
840 |
841 | % Check if error bars are desired
842 | if strcmp(options.ErrorBars, 'on')
843 | % Calculate mean and standard deviation
844 | P_mean = mean(P, 1);
845 | P_std = std(P, 0, 1);
846 |
847 | % Check if plus or minus error is specified
848 | if isempty(options.ErrorPositive) ||...
849 | isempty(options.ErrorNegative)
850 | % Default values
851 | options.ErrorPositive = P_std;
852 | options.ErrorNegative = P_std;
853 | end
854 |
855 | % Display to command window
856 | fprintf("Error Bar Properties\n");
857 | fprintf("--------------------\n")
858 | format_str = repmat('%.2f ', 1, length(P_mean));
859 | fprintf(" Average values: " + format_str + "\n", P_mean);
860 | fprintf("Standard deviation: " + format_str + "\n", P_std);
861 |
862 | % Mean +/- standard deviation
863 | P_mean = [P_mean; P_mean + options.ErrorPositive; P_mean - options.ErrorNegative];
864 |
865 | % Scale points to range from [0, 1] and apply offset
866 | P_mean = (P_mean - axes_range(1, :)) ./ axes_range(3, :);
867 | P_mean = P_mean * (1 - rho_offset) + rho_offset;
868 |
869 | % Convert from polar to cartesian
870 | A_theta = theta(1:end-1);
871 | A_scaled = P_mean;
872 | [x_points, y_points] = pol2cart(A_theta, A_scaled);
873 |
874 | % Scatter plot mean values
875 | scatter(ax, x_points(1, :), y_points(1, :),...
876 | 'Marker', options.Marker{1},...
877 | 'SizeData', options.MarkerSize(1),...
878 | 'MarkerFaceColor', options.Color(1, :),...
879 | 'MarkerEdgeColor', options.Color(1, :),...
880 | 'MarkerFaceAlpha', options.MarkerTransparency(1),...
881 | 'MarkerEdgeAlpha', options.MarkerTransparency(1),...
882 | 'Visible', options.PlotVisible);
883 |
884 | % Iterate through each group
885 | for ii = 1:size(x_points, 2)
886 | % Mean +- standard deviation
887 | X = x_points(2:3, ii)';
888 | Y = y_points(2:3, ii)';
889 |
890 | % Plot error bar
891 | h = plot(ax, X, Y,...
892 | 'LineStyle', options.LineStyle{1},...
893 | 'Color', options.Color(1, :),...
894 | 'LineWidth', options.LineWidth(1),...
895 | 'Visible', options.PlotVisible);
896 |
897 | % Turn off legend annotation
898 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
899 |
900 | % Perpendicular line
901 | v = [diff(X); diff(Y)];
902 | v = 0.05 * v / vecnorm(v);
903 |
904 | % Top and bottom
905 | for jj = 1:length(X)
906 | % Plot end tip
907 | x_endtip = [X(jj)+v(2), X(jj)-v(2)];
908 | y_endtip = [Y(jj)-v(1), Y(jj)+v(1)];
909 | h = plot(x_endtip, y_endtip,...
910 | 'LineStyle', options.LineStyle{1},...
911 | 'Color', options.Color(1, :),...
912 | 'LineWidth', options.LineWidth(1),...
913 | 'Visible', options.PlotVisible);
914 |
915 | % Turn off legend annotation
916 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
917 | end
918 | end
919 |
920 | % Plot empty line with combined attributes for legend
921 | plot(ax, nan, nan,...
922 | 'Marker', options.Marker{1},...
923 | 'MarkerSize', options.MarkerSize(1)/6,...
924 | 'MarkerFaceColor', options.Color(1, :),...
925 | 'MarkerEdgeColor', options.Color(1, :),...
926 | 'LineStyle', options.LineStyle{1},...
927 | 'Color', options.Color(1, :),...
928 | 'LineWidth', options.LineWidth(1),...
929 | 'Visible', options.PlotVisible);
930 | end
931 |
932 | % Pre-allocation
933 | S_info = struct(...
934 | 'data_group_num', nan(num_data_groups, 1),...
935 | 'perimeter', nan(num_data_groups, 1),...
936 | 'area', nan(num_data_groups, 1));
937 |
938 | % Iterate through number of data groups
939 | for ii = 1:num_data_groups
940 | % Check if error bars are desired
941 | if strcmp(options.ErrorBars, 'off')
942 | % Initialize
943 | A_scaled = P_scaled(ii, :);
944 | A_theta = theta(1:end-1);
945 |
946 | % Find the index of Inf values
947 | inf_index = isinf(A_scaled);
948 | noninf_index = find(~inf_index);
949 |
950 | % Check if any Inf values detected
951 | if any(inf_index)
952 | % Remove Inf values
953 | A_scaled(inf_index) = nan;
954 | A_theta(inf_index) = nan;
955 | end
956 |
957 | % Convert polar to cartesian coordinates
958 | [x_points, y_points] = pol2cart(A_theta, A_scaled);
959 |
960 | % Calculate the perimeter and area
961 | pgon = polyshape(x_points, y_points);
962 | S_info(ii).data_group_num = "D" + ii;
963 | S_info(ii).perimeter = perimeter(pgon);
964 | S_info(ii).area = area(pgon);
965 |
966 | % Make points circular
967 | x_circular = [x_points, x_points(1)];
968 | y_circular = [y_points, y_points(1)];
969 |
970 | % Plot data points
971 | h = plot(ax, x_circular, y_circular,...
972 | 'LineStyle', options.LineStyle{ii},...
973 | 'Color', options.Color(ii, :),...
974 | 'LineWidth', options.LineWidth(ii),...
975 | 'Visible', options.PlotVisible);
976 | h.Color(4) = options.LineTransparency(ii);
977 |
978 | % Turn off legend annotation
979 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
980 |
981 | h = scatter(ax, x_points, y_points,...
982 | 'Marker', options.Marker{ii},...
983 | 'SizeData', options.MarkerSize(ii),...
984 | 'MarkerFaceColor', options.Color(ii, :),...
985 | 'MarkerEdgeColor', options.Color(ii, :),...
986 | 'MarkerFaceAlpha', options.MarkerTransparency(ii),...
987 | 'MarkerEdgeAlpha', options.MarkerTransparency(ii),...
988 | 'Visible', options.PlotVisible);
989 |
990 | % Turn off legend annotation
991 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
992 |
993 | % Plot empty line with combined attributes for legend
994 | plot(ax, nan, nan,...
995 | 'Marker', options.Marker{ii},...
996 | 'MarkerSize', options.MarkerSize(ii)/6,...
997 | 'MarkerFaceColor', options.Color(ii, :),...
998 | 'MarkerEdgeColor', options.Color(ii, :),...
999 | 'LineStyle', options.LineStyle{ii},...
1000 | 'Color', options.Color(ii, :),...
1001 | 'LineWidth', options.LineWidth(ii),...
1002 | 'Visible', options.PlotVisible);
1003 | end
1004 |
1005 | % Iterate through number of data points
1006 | if ismember(options.AxesDisplay, {'data', 'data-percent'})
1007 | for jj = noninf_index
1008 | % Convert polar to cartesian coordinates
1009 | [current_theta, current_rho] = cart2pol(x_points(jj), y_points(jj));
1010 | [x_pos, y_pos] = pol2cart(current_theta, current_rho+options.AxesDataOffset);
1011 |
1012 | % Data value
1013 | data_value = P(ii, jj);
1014 |
1015 | % Format data value
1016 | if strcmp(options.AxesDisplay, 'data-percent')
1017 | text_str = sprintf(sprintf('%%.%if%%%%', options.AxesPrecision(jj)), data_value*100);
1018 | else
1019 | text_str = sprintf(sprintf('%%.%if', options.AxesPrecision(jj)), data_value);
1020 | end
1021 |
1022 | % Display axes text
1023 | text(ax, x_pos, y_pos, text_str,...
1024 | 'Units', 'Data',...
1025 | 'Color', options.AxesFontColor(ii, :),...
1026 | 'FontName', options.AxesFont,...
1027 | 'FontSize', options.AxesFontSize,...
1028 | 'HorizontalAlignment', 'center',...
1029 | 'VerticalAlignment', 'middle');
1030 | end
1031 | end
1032 |
1033 | % Check if fill option is toggled on
1034 | switch options.FillOption{ii}
1035 | case 'on'
1036 | % Fill area within polygon
1037 | h = patch(ax, x_circular, y_circular, options.Color(ii, :),...
1038 | 'EdgeColor', 'none',...
1039 | 'FaceAlpha', options.FillTransparency(ii));
1040 |
1041 | % Turn off legend annotation
1042 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1043 |
1044 | case 'interp'
1045 | % Fill area within polygon
1046 | c_data = reshape(options.FillCData, [], 1);
1047 | h = patch(ax, x_points, y_points, c_data,...
1048 | 'EdgeColor', 'none',...
1049 | 'FaceAlpha', options.FillTransparency(ii));
1050 |
1051 | % Turn off legend annotation
1052 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1053 | end
1054 | end
1055 |
1056 | % Save to figure handle
1057 | fig.UserData = S_info;
1058 |
1059 | % Check if axes shaded is on
1060 | if strcmp(options.AxesShaded, 'on')
1061 | % Check if any axes direction was flipped
1062 | if any(axes_direction_index)
1063 | % Flip shaded limits
1064 | A_flip = P_shaded(:, axes_direction_index);
1065 | A_flip = flipud(A_flip);
1066 | P_shaded(:, axes_direction_index) = A_flip;
1067 | end
1068 |
1069 | % Iterate through number of shaded groups
1070 | for ii = 1:num_shaded
1071 | % Initialize
1072 | P_shaded_lower = P_shaded(2*ii-1, :);
1073 | P_shaded_upper = P_shaded(2*ii, :);
1074 | P_temp = [P_shaded_lower; P_shaded_upper];
1075 |
1076 | % Polar verticies of patch
1077 | P_temp = [P_temp, P_temp(:, 1)];
1078 | P_temp = [P_temp(1, :), P_temp(2, :)];
1079 | patch_rho = reshape(P_temp, 1, []);
1080 | patch_theta = [theta, theta];
1081 |
1082 | % Convert polar to cartesian coordinates
1083 | [x_points, y_points] = pol2cart(patch_theta, patch_rho);
1084 |
1085 | % Interweave lower and upper limits
1086 | x_points = reshape(x_points, [], 2)';
1087 | y_points = reshape(y_points, [], 2)';
1088 |
1089 | x_points = x_points(:);
1090 | y_points = y_points(:);
1091 |
1092 | % Increment through groups of four vertices at a time
1093 | for jj = 1:2:length(x_points)-2
1094 | % Verticies and face points
1095 | v = [x_points(jj:jj+3), y_points(jj:jj+3)];
1096 | f = [1 2 4 3];
1097 |
1098 | % Patch polygon
1099 | h = patch(ax, 'Faces', f, 'Vertices', v,...
1100 | 'FaceColor', options.AxesShadedColor{ii},...
1101 | 'EdgeColor', 'none',...
1102 | 'FaceAlpha', options.AxesShadedTransparency(ii));
1103 | h.Annotation.LegendInformation.IconDisplayStyle = 'off';
1104 | end
1105 | end
1106 | end
1107 |
1108 | % Get object type of children
1109 | children_type = get(fig.Children, 'Type');
1110 |
1111 | % Check if children is titled layout
1112 | if strcmp(children_type, 'titledlayout')
1113 | % Set handle to axes children
1114 | obj = ax.Children;
1115 | else
1116 | % Set handle to axes
1117 | obj = ax;
1118 | end
1119 |
1120 | % Find object handles
1121 | text_handles = findobj(obj.Children,...
1122 | 'Type', 'Text');
1123 | patch_handles = findobj(obj.Children,...
1124 | 'Type', 'Patch');
1125 | isocurve_handles = findobj(obj.Children,...
1126 | 'Color', options.AxesColor,...
1127 | '-and', 'Type', 'Line');
1128 | plot_handles = findobj(obj.Children, '-not',...
1129 | 'Color', options.AxesColor,...
1130 | '-and', 'Type', 'Line');
1131 |
1132 | % Manually set the stack order
1133 | uistack(plot_handles, 'bottom');
1134 | uistack(patch_handles, 'bottom');
1135 | uistack(isocurve_handles, 'bottom');
1136 | uistack(text_handles, 'top');
1137 |
1138 | %%% Labels %%%
1139 | % Check if axes labels rotate is on
1140 | if strcmp(options.AxesLabelsRotate, 'on')
1141 | % Find number of degrees to rotate text by
1142 | rotate_deg = rad2deg(text_rotation(theta));
1143 |
1144 | % Find the horizontal alignments to align closer to axes
1145 | horz_aligns = text_alignment(theta);
1146 | else
1147 | % No rotation
1148 | rotate_deg = zeros(1, length(theta));
1149 | end
1150 |
1151 | % Check labels argument
1152 | if ~strcmp(options.AxesLabels, 'none')
1153 | % Iterate through number of data points
1154 | for ii = 1:length(options.AxesLabels)
1155 | % Horizontal text alignment by quadrant
1156 | [horz_align, ~] = quadrant_position(theta(ii));
1157 |
1158 | % Check if axes labels rotate is on
1159 | if strcmp(options.AxesLabelsRotate, 'on')
1160 | % Adjust horizontal text alignment
1161 | horz_align = horz_aligns{ii};
1162 | end
1163 |
1164 | % Convert polar to cartesian coordinates
1165 | [x_pos, y_pos] = pol2cart(theta(ii), rho(end)+options.AxesLabelsOffset);
1166 |
1167 | % Display text label
1168 | text(ax, x_pos, y_pos, options.AxesLabels{ii},...
1169 | 'Units', 'Data',...
1170 | 'HorizontalAlignment', horz_align,...
1171 | 'VerticalAlignment', 'middle',...
1172 | 'EdgeColor', options.AxesLabelsEdge,...
1173 | 'BackgroundColor', options.BackgroundColor,...
1174 | 'FontName', options.LabelFont,...
1175 | 'FontSize', options.LabelFontSize,...
1176 | 'Interpreter', options.AxesInterpreter{ii},...
1177 | 'Rotation', rotate_deg(ii));
1178 | end
1179 | end
1180 |
1181 | end
1182 |
1183 | function horz_aligns = text_alignment(theta)
1184 | % Pre-allocate cell
1185 | horz_aligns = cell(length(theta), 1);
1186 | horz_aligns(:) = {'center'};
1187 |
1188 | % Iterate through each theta
1189 | for kk = 1:length(theta)
1190 | % Adjust horizontal alignment accordingly
1191 | if theta(kk) <= pi/2
1192 | horz_aligns{kk} = 'left';
1193 | elseif theta(kk) < 3*pi/2
1194 | horz_aligns{kk} = 'right';
1195 | elseif theta(kk) <= 2*pi
1196 | horz_aligns{kk} = 'left';
1197 | end
1198 | end
1199 |
1200 | end
1201 |
1202 | function rotate_deg = text_rotation(theta)
1203 | % Find how much to rotate text
1204 | rotate_deg = theta;
1205 |
1206 | % Iterate through each theta
1207 | for kk = 1:length(theta)
1208 | % Adjust sign and rotation accordingly
1209 | if theta(kk) == 0
1210 | rotate_deg(kk) = 0;
1211 | elseif theta(kk) > 0 && theta(kk) <= pi/2
1212 | rotate_deg(kk) = theta(kk);
1213 | elseif theta(kk) > pi/2 && theta(kk) < pi
1214 | rotate_deg(kk) = -(pi - theta(kk));
1215 | elseif theta(kk) == pi
1216 | rotate_deg(kk) = 0;
1217 | elseif theta(kk) > pi && theta(kk) < 3*pi/2
1218 | rotate_deg(kk) = -(pi - theta(kk));
1219 | elseif theta(kk) >= 3*pi/2
1220 | rotate_deg(kk) = -(2*pi - theta(kk));
1221 | end
1222 | end
1223 |
1224 | end
1225 |
1226 | function [horz_align, vert_align] = quadrant_position(theta_point)
1227 | % Find out which quadrant the point is in
1228 | if theta_point == 0
1229 | quadrant = 0;
1230 | elseif theta_point == pi/2
1231 | quadrant = 1.5;
1232 | elseif theta_point == pi
1233 | quadrant = 2.5;
1234 | elseif theta_point == 3*pi/2
1235 | quadrant = 3.5;
1236 | elseif theta_point == 2*pi
1237 | quadrant = 0;
1238 | elseif theta_point > 0 && theta_point < pi/2
1239 | quadrant = 1;
1240 | elseif theta_point > pi/2 && theta_point < pi
1241 | quadrant = 2;
1242 | elseif theta_point > pi && theta_point < 3*pi/2
1243 | quadrant = 3;
1244 | elseif theta_point > 3*pi/2 && theta_point < 2*pi
1245 | quadrant = 4;
1246 | end
1247 |
1248 | % Adjust label alignment depending on quadrant
1249 | switch quadrant
1250 | case 0
1251 | horz_align = 'left';
1252 | vert_align = 'middle';
1253 | case 1
1254 | horz_align = 'left';
1255 | vert_align = 'bottom';
1256 | case 1.5
1257 | horz_align = 'center';
1258 | vert_align = 'bottom';
1259 | case 2
1260 | horz_align = 'right';
1261 | vert_align = 'bottom';
1262 | case 2.5
1263 | horz_align = 'right';
1264 | vert_align = 'middle';
1265 | case 3
1266 | horz_align = 'right';
1267 | vert_align = 'top';
1268 | case 3.5
1269 | horz_align = 'center';
1270 | vert_align = 'top';
1271 | case 4
1272 | horz_align = 'left';
1273 | vert_align = 'top';
1274 | end
1275 | end
1276 |
1277 | %%% Custom Validation Functions %%%
1278 | % Validate axes labels
1279 | function validateAxesLabels(axes_labels, P)
1280 | if ~strcmp(axes_labels, 'none')
1281 | validateattributes(axes_labels, {'cell'}, {'size', [1, size(P, 2)]}, mfilename, 'axes_labels')
1282 | end
1283 | end
1284 |
1285 | % Validate axes limits
1286 | function validateAxesLimits(axes_limits, P)
1287 | if ~isempty(axes_limits)
1288 | validateattributes(axes_limits, {'double'}, {'size', [2, size(P, 2)]}, mfilename, 'axes_limits')
1289 |
1290 | % Lower and upper limits
1291 | lower_limits = axes_limits(1, :);
1292 | upper_limits = axes_limits(2, :);
1293 |
1294 | % Difference in upper and lower limits
1295 | diff_limits = upper_limits - lower_limits;
1296 |
1297 | % Check to make sure upper limit is greater than lower limit
1298 | if any(diff_limits < 0)
1299 | error('Error: Please make sure max axes limits are greater than the min axes limits.');
1300 | end
1301 |
1302 | % Check the range of axes limits
1303 | if any(diff_limits == 0)
1304 | error('Error: Please make sure the min and max axes limits are different.');
1305 | end
1306 | end
1307 | end
--------------------------------------------------------------------------------
/spider_plot_R2019b_examples.mlx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/spider_plot_R2019b_examples.mlx
--------------------------------------------------------------------------------
/spider_plot_class_examples.mlx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/spider_plot_class_examples.mlx
--------------------------------------------------------------------------------
/spider_plot_examples.mlx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NewGuy012/spider_plot/8a5cc648f63f0c9152be90f4316d412abd725e56/spider_plot_examples.mlx
--------------------------------------------------------------------------------