comparison scripts/plot/util/hgtransform.m @ 23255:aaf91b4f48e4

hgtransform.m: New function (bug #50466). * NEWS: Announce new function. * scripts/plot/util/hgtransform.m: New function. * scripts/plot/util/module.mk: Add to build system. * plot.txi: Add new section "Transform Groups" to Advanced Plotting to contain docstring. * octave.texi: Add new section "Transform Groups" to Table of Contents. * __unimplemented__.m: Remove from unimplemented list.
author Rik <rik@octave.org>
date Wed, 08 Mar 2017 15:00:35 -0800
parents
children a470f7fee1cf
comparison
equal deleted inserted replaced
23254:7eeb2c561a1a 23255:aaf91b4f48e4
1 ## Copyright (C) 2017 Rik Wehbring
2 ##
3 ## This file is part of Octave.
4 ##
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or
8 ## (at your option) any later version.
9 ##
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ## GNU General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING. If not, see
17 ## <http://www.gnu.org/licenses/>.
18
19 ## -*- texinfo -*-
20 ## @deftypefn {@var{h} =} {} hgtransform ()
21 ## @deftypefnx {@var{h} =} {} hgtransform (@var{property}, @var{value}, @dots{})
22 ## @deftypefnx {@var{h} =} {} hgtransform (@var{hax}, @dots{})
23 ##
24 ## Create a graphics transform object.
25 ##
26 ## FIXME: Need to write documentation.
27 ## FIXME: Add 'makehgtform' to seealso list when it is implemented.
28 ## @seealso{hggroup}
29 ## @end deftypefn
30
31
32 ## FIXME: hgtransform should be a C++ graphics object, not an m-file.
33 ## For the moment (3/7/17), it is quicker to implement something in
34 ## an m-file. But, this approach requires double the memory (original
35 ## and transformed data), and a system of listeners and callbacks.
36 ## In OpenGL toolkits it should be possible to simply insert a transform
37 ## somewhere in gl-render.cc to have this done on the fly.
38
39 function h = hgtransform (varargin)
40
41 [hax, varargin] = __plt_get_axis_arg__ ("hgtransform", varargin{:});
42
43 if (isempty (hax))
44 hax = gca ();
45 endif
46
47 htmp = hggroup (hax);
48
49 addproperty ("matrix", htmp, "data", eye (4));
50 addproperty ("__orig_data__", htmp, "any", struct ("h", {}));
51 if (! isempty (varargin))
52 set (htmp, varargin{:});
53 endif
54 addlistener (htmp, "matrix", @matrix_cb);
55 addlistener (htmp, "children", @children_cb);
56
57 if (nargout > 0)
58 h = htmp;
59 endif
60
61 endfunction
62
63 function matrix_cb (hgt, ~)
64
65 M = get (hgt, "matrix");
66 ## FIXME: Need better input validation on transform matrix M.
67 ## Disallow shear, perspective transforms.
68 if (! isreal (M) || ! ismatrix (M) || rows (M) != 4 || columns (M) != 4)
69 error ("hgtransform: transform must be 4x4 real matrix");
70 endif
71
72 hkids = get (hgt, "children");
73 xform_data (hgt, hkids);
74
75 endfunction
76
77 function xform_data (hgt, hlist)
78
79 M = get (hgt, "matrix");
80 orig_data = get (hgt, "__orig_data__");
81
82 for hk = hlist.'
83
84 idx = find (hk == [orig_data.h]);
85 if (! idx)
86 warning ("hgtransform: original data not found for %f", hk);
87 continue;
88 endif
89
90 xd = double (orig_data(idx).xdata);
91 xsz = size (xd);
92
93 yd = double (orig_data(idx).ydata);
94 ysz = size (yd);
95
96 zd = double (orig_data(idx).zdata);
97 zsz = size (zd);
98 z_empty = isempty (zd);
99
100 if (isempty (zd))
101 ## Common case of 2-D data.
102 zd = zeros (1, numel (xd));
103 elseif (isvector (xd) && isvector (yd))
104 ## Handle surface data which may be a vector/matrix combination
105 if (isvector (zd))
106 ## Do nothing. All data will be forced to row vectors below
107 elseif (length (xd) == rows (zd) && length (yd) == columns (zd))
108 [xd, yd] = meshgrid (xd, yd);
109 xsz = size (xd);
110 ysz = size (yd);
111 endif
112 endif
113
114 ## Force row vectors for later concatenation
115 xd = xd(:).';
116 yd = yd(:).';
117 zd = zd(:).';
118
119 ## FIXME: To minimize memory, better to construct data matrix in-place?
120 data = [xd; yd; zd; ones(1, columns(xd))];
121 tol = 2 * max (eps (data(1:3,:)));
122 data = M * data;
123 ## Need to trim or rotations which produce values near 0 will be strange.
124 data(abs (data) < tol) = 0;
125
126 set (hk, "xdata", reshape (data(1,:), xsz));
127 set (hk, "ydata", reshape (data(2,:), ysz));
128 if (! z_empty)
129 set (hk, "zdata", reshape (data(3,:), zsz));
130 endif
131 endfor
132
133 endfunction
134
135 function children_cb (hgt, ~)
136
137 hkids = get (hgt, "children");
138 orig_data = get (hgt, "__orig_data__");
139 hlist = [orig_data.h];
140
141 ## Delete any children that have been removed
142 hdel = setdiff (hlist, hkids);
143 if (! isempty (hdel))
144 for hk = hdel.'
145 idx = find (hk == hlist);
146 if (ishghandle (hk))
147 ## child was re-parented to something else, restore data
148 set (hk, "xdata", orig_data(idx).xdata);
149 set (hk, "ydata", orig_data(idx).ydata);
150 set (hk, "zdata", orig_data(idx).zdata);
151 endif
152 endfor
153 orig_data = orig_data(hlist != hdel);
154 hlist = hlist(hlist != hdel);
155 endif
156
157 ## Add new children
158 hnew = setdiff (hkids, hlist);
159 for hk = hnew.'
160 orig_data(end+1).h = hk;
161 orig_data(end).xdata = get (hk, "xdata");
162 orig_data(end).ydata = get (hk, "ydata");
163 orig_data(end).zdata = get (hk, "zdata");
164 endfor
165
166 set (hgt, "__orig_data__", orig_data);
167
168 ## Update data of new children only
169 xform_data (hgt, hnew);
170
171 endfunction
172
173
174 ## Need BIST tests here