Mercurial > octave
view scripts/plot/draw/isosurface.m @ 22231:01ba6ebc52e4
Add function "reducevolume" (patch #8856).
* scripts/plot/draw/reducevolume.m: New function.
* scripts/plot/draw/module.mk: Add to build system.
* __unimplemented__.m: Remove "reducevolume" from list.
* NEWS: Announce new function.
* doc/interpreter/plot.txi: Add to documentation.
* scripts/plot/draw/isosurface.m: Add "reducevolume" to @seealso.
author | Markus Muetzel <markus.muetzel@gmx.de> |
---|---|
date | Mon, 18 Jul 2016 19:49:39 +0200 |
parents | e50536734855 |
children | a8fd02bc895b |
line wrap: on
line source
## Copyright (C) 2009-2016 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 {} {[@var{fv}] =} isosurface (@var{val}, @var{iso}) ## @deftypefnx {} {[@var{fv}] =} isosurface (@var{val}) ## @deftypefnx {} {[@var{fv}] =} isosurface (@var{x}, @var{y}, @var{z}, @var{val}, @var{iso}) ## @deftypefnx {} {[@var{fv}] =} isosurface (@var{x}, @var{y}, @var{z}, @var{val}) ## @deftypefnx {} {[@var{fvc}] =} isosurface (@dots{}, @var{col}) ## @deftypefnx {} {[@var{fv}] =} isosurface (@dots{}, "noshare") ## @deftypefnx {} {[@var{fv}] =} isosurface (@dots{}, "verbose") ## @deftypefnx {} {[@var{f}, @var{v}] =} isosurface (@dots{}) ## @deftypefnx {} {[@var{f}, @var{v}, @var{c}] =} isosurface (@dots{}) ## @deftypefnx {} {} isosurface (@dots{}) ## ## Calculate isosurface of 3-D data. ## ## If called with one output argument and the first input argument ## @var{val} is a three-dimensional array that contains the data of an ## isosurface geometry and the second input argument @var{iso} keeps the ## isovalue as a scalar value then return a structure array @var{fv} ## that contains the fields @var{faces} and @var{vertices} at computed ## points @command{[x, y, z] = meshgrid (1:l, 1:m, 1:n)}. The output ## argument @var{fv} can directly be taken as an input argument for the ## @command{patch} function. ## ## If @var{iso} is omitted or empty, a "good" value for an isosurface is ## determined from @var{val}. ## ## If called with further input arguments @var{x}, @var{y} and @var{z} ## which are three--dimensional arrays with the same size as @var{val} or ## vectors with lengths corresponding to the dimensions of @var{val}, then the ## volume data is taken at those given points. If @var{x}, @var{y} or @var{z} ## are empty, the grid corresponds to the indices in the respective direction ## (see @command{meshgrid}). ## ## If called with the input argument @var{col} which is a ## three-dimensional array of the same size as @var{val}, take ## those values for the interpolation of coloring the isosurface ## geometry. In this case, the structure array @var{fv} has the additional field ## @var{facevertexcdata}. ## ## If given the string input argument @qcode{"noshare"}, vertices might be ## returned multiple times for different faces. The default behavior is to ## search vertices shared by adjacent faces with @command{unique} which might be ## time consuming. ## The string input argument @qcode{"verbose"} is only for compatibility and ## has no effect. ## The string input arguments must be passed after the other arguments. ## ## If called with two or three output arguments, return the information about ## the faces @var{f}, vertices @var{v} and color data @var{c} as separate arrays ## instead of a single structure array. ## ## If called with no output argument, the isosurface geometry is directly ## processed with the @command{patch} command and a light object is added to ## the axes if not yet present. ## ## For example, ## ## @example ## @group ## [x, y, z] = meshgrid (1:5, 1:5, 1:5); ## val = rand (5, 5, 5); ## isosurface (x, y, z, val, .5); ## @end group ## @end example ## ## @noindent ## will directly draw a random isosurface geometry in a graphics window. ## ## Another example for an isosurface geometry with different additional ## coloring ## @c Set example in small font to prevent overfull line ## ## @smallexample ## N = 15; # Increase number of vertices in each direction ## iso = .4; # Change isovalue to .1 to display a sphere ## lin = linspace (0, 2, N); ## [x, y, z] = meshgrid (lin, lin, lin); ## val = abs ((x-.5).^2 + (y-.5).^2 + (z-.5).^2); ## figure (); # Open another figure window ## ## subplot (2,2,1); view (-38, 20); ## [f, v] = isosurface (x, y, z, val, iso); ## p = patch ("Faces", f, "Vertices", v, "EdgeColor", "none"); ## set (gca, "PlotBoxAspectRatioMode", "manual", ... ## "PlotBoxAspectRatio", [1 1 1]); ## isonormals (x, y, z, val, p) ## set (p, "FaceColor", "green", "FaceLighting", "gouraud"); ## light ("Position", [1 1 5]); ## ## subplot (2,2,2); view (-38, 20); ## p = patch ("Faces", f, "Vertices", v, "EdgeColor", "blue"); ## set (gca, "PlotBoxAspectRatioMode", "manual", ... ## "PlotBoxAspectRatio", [1 1 1]); ## isonormals (x, y, z, val, p) ## set (p, "FaceColor", "none", "EdgeLighting", "gouraud"); ## light ("Position", [1 1 5]); ## ## subplot (2,2,3); view (-38, 20); ## [f, v, c] = isosurface (x, y, z, val, iso, y); ## p = patch ("Faces", f, "Vertices", v, "FaceVertexCData", c, ... ## "FaceColor", "interp", "EdgeColor", "none"); ## set (gca, "PlotBoxAspectRatioMode", "manual", ... ## "PlotBoxAspectRatio", [1 1 1]); ## isonormals (x, y, z, val, p) ## set (p, "FaceLighting", "gouraud"); ## light ("Position", [1 1 5]); ## ## subplot (2,2,4); view (-38, 20); ## p = patch ("Faces", f, "Vertices", v, "FaceVertexCData", c, ... ## "FaceColor", "interp", "EdgeColor", "blue"); ## set (gca, "PlotBoxAspectRatioMode", "manual", ... ## "PlotBoxAspectRatio", [1 1 1]); ## isonormals (x, y, z, val, p) ## set (p, "FaceLighting", "gouraud"); ## light ("Position", [1 1 5]); ## @end smallexample ## ## @seealso{isonormals, isocolors, smooth3, reducevolume} ## @end deftypefn ## Author: Martin Helm <martin@mhelm.de> ## FIXME: Add support for string input argument "verbose" ## (needs changes to __marching_cube__.m) function varargout = isosurface (varargin) if (nargin < 1 || nargin > 8 || nargout > 3) print_usage (); endif [x, y, z, val, iso, colors, noshare, verbose] = __get_check_isosurface_args__ (nargout, varargin{:}); calc_colors = ! isempty (colors); if (calc_colors) [fvc.faces, fvc.vertices, fvc.facevertexcdata] = ... __marching_cube__ (x, y, z, val, iso, colors); else [fvc.faces, fvc.vertices] = __marching_cube__ (x, y, z, val, iso); endif if (isempty (fvc.vertices) || isempty (fvc.faces)) warning ("isosurface: triangulation is empty"); endif if (!noshare) [fvc.faces, fvc.vertices, J] = __unite_shared_vertices__ (fvc.faces, fvc.vertices); if (calc_colors) fvc.facevertexcdata(J) = []; # share very close vertices endif endif switch (nargout) case 0 ## plot the calculated surface if (calc_colors) fc = fvc.facevertexcdata; else fc = iso; endif ## FIXME: Matlab uses "EdgeColor", "none". But that would look odd ## with gnuplot. pa = patch ("Faces", fvc.faces, "Vertices", fvc.vertices, "FaceVertexCData", fc, "FaceColor", "flat", "EdgeColor", "k", "FaceLighting", "gouraud"); hax = gca (); if (! ishold ()) set (hax, "View", [-37.5, 30], "Box", "off"); endif isonormals (x, y, z, val, pa); lights = findobj (hax, "Type", "light"); if (isempty (lights)) ## FIXME: Matlab seems to use camlight (patch #9014) here light (); endif case 1 varargout = {fvc}; case 2 varargout = {fvc.faces, fvc.vertices}; otherwise ## 3 varargout = {fvc.faces, fvc.vertices, fvc.facevertexcdata}; endswitch endfunction function [x, y, z, data, iso, colors, noshare, verbose] = __get_check_isosurface_args__ (nout, varargin) ## get arguments from input and check values x = []; y = []; z = []; data = []; iso = []; colors = []; ## default values noshare = false; verbose = false; nin = length (varargin); num_string_inputs = 0; for i_arg = (nin:-1:nin-1) ## check whether last maximum 2 input arguments are strings and assign parameters if (!ischar (varargin{i_arg}) || i_arg < 1) ## string arguments must be at the end break end switch (tolower (varargin{i_arg})) case {"v", "verbose"} verbose = true; num_string_inputs++; case {"n", "noshare"} noshare = true; num_string_inputs++; otherwise error ("isosurface: parameter '%s' not supported", varargin{i_arg}) endswitch endfor ## assign arguments switch (nin - num_string_inputs) case 1 ## isosurface (val, ...) data = varargin{1}; case 2 ## isosurface (val, iso, ...) or isosurface (val, col, ...) data = varargin{1}; if (isscalar (varargin{2}) || isempty (varargin{2})) iso = varargin{2}; else colors = varargin{2}; endif case 3 ## isosurface (val, iso, col, ...) data = varargin{1}; iso = varargin{2}; colors = varargin{3}; case 4 ## isosurface (x, y, z, val, ...) x = varargin{1}; y = varargin{2}; z = varargin{3}; data = varargin{4}; case 5 ## isosurface (x, y, z, val, iso, ...) or isosurface (x, y, z, val, col, ...) x = varargin{1}; y = varargin{2}; z = varargin{3}; data = varargin{4}; if (isscalar (varargin{5}) || isempty (varargin{5})) iso = varargin{5}; else colors = varargin{5}; endif case 6 ## isosurface (x, y, z, val, iso, col, ...) x = varargin{1}; y = varargin{2}; z = varargin{3}; data = varargin{4}; iso = varargin{5}; colors = varargin{6}; otherwise error ("isosurface: wrong number of input arguments") endswitch ## check dimensions of data data_size = size (data); if (ndims (data) != 3 || any (data_size(1:3) < 2)) error ("isosurface: VAL must be a non-singleton 3-dimensional matrix"); endif if (isempty (x)) x = 1:size (data, 2); endif if (isempty (y)) y = 1:size (data, 1); endif if (isempty (z)) z = 1:size (data, 3); endif ## check x if (isvector (x) && length (x) == data_size(2)) x = repmat (x(:)', [data_size(1) 1 data_size(3)]); elseif (! size_equal (data, x)) error ("isosurface: X must match the size of VAL"); endif ## check y if (isvector (y) && length (y) == data_size(1)) y = repmat (y(:), [1 data_size(2) data_size(3)]); elseif (! size_equal (data, y)) error ("isosurface: Y must match the size of VAL"); endif ## check z if (isvector (z) && length (z) == data_size(3)) z = repmat (reshape (z(:), [1 1 length(z)]), [data_size(1) data_size(2) 1]); elseif (! size_equal (data, z)) error ("isosurface: Z must match the size of VAL"); endif ## check iso if (isempty (iso)) ## calculate "good" iso value from data iso = __calc_isovalue_from_data__ (data); endif if ~isscalar (iso) error ("isosurface: ISO must be a scalar") endif ## check colors if (! isempty (colors)) if (! size_equal (data, colors)) error ("isosurface: COL must match the size of VAL") endif if (nout == 2) warning ("isosurface: colors will be calculated, but no output argument to receive it."); endif elseif (nout == 3) error ("isosurface: COL must be passed to return C") endif endfunction %!demo %! clf; %! [x,y,z] = meshgrid (-2:0.5:2, -2:0.5:2, -2:0.5:2); %! val = x.^2 + y.^2 + z.^2; %! isosurface (x, y, z, val, 3); %! isosurface (x, y, z, val, 5); %! axis equal; %! title ('isosurfaces of two nested spheres'); %!demo %! x = 0:2; %! y = 0:3; %! z = 0:1; %! [xx, yy, zz] = meshgrid (x, y, z); %! val = [0, 0, 0; 0, 0, 0; 0, 0, 1; 0, 0, 1]; %! val(:,:,2) = [0, 0, 0; 0, 0, 1; 0, 1, 2; 0, 1, 2]; %! iso = 0.8; %% three arguments, no output %! figure; %! subplot (2, 2, 1); isosurface (val, iso, yy); view(3) %% six arguments, no output (x, y, z are vectors) %! subplot (2, 2, 2); isosurface (x, y, z, val, iso, yy); view (3) %% six arguments, no output (x, y, z are matrices) %! subplot (2, 2, 3); isosurface (xx, yy, zz, val, iso, yy); view (3) %% six arguments, no output (mixed x, y, z) and option "noshare" %! subplot (2, 2, 4); isosurface (x, yy, z, val, iso, yy, "noshare"); view (3) %! annotation("textbox", [0 0.95 1 0.1], ... %! "String", ["Apart from the first plot having a different scale, " ... %! "all four plots must look the same.\n" ... %! "The last plot might have different colors but must have " ... %! "the same shape."], ... %! "HorizontalAlignment", "left"); %!shared x, y, z, xx, yy, zz, val, iso %! x = 0:2; %! y = 0:3; %! z = 0:1; %! [xx, yy, zz] = meshgrid (x, y, z); %! val = [0, 0, 0; 0, 0, 0; 0, 0, 1; 0, 0, 1]; %! val(:,:,2) = [0, 0, 0; 0, 0, 1; 0, 1, 2; 0, 1, 2]; %! iso = 0.8; ## one argument, one output %!test %! fv = isosurface (val); %! assert (isfield (fv, "vertices"), true); %! assert (isfield (fv, "faces"), true); %! assert (size (fv.vertices), [5 3]); %! assert (size (fv.faces), [3 3]); ## two arguments (second is ISO), one output %!test %! fv = isosurface (val, iso); %! assert (isfield (fv, "vertices"), true); %! assert (isfield (fv, "faces"), true); %! assert (size (fv.vertices), [11 3]); %! assert (size (fv.faces), [10 3]); ## two arguments (second is COL), one output %!test %! fvc = isosurface (val, yy); %! assert (isfield (fvc, "vertices"), true); %! assert (isfield (fvc, "faces"), true); %! assert (isfield (fvc, "facevertexcdata"), true); %! assert (size (fvc.vertices), [5 3]); %! assert (size (fvc.faces), [3 3]); %! assert (size (fvc.facevertexcdata), [5 1]); ## three arguments, one output %!test %! fvc = isosurface (val, iso, yy); %! assert (isfield (fvc, "vertices"), true); %! assert (isfield (fvc, "faces"), true); %! assert (isfield (fvc, "facevertexcdata"), true); %! assert (size (fvc.vertices), [11 3]); %! assert (size (fvc.faces), [10 3]); %! assert (size (fvc.facevertexcdata), [11 1]); ## four arguments, one output %!test %! fv = isosurface (x, [], z, val); %! assert (isfield (fv, "vertices"), true); %! assert (isfield (fv, "faces"), true); %! assert (size (fv.vertices), [5 3]); %! assert (size (fv.faces), [3 3]); ## five arguments (fifth is ISO), one output %!test %! fv = isosurface (xx, y, [], val, iso); %! assert (isfield (fv, "vertices"), true); %! assert (isfield (fv, "faces"), true); %! assert (size (fv.vertices), [11 3]); %! assert (size (fv.faces), [10 3]); ## five arguments (fifth is COL), one output %!test %! fvc = isosurface ([], yy, z, val, yy); %! assert (isfield (fvc, "vertices"), true); %! assert (isfield (fvc, "faces"), true); %! assert (isfield (fvc, "facevertexcdata"), true); %! assert (size (fvc.vertices), [5 3]); %! assert (size (fvc.faces), [3 3]); %! assert (size (fvc.facevertexcdata), [5 1]); ## six arguments, one output %!test %! fvc = isosurface (xx, yy, zz, val, iso, yy); %! assert (isfield (fvc, "vertices"), true); %! assert (isfield (fvc, "faces"), true); %! assert (isfield (fvc, "facevertexcdata"), true); %! assert (size (fvc.vertices), [11 3]); %! assert (size (fvc.faces), [10 3]); %! assert (size (fvc.facevertexcdata), [11 1]); ## five arguments (fifth is ISO), two outputs %!test %! [f, v] = isosurface (x, y, z, val, iso); %! assert (size (f), [10 3]); %! assert (size (v), [11 3]); ## six arguments, three outputs %!test %! [f, v, c] = isosurface (x, y, z, val, iso, yy); %! assert (size (f), [10 3]); %! assert (size (v), [11 3]); %! assert (size (c), [11 1]); ## two arguments (second is ISO) and one string, one output %!test %! fv = isosurface (val, iso, "verbose"); %! assert (isfield (fv, "vertices"), true); %! assert (isfield (fv, "faces"), true); %! assert (size (fv.vertices), [11 3]); %! assert (size (fv.faces), [10 3]); ## six arguments and two strings, one output %!test %! fvc = isosurface (xx, yy, zz, val, iso, yy, "v", "noshare"); %! assert (isfield (fvc, "vertices"), true); %! assert (isfield (fvc, "faces"), true); %! assert (isfield (fvc, "facevertexcdata"), true); %! assert (size (fvc.vertices), [20 3]); %! assert (size (fvc.faces), [10 3]); %! assert (size (fvc.facevertexcdata), [20 1]); ## five arguments (fifth is COL) and two strings (different order), one output %!test %! fvc = isosurface (xx, yy, zz, val, yy, "n", "v"); %! assert (isfield (fvc, "vertices"), true); %! assert (isfield (fvc, "faces"), true); %! assert (isfield (fvc, "facevertexcdata"), true); %! assert (size (fvc.vertices), [7 3]); %! assert (size (fvc.faces), [3 3]); %! assert (size (fvc.facevertexcdata), [7 1]); ## test for each error and warning %!test %!error <X must match the size of VAL> x = 1:2:24; fvc = isosurface (x, y, z, val, iso); %!error <Y must match the size of VAL> y = -14:6:11; fvc = isosurface (x, y, z, val, iso); %!error <Z must match the size of VAL> z = linspace (16, 18, 5); fvc = isosurface (x, y, z, val, iso); %!error <X must match the size of VAL> x = 1:2:24; [xx, yy, zz] = meshgrid (x, y, z); fvc = isosurface (xx, yy, zz, val, iso); %!error <X must match the size of VAL> y = -14:6:11; [xx, yy, zz] = meshgrid (x, y, z); fvc = isosurface (xx, yy, zz, val, iso); %!error <X must match the size of VAL> z = linspace (16, 18, 3); [xx, yy, zz] = meshgrid (x, y, z); fvc = isosurface (xx, yy, zz, val, iso); %!error <VAL must be a non-singleton 3-dimensional matrix> val = reshape(1:6*8, [6 8]); fvc = isosurface (val, iso); %!error <VAL must be a non-singleton 3-dimensional matrix> val = reshape(1:6*8, [6 1 8]); fvc = isosurface (val, iso); %!error <ISO must be a scalar> fvc = isosurface (val, [iso iso], yy); %!error <COL must match the size of VAL> fvc = isosurface (val, [iso iso]); %!warning <colors will be calculated, but no output argument to receive it.> [f, v] = isosurface (val, iso, yy); %!error <COL must be passed to return C> [f, v, c] = isosurface (val, iso); %!error <Invalid call to isosurface> fvc = isosurface (); %!error <Invalid call to isosurface> [f, v, c, a] = isosurface (val, iso); %!error <wrong number of input arguments> fvc = isosurface (xx, yy, zz, val, iso, yy, 5); %!error <parameter 'test_string' not supported> fvc = isosurface (val, iso, "test_string"); ## test for __calc_isovalue_from_data__ ## FIXME: private function cannot be tested, unless bug #38776 is resolved. %!xtest %! assert (__calc_isovalue_from_data__ (1:5), 3.02);