1########################################################################
2##
3## Copyright (C) 2016-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  {} {} material shiny
28## @deftypefnx {} {} material dull
29## @deftypefnx {} {} material metal
30## @deftypefnx {} {} material default
31## @deftypefnx {} {} material ([@var{as}, @var{ds}, @var{ss}])
32## @deftypefnx {} {} material ([@var{as}, @var{ds}, @var{ss}, @var{se}])
33## @deftypefnx {} {} material ([@var{as}, @var{ds}, @var{ss}, @var{se}, @var{scr}])
34## @deftypefnx {} {} material (@var{hlist}, @dots{})
35## @deftypefnx {} {@var{mtypes} =} material ()
36## @deftypefnx {} {@var{refl_props} =} material (@var{mtype_string})
37## Set reflectance properties for the lighting of surfaces and patches.
38##
39## This function changes the ambient, diffuse, and specular strengths, as well
40## as the specular exponent and specular color reflectance, of all
41## @code{patch} and @code{surface} objects in the current axes.  This can be
42## used to simulate, to some extent, the reflectance properties of certain
43## materials when used with @code{light}.
44##
45## When called with a string, the aforementioned properties are set
46## according to the values in the following table:
47##
48## @multitable @columnfractions .0 .2 .15 .15 .15 .15 .15 .0
49## @headitem @tab @var{mtype} @tab ambient- strength @tab diffuse-
50## strength @tab specular- strength @tab specular- exponent @tab specular-
51## color- reflectance @tab
52## @item @tab @qcode{"shiny"} @tab 0.3 @tab 0.6 @tab 0.9 @tab 20 @tab 1.0 @tab
53## @item @tab @qcode{"dull"} @tab 0.3 @tab 0.8 @tab 0.0 @tab 10 @tab 1.0 @tab
54## @item @tab @qcode{"metal"} @tab 0.3 @tab 0.3 @tab 1.0 @tab 25 @tab 0.5 @tab
55## @item @tab @qcode{"default"} @tab @qcode{"default"} @tab @qcode{"default"} @tab @qcode{"default"} @tab
56## @qcode{"default"} @tab @qcode{"default"} @tab
57## @end multitable
58##
59## When called with a vector of three elements, the ambient, diffuse, and
60## specular strengths of all @code{patch} and @code{surface} objects in the
61## current axes are updated.  An optional fourth vector element updates the
62## specular exponent, and an optional fifth vector element updates the
63## specular color reflectance.
64##
65## A list of graphic handles can also be passed as the first argument.  In
66## this case, the properties of these handles and all child @code{patch} and
67## @code{surface} objects will be updated.
68##
69## Additionally, @code{material} can be called with a single output argument.
70## If called without input arguments, a column cell vector @var{mtypes} with
71## the strings for all available materials is returned.  If the one input
72## argument @var{mtype_string} is the name of a material, a 1x5 cell vector
73## @var{refl_props} with the reflectance properties of that material is
74## returned.  In both cases, no graphic properties are changed.
75##
76## @seealso{light, fill, mesh, patch, pcolor, surf, surface}
77## @end deftypefn
78
79function retval = material (varargin)
80
81  if (! ((nargout == 0 && (nargin == 1 || nargin == 2))
82         || (nargout == 1 && (nargin == 0 || nargin == 1))))
83    print_usage ();
84  endif
85
86  ## resolve input
87  h = [];
88  if (nargout == 0)
89    ## Check whether first argument is list of graphics handles.
90    if (all (ishghandle (varargin{1})))
91      h = varargin{1};
92      varargin(1) = [];
93    endif
94
95    ## There must be one (additional) argument.
96    if (numel (varargin) != 1)
97      if (nargin == 2)
98        error (["material: When called with two arguments, the first argument " ...
99                "must be a list of handles to graphics objects."]);
100      else
101        print_usage ();
102      endif
103    endif
104  elseif (nargin == 0)
105    ## Return name of materials.
106    retval = {"shiny"; "dull"; "metal"; "default"};
107    return;
108  endif
109
110  mtype = varargin{1};
111
112  se = [];
113  scr = [];
114
115  ## check material type
116  if (ischar (mtype))
117    switch (lower (mtype))
118      case "shiny"
119        as = 0.3;
120        ds = 0.6;
121        ss = 0.9;
122        se = 20;
123        scr = 1.0;
124
125      case "dull"
126        as = 0.3;
127        ds = 0.8;
128        ss = 0.0;
129        se = 10;
130        scr = 1.0;
131
132      case "metal"
133        as = 0.3;
134        ds = 0.3;
135        ss = 1.0;
136        se = 25;
137        scr = .5;
138
139      case "default"
140        as = "default";
141        ds = "default";
142        ss = "default";
143        se = "default";
144        scr = "default";
145
146      otherwise
147        error ("material: unknown material type '%s'", mtype);
148
149    endswitch
150
151    if (nargout == 1)
152      ## Return 1x5 cell vector with reflectance properties.
153      retval = {as, ds, ss, se, scr};
154      return;
155    endif
156
157  elseif (nargout == 1)
158    ## If we reach here with one output argument, the input was wrong.
159    print_usage ();
160
161  elseif (isvector (mtype))
162    num_mtype = numel (mtype);
163    if (num_mtype < 3 || num_mtype > 5)
164      error ("material: incorrect number of elements in material vector");
165    endif
166    as = mtype(1);
167    ds = mtype(2);
168    ss = mtype(3);
169    if (num_mtype >= 4)
170      se = mtype(4);
171      if (num_mtype == 5)
172        scr = mtype(5);
173      endif
174    endif
175
176  else
177    error ("material: MTYPE must be a named material or a vector");
178  endif
179
180  if (isempty (h))
181    h = gca ();
182  endif
183  ## find all patch and surface objects in current axes
184  hps = findobj (h, "Type", "patch", "-or", "Type", "surface");
185
186  ## set properties
187  set (hps,
188       "ambientstrength", as, "diffusestrength", ds, "specularstrength", ss);
189
190  if (! isempty (se))
191    set (hps, "specularexponent", se);
192    if (! isempty (scr))
193      set (hps, "specularcolorreflectance", scr);
194    endif
195  endif
196
197endfunction
198
199
200%!demo
201%! clf;
202%! ## patch
203%! [x,y,z] = meshgrid (-2:0.2:2, -2:0.2:2, -2:0.2:2);
204%! val = x.^2 + y.^2 + z.^2;
205%! fv1 = isosurface (x, y, z, val, 1);
206%! h_patch = patch (fv1, "FaceColor", "r", "EdgeColor", "none", ...
207%!                       "FaceLighting", "Gouraud");
208%! isonormals (x, y, z, val, h_patch);
209%! axis equal;  axis tight;
210%! view (3);
211%! box off;
212%! drawnow ();
213%! light ();
214%! material ([0 0.5 1 10 .5]);
215%! title ("material() with numeric input");
216
217%!demo
218%! clf;
219%! ## surface
220%! hax = axes ();
221%! surf (hax, peaks, "LineStyle", "none", "FaceLighting", "Gouraud");
222%! view (3);
223%! light ();
224%! material metal;
225%! title ("material metal");
226
227%!test
228%! hf = figure ("Visible", "off");
229%! unwind_protect
230%!   hp = patch;
231%!   hs = surface;
232%!   material dull
233%!   assert (get (hp, "ambientstrength"), 0.3);
234%!   assert (get (hs, "ambientstrength"), 0.3);
235%!   assert (get (hp, "diffusestrength"), 0.8);
236%!   assert (get (hs, "diffusestrength"), 0.8);
237%!   assert (get (hp, "specularstrength"), 0.0);
238%!   assert (get (hs, "specularstrength"), 0.0);
239%!   assert (get (hp, "specularexponent"), 10);
240%!   assert (get (hs, "specularexponent"), 10);
241%!   assert (get (hp, "specularcolorreflectance"), 1.0);
242%!   assert (get (hs, "specularcolorreflectance"), 1.0);
243%!   material default
244%!   assert (get (hp, "ambientstrength"), get (0, "defaultpatchambientstrength"));
245%!   assert (get (hs, "ambientstrength"), get (0, "defaultsurfaceambientstrength"));
246%!   assert (get (hp, "diffusestrength"), get (0, "defaultpatchdiffusestrength"));
247%!   assert (get (hs, "diffusestrength"), get (0, "defaultsurfacediffusestrength"));
248%!   assert (get (hp, "specularstrength"), get (0, "defaultpatchspecularstrength"));
249%!   assert (get (hs, "specularstrength"), get (0, "defaultsurfacespecularstrength"));
250%!   assert (get (hp, "specularexponent"), get (0, "defaultpatchspecularexponent"));
251%!   assert (get (hs, "specularexponent"), get (0, "defaultsurfacespecularexponent"));
252%!   assert (get (hp, "specularcolorreflectance"), get (0, "defaultpatchspecularcolorreflectance"));
253%!   assert (get (hs, "specularcolorreflectance"), get (0, "defaultsurfacespecularcolorreflectance"));
254%!   material ([0.5 0.6 0.7 20 0.8])
255%!   assert (get (hp, "ambientstrength"), 0.5);
256%!   assert (get (hs, "ambientstrength"), 0.5);
257%!   assert (get (hp, "diffusestrength"), 0.6);
258%!   assert (get (hs, "diffusestrength"), 0.6);
259%!   assert (get (hp, "specularstrength"), 0.7);
260%!   assert (get (hs, "specularstrength"), 0.7);
261%!   assert (get (hp, "specularexponent"), 20);
262%!   assert (get (hs, "specularexponent"), 20);
263%!   assert (get (hp, "specularcolorreflectance"), 0.8);
264%!   assert (get (hs, "specularcolorreflectance"), 0.8);
265%!   material (hp, "shiny")
266%!   assert (get (hp, "ambientstrength"), 0.3);
267%!   assert (get (hs, "ambientstrength"), 0.5);
268%!   assert (get (hp, "diffusestrength"), 0.6);
269%!   assert (get (hs, "diffusestrength"), 0.6);
270%!   assert (get (hp, "specularstrength"), 0.9);
271%!   assert (get (hs, "specularstrength"), 0.7);
272%!   assert (get (hp, "specularexponent"), 20);
273%!   assert (get (hs, "specularexponent"), 20);
274%!   assert (get (hp, "specularcolorreflectance"), 1.0);
275%!   assert (get (hs, "specularcolorreflectance"), 0.8);
276%!   material (hf, "metal")
277%!   assert (get (hp, "ambientstrength"), 0.3);
278%!   assert (get (hs, "ambientstrength"), 0.3);
279%!   assert (get (hp, "diffusestrength"), 0.3);
280%!   assert (get (hs, "diffusestrength"), 0.3);
281%!   assert (get (hp, "specularstrength"), 1.0);
282%!   assert (get (hs, "specularstrength"), 1.0);
283%!   assert (get (hp, "specularexponent"), 25);
284%!   assert (get (hs, "specularexponent"), 25);
285%!   assert (get (hp, "specularcolorreflectance"), 0.5);
286%!   assert (get (hs, "specularcolorreflectance"), 0.5);
287%! unwind_protect_cleanup
288%!   close (hf);
289%! end_unwind_protect
290
291%!test
292%! mtypes = material ();
293%! assert (iscell (mtypes));
294%! assert (size (mtypes, 2), 1);
295%! assert (all (cellfun (@ischar, mtypes)));
296
297%!test
298%! refl_props = material ("metal");
299%! assert (refl_props, {0.3, 0.3, 1, 25, 0.5})
300
301## Test input validation
302%!error <Invalid call to material> material ()
303%!error <Invalid call to material> material (-1, 2, 3)
304%!error <Invalid call to material> a = material (-1)
305%!error <Invalid call to material> a = material (-1, 2)
306%!error <Invalid call to material> a = material ({})
307%!error <Invalid call to material> a = material ([.3 .4 .5])
308%!error <Invalid call to material> [a, b] = material ()
309%!error <first argument must be a list of handles> material (-1, "metal")
310%!error <unknown material type 'foo'> material foo
311%!error <incorrect number of elements in material vector> material (-1)
312%!error <incorrect number of elements in material vector> material ([1 2 3 4 5 6])
313%!error <MTYPE must be a named material or a vector> material ({})
314
315%!error <Invalid call to material.>
316%! hf = figure ("visible", "off");
317%! unwind_protect
318%!   material (hf);
319%! unwind_protect_cleanup
320%!   close (hf);
321%! end_unwind_protect
322