1######################################################################## 2## 3## Copyright (C) 2014-2021 The Octave Project Developers 4## 5## See the file COPYRIGHT.md in the top-level directory of this 6## distribution or <https://octave.org/copyright/>. 7## 8## This file is part of Octave. 9## 10## Octave is free software: you can redistribute it and/or modify it 11## under the terms of the GNU General Public License as published by 12## the Free Software Foundation, either version 3 of the License, or 13## (at your option) any later version. 14## 15## Octave is distributed in the hope that it will be useful, but 16## WITHOUT ANY WARRANTY; without even the implied warranty of 17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18## GNU General Public License for more details. 19## 20## You should have received a copy of the GNU General Public License 21## along with Octave; see the file COPYING. If not, see 22## <https://www.gnu.org/licenses/>. 23## 24######################################################################## 25 26## -*- texinfo -*- 27## @deftypefn {} {} rotate (@var{h}, @var{direction}, @var{alpha}) 28## @deftypefnx {} {} rotate (@dots{}, @var{origin}) 29## Rotate the plot object @var{h} through @var{alpha} degrees around the line 30## with direction @var{direction} and origin @var{origin}. 31## 32## The default value of @var{origin} is the center of the axes object that is 33## the parent of @var{h}. 34## 35## If @var{h} is a vector of handles, they must all have the same parent axes 36## object. 37## 38## Graphics objects that may be rotated are lines, surfaces, patches, and 39## images. 40## @end deftypefn 41 42function rotate (h, direction, alpha, origin) 43 44 ## Note in doc string about compatibility issues with calculation of 45 ## default origin due to possible differences in the auto-scaling 46 ## algorithm between Octave and Matlab. 47 48 if (nargin < 3 || nargin > 4) 49 print_usage (); 50 endif 51 52 is_h = ishghandle (h); 53 if (is_h) 54 ax_list = get (h, "parent"); 55 if (iscell (ax_list)) 56 ax_list = cell2mat (ax_list); 57 endif 58 if (ax_list == ax_list(1)) 59 ax = ax_list(1); 60 else 61 error ("rotate: all handles must be children of the same axes object"); 62 endif 63 else 64 error ("rotate: H must be an array of one or more graphics handles"); 65 endif 66 67 if (! (isnumeric (direction) && numel (direction) == 3)) 68 error ("rotate: invalid direction"); 69 endif 70 71 if (! (isnumeric (alpha) && isscalar (alpha))) 72 error ("rotate: invalid rotation angle"); 73 endif 74 75 t = get (h, "type"); 76 77 is_image = strcmp (t, "image"); 78 is_line = strcmp (t, "line"); 79 is_patch = strcmp (t, "patch"); 80 is_surface = strcmp (t, "surface"); 81 82 if (! all (is_image | is_line | is_patch | is_surface)) 83 error ("rotate: expecting image, line, patch, or surface objects"); 84 endif 85 86 if (nargin == 4) 87 if (! (isnumeric (origin) && numel (origin) == 3)) 88 error ("rotate: invalid ORIGIN"); 89 endif 90 else 91 ## Should Z limit be considered when computing origin? 92 93 use_zlim = any (is_patch | is_surface); 94 95 if (! use_zlim && any (is_line)) 96 idx = find (is_line)'; 97 for i = idx 98 if (! isempty (get (h(i), "zdata"))) 99 use_zlim = true; 100 break; 101 endif 102 endfor 103 endif 104 105 xlim = get (ax, "xlim"); 106 ylim = get (ax, "ylim"); 107 108 a = (xlim(1) + xlim(2)) / 2; 109 b = (ylim(1) + ylim(2)) / 2; 110 111 if (use_zlim) 112 zlim = get (ax, "zlim"); 113 c = (zlim(1) + zlim(2)) / 2; 114 else 115 c = 0; 116 endif 117 118 origin = [a, b, c]; 119 endif 120 121 direction /= norm (direction); 122 123 u = direction(1); 124 v = direction(2); 125 w = direction(3); 126 127 a = origin(1); 128 b = origin(2); 129 c = origin(3); 130 131 sa = sind (alpha); 132 ca = cosd (alpha); 133 134 for i = 1:numel (h) 135 x = get (h(i), "xdata"); 136 y = get (h(i), "ydata"); 137 138 if (is_image(i)) 139 z = zeros (size (x)); 140 else 141 z = get (h(i), "zdata"); 142 if (isempty (z)) 143 z = zeros (size (x)); 144 elseif (isvector (x) && isvector (y) && ! isvector (z)) 145 [x, y] = meshgrid (x, y); 146 endif 147 endif 148 149 if (a == 0 && b == 0 && c == 0) 150 tmp = (u*x + v*y + w*z) * (1 - ca); 151 152 xr = u*tmp + x*ca + (-w*y + v*z)*sa; 153 yr = v*tmp + y*ca + (w*x - u*z)*sa; 154 zr = w*tmp + z*ca + (-v*x + u*y)*sa; 155 else 156 one_m_ca = 1 - ca; 157 tmp = u*x + v*y + w*z; 158 159 xr = ((a*(v**2 + w**2) - u*(b*v + c*w - tmp))*one_m_ca 160 + x*ca + (-c*v + b*w - w*y + v*z)*sa); 161 yr = ((b*(u**2 + w**2) - v*(a*u + c*w - tmp))*one_m_ca 162 + y*ca + (c*u - a*w + w*x - u*z)*sa); 163 zr = ((c*(u**2 + v**2) - w*(a*u + b*v - tmp))*one_m_ca 164 + z*ca + (-b*u + a*v - v*x + u*y)*sa); 165 endif 166 167 set (h(i), "xdata", xr, "ydata", yr); 168 169 if (! is_image(i)) 170 set (h(i), "zdata", zr); 171 endif 172 endfor 173 174endfunction 175 176 177## Test input validation 178%!shared h1, h2, o1, o2, o3 179%! h1 = figure ("visible", "off"); 180%! o1 = line (); 181%! h2 = figure ("visible", "off"); 182%! o2 = line (); 183%! o3 = text (0, 0, "foobar"); 184%!error rotate () 185%!error rotate (o1) 186%!error rotate (o1, [0,0,0]) 187%!error <all handles must be children of the same axes object> rotate ([o1, o2], [0,0,0], 90) 188%!error <invalid direction> rotate (o1, "foo", 90) 189%!error <invalid rotation angle> rotate (o1, [0,0,0], "foo") 190%!error <invalid ORIGIN> rotate (o1, [0,0,0], 90, "foo") 191%!error rotate (o1, [0,0,0], 90, [0,0,0], 1) 192%!error <H must be an array of one or more graphics handles> rotate (NaN, [0,0,0], 90) 193%!error <expecting image, line, patch, or surface objects> rotate (o3, [0,0,0], 90) 194%!test 195%! close (h1); 196%! close (h2); 197