├── 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 | [![View spider_plot on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/59561-spider_plot) 2 | [![Open in MATLAB Online](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](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 | [![View spider_plot on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/59561-spider_plot) 871 | [![Open in MATLAB Online](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](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 --------------------------------------------------------------------------------