# HG changeset patch # User Colin Macdonald # Date 1468560360 25200 # Node ID 141e6e16d900fdb0a3eaabb5d6bf6d47bbbe0951 # Parent bdd4a8476c39a870299a36852a8a3eaef731e5bb Add function camlookat (patch #9049). * scripts/plot/appearance/camlookat.m: Add new function. * scripts/plot/appearance/module.mk: Add new file. * doc/interpreter/plot.txi: Add docstrings to manual. * scripts/help/__unimplemented__.m: Remove new functions from list. * scripts/plot/appearance/camorbit.m, camroll.m, camzoom.m: Update seealso. diff -r bdd4a8476c39 -r 141e6e16d900 doc/interpreter/plot.txi --- a/doc/interpreter/plot.txi Wed Oct 11 07:06:23 2017 +0200 +++ b/doc/interpreter/plot.txi Thu Jul 14 22:26:00 2016 -0700 @@ -464,6 +464,8 @@ @DOCSTRING(view) +@DOCSTRING(camlookat) + @DOCSTRING(campos) @DOCSTRING(camorbit) diff -r bdd4a8476c39 -r 141e6e16d900 scripts/help/__unimplemented__.m --- a/scripts/help/__unimplemented__.m Wed Oct 11 07:06:23 2017 +0200 +++ b/scripts/help/__unimplemented__.m Thu Jul 14 22:26:00 2016 -0700 @@ -574,7 +574,6 @@ "callSoapService", "camdolly", "cameratoolbar", - "camlookat", "campan", "camproj", "categorical", diff -r bdd4a8476c39 -r 141e6e16d900 scripts/plot/appearance/camlookat.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/plot/appearance/camlookat.m Thu Jul 14 22:26:00 2016 -0700 @@ -0,0 +1,255 @@ +## 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 . + +## -*- texinfo -*- +## @deftypefn {} {} camlookat () +## @deftypefnx {} {} camlookat (@var{h}) +## @deftypefnx {} {} camlookat (@var{handle_list}) +## @deftypefnx {} {} camlookat (@var{hax}) +## Move the camera and adjust its properties to look at objects. +## +## When the input is a handle @var{h}, the camera is set to point toward the +## center of the bounding box of @var{h}. The camera's position is adjusted so +## the bounding box approximately fills the field of view. +## +## This command fixes the camera's viewing direction +## (@code{camtarget() - campos()}), camera up vector (@pxref{XREFcamup,,camup}) +## and viewing angle (@pxref{XREFcamva,,camva}). The camera target +## (@pxref{XREFcamtarget,,camtarget}) and camera position +## (@pxref{XREFcampos,,campos}) are changed. +## +## +## If the argument is a list @var{handle_list}, then a single bounding box for +## all the objects is computed and the camera is then adjusted as above. +## +## If the argument is an axis object @var{hax}, then the children of the axis +## are used as @var{handle_list}. When called with no inputs, it uses the +## current axis (@pxref{XREFgca,,gca}). +## +## @seealso{camorbit, camzoom, camroll} +## @end deftypefn + + +function camlookat (hh) + + if (nargin > 1) + print_usage (); + endif + + if (nargin == 0) + hh = get (gca (), "children"); + elseif (nargin == 1) + if (isaxes (hh)) + hh = get (hh, "children"); + endif + endif + + if (isempty (hh)) + return + end + + x0 = x1 = y0 = y1 = z0 = z1 = []; + for i = 1:numel (hh) + h = hh(i); + + if (! ishandle (h)) + error ("camlookat: Inputs must be handles.") + end + + x0_ = min (get (h, "xdata")(:)); + x1_ = max (get (h, "xdata")(:)); + y0_ = min (get (h, "ydata")(:)); + y1_ = max (get (h, "ydata")(:)); + z0_ = min (get (h, "zdata")(:)); + z1_ = max (get (h, "zdata")(:)); + + if (i == 1) + x0 = x0_; x1 = x1_; + y0 = y0_; y1 = y1_; + z0 = z0_; z1 = z1_; + else + x0 = min (x0, x0_); x1 = max (x1, x1_); + y0 = min (y0, y0_); y1 = max (y1, y1_); + z0 = min (z0, z0_); z1 = max (z1, z1_); + endif + endfor + + ## current view direction and projection operator + curdir = camtarget () - campos (); + curdir /= norm (curdir); + P = eye (3) - (curdir.' * curdir); + + ## target to middle of bounding box + mid = [x0+x1; y0+y1; z0+z1]/2; + + ## vertices of the bounding box + bb = [x0 x0 x0 x0 x1 x1 x1 x1; + y0 y0 y1 y1 y0 y0 y1 y1; + z0 z1 z0 z1 z0 z1 z0 z1]; + + ## project bounding box onto view plane + Pbb = P*(bb - mid) + mid; + + ## estimate size based on projected bb, choose distance for campos + ## (XXX: only matches Matlab to about 1 digit, see xtests) + sz = max (norm (Pbb - mid, 2, "cols")); + dist = 2*sz / tand (camva ()); + + ## avoid auto-adjusting + camva ("manual") + + camtarget (mid.') + + campos (mid.' - dist*curdir) + +endfunction + + +%!demo +%! [x, y, z] = peaks (); +%! surf(x, y, z/5); +%! hold on +%! [x, y, z] = sphere (); +%! s1 = surf(x/2, y/2+1.5, z/2+2); +%! s2 = surf(x/5+0.2, y/5-2, z/5+1); +%! axis equal +%! axis tight +%! pause +%! camlookat (s1) +%! pause +%! camlookat (s2) +%! pause +%! camlookat ([s1 s2]) + + +%!test +%! ## not an error (does nothing) +%! camlookat ([]) + +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! sphere (); +%! camlookat (); +%! assert (camva ("mode"), "manual"); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## direction is preserved +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! [x y z] = sphere (); +%! h1 = surf (x + 1, y, z); +%! hold on +%! h2 = surf (x - 1, y + 2, z + 1); +%! dir = camtarget () - campos (); +%! dir /= norm (dir); +%! camlookat (h1); +%! dir2 = camtarget () - campos (); +%! dir2 /= norm (dir2); +%! assert (dir, dir2, -2*eps); +%! camlookat (h2); +%! dir2 = camtarget () - campos (); +%! dir2 /= norm (dir2); +%! assert (dir, dir2, -2*eps) +%! camlookat ([h1 h2]); +%! dir2 = camtarget () - campos (); +%! dir2 /= norm (dir2); +%! assert (dir, dir2, -2*eps); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## look at axes not same as default auto view +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! sphere (); +%! zlim ([0 10]); +%! xlim ([0 5]); +%! A = camtarget (); +%! assert (A, [2.5 0 5]); +%! camlookat (); +%! B = camtarget (); +%! assert (B, [0 0 0]); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## list, empty and hax input give same results +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! [x y z] = sphere (); +%! h1 = surf (x + 1, y, z); +%! hold on +%! h2 = surf (x - 1, y + 2, z + 1); +%! camlookat (); +%! T1 = camtarget (); +%! P1 = campos (); +%! camtarget ("auto"); +%! campos ("auto"); +%! camlookat ([h1 h2]); +%! T2 = camtarget (); +%! P2 = campos (); +%! assert (T1, T2, -10*eps); +%! assert (P1, P2, -10*eps); +%! camtarget ("auto"); +%! campos ("auto"); +%! camlookat (gca ()); +%! T3 = camtarget (); +%! P3 = campos (); +%! assert (T1, T3, -10*eps); +%! assert (P1, P3, -10*eps); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## compare to matlab2014a output +%!xtest +%! hf = figure ("visible", "off"); +%! unwind_protect +%! [x, y, z] = peaks (); +%! s3 = surf(x, y, z/5); +%! hold on +%! [x, y, z] = sphere (); +%! s2 = surf(x/2, y/2+1.5, z/2+2); +%! s1 = mesh(x/2-4, 3*y, z/2 - 1); +%! axis equal +%! axis tight +%! camlookat (s1) +%! assert (camtarget (), [-4 0 -1], -eps) +%! assert (campos (), [-22.806319527016 -24.5088727773662 16.8359421671461], -1e-7) +%! camlookat (s2) +%! assert (camtarget (), [0 1.5 2], -eps) +%! assert (campos (), [-5.82093528266174 -6.08599055403138 7.52058391388657], -1e-7) +%! camlookat (s3) +%! assert (camtarget (), [0 0 0.1528529020838], 1e-10) +%! assert (campos (), [-30.3728392082653 -39.5826547014375 28.9585000034444], -1e-7) +%! camlookat () +%! assert (camtarget (), [-0.75 0 0.5], -eps) +%! assert (campos (), [-35.7955620339723 -45.6722656481532 33.7372645671114], -1e-7) +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## Test input validation +%!error camlookat (1, 2) +%!error camlookat ("a") diff -r bdd4a8476c39 -r 141e6e16d900 scripts/plot/appearance/camorbit.m --- a/scripts/plot/appearance/camorbit.m Wed Oct 11 07:06:23 2017 +0200 +++ b/scripts/plot/appearance/camorbit.m Thu Jul 14 22:26:00 2016 -0700 @@ -89,7 +89,7 @@ ## By default, this command affects the current axis; alternatively, an axis ## can be specified by the optional argument @var{hax}. ## -## @seealso{camzoom, camroll} +## @seealso{camzoom, camroll, camlookat} ## @end deftypefn diff -r bdd4a8476c39 -r 141e6e16d900 scripts/plot/appearance/camroll.m --- a/scripts/plot/appearance/camroll.m Wed Oct 11 07:06:23 2017 +0200 +++ b/scripts/plot/appearance/camroll.m Thu Jul 14 22:26:00 2016 -0700 @@ -52,7 +52,7 @@ ## By default, these commands affect the current axis; alternatively, an axis ## can be specified by the optional argument @var{ax}. ## -## @seealso{camzoom, camorbit, camup} +## @seealso{camzoom, camorbit, camlookat, camup} ## @end deftypefn diff -r bdd4a8476c39 -r 141e6e16d900 scripts/plot/appearance/camzoom.m --- a/scripts/plot/appearance/camzoom.m Wed Oct 11 07:06:23 2017 +0200 +++ b/scripts/plot/appearance/camzoom.m Thu Jul 14 22:26:00 2016 -0700 @@ -50,7 +50,7 @@ ## By default, these commands affect the current axis; alternatively, an axis ## can be specified by the optional argument @var{ax}. ## -## @seealso{camroll, camorbit, camva} +## @seealso{camroll, camorbit, camlookat, camva} ## @end deftypefn diff -r bdd4a8476c39 -r 141e6e16d900 scripts/plot/appearance/module.mk --- a/scripts/plot/appearance/module.mk Wed Oct 11 07:06:23 2017 +0200 +++ b/scripts/plot/appearance/module.mk Thu Jul 14 22:26:00 2016 -0700 @@ -13,8 +13,9 @@ %reldir%/annotation.m \ %reldir%/axis.m \ %reldir%/box.m \ + %reldir%/camlookat.m \ + %reldir%/camorbit.m \ %reldir%/campos.m \ - %reldir%/camorbit.m \ %reldir%/camroll.m \ %reldir%/camtarget.m \ %reldir%/camup.m \