view scripts/plot/shrinkfaces.m @ 16950:b34202b24212

fplot.m: Overhaul function for Matlab compatibility and performance (bug #38961). * scripts/plot/fplot.m: Add ability to specify n,tol,fmt in any order and simultaneously. Return data rather than plotting it if asked. Use additional test on progress of algorithm to decide whether to quit. Add %!demo and %!tests.
author Rik <rik@octave.org>
date Thu, 11 Jul 2013 09:25:54 -0700
parents ddac88d32d6a
children eaab03308c0b
line wrap: on
line source

## Copyright (C) 2012 Martin Helm
##
## This file is part of Octave.
##
## Octave is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or (at
## your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {Function File} {} shrinkfaces (@var{p}, @var{sf})
## @deftypefnx {Function File} {@var{nfv} =} shrinkfaces (@var{p}, @var{sf})
## @deftypefnx {Function File} {@var{nfv} =} shrinkfaces (@var{fv}, @var{sf})
## @deftypefnx {Function File} {@var{nfv} =} shrinkfaces (@var{f}, @var{v}, @var{sf})
## @deftypefnx {Function File} {[@var{nf}, @var{nv}] =} shrinkfaces (@dots{})
##
## Reduce the faces area for a given patch, structure or explicit faces
## and points matrices by a scale factor @var{sf}.  The structure
## @var{fv} must contain the fields 'faces' and 'vertices'.  If the
## factor @var{sf} is omitted then a default of 0.3 is used.
##
## Given a patch handle as the first input argument and no output
## parameters, perform the shrinking of the patch faces in place and
## redraw the patch.
##
## If called with one output argument, return a structure with fields
## 'faces', 'vertices', and 'facevertexcdata' containing the data after
## shrinking which can then directly be used as an input argument for the
## @command{patch} function.
##
## Performing the shrinking on faces which are not convex can lead to
## undesired results.
##
## For example,
##
## @example
## @group
## [phi r] = meshgrid (linspace (0, 1.5*pi, 16), linspace (1, 2, 4));
## tri = delaunay (phi(:), r(:));
## v = [r(:).*sin(phi(:)) r(:).*cos(phi(:))];
## clf ()
## p = patch ("Faces", tri, "Vertices", v, "FaceColor", "none");
## fv = shrinkfaces (p);
## patch (fv)
## axis equal
## grid on
## @end group
## @end example
##
## @noindent
## draws a triangulated 3/4 circle and the corresponding shrunken
## version.
## @seealso{patch}
## @end deftypefn

## Author: Martin Helm <martin@mhelm.de>

function [nf, nv] = shrinkfaces (varargin)

  if (nargin < 1 || nargin > 3 || nargout > 2)
    print_usage ();
  endif
  
  sf = 0.3;
  p = varargin{1};
  colors = [];

  if (ishandle (p) && nargin < 3)
    faces = get (p, "Faces");
    vertices = get (p, "Vertices");
    colors = get (p, "FaceVertexCData");
    if (nargin == 2)
      sf = varargin{2};
    endif
  elseif (isstruct (p) && nargin < 3)
    faces = p.faces;
    vertices = p.vertices;
    if (isfield (p, "facevertexcdata"))
      colors = p.facevertexcdata;
    endif
    if (nargin == 2)
      sf = varargin{2};
    endif
  elseif (ismatrix (p) && nargin >= 2 && ismatrix (varargin{2}))
    faces = p;
    vertices = varargin{2};
    if (nargin == 3)
      sf = varargin{3};
    endif
  else
    print_usage ();
  endif
  
  if (! isscalar (sf) || sf <= 0)
    error ("shrinkfaces: scale factor must be a positive scalar");
  endif

  n = columns (vertices);
  if (n < 2 || n > 3)
    error ("shrinkfaces: only 2D and 3D patches are supported");
  endif

  m = columns (faces);
  if (m < 3)
    error ("shrinkfaces: faces must consist of at least 3 vertices");
  endif

  v = vertices(faces'(:), :);
  if (isempty (colors) || rows (colors) == rows (faces))
    c = colors;
  elseif (rows (colors) == rows (vertices))
    c = colors(faces'(:), :);
  else
    ## Discard inconsistent color data.
    c = [];
  endif
  sv = rows (v);
  ## we have to deal with a probably very large number of vertices, so
  ## use sparse we use as midpoint (1/m, ..., 1/m) in generalized
  ## barycentric coordinates.
  midpoints = full (kron ( speye (sv / m), ones (m, m) / m) * sparse (v));
  v = sqrt (sf) * (v - midpoints) + midpoints;
  f = reshape (1:sv, m, sv / m)';
  
  switch (nargout)
    case 0
      if (ishandle (p))
        ## avoid exceptions
        set (p, "FaceVertexCData", [], "CData", []);
        set (p, "Vertices", v, "Faces", f, "FaceVertexCData", c);
      else
        nf = struct ("faces", f, "vertices", v, "facevertexcdata", c);
      endif
    case 1
      nf = struct ("faces", f, "vertices", v, "facevertexcdata", c);
    case 2
      nf = f;
      nv = v;
  endswitch

endfunction


%!demo
%! clf;
%! faces = [1 2 3; 1 3 4];
%! vertices = [0 0; 1 0; 1 1; 0 1];
%! patch ('Faces', faces, 'Vertices', vertices, 'FaceColor', 'none');
%! fv = shrinkfaces (faces, vertices, 0.25);
%! patch (fv);
%! axis equal;

%!demo
%! clf;
%! faces = [1 2 3 4; 5 6 7 8];
%! vertices = [0 0; 1 0; 2 1; 1 1; 2 0; 3 0; 4 1; 3.5 1];
%! patch ('Faces', faces, 'Vertices', vertices, 'FaceColor', 'none');
%! fv = shrinkfaces (faces, vertices, 0.25);
%! patch (fv);
%! axis equal;
%! grid on;

%!demo
%! clf;
%! faces = [1 2 3 4];
%! vertices = [-1 2; 0 0; 1 2; 0 1];
%! patch ('Faces', faces, 'Vertices', vertices, 'FaceColor', 'none');
%! fv = shrinkfaces (faces, vertices, 0.25);
%! patch (fv);
%! axis equal;
%! grid on;
%! title 'faces which are not convex are clearly not allowed'

%!demo
%! clf;
%! [phi r] = meshgrid (linspace (0, 1.5*pi, 16), linspace (1, 2, 4));
%! tri = delaunay (phi(:), r(:));
%! v = [r(:).*sin(phi(:)) r(:).*cos(phi(:))];
%! p = patch ('Faces', tri, 'Vertices', v, 'FaceColor', 'none');
%! fv = shrinkfaces (p);
%! patch (fv);
%! axis equal;
%! grid on;

%!demo
%! clf;
%! N = 10;  % N intervals per axis
%! [x, y, z] = meshgrid (linspace (-4,4,N+1));
%! val = x.^3 + y.^3 + z.^3;
%! fv = isosurface (x, y, z, val, 3, z);
%!
%! p = patch ('Faces', fv.faces, 'Vertices', fv.vertices, 'FaceVertexCData', ...
%!            fv.facevertexcdata, 'FaceColor', 'interp', 'EdgeColor', 'black');
%! axis equal;
%! view (115, 30);
%! drawnow;
%! shrinkfaces (p, 0.6);

%!shared faces, vertices, nfv, nfv2
%! faces = [1 2 3];
%! vertices = [0 0 0; 1 0 0; 1 1 0];
%! nfv = shrinkfaces (faces, vertices, 0.7);
%! nfv2 = shrinkfaces (nfv, 1/0.7);
%!assert (isfield (nfv, "faces"))
%!assert (isfield (nfv, "vertices"))
%!assert (size (nfv.faces), [1 3])
%!assert (size (nfv.vertices), [3 3])
%!assert (norm (nfv2.vertices - vertices), 0, 2*eps)