changeset 24128:5ce4dfe5d906

Add basic camera set/get functions (patch #9046). * scripts/plot/appearance/campos.m, camtarget.m, camup.m, camva.m: New functions. * scripts/plot/appearance/module.mk: Add functions. * doc/interpreter/plot.txi: Add docstrings to manual. * scripts/help/__unimplemented__.m: Remove new functions from list. * scripts/plot/draw/camlight: Add cross references.
author Colin Macdonald <cbm@m.fsf.org>
date Tue, 12 Jul 2016 22:32:28 -0700
parents b188be2b962b
children f80dc6db9d18
files doc/interpreter/plot.txi scripts/help/__unimplemented__.m scripts/plot/appearance/campos.m scripts/plot/appearance/camtarget.m scripts/plot/appearance/camup.m scripts/plot/appearance/camva.m scripts/plot/appearance/module.mk scripts/plot/draw/camlight.m
diffstat 8 files changed, 730 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/plot.txi	Tue Jul 12 23:33:36 2016 -0700
+++ b/doc/interpreter/plot.txi	Tue Jul 12 22:32:28 2016 -0700
@@ -464,6 +464,14 @@
 
 @DOCSTRING(view)
 
+@DOCSTRING(campos)
+
+@DOCSTRING(camtarget)
+
+@DOCSTRING(camup)
+
+@DOCSTRING(camva)
+
 @DOCSTRING(slice)
 
 @DOCSTRING(ribbon)
--- a/scripts/help/__unimplemented__.m	Tue Jul 12 23:33:36 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",
--- /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,178 @@
+## 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 -*-
+## @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")
+## campos ()
+##   @result{} ans =
+##       -27.394  -35.701   64.079
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands affect 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 (nargin > 1)
+    print_usage ();
+  endif
+
+  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?
+%! x1 = campos ()
+%! ## move the camera upwards
+%! campos (x1 + [0 0 2])
+%! x2 = campos ()
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   hax = axes ("parent", hf);
+%!   sphere (hax);
+%!   x = campos ();
+%!   campos ([1 2 3]);
+%!   y = campos ();
+%!   assert (y, [1 2 3]);
+%!   campos (x);
+%!   x2 = campos ();
+%!   assert (x, x2);
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere();
+%!   p_orig = campos ();
+%!   m = campos ("mode");
+%!   assert (strcmp (m, "auto"));
+%!
+%!   campos ([1 2 3]);
+%!   m = campos ("mode");
+%!   assert (strcmp (m, "manual"));
+%!
+%!   campos ("auto");
+%!   p = campos ();
+%!   assert (p, p_orig);
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
+
+## test ax input by creating another axis
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!  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);
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
+
+## Test input validation
+%!error <Invalid call> campos (1, 2)
+%!error <invalid value>
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!  campos ("mod");
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
--- /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,187 @@
+## 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 -*-
+## @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 affect 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 (nargin > 1)
+    print_usage ();
+  endif
+
+  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?
+%! x1 = camtarget ()
+%! ## point the camera upwards
+%! camtarget (x1 + [0 0 1])
+%! x2 = camtarget ()
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere ();
+%!   x = camtarget ();
+%!   camtarget ([1 2 3]);
+%!   y = camtarget ();
+%!   assert (y, [1 2 3]);
+%!   camtarget (x);
+%!   x2 = camtarget ();
+%!   assert (x, x2);
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere ();
+%!   x_orig = camtarget ();
+%!   m = camtarget ("mode");
+%!   assert (strcmp (m, "auto"));
+%!
+%!   camtarget ([1 2 3]);
+%!   m = camtarget ("mode");
+%!   assert (strcmp (m, "manual"));
+%!
+%!   camtarget ("auto");
+%!   x = camtarget ();
+%!   assert (x, x_orig);
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
+
+## test ax input by creating another axis
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   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);
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
+
+## Test input validation
+%!error <Invalid> camtarget (1, 2)
+%!error <invalid value>
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!  camtarget ("mod")
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
--- /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,180 @@
+## 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 -*-
+## @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 does not modify the camera target
+## (@pxref{XREFcamtarget,,camtarget}). Thus, the camera up vector might not be
+## orthogonal to the direction of the camera's view:
+## @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
+## camup ("auto")
+## camup ()
+##   @result{}   0   0   1
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands affect 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 (nargin > 1)
+    print_usage ();
+  endif
+
+  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?
+%! x1 = camup ()
+%! ## re-orient the camera with a new up-vector
+%! camup ([1 0 0])
+%! x2 = camup ()
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere ();
+%!   x = camup ();
+%!   camup ([1 2 3]);
+%!   y = camup ();
+%!   assert (y, [1 2 3]);
+%!   camup (x);
+%!   x2 = camup ();
+%!   assert (x, x2);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere ();
+%!   p_orig = camup ();
+%!   m = camup ("mode");
+%!   assert (strcmp (m, "auto"));
+%!
+%!   camup ([1 2 3]);
+%!   m = camup ("mode");
+%!   assert (strcmp (m, "manual"));
+%!
+%!   camup ("auto");
+%!   p = camup ();
+%!   assert (p, p_orig);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+## test ax input by creating another axis
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   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);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+## Test input validation
+%!error <Invalid call> camup (1, 2)
+%!error <invalid value>
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!  camup ("mod")
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
--- /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,169 @@
+## 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 -*-
+## @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
+## camva ()
+##   @result{} ans =  10.340
+## close (hf)
+## @end group
+## @end example
+##
+## By default, these commands affect 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 (nargin > 1)
+    print_usage ();
+  endif
+
+  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
+%! a1 = camva ()
+%! ## get a close-up view by decreasing the viewing angle:
+%! camva (0.5*a1)
+%! a2 = camva ()
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere ();
+%!   x = camva ();
+%!   camva (5);
+%!   y = camva ();
+%!   assert (y, 5);
+%!   camva (x);
+%!   x2 = camva ();
+%!   assert (x, x2);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   sphere();
+%!   a_orig = camva ();
+%!   m = camva ("mode");
+%!   assert (strcmp (m, "auto"));
+%!
+%!   camva (15);
+%!   m = camva ("mode");
+%!   assert (strcmp (m, "manual"));
+%!
+%!   camva ("auto");
+%!   a = camva ();
+%!   assert (a, a_orig);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+## test ax input by creating another axis
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   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);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+## Test input validation
+%!error <Invalid call> camva (1, 2)
+%!error <invalid value>
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!  camva ("mod")
+%! unwind_protect_cleanup
+%!   delete (hf);
+%! end_unwind_protect
--- a/scripts/plot/appearance/module.mk	Tue Jul 12 23:33:36 2016 -0700
+++ b/scripts/plot/appearance/module.mk	Tue Jul 12 22:32:28 2016 -0700
@@ -12,6 +12,10 @@
   %reldir%/annotation.m \
   %reldir%/axis.m \
   %reldir%/box.m \
+  %reldir%/campos.m \
+  %reldir%/camtarget.m \
+  %reldir%/camup.m \
+  %reldir%/camva.m \
   %reldir%/caxis.m \
   %reldir%/clabel.m \
   %reldir%/daspect.m \
--- a/scripts/plot/draw/camlight.m	Tue Jul 12 23:33:36 2016 -0700
+++ b/scripts/plot/draw/camlight.m	Tue Jul 12 22:32:28 2016 -0700
@@ -69,9 +69,10 @@
 ## @end group
 ## @end example
 ##
-## Here the light is first pitched upwards from the camera position by 30
-## degrees.  It is then yawed by 45 degrees to the right.  Both rotations are
-## centered around the camera target.
+## Here the light is first pitched upwards (@pxref{XREFcamup,,camup}) from the
+## camera position (@pxref{XREFcampos,,campos}) by 30 degrees.  It is then yawed
+## by 45 degrees to the right.  Both rotations are centered around the camera
+## target (@pxref{XREFcamtarget,,camtarget}).
 ##
 ## Return a handle to further manipulate the light object
 ##