1######################################################################## 2## 3## Copyright (C) 2012-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 {} {} shrinkfaces (@var{p}, @var{sf}) 28## @deftypefnx {} {@var{nfv} =} shrinkfaces (@var{p}, @var{sf}) 29## @deftypefnx {} {@var{nfv} =} shrinkfaces (@var{fv}, @var{sf}) 30## @deftypefnx {} {@var{nfv} =} shrinkfaces (@var{f}, @var{v}, @var{sf}) 31## @deftypefnx {} {[@var{nf}, @var{nv}] =} shrinkfaces (@dots{}) 32## 33## Reduce the size of faces in a patch by the shrink factor @var{sf}. 34## 35## The patch object can be specified by a graphics handle (@var{p}), a patch 36## structure (@var{fv}) with the fields @qcode{"faces"} and @qcode{"vertices"}, 37## or as two separate matrices (@var{f}, @var{v}) of faces and vertices. 38## 39## The shrink factor @var{sf} is a positive number specifying the percentage 40## of the original area the new face will occupy. If no factor is given the 41## default is 0.3 (a reduction to 30% of the original size). A factor greater 42## than 1.0 will result in the expansion of faces. 43## 44## Given a patch handle as the first input argument and no output parameters, 45## perform the shrinking of the patch faces in place and redraw the patch. 46## 47## If called with one output argument, return a structure with fields 48## @qcode{"faces"}, @qcode{"vertices"}, and @qcode{"facevertexcdata"} 49## containing the data after shrinking. This structure can be used directly 50## as an input argument to the @code{patch} function. 51## 52## @strong{Caution:}: Performing the shrink operation on faces which are not 53## convex can lead to undesirable results. 54## 55## Example: a triangulated 3/4 circle and the corresponding shrunken version. 56## 57## @example 58## @group 59## [phi r] = meshgrid (linspace (0, 1.5*pi, 16), linspace (1, 2, 4)); 60## tri = delaunay (phi(:), r(:)); 61## v = [r(:).*sin(phi(:)) r(:).*cos(phi(:))]; 62## clf () 63## p = patch ("Faces", tri, "Vertices", v, "FaceColor", "none"); 64## fv = shrinkfaces (p); 65## patch (fv) 66## axis equal 67## grid on 68## @end group 69## @end example 70## 71## @seealso{patch} 72## @end deftypefn 73 74function [nf, nv] = shrinkfaces (varargin) 75 76 if (nargin < 1 || nargin > 3 || nargout > 2) 77 print_usage (); 78 endif 79 80 sf = 0.3; 81 colors = []; 82 p = varargin{1}; 83 84 if (isscalar (p) && isgraphics (p, "patch") && nargin < 3) 85 faces = get (p, "Faces"); 86 vertices = get (p, "Vertices"); 87 colors = get (p, "FaceVertexCData"); 88 if (nargin == 2) 89 sf = varargin{2}; 90 endif 91 elseif (isstruct (p) && nargin < 3) 92 faces = p.faces; 93 vertices = p.vertices; 94 if (isfield (p, "facevertexcdata")) 95 colors = p.facevertexcdata; 96 endif 97 if (nargin == 2) 98 sf = varargin{2}; 99 endif 100 elseif (ismatrix (p) && nargin >= 2 && ismatrix (varargin{2})) 101 faces = p; 102 vertices = varargin{2}; 103 if (nargin == 3) 104 sf = varargin{3}; 105 endif 106 else 107 print_usage (); 108 endif 109 110 if (! isscalar (sf) || sf <= 0) 111 error ("shrinkfaces: scale factor must be a positive scalar"); 112 endif 113 114 nc = columns (vertices); 115 if (nc < 2 || nc > 3) 116 error ("shrinkfaces: only 2-D and 3-D patches are supported"); 117 endif 118 119 m = columns (faces); 120 if (m < 3) 121 error ("shrinkfaces: faces must consist of at least 3 vertices"); 122 endif 123 124 v = vertices(faces'(:), :); 125 if (isempty (colors) || rows (colors) == rows (faces)) 126 c = colors; 127 elseif (rows (colors) == rows (vertices)) 128 c = colors(faces'(:), :); 129 else 130 c = []; # Discard inconsistent color data. 131 endif 132 sv = rows (v); 133 ## We have to deal with a possibly very large number of vertices, so use 134 ## sparse as midpoint (1/m, ..., 1/m) in generalized barycentric coordinates. 135 midpoints = full (kron (speye (sv / m), ones (m, m) / m) * sparse (v)); 136 v = sqrt (sf) * (v - midpoints) + midpoints; 137 f = reshape (1:sv, m, sv / m)'; 138 139 switch (nargout) 140 case 0 141 if (ishghandle (p)) 142 ## avoid exceptions 143 set (p, "FaceVertexCData", [], "CData", [], 144 "Vertices", v, "Faces", f, "FaceVertexCData", c); 145 else 146 nf = struct ("faces", f, "vertices", v, "facevertexcdata", c); 147 endif 148 case 1 149 nf = struct ("faces", f, "vertices", v, "facevertexcdata", c); 150 case 2 151 nf = f; 152 nv = v; 153 endswitch 154 155endfunction 156 157 158%!demo 159%! clf; 160%! faces = [1 2 3; 1 3 4]; 161%! vertices = [0 0; 1 0; 1 1; 0 1]; 162%! patch ("Faces", faces, "Vertices", vertices, "FaceColor", "none"); 163%! fv = shrinkfaces (faces, vertices, 0.25); 164%! patch (fv); 165%! axis auto; # Kludge required for Octave 166%! axis equal; 167%! title ("shrinkfaces() on triangular shapes"); 168 169%!demo 170%! clf; 171%! faces = [1 2 3 4; 5 6 7 8]; 172%! vertices = [0 0; 1 0; 2 1; 1 1; 2 0; 3 0; 4 1; 3.5 1]; 173%! patch ("Faces", faces, "Vertices", vertices, "FaceColor", "none"); 174%! fv = shrinkfaces (faces, vertices, 0.25); 175%! patch (fv); 176%! axis auto; # Kludge required for Octave 177%! axis equal; 178%! grid on; 179%! title ("shrinkfaces() on rhomboid shapes"); 180 181%!demo 182%! clf; 183%! faces = [1 2 3 4]; 184%! vertices = [-1 2; 0 0; 1 2; 0 1]; 185%! patch ("Faces", faces, "Vertices", vertices, "FaceColor", "none"); 186%! fv = shrinkfaces (faces, vertices, 0.25); 187%! patch (fv); 188%! axis auto; # Kludge required for Octave 189%! axis equal; 190%! grid on; 191%! title ("shrinkfaces() does not work on concave shapes"); 192 193%!demo 194%! clf; 195%! [phi r] = meshgrid (linspace (0, 1.5*pi, 16), linspace (1, 2, 4)); 196%! tri = delaunay (phi(:), r(:)); 197%! v = [r(:).*sin(phi(:)) r(:).*cos(phi(:))]; 198%! p = patch ("Faces", tri, "Vertices", v, "FaceColor", "none"); 199%! fv = shrinkfaces (p); 200%! patch (fv); 201%! axis auto; # Kludge required for Octave 202%! axis equal; 203%! grid on; 204%! title ("shrinkfaces() on 2-D complex shapes tessellated with triangles"); 205 206%!demo 207%! clf; 208%! N = 10; # N intervals per axis 209%! [x, y, z] = meshgrid (linspace (-4,4,N+1)); 210%! val = x.^3 + y.^3 + z.^3; 211%! fv = isosurface (x, y, z, val, 3, z, "noshare"); 212%! 213%! p = patch ("Faces", fv.faces, "Vertices", fv.vertices, "FaceVertexCData", ... 214%! fv.facevertexcdata, "FaceColor", "interp", "EdgeColor", "black"); 215%! axis auto; # Kludge required for Octave 216%! axis equal; 217%! view (115, 30); 218%! drawnow; 219%! shrinkfaces (p, 0.6); 220%! title ("shrinkfaces() on 3-D complex shapes"); 221 222%!shared faces, vertices, nfv, nfv2 223%! faces = [1 2 3]; 224%! vertices = [0 0 0; 1 0 0; 1 1 0]; 225%! nfv = shrinkfaces (faces, vertices, 0.7); 226%! nfv2 = shrinkfaces (nfv, 1/0.7); 227%!assert (isfield (nfv, "faces")) 228%!assert (isfield (nfv, "vertices")) 229%!assert (size (nfv.faces), [1 3]) 230%!assert (size (nfv.vertices), [3 3]) 231%!assert (norm (nfv2.vertices - vertices), 0, 2*eps) 232 233## Test input validation 234%!error shrinkfaces () 235%!error shrinkfaces (1,2,3,4) 236%!error [a,b,c] = shrinkfaces (1) 237%!error <scale factor must be a positive scalar> shrinkfaces (nfv, ones (2)) 238%!error <scale factor must be a positive scalar> shrinkfaces (nfv, 0) 239%!error <only 2-D and 3-D patches are supported> shrinkfaces (faces, ones (3,1)) 240%!error <only 2-D and 3-D patches are supported> shrinkfaces (faces, ones (3,4)) 241%!error <faces must consist of at least 3 vertices> shrinkfaces (faces(1:2), vertices) 242