########################################################################
##
## Copyright (C) 2016-2021 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or .
##
## 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
## .
##
########################################################################
## -*- texinfo -*-
## @deftypefn {} {} material shiny
## @deftypefnx {} {} material dull
## @deftypefnx {} {} material metal
## @deftypefnx {} {} material default
## @deftypefnx {} {} material ([@var{as}, @var{ds}, @var{ss}])
## @deftypefnx {} {} material ([@var{as}, @var{ds}, @var{ss}, @var{se}])
## @deftypefnx {} {} material ([@var{as}, @var{ds}, @var{ss}, @var{se}, @var{scr}])
## @deftypefnx {} {} material (@var{hlist}, @dots{})
## @deftypefnx {} {@var{mtypes} =} material ()
## @deftypefnx {} {@var{refl_props} =} material (@var{mtype_string})
## Set reflectance properties for the lighting of surfaces and patches.
##
## This function changes the ambient, diffuse, and specular strengths, as well
## as the specular exponent and specular color reflectance, of all
## @code{patch} and @code{surface} objects in the current axes. This can be
## used to simulate, to some extent, the reflectance properties of certain
## materials when used with @code{light}.
##
## When called with a string, the aforementioned properties are set
## according to the values in the following table:
##
## @multitable @columnfractions .0 .2 .15 .15 .15 .15 .15 .0
## @headitem @tab @var{mtype} @tab ambient- strength @tab diffuse-
## strength @tab specular- strength @tab specular- exponent @tab specular-
## color- reflectance @tab
## @item @tab @qcode{"shiny"} @tab 0.3 @tab 0.6 @tab 0.9 @tab 20 @tab 1.0 @tab
## @item @tab @qcode{"dull"} @tab 0.3 @tab 0.8 @tab 0.0 @tab 10 @tab 1.0 @tab
## @item @tab @qcode{"metal"} @tab 0.3 @tab 0.3 @tab 1.0 @tab 25 @tab 0.5 @tab
## @item @tab @qcode{"default"} @tab @qcode{"default"} @tab @qcode{"default"} @tab @qcode{"default"} @tab
## @qcode{"default"} @tab @qcode{"default"} @tab
## @end multitable
##
## When called with a vector of three elements, the ambient, diffuse, and
## specular strengths of all @code{patch} and @code{surface} objects in the
## current axes are updated. An optional fourth vector element updates the
## specular exponent, and an optional fifth vector element updates the
## specular color reflectance.
##
## A list of graphic handles can also be passed as the first argument. In
## this case, the properties of these handles and all child @code{patch} and
## @code{surface} objects will be updated.
##
## Additionally, @code{material} can be called with a single output argument.
## If called without input arguments, a column cell vector @var{mtypes} with
## the strings for all available materials is returned. If the one input
## argument @var{mtype_string} is the name of a material, a 1x5 cell vector
## @var{refl_props} with the reflectance properties of that material is
## returned. In both cases, no graphic properties are changed.
##
## @seealso{light, fill, mesh, patch, pcolor, surf, surface}
## @end deftypefn
function retval = material (varargin)
if (! ((nargout == 0 && (nargin == 1 || nargin == 2))
|| (nargout == 1 && (nargin == 0 || nargin == 1))))
print_usage ();
endif
## resolve input
h = [];
if (nargout == 0)
## Check whether first argument is list of graphics handles.
if (all (ishghandle (varargin{1})))
h = varargin{1};
varargin(1) = [];
endif
## There must be one (additional) argument.
if (numel (varargin) != 1)
if (nargin == 2)
error (["material: When called with two arguments, the first argument " ...
"must be a list of handles to graphics objects."]);
else
print_usage ();
endif
endif
elseif (nargin == 0)
## Return name of materials.
retval = {"shiny"; "dull"; "metal"; "default"};
return;
endif
mtype = varargin{1};
se = [];
scr = [];
## check material type
if (ischar (mtype))
switch (lower (mtype))
case "shiny"
as = 0.3;
ds = 0.6;
ss = 0.9;
se = 20;
scr = 1.0;
case "dull"
as = 0.3;
ds = 0.8;
ss = 0.0;
se = 10;
scr = 1.0;
case "metal"
as = 0.3;
ds = 0.3;
ss = 1.0;
se = 25;
scr = .5;
case "default"
as = "default";
ds = "default";
ss = "default";
se = "default";
scr = "default";
otherwise
error ("material: unknown material type '%s'", mtype);
endswitch
if (nargout == 1)
## Return 1x5 cell vector with reflectance properties.
retval = {as, ds, ss, se, scr};
return;
endif
elseif (nargout == 1)
## If we reach here with one output argument, the input was wrong.
print_usage ();
elseif (isvector (mtype))
num_mtype = numel (mtype);
if (num_mtype < 3 || num_mtype > 5)
error ("material: incorrect number of elements in material vector");
endif
as = mtype(1);
ds = mtype(2);
ss = mtype(3);
if (num_mtype >= 4)
se = mtype(4);
if (num_mtype == 5)
scr = mtype(5);
endif
endif
else
error ("material: MTYPE must be a named material or a vector");
endif
if (isempty (h))
h = gca ();
endif
## find all patch and surface objects in current axes
hps = findobj (h, "Type", "patch", "-or", "Type", "surface");
## set properties
set (hps,
"ambientstrength", as, "diffusestrength", ds, "specularstrength", ss);
if (! isempty (se))
set (hps, "specularexponent", se);
if (! isempty (scr))
set (hps, "specularcolorreflectance", scr);
endif
endif
endfunction
%!demo
%! clf;
%! ## patch
%! [x,y,z] = meshgrid (-2:0.2:2, -2:0.2:2, -2:0.2:2);
%! val = x.^2 + y.^2 + z.^2;
%! fv1 = isosurface (x, y, z, val, 1);
%! h_patch = patch (fv1, "FaceColor", "r", "EdgeColor", "none", ...
%! "FaceLighting", "Gouraud");
%! isonormals (x, y, z, val, h_patch);
%! axis equal; axis tight;
%! view (3);
%! box off;
%! drawnow ();
%! light ();
%! material ([0 0.5 1 10 .5]);
%! title ("material() with numeric input");
%!demo
%! clf;
%! ## surface
%! hax = axes ();
%! surf (hax, peaks, "LineStyle", "none", "FaceLighting", "Gouraud");
%! view (3);
%! light ();
%! material metal;
%! title ("material metal");
%!test
%! hf = figure ("Visible", "off");
%! unwind_protect
%! hp = patch;
%! hs = surface;
%! material dull
%! assert (get (hp, "ambientstrength"), 0.3);
%! assert (get (hs, "ambientstrength"), 0.3);
%! assert (get (hp, "diffusestrength"), 0.8);
%! assert (get (hs, "diffusestrength"), 0.8);
%! assert (get (hp, "specularstrength"), 0.0);
%! assert (get (hs, "specularstrength"), 0.0);
%! assert (get (hp, "specularexponent"), 10);
%! assert (get (hs, "specularexponent"), 10);
%! assert (get (hp, "specularcolorreflectance"), 1.0);
%! assert (get (hs, "specularcolorreflectance"), 1.0);
%! material default
%! assert (get (hp, "ambientstrength"), get (0, "defaultpatchambientstrength"));
%! assert (get (hs, "ambientstrength"), get (0, "defaultsurfaceambientstrength"));
%! assert (get (hp, "diffusestrength"), get (0, "defaultpatchdiffusestrength"));
%! assert (get (hs, "diffusestrength"), get (0, "defaultsurfacediffusestrength"));
%! assert (get (hp, "specularstrength"), get (0, "defaultpatchspecularstrength"));
%! assert (get (hs, "specularstrength"), get (0, "defaultsurfacespecularstrength"));
%! assert (get (hp, "specularexponent"), get (0, "defaultpatchspecularexponent"));
%! assert (get (hs, "specularexponent"), get (0, "defaultsurfacespecularexponent"));
%! assert (get (hp, "specularcolorreflectance"), get (0, "defaultpatchspecularcolorreflectance"));
%! assert (get (hs, "specularcolorreflectance"), get (0, "defaultsurfacespecularcolorreflectance"));
%! material ([0.5 0.6 0.7 20 0.8])
%! assert (get (hp, "ambientstrength"), 0.5);
%! assert (get (hs, "ambientstrength"), 0.5);
%! assert (get (hp, "diffusestrength"), 0.6);
%! assert (get (hs, "diffusestrength"), 0.6);
%! assert (get (hp, "specularstrength"), 0.7);
%! assert (get (hs, "specularstrength"), 0.7);
%! assert (get (hp, "specularexponent"), 20);
%! assert (get (hs, "specularexponent"), 20);
%! assert (get (hp, "specularcolorreflectance"), 0.8);
%! assert (get (hs, "specularcolorreflectance"), 0.8);
%! material (hp, "shiny")
%! assert (get (hp, "ambientstrength"), 0.3);
%! assert (get (hs, "ambientstrength"), 0.5);
%! assert (get (hp, "diffusestrength"), 0.6);
%! assert (get (hs, "diffusestrength"), 0.6);
%! assert (get (hp, "specularstrength"), 0.9);
%! assert (get (hs, "specularstrength"), 0.7);
%! assert (get (hp, "specularexponent"), 20);
%! assert (get (hs, "specularexponent"), 20);
%! assert (get (hp, "specularcolorreflectance"), 1.0);
%! assert (get (hs, "specularcolorreflectance"), 0.8);
%! material (hf, "metal")
%! assert (get (hp, "ambientstrength"), 0.3);
%! assert (get (hs, "ambientstrength"), 0.3);
%! assert (get (hp, "diffusestrength"), 0.3);
%! assert (get (hs, "diffusestrength"), 0.3);
%! assert (get (hp, "specularstrength"), 1.0);
%! assert (get (hs, "specularstrength"), 1.0);
%! assert (get (hp, "specularexponent"), 25);
%! assert (get (hs, "specularexponent"), 25);
%! assert (get (hp, "specularcolorreflectance"), 0.5);
%! assert (get (hs, "specularcolorreflectance"), 0.5);
%! unwind_protect_cleanup
%! close (hf);
%! end_unwind_protect
%!test
%! mtypes = material ();
%! assert (iscell (mtypes));
%! assert (size (mtypes, 2), 1);
%! assert (all (cellfun (@ischar, mtypes)));
%!test
%! refl_props = material ("metal");
%! assert (refl_props, {0.3, 0.3, 1, 25, 0.5})
## Test input validation
%!error material ()
%!error material (-1, 2, 3)
%!error a = material (-1)
%!error a = material (-1, 2)
%!error a = material ({})
%!error a = material ([.3 .4 .5])
%!error [a, b] = material ()
%!error material (-1, "metal")
%!error material foo
%!error material (-1)
%!error material ([1 2 3 4 5 6])
%!error material ({})
%!error
%! hf = figure ("visible", "off");
%! unwind_protect
%! material (hf);
%! unwind_protect_cleanup
%! close (hf);
%! end_unwind_protect