Mercurial > octave
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 |