Mercurial > octave
changeset 22288:57fded74ee22
Add camlight function (patch #9014).
* scripts/plot/draw/camlight.m: New function.
* scripts/plot/draw/private/__rotate_around_axis__.m: New function, split from
rotate.m
* NEWS: Announce new function.
* __unimplemented__.m: Remove camlight from list.
* scripts/plot/draw/module.mk: Update build system.
* plot.txi: Add docstring to manual.
author | Colin Macdonald <cbm@m.fsf.org> |
---|---|
date | Mon, 11 Jul 2016 23:31:21 -0700 |
parents | d9913b55ef15 |
children | ed023556d4fa |
files | NEWS doc/interpreter/plot.txi scripts/help/__unimplemented__.m scripts/plot/draw/camlight.m scripts/plot/draw/module.mk scripts/plot/draw/private/__rotate_around_axis__.m |
diffstat | 6 files changed, 366 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Sun Aug 14 13:50:13 2016 +0100 +++ b/NEWS Mon Jul 11 23:31:21 2016 -0700 @@ -72,8 +72,8 @@ output argument is requested, instead the vector or array of interpolated values is always returned for Matlab compatibility. - ** The new function "light" and corresponding graphics object provide - light and shadow effects for patch and surface objects. + ** The new function "light" and the corresponding graphics object + provide light and shadow effects for patch and surface objects. ** The surfnorm function now returns unnormalized (magnitude != 1) normal vectors for compatibility with Matlab. @@ -110,6 +110,7 @@ ** Other new functions added in 4.2: audioformats + camlight deg2rad dialog evalc
--- a/doc/interpreter/plot.txi Sun Aug 14 13:50:13 2016 +0100 +++ b/doc/interpreter/plot.txi Mon Jul 11 23:31:21 2016 -0700 @@ -402,6 +402,8 @@ @DOCSTRING(material) +@DOCSTRING(camlight) + @DOCSTRING(meshgrid) @DOCSTRING(ndgrid)
--- a/scripts/help/__unimplemented__.m Sun Aug 14 13:50:13 2016 +0100 +++ b/scripts/help/__unimplemented__.m Mon Jul 11 23:31:21 2016 -0700 @@ -569,7 +569,6 @@ "callSoapService", "camdolly", "cameratoolbar", - "camlight", "camlookat", "camorbit", "campan",
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/plot/draw/camlight.m Mon Jul 11 23:31:21 2016 -0700 @@ -0,0 +1,300 @@ +## 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 {} {} camlight {} +## @deftypefnx {} {} camlight right +## @deftypefnx {} {} camlight left +## @deftypefnx {} {} camlight headlight +## @deftypefnx {} {} camlight (@var{az}, @var{el}) +## @deftypefnx {} {} camlight (@dots{}, @var{style}) +## @deftypefnx {} {} camlight (@var{hl}, @dots{}) +## @deftypefnx {} {@var{h} =} camlight (@dots{}) +## Add a light object to a figure using a simple interface. +## +## When called with no arguments, a light object is added to the current plot +## and is placed slightly above and to the right of the camera's current +## position: this is equivalent to @code{camlight right}. The commands +## @code{camlight left} and @code{camlight headlight} behave similarly with +## the placement being either left of the camera position or centered on the +## camera position. +## +## For more control, the light position can be specified by an azimuthal +## rotation @var{az} and an elevation angle @var{el}, both in degrees, +## relative to the current properties of the camera. +## +## The optional string @var{style} specifies whether the light is a local point +## source (@qcode{"local"}, the default) or placed at infinite distance +## (@qcode{"infinite"}). +## +## If the first argument @var{hl} is a handle to a light object, then act on +## this light object rather than creating a new object. +## +## The optional return value @var{h} is a graphics handle to the light object. +## This can be used to move or further change properties of the light object. +## +## Examples: +## +## Add a light object to a plot +## +## @example +## @group +## @c doctest: +SKIP +## sphere (36); +## camlight +## @end group +## @end example +## +## Position the light source exactly +## +## @example +## @group +## @c doctest: +SKIP +## 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 45 degrees to the right. Both rotations are +## centered around the camera target. +## +## Return a handle to further manipulate the light object +## +## @example +## @group +## @c doctest: +SKIP +## clf +## sphere (36); +## hl = camlight ("left"); +## set (hl, "color", "r"); +## @end group +## @end example +## +## @seealso{light} +## @end deftypefn + +function h = camlight (varargin) + + if (nargin > 4) + print_usage (); + endif + + ## Note: There is a very small chance of a collision between a numeric double + ## specifying azimuth and a light handle object (also a numeric double). + ## We don't worry about that. + if (numel (varargin) > 0 && numel (varargin{1}) == 1 + && ishandle (varargin{1})) + hl = varargin{1}; + if (! ishghandle (hl, "light")) + error ("camlight: HL must be a handle to a light object"); + endif + varargin(1) = []; + nargin = nargin - 1; + else + hl = []; + endif + + style = "local"; + where = ""; + + if (nargin == 0) + where = "right"; + + elseif (nargin == 1 && ischar (varargin{1})) + arg1 = varargin{1}; + if (strcmpi (arg1, "local") || strcmpi (arg1, "infinite")) + style = arg1; + where = "right"; + else + where = arg1; + endif + + elseif (nargin == 2) + arg1 = varargin{1}; + arg2 = varargin{2}; + if (isnumeric (arg1) && isscalar (arg1) + && isnumeric (arg2) && isscalar (arg2)) + az = arg1; + el = arg2; + elseif (ischar (arg1) && ischar (arg2)) + where = arg1; + style = arg2; + else + print_usage (); + endif + + elseif (nargin == 3 && ischar (varargin{3}) + && isnumeric (varargin{1}) && isscalar (varargin{1}) + && isnumeric (varargin{2}) && isscalar (varargin{2})) + [az, el, style] = varargin{1:3}; + + else + print_usage (); + endif + + if (! isempty (where)) + switch (tolower (where)) + case "left" + az = -30; + el = 30; + + case "right" + az = 30; + el = 30; + + case "headlight" + az = 0; + el = 0; + + otherwise + error ("camlight: invalid light position '%s'", where); + + 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 /= 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); + + ## 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 = [pos{:}]; + + if (isempty (hl)) + hl = light ("Position", pos, "style", style); + else + set (hl, "Position", pos, "style", style); + endif + + if (nargout > 0) + h = hl; + endif + +endfunction + + +%!demo +%! ## Adding lights to a scene +%! sphere (64); +%! camlight +%! +%! ## Add a second light +%! camlight left + +%!demo +%! sphere (48); +%! title ("This light has a fixed position, even if the camera moves"); +%! axis equal; +%! shading flat; +%! view (30, 30); +%! +%! camlight +%! +%! for a = 30:2:390 +%! view (a, 30); +%! drawnow (); +%! pause (0.01); +%! end + +%!demo +%! sphere (48); +%! title ("Move the camera and update the light position"); +%! axis equal; shading flat +%! view (30, 30); +%! +%! hl = camlight (); # keep a handle to the light +%! +%! for a = 30:2:390 +%! view (a, 30); +%! camlight (hl, "right"); # update light position +%! drawnow (); +%! pause (0.01); +%! end + +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! sphere (24); +%! set (gca (), "cameraposition", [8 12 7.3]); +%! set (gca (), "cameraupvector", [0 2 2]); +%! set (gca (), "cameratarget", [0.5 -0.3 -0.3]); +%! h = camlight (45, 20); +%! A = get (h, "position"); +%! ## From maillist, someone tested on Matlab R2015b for OSX: +%! B = [-3.301207088157029 15.474861455795917 1.115828634895176]; +%! assert (A, B, -20*eps); +%! +%! h = camlight (300, -190); +%! A = get (h, "position"); +%! B = [-11.054849015640563 2.931330143100648 -11.315623892092518]; +%! assert (A, B, -20*eps); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## Move a light +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! sphere (); +%! h1 = camlight ("left"); +%! h2 = camlight (); +%! p2 = get (h2, "position"); +%! camlight (h1, "right") +%! p1 = get (h1, "position"); +%! assert (p1, p2); +%! camlight (h1); +%! p1 = get (h1, "position"); +%! assert (p1, p2); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## Updating style also moves light +%!test +%! hf = figure ("visible", "off"); +%! unwind_protect +%! sphere (); +%! h1 = camlight ("right"); +%! h = camlight ("headlight"); +%! p1 = get (h1, "position"); +%! h2 = camlight (h, "local"); +%! p2 = get (h2, "position"); +%! assert (h, h2); +%! assert (p1, p2); +%! unwind_protect_cleanup +%! close (hf); +%! end_unwind_protect + +## Test input validation +%!error camlight (1,2,3,4,5) +%!error <HL must be a handle to a light object> camlight (0, "left") +%!error <Invalid call> camlight ({1}, {2}) +%!error <Invalid call> camlight (rand (), 1, 2, 3) +%!error <invalid light position 'foobar'> camlight ("foobar") +%!error <invalid light position 'foobar'> camlight ("foobar", "local") +
--- a/scripts/plot/draw/module.mk Sun Aug 14 13:50:13 2016 +0100 +++ b/scripts/plot/draw/module.mk Mon Jul 11 23:31:21 2016 -0700 @@ -16,6 +16,7 @@ scripts/plot/draw/private/__pie__.m \ scripts/plot/draw/private/__plt__.m \ scripts/plot/draw/private/__quiver__.m \ + scripts/plot/draw/__rotate_around_axis__.m \ scripts/plot/draw/private/__scatter__.m \ scripts/plot/draw/private/__stem__.m \ scripts/plot/draw/private/__unite_shared_vertices__.m @@ -24,6 +25,7 @@ scripts/plot/draw/area.m \ scripts/plot/draw/barh.m \ scripts/plot/draw/bar.m \ + scripts/plot/draw/camlight.m \ scripts/plot/draw/colorbar.m \ scripts/plot/draw/comet3.m \ scripts/plot/draw/comet.m \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/plot/draw/private/__rotate_around_axis__.m Mon Jul 11 23:31:21 2016 -0700 @@ -0,0 +1,59 @@ +## Copyright (C) 2014-2015 John W. Eaton +## 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{xr}, @var{yr}, @var{zr}] =} __rotate_around_axis__ (@var{x}, @var{y}, @var{z}, @var{angle}, @var{dir}, @var{origin}) +## Rotate the points given by X, Y, Z about an axis by ANGLE degrees. +## The axis is specified by the vector DIR and the point ORIGIN. +## @end deftypefn + +function [xr, yr, zr] = __rotate_around_axis__ (x, y, z, angle, dir, origin); + + dir /= norm (dir); + u = dir(1); + v = dir(2); + w = dir(3); + + a = origin(1); + b = origin(2); + c = origin(3); + + sa = sind (angle); + ca = cosd (angle); + + if (a == 0 && b == 0 && c == 0) + tmp = (u*x + v*y + w*z) * (1 - ca); + + xr = u*tmp + x*ca + (-w*y + v*z)*sa; + yr = v*tmp + y*ca + (w*x - u*z)*sa; + zr = w*tmp + z*ca + (-v*x + u*y)*sa; + else + one_m_ca = 1 - ca; + tmp = u*x + v*y + w*z; + + xr = ((a*(v**2 + w**2) - u*(b*v + c*w - tmp))*one_m_ca + + x*ca + (-c*v + b*w - w*y + v*z)*sa); + yr = ((b*(u**2 + w**2) - v*(a*u + c*w - tmp))*one_m_ca + + y*ca + (c*u - a*w + w*x - u*z)*sa); + zr = ((c*(u**2 + v**2) - w*(a*u + b*v - tmp))*one_m_ca + + z*ca + (-b*u + a*v - v*x + u*y)*sa); + endif + +endfunction +