# HG changeset patch
# User Colin Macdonald <cbm@m.fsf.org>
# Date 1468289647 25200
#      Mon Jul 11 19:14:07 2016 -0700
# Node ID d437644337b3b4410ebd2ee97bd4ea368156de0d
# Parent  b667ae305f0d7b46319ae0dc74229d0db47827d5
Add some camera manipulation functions (patch #9046).

* doc/interpreter/plot.txi: Add docstrings to manual.
* scripts/help/__unimplemented__.m: Trim list.
* scripts/plot/appearance/campos.m: New function.
* scripts/plot/appearance/camroll.m: New function.
* scripts/plot/appearance/camtarget.m: New function.
* scripts/plot/appearance/camup.m: New function.
* scripts/plot/appearance/camva.m: New function.
* scripts/plot/appearance/module.mk: Update.

diff -r b667ae305f0d -r d437644337b3 doc/interpreter/plot.txi
--- a/doc/interpreter/plot.txi	Sun Jul 10 00:26:57 2016 -0700
+++ b/doc/interpreter/plot.txi	Mon Jul 11 19:14:07 2016 -0700
@@ -400,6 +400,16 @@
 
 @DOCSTRING(view)
 
+@DOCSTRING(campos)
+
+@DOCSTRING(camroll)
+
+@DOCSTRING(camtarget)
+
+@DOCSTRING(camup)
+
+@DOCSTRING(camva)
+
 @DOCSTRING(slice)
 
 @DOCSTRING(ribbon)
diff -r b667ae305f0d -r d437644337b3 scripts/help/__unimplemented__.m
--- a/scripts/help/__unimplemented__.m	Sun Jul 10 00:26:57 2016 -0700
+++ b/scripts/help/__unimplemented__.m	Mon Jul 11 19:14:07 2016 -0700
@@ -577,12 +577,7 @@
   "camlookat",
   "camorbit",
   "campan",
-  "campos",
   "camproj",
-  "camroll",
-  "camtarget",
-  "camup",
-  "camva",
   "camzoom",
   "categorical",
   "categories",
diff -r b667ae305f0d -r d437644337b3 scripts/plot/appearance/campos.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/campos.m	Mon Jul 11 19:14:07 2016 -0700
@@ -0,0 +1,164 @@
+## Copyright (C) 2016 Colin B. Macdonald
+##
+## 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.
+##
+## This software 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 this software; see the file COPYING.
+## If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @documentencoding UTF-8
+## @deftypefn  {} {@var{P} =} campos ()
+## @deftypefnx {} {} campos ([@var{x} @var{y} @var{z}])
+## @deftypefnx {} {@var{mode} =} campos ("mode")
+## @deftypefnx {} {} campos (@var{mode})
+## @deftypefnx {} {} campos (@var{ax}, @dots{})
+## Set or get the camera position.
+##
+## The default camera position is determined automatically based on
+## the scene.  For example, to get the camera position:
+## @example
+## @group
+## hf = figure();
+## peaks()
+## p = campos ()
+##   @result{} p =
+##       -27.394  -35.701   64.079
+## @end group
+## @end example
+##
+## We can then move the camera further up the z-axis:
+## @example
+## @group
+## campos (p + [0 0 10])
+## campos ()
+##   @result{} ans =
+##       -27.394  -35.701   74.079
+## @end group
+## @end example
+##
+## Having made that change, the camera position @var{mode} is now
+## manual:
+## @example
+## @group
+## campos ("mode")
+##   @result{} manual
+## @end group
+## @end example
+##
+## We can set it back to automatic:
+## @example
+## @group
+## campos ("auto")
+## @c doctest: +XFAIL
+## @c https://savannah.gnu.org/bugs/?44503
+## campos ()
+##   @result{} ans =
+##       -27.394  -35.701   64.079
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands effect the current axis; alternatively
+## an axis can be specified by the optional argument @var{ax}.
+##
+## @seealso{camup, camtarget, camva}
+## @end deftypefn
+
+
+function p = campos (varargin)
+
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("campos", varargin{:});
+
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
+  endif
+
+  prop = "cameraposition";
+  if (nargin == 0)
+    p = get (hax, prop);
+  elseif (nargin == 1 && isnumeric (varargin{1}) && numel (varargin{1}) == 3)
+    set (hax, prop, varargin{1});
+  elseif (nargin == 1 && ischar (varargin{1}))
+    s = varargin{1};
+    if (strcmp (s, "mode"))
+      p = get (hax, [prop "mode"]);
+    else
+      set (hax, [prop "mode"], s);
+    endif
+  else
+    print_usage ();
+  endif
+
+endfunction
+
+
+%!demo
+%! sphere ()
+%! ## where is camera located?
+%! x = campos ()
+%! ## move the camera upwards
+%! campos (x + [0 0 2])
+
+
+%!shared hf
+%! hf = figure ("visible", "off");
+
+%!error <Invalid> campos (1, 2, 3)
+%!error <invalid> campos ("mod")
+
+%!test
+%! sphere ()
+%! x = campos ();
+%! campos ([1 2 3])
+%! y = campos ();
+%! assert (y, [1 2 3])
+%! campos (x)
+%! x2 = campos ();
+%! assert (x, x2)
+
+%!test
+%! clf; sphere()
+%! porig = campos ();
+%! m = campos ("mode");
+%! assert (strcmp (m, "auto"))
+%!
+%! campos ([1 2 3])
+%! m = campos ("mode");
+%! assert (strcmp (m, "manual"))
+%!
+%! campos ("auto");
+%! # XXX: next line is workaround for https://savannah.gnu.org/bugs/?44503
+%! hold on; sphere (4)
+%! p = campos ();
+%! assert (p, porig)
+
+%!test
+%! ## test ax input by creating another axis
+%! clf
+%! subplot (1, 2, 1); sphere (); hax1 = gca ();
+%! subplot (1, 2, 2); peaks (); hax2 = gca ();
+%! campos (hax1, [20 0 0])
+%! subplot (1, 2, 1)
+%! x = campos ();
+%! z = campos (hax2);
+%! subplot (1, 2, 2)
+%! y = campos ();
+%! assert (x, [20 0 0])
+%! assert (norm (y - [20 0 0]) > 1)
+%! assert (y, z)
+
+%!test
+%! close (hf)
diff -r b667ae305f0d -r d437644337b3 scripts/plot/appearance/camroll.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camroll.m	Mon Jul 11 19:14:07 2016 -0700
@@ -0,0 +1,142 @@
+## Copyright (C) 2016 Colin B. Macdonald
+##
+## 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.
+##
+## This software 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 this software; see the file COPYING.
+## If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @documentencoding UTF-8
+## @deftypefn  {} {} camroll (@var{theta})
+## @deftypefnx {} {} camroll (@var{ax}, @var{theta})
+## Roll the camera.
+##
+## Roll the camera clockwise by @var{theta} degrees.
+## For example, the following command will roll the camera by
+## 30 degrees clockwise (to the right); this will cause the scene
+## to appear to roll by 30 degrees to the left:
+## @example
+## @group
+## @c doctest: +SKIP
+## peaks ()
+## camroll (30)
+## @end group
+## @end example
+##
+## Roll the camera back:
+## @example
+## @group
+## @c doctest: +SKIP
+## camroll (-30)
+## @end group
+## @end example
+##
+## To restore the automatic behaviour, we need a different command:
+## @example
+## @group
+## @c doctest: +SKIP
+## camup ("auto")
+## @end group
+## @end example
+##
+## By default, these commands effect the current axis; alternatively
+## an axis can be specified by the optional argument @var{ax}.
+##
+## @seealso{camup}
+## @end deftypefn
+
+
+function p = camroll (varargin)
+
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("camroll", varargin{:});
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  a = varargin{1};
+
+  if (! (isnumeric (a) && isscalar (a) ))
+    print_usage ();
+  endif
+
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
+  endif
+
+  view_ax = camtarget (hax) - campos (hax);
+  view_ax /= norm (view_ax);
+  ## orthogonalize the camup vector
+  up = camup (hax) - view_ax*dot (camup (hax), view_ax);
+  up /= norm (up);
+
+  ## rotate the modified camup vector around the view axis
+  up = num2cell (up);
+  [up{:}] = __rotate_around_axis__ (up{:}, a, view_ax, [0 0 0]);
+  up = [up{:}];
+  camup (hax, up)
+
+endfunction
+
+
+%!demo
+%! peaks ()
+%! camroll (30)
+
+
+%!error <Invalid> camroll (1, 2, 3)
+%!error <Invalid> camroll ("mod")
+%!error <Invalid> camroll (1, [1 2])
+
+%!shared hf
+%! hf = figure ("visible", "off");
+
+%!test
+%! sphere (10)
+%! campos ([10 0 0])
+%! camroll (30)
+%! p = camup ();
+%! assert (p, [0 1/2 sqrt(3)/2], eps)
+
+%!test
+%! ## test rolling, then rolling back
+%! clf; peaks ()
+%! p = camup ();
+%! assert (p, [0 0 1], eps)
+%! camroll (30)
+%! p = camup ();
+%! ## from Matlab R2014a
+%! q = [0.826398839602911  0.255644120004753  0.50170812412194];
+%! assert (p, q, 10*eps)
+%! camroll (-30)
+%! ## note it does not go back to [0 0 1]: instead orthog to camera view:
+%! p = camup ();
+%! assert (dot (p, camtarget () - campos ()), 0, eps)
+%! q = [0.496200420425837  0.646660977913424  0.57932264103285];
+%! assert (p, q, 10*eps)
+
+%!test
+%! ## test ax input by creating another axis
+%! clf;
+%! subplot (1, 2, 1); sphere (); hax = gca ();
+%! subplot (1, 2, 2); sphere (); camroll (hax, 30)
+%! subplot (1, 2, 1); x = camup ();
+%! subplot (1, 2, 2); y = camup ();
+%! assert (x, [0.660278 0.039151 0.750000], -1e-5)
+%! assert (y, [0 0 1])
+
+%!test
+%! close (hf)
diff -r b667ae305f0d -r d437644337b3 scripts/plot/appearance/camtarget.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camtarget.m	Mon Jul 11 19:14:07 2016 -0700
@@ -0,0 +1,172 @@
+## Copyright (C) 2016 Colin B. Macdonald
+##
+## 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.
+##
+## This software 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 this software; see the file COPYING.
+## If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @documentencoding UTF-8
+## @deftypefn  {} {@var{T} =} camtarget ()
+## @deftypefnx {} {} camtarget ([@var{x} @var{y} @var{z}])
+## @deftypefnx {} {@var{mode} =} camtarget ("mode")
+## @deftypefnx {} {} camtarget (@var{mode})
+## @deftypefnx {} {} camtarget (@var{ax}, @dots{})
+## Set or get where the camera is pointed.
+##
+## The camera target is a point in space where the camera is
+## pointing.  Usually, it is determined automatically based on
+## the scene:
+## @example
+## @group
+## hf = figure();
+## sphere (36)
+## v = camtarget ()
+##   @result{} v =
+##       0   0   0
+## @end group
+## @end example
+##
+## We can turn the camera to point at a new target:
+## @example
+## @group
+## camtarget ([1 1 1])
+## camtarget ()
+##   @result{}   1   1   1
+## @end group
+## @end example
+##
+## Having done so, the camera target @var{mode} is manual:
+## @example
+## @group
+## camtarget ("mode")
+##   @result{} manual
+## @end group
+## @end example
+## This means, for example, adding new objects to the scene will not
+## retarget the camera:
+## @example
+## @group
+## hold on;
+## peaks ()
+## camtarget ()
+##   @result{}   1   1   1
+## @end group
+## @end example
+##
+## We can reset it to be automatic:
+## @example
+## @group
+## @c doctest: +XFAIL
+## @c https://savannah.gnu.org/bugs/?44503
+## camtarget ("auto")
+## camtarget ()
+##   @result{}   0   0   0.76426
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands effect the current axis; alternatively
+## an axis can be specified by the optional argument @var{ax}.
+##
+## @seealso{campos, camup, camva}
+## @end deftypefn
+
+
+function p = camtarget (varargin)
+
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("camtarget", varargin{:});
+
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
+  endif
+
+  prop = "cameratarget";
+  if (nargin == 0)
+    p = get (hax, prop);
+  elseif (nargin == 1 && isnumeric (varargin{1}) && numel (varargin{1}) == 3)
+    set (hax, prop, varargin{1});
+  elseif (nargin == 1 && ischar (varargin{1}))
+    s = varargin{1};
+    if (strcmp (s, "mode"))
+      p = get (hax, [prop "mode"]);
+    else
+      set (hax, [prop "mode"], s);
+    endif
+  else
+    print_usage ();
+  endif
+
+endfunction
+
+
+%!demo
+%! sphere ()
+%! ## where is camera pointing?
+%! x = camtarget ()
+%! ## point the camera upwards
+%! camtarget (x + [0 0 1])
+
+
+%!shared hf
+%! hf = figure ("visible", "off");
+
+%!error <Invalid> camtarget (1, 2, 3)
+%!error <invalid> camtarget ("mod")
+
+%!test
+%! sphere ()
+%! x = camtarget ();
+%! camtarget ([1 2 3])
+%! y = camtarget ();
+%! assert (y, [1 2 3])
+%! camtarget (x)
+%! x2 = camtarget ();
+%! assert (x, x2)
+
+%!test
+%! clf; sphere ()
+%! xorig = camtarget ();
+%! m = camtarget ("mode");
+%! assert (strcmp (m, "auto"))
+%!
+%! camtarget ([1 2 3])
+%! m = camtarget ("mode");
+%! assert (strcmp (m, "manual"))
+%!
+%! camtarget ("auto")
+%! # XXX: next line is workaround for https://savannah.gnu.org/bugs/?44503
+%! hold on; sphere (4)
+%! x = camtarget ();
+%! assert (x, xorig)
+
+%!test
+%! ## test ax input by creating another axis
+%! clf
+%! subplot (1, 2, 1); sphere (); hax1 = gca ();
+%! subplot (1, 2, 2); peaks (); hax2 = gca ();
+%! camtarget (hax1, [0.1 0.2 0.3])
+%! subplot (1, 2, 1)
+%! x = camtarget ();
+%! z = camtarget (hax2);
+%! subplot (1, 2, 2)
+%! y = camtarget ();
+%! assert (x, [0.1 0.2 0.3])
+%! assert (norm (y - [0.1 0.2 0.3]) > 0.1)
+%! assert (y, z)
+
+%!test
+%! close (hf)
diff -r b667ae305f0d -r d437644337b3 scripts/plot/appearance/camup.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camup.m	Mon Jul 11 19:14:07 2016 -0700
@@ -0,0 +1,170 @@
+## Copyright (C) 2016 Colin B. Macdonald
+##
+## 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.
+##
+## This software 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 this software; see the file COPYING.
+## If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @documentencoding UTF-8
+## @deftypefn  {} {@var{up} =} camup ()
+## @deftypefnx {} {} camup ([@var{x} @var{y} @var{z}])
+## @deftypefnx {} {@var{mode} =} camup ("mode")
+## @deftypefnx {} {} camup (@var{mode})
+## @deftypefnx {} {} camup (@var{ax}, @dots{})
+## Set or get the camera up vector.
+##
+## By default, the camera is oriented so that ``up'' corresponds
+## to the positive z-axis:
+## @example
+## @group
+## hf = figure ();
+## sphere (36)
+## v = camup ()
+##   @result{} v =
+##       0   0   1
+## @end group
+## @end example
+##
+## Specifying a new ``up vector'' rolls the camera and sets the
+## mode to manual:
+## @example
+## @group
+## camup ([1 1 0])
+## camup ()
+##   @result{}   1   1   0
+## camup ("mode")
+##   @result{} manual
+## @end group
+## @end example
+##
+## Modifying the up vector can be confusing: one might expect the
+## up vector to be orthogonal to the direction of the camera's
+## view.  But this need not be so:
+## @example
+## camup ([1 2 3])
+## dot (camup (), camtarget () - campos ())
+##   @result{} 6...
+## @end example
+## A consequence is that ``pulling back'' on the up vector does not
+## pitch the camera view (as that would require changing the
+## target).  Setting the up vector is thus typically used only to
+## roll the camera; and for this purpose there is a more
+## intuitive command, @pxref{camroll}.
+##
+## Finally, we can reset the up vector to automatic mode:
+## @example
+## @group
+## @c doctest: +XFAIL
+## @c https://savannah.gnu.org/bugs/?44503
+## camup ("auto")
+## camup ()
+##   @result{}   0   0   1
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands effect the current axis; alternatively
+## an axis can be specified by the optional argument @var{ax}.
+##
+## @seealso{camroll, campos, camtarget, camva}
+## @end deftypefn
+
+
+function p = camup (varargin)
+
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("camup", varargin{:});
+
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
+  endif
+
+  prop = "cameraupvector";
+  if (nargin == 0)
+    p = get (hax, prop);
+  elseif (nargin == 1 && isnumeric (varargin{1}) && numel (varargin{1}) == 3)
+    set (hax, prop, varargin{1});
+  elseif (nargin == 1 && ischar (varargin{1}))
+    s = varargin{1};
+    if (strcmp (s, "mode"))
+      p = get (hax, [prop "mode"]);
+    else
+      set (hax, [prop "mode"], s);
+    endif
+  else
+    print_usage ();
+  endif
+
+endfunction
+
+
+%!demo
+%! sphere ()
+%! ## what direction is "up" for the camera?
+%! x = camup ()
+%! ## re-orient the camera with a new up-vector
+%! camup ([1 0 0])
+
+
+%!shared hf
+%! hf = figure ("visible", "off");
+
+%!error <Invalid> camup (1, 2, 3)
+%!error <invalid> camup ("mod")
+
+%!test
+%! sphere ()
+%! x = camup ();
+%! camup ([1 2 3])
+%! y = camup ();
+%! assert (y, [1 2 3])
+%! camup (x)
+%! x2 = camup ();
+%! assert (x, x2)
+
+%!test
+%! clf; sphere ()
+%! porig = camup ();
+%! m = camup ("mode");
+%! assert (strcmp (m, "auto"))
+%!
+%! camup ([1 2 3]);
+%! m = camup ("mode");
+%! assert (strcmp (m, "manual"))
+%!
+%! camup ("auto");
+%! # XXX: next line is workaround for https://savannah.gnu.org/bugs/?44503
+%! hold on; sphere (4)
+%! p = camup ();
+%! assert (p, porig)
+
+%!test
+%! ## test ax input by creating another axis
+%! clf;
+%! subplot (1, 2, 1); sphere (); hax1 = gca ();
+%! subplot (1, 2, 2); peaks (); hax2 = gca ();
+%! camup (hax1, [1 0 0])
+%! subplot (1, 2, 1)
+%! x = camup ();
+%! z = camup (hax2);
+%! subplot (1, 2, 2)
+%! y = camup ();
+%! assert (x, [1 0 0])
+%! assert (norm (y - [1 0 0]) > 0.1)
+%! assert (y, z)
+
+%!test
+%! close (hf)
diff -r b667ae305f0d -r d437644337b3 scripts/plot/appearance/camva.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camva.m	Mon Jul 11 19:14:07 2016 -0700
@@ -0,0 +1,155 @@
+## Copyright (C) 2016 Colin B. Macdonald
+##
+## 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.
+##
+## This software 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 this software; see the file COPYING.
+## If not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @documentencoding UTF-8
+## @deftypefn  {} {@var{a} =} camva ()
+## @deftypefnx {} {} camva (@var{a})
+## @deftypefnx {} {@var{mode} =} camva ("mode")
+## @deftypefnx {} {} camva (@var{mode})
+## @deftypefnx {} {} camva (@var{ax}, @dots{})
+## Set or get the camera viewing angle.
+##
+## The camera has a viewing angle which determines how much can be
+## seen.  By default this is:
+## @example
+## @group
+## hf = figure();
+## sphere (36)
+## a = camva ()
+##   @result{} a =  10.340
+## @end group
+## @end example
+##
+## To get a wider-angle view, we could double the viewing angle.
+## This will also set the mode to manual:
+## @example
+## @group
+## camva (2*a)
+## camva ("mode")
+##   @result{} manual
+## @end group
+## @end example
+##
+## We can set it back to automatic:
+## @example
+## @group
+## camva ("auto")
+## camva ("mode")
+##   @result{} auto
+## @c doctest: +XFAIL
+## @c https://savannah.gnu.org/bugs/?44503
+## camva ()
+##   @result{} ans =  10.340
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands effect the current axis; alternatively
+## an axis can be specified by the optional argument @var{ax}.
+##
+## @seealso{campos, camtarget, camup}
+## @end deftypefn
+
+
+function p = camva (varargin)
+
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("camva", varargin{:});
+
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
+  endif
+
+  prop = "cameraviewangle";
+  if (nargin == 0)
+    p = get (hax, prop);
+  elseif (nargin == 1 && isnumeric (varargin{1}) && isscalar (varargin{1}))
+    set (hax, prop, varargin{1});
+  elseif (nargin == 1 && ischar (varargin{1}))
+    s = varargin{1};
+    if (strcmp (s, "mode"))
+      p = get (hax, [prop "mode"]);
+    else
+      set (hax, [prop "mode"], s);
+    endif
+  else
+    print_usage ();
+  endif
+
+endfunction
+
+
+%!demo
+%! peaks ()
+%! ## query the viewing angle
+%! a = camva ()
+%! ## get a close-up view by decreasing the viewing angle:
+%! camva (0.5*a)
+
+
+%!shared hf
+%! hf = figure ("visible", "off");
+
+%!error <Invalid> camva (1, 2, 3)
+%!error <invalid> camva ("mod")
+
+%!test
+%! sphere ()
+%! x = camva ();
+%! camva (5)
+%! y = camva ();
+%! assert (y, 5)
+%! camva (x)
+%! x2 = camva ();
+%! assert (x, x2)
+
+%!test
+%! clf; sphere()
+%! aorig = camva ();
+%! m = camva ("mode");
+%! assert (strcmp (m, "auto"))
+%!
+%! camva (15)
+%! m = camva ("mode");
+%! assert (strcmp (m, "manual"))
+%!
+%! camva ("auto");
+%! # XXX: next line is workaround for https://savannah.gnu.org/bugs/?44503
+%! hold on; sphere (4)
+%! a = camva ();
+%! assert (a, aorig)
+
+%!test
+%! ## test ax input by creating another axis
+%! clf
+%! subplot (1, 2, 1); sphere (); hax1 = gca ();
+%! subplot (1, 2, 2); sphere (); hax2 = gca ();
+%! camva (hax1, 5)
+%! subplot (1, 2, 1)
+%! x = camva ();
+%! z = camva (hax2);
+%! subplot (1, 2, 2)
+%! y = camva ();
+%! assert (x, 5)
+%! assert (abs (z - 5) > 1)
+%! assert (y, z)
+
+%!test
+%! close (hf)
diff -r b667ae305f0d -r d437644337b3 scripts/plot/appearance/module.mk
--- a/scripts/plot/appearance/module.mk	Sun Jul 10 00:26:57 2016 -0700
+++ b/scripts/plot/appearance/module.mk	Mon Jul 11 19:14:07 2016 -0700
@@ -12,6 +12,11 @@
   scripts/plot/appearance/annotation.m \
   scripts/plot/appearance/axis.m \
   scripts/plot/appearance/box.m \
+  scripts/plot/appearance/campos.m \
+  scripts/plot/appearance/camroll.m \
+  scripts/plot/appearance/camtarget.m \
+  scripts/plot/appearance/camup.m \
+  scripts/plot/appearance/camva.m \
   scripts/plot/appearance/caxis.m \
   scripts/plot/appearance/clabel.m \
   scripts/plot/appearance/daspect.m \
