# HG changeset patch
# User Colin Macdonald <cbm@m.fsf.org>
# Date 1468387948 25200
#      Tue Jul 12 22:32:28 2016 -0700
# Node ID 79f94a91989b0e5da23c2d507ad4d8c1e93bc352
# Parent  b667ae305f0d7b46319ae0dc74229d0db47827d5
Add basic camera set/get 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/camtarget.m: New function.
* scripts/plot/appearance/camup.m: New function.
* scripts/plot/appearance/camva.m: New function.
* scripts/plot/appearance/module.mk: Update.
* scripts/plot/draw/camlight: Use the new set/get fcns.

diff -r b667ae305f0d -r 79f94a91989b doc/interpreter/plot.txi
--- a/doc/interpreter/plot.txi	Sun Jul 10 00:26:57 2016 -0700
+++ b/doc/interpreter/plot.txi	Tue Jul 12 22:32:28 2016 -0700
@@ -400,6 +400,14 @@
 
 @DOCSTRING(view)
 
+@DOCSTRING(campos)
+
+@DOCSTRING(camtarget)
+
+@DOCSTRING(camup)
+
+@DOCSTRING(camva)
+
 @DOCSTRING(slice)
 
 @DOCSTRING(ribbon)
diff -r b667ae305f0d -r 79f94a91989b scripts/help/__unimplemented__.m
--- a/scripts/help/__unimplemented__.m	Sun Jul 10 00:26:57 2016 -0700
+++ b/scripts/help/__unimplemented__.m	Tue Jul 12 22:32:28 2016 -0700
@@ -577,12 +577,8 @@
   "camlookat",
   "camorbit",
   "campan",
-  "campos",
   "camproj",
   "camroll",
-  "camtarget",
-  "camup",
-  "camva",
   "camzoom",
   "categorical",
   "categories",
diff -r b667ae305f0d -r 79f94a91989b scripts/plot/appearance/campos.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/campos.m	Tue Jul 12 22:32:28 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 79f94a91989b scripts/plot/appearance/camtarget.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camtarget.m	Tue Jul 12 22:32:28 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 79f94a91989b scripts/plot/appearance/camup.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camup.m	Tue Jul 12 22:32:28 2016 -0700
@@ -0,0 +1,168 @@
+## 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)
+##
+## 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{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 79f94a91989b scripts/plot/appearance/camva.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/camva.m	Tue Jul 12 22:32:28 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 79f94a91989b 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	Tue Jul 12 22:32:28 2016 -0700
@@ -12,6 +12,10 @@
   scripts/plot/appearance/annotation.m \
   scripts/plot/appearance/axis.m \
   scripts/plot/appearance/box.m \
+  scripts/plot/appearance/campos.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 \
diff -r b667ae305f0d -r 79f94a91989b scripts/plot/draw/camlight.m
--- a/scripts/plot/draw/camlight.m	Sun Jul 10 00:26:57 2016 -0700
+++ b/scripts/plot/draw/camlight.m	Tue Jul 12 22:32:28 2016 -0700
@@ -52,10 +52,10 @@
 ## camlight (45, 30)
 ## @end group
 ## @end example
-## Here the light is first pitched upwards from the
-## camera position by 30 degrees.  It is then yawed by
+## Here the light is first pitched upwards (@pxref{camup}) from the
+## camera position (@pxref{campos}) by 30 degrees.  It is then yawed by
 ## 45 degrees to the right.  Both rotations are centered around the
-## camera target.
+## camera target (@pxref{camtarget}).
 ##
 ## An optional string @var{style} specifies whether the light is a
 ## local point source (@qcode{"local"}, the default) or placed at
@@ -149,21 +149,17 @@
     endswitch
   endif
 
-  cam_up = get (gca (), "cameraupvector");
-  cam_pos = get (gca (), "cameraposition");
-  cam_target = get (gca (), "cameratarget");
-
-  view_ax = cam_target - cam_pos;
+  view_ax = camtarget () - campos ();
   view_ax /= norm (view_ax);
   ## orthogonalize the camup vector
-  yaw_ax = cam_up - view_ax*dot (cam_up, view_ax);
-  pitch_ax = cross (cam_up, view_ax);
+  yaw_ax = camup () - view_ax*dot (camup (), view_ax);
+  pitch_ax = cross (camup (), view_ax);
 
   ## First pitch up by 'el', then yaw by 'az'.
   ## (order matters, this matches experiments with Matlab).
-  pos = num2cell (cam_pos);
-  [pos{:}] = __rotate_around_axis__ (pos{:}, el, pitch_ax, cam_target);
-  [pos{:}] = __rotate_around_axis__ (pos{:}, az, yaw_ax, cam_target);
+  pos = num2cell (campos ());
+  [pos{:}] = __rotate_around_axis__ (pos{:}, el, pitch_ax, camtarget ());
+  [pos{:}] = __rotate_around_axis__ (pos{:}, az, yaw_ax, camtarget ());
   pos = [pos{:}];
 
   if (isempty (h))
@@ -235,9 +231,9 @@
 
 %!test
 %! clf; sphere (24)
-%! set (gca (), "cameraposition", [8 12 7.3])
-%! set (gca (), "cameraupvector", [0 2 2])
-%! set (gca (), "cameratarget", [0.5 -0.3 -0.3])
+%! campos ([8 12 7.3])
+%! camup ([0 2 2])
+%! camtarget ([0.5 -0.3 -0.3])
 %! h = camlight (45, 20);
 %! A = get (h, "position");
 %! ## From maillist, someone tested on Matlab R2015b for OSX:
