comparison libinterp/corefcn/graphics.cc @ 16892:68fc671a9339

maint: Collapse interpfcn and interpfcn-core directories into corefcn directory. * libgui/src/module.mk: Remove -I references to interp-core, interpfcn, add reference to corefcn. * libinterp/Makefile.am: Remove -I references to interp-core, interpfcn, add reference to corefcn. * libinterp/corefcn/module.mk: Add files from interp-core, interpfcn to build system. Copy over special rules from module.mk files in interp-core andd interpfcn. * src/Makefile.am: Replace references to interp-core, interpfcn with those to corefcn. * libinterp/corefcn/Cell.cc, libinterp/corefcn/Cell.h, libinterp/corefcn/action-container.h, libinterp/corefcn/c-file-ptr-stream.cc, libinterp/corefcn/c-file-ptr-stream.h, libinterp/corefcn/comment-list.cc, libinterp/corefcn/comment-list.h, libinterp/corefcn/cutils.c, libinterp/corefcn/cutils.h, libinterp/corefcn/data.cc, libinterp/corefcn/data.h, libinterp/corefcn/debug.cc, libinterp/corefcn/debug.h, libinterp/corefcn/defaults.cc, libinterp/corefcn/defaults.in.h, libinterp/corefcn/defun-dld.h, libinterp/corefcn/defun-int.h, libinterp/corefcn/defun.cc, libinterp/corefcn/defun.h, libinterp/corefcn/dirfns.cc, libinterp/corefcn/dirfns.h, libinterp/corefcn/display.cc, libinterp/corefcn/display.h, libinterp/corefcn/dynamic-ld.cc, libinterp/corefcn/dynamic-ld.h, libinterp/corefcn/error.cc, libinterp/corefcn/error.h, libinterp/corefcn/event-queue.h, libinterp/corefcn/file-io.cc, libinterp/corefcn/file-io.h, libinterp/corefcn/gl-render.cc, libinterp/corefcn/gl-render.h, libinterp/corefcn/gl2ps-renderer.cc, libinterp/corefcn/gl2ps-renderer.h, libinterp/corefcn/gl2ps.c, libinterp/corefcn/gl2ps.h, libinterp/corefcn/graphics.cc, libinterp/corefcn/graphics.in.h, libinterp/corefcn/gripes.cc, libinterp/corefcn/gripes.h, libinterp/corefcn/help.cc, libinterp/corefcn/help.h, libinterp/corefcn/hook-fcn.cc, libinterp/corefcn/hook-fcn.h, libinterp/corefcn/input.cc, libinterp/corefcn/input.h, libinterp/corefcn/jit-ir.cc, libinterp/corefcn/jit-ir.h, libinterp/corefcn/jit-typeinfo.cc, libinterp/corefcn/jit-typeinfo.h, libinterp/corefcn/jit-util.cc, libinterp/corefcn/jit-util.h, libinterp/corefcn/load-path.cc, libinterp/corefcn/load-path.h, libinterp/corefcn/load-save.cc, libinterp/corefcn/load-save.h, libinterp/corefcn/ls-ascii-helper.cc, libinterp/corefcn/ls-ascii-helper.h, libinterp/corefcn/ls-hdf5.cc, libinterp/corefcn/ls-hdf5.h, libinterp/corefcn/ls-mat-ascii.cc, libinterp/corefcn/ls-mat-ascii.h, libinterp/corefcn/ls-mat4.cc, libinterp/corefcn/ls-mat4.h, libinterp/corefcn/ls-mat5.cc, libinterp/corefcn/ls-mat5.h, libinterp/corefcn/ls-oct-ascii.cc, libinterp/corefcn/ls-oct-ascii.h, libinterp/corefcn/ls-oct-binary.cc, libinterp/corefcn/ls-oct-binary.h, libinterp/corefcn/ls-utils.cc, libinterp/corefcn/ls-utils.h, libinterp/corefcn/matherr.c, libinterp/corefcn/mex.cc, libinterp/corefcn/mex.h, libinterp/corefcn/mexproto.h, libinterp/corefcn/mxarray.in.h, libinterp/corefcn/oct-errno.h, libinterp/corefcn/oct-errno.in.cc, libinterp/corefcn/oct-fstrm.cc, libinterp/corefcn/oct-fstrm.h, libinterp/corefcn/oct-hdf5.h, libinterp/corefcn/oct-hist.cc, libinterp/corefcn/oct-hist.h, libinterp/corefcn/oct-iostrm.cc, libinterp/corefcn/oct-iostrm.h, libinterp/corefcn/oct-lvalue.cc, libinterp/corefcn/oct-lvalue.h, libinterp/corefcn/oct-map.cc, libinterp/corefcn/oct-map.h, libinterp/corefcn/oct-obj.cc, libinterp/corefcn/oct-obj.h, libinterp/corefcn/oct-prcstrm.cc, libinterp/corefcn/oct-prcstrm.h, libinterp/corefcn/oct-procbuf.cc, libinterp/corefcn/oct-procbuf.h, libinterp/corefcn/oct-stdstrm.h, libinterp/corefcn/oct-stream.cc, libinterp/corefcn/oct-stream.h, libinterp/corefcn/oct-strstrm.cc, libinterp/corefcn/oct-strstrm.h, libinterp/corefcn/oct.h, libinterp/corefcn/octave-link.cc, libinterp/corefcn/octave-link.h, libinterp/corefcn/pager.cc, libinterp/corefcn/pager.h, libinterp/corefcn/pr-output.cc, libinterp/corefcn/pr-output.h, libinterp/corefcn/procstream.cc, libinterp/corefcn/procstream.h, libinterp/corefcn/profiler.cc, libinterp/corefcn/profiler.h, libinterp/corefcn/pt-jit.cc, libinterp/corefcn/pt-jit.h, libinterp/corefcn/sighandlers.cc, libinterp/corefcn/sighandlers.h, libinterp/corefcn/siglist.c, libinterp/corefcn/siglist.h, libinterp/corefcn/sparse-xdiv.cc, libinterp/corefcn/sparse-xdiv.h, libinterp/corefcn/sparse-xpow.cc, libinterp/corefcn/sparse-xpow.h, libinterp/corefcn/symtab.cc, libinterp/corefcn/symtab.h, libinterp/corefcn/sysdep.cc, libinterp/corefcn/sysdep.h, libinterp/corefcn/toplev.cc, libinterp/corefcn/toplev.h, libinterp/corefcn/txt-eng-ft.cc, libinterp/corefcn/txt-eng-ft.h, libinterp/corefcn/txt-eng.h, libinterp/corefcn/unwind-prot.cc, libinterp/corefcn/unwind-prot.h, libinterp/corefcn/utils.cc, libinterp/corefcn/utils.h, libinterp/corefcn/variables.cc, libinterp/corefcn/variables.h, libinterp/corefcn/workspace-element.h, libinterp/corefcn/xdiv.cc, libinterp/corefcn/xdiv.h, libinterp/corefcn/xgl2ps.c, libinterp/corefcn/xnorm.cc, libinterp/corefcn/xnorm.h, libinterp/corefcn/xpow.cc, libinterp/corefcn/xpow.h, libinterp/corefcn/zfstream.cc, libinterp/corefcn/zfstream.h: Files moved from interp-core and interpfcn directories. * libinterp/interp-core/Cell.cc, libinterp/interp-core/Cell.h, libinterp/interp-core/action-container.h, libinterp/interp-core/c-file-ptr-stream.cc, libinterp/interp-core/c-file-ptr-stream.h, libinterp/interp-core/comment-list.cc, libinterp/interp-core/comment-list.h, libinterp/interp-core/cutils.c, libinterp/interp-core/cutils.h, libinterp/interp-core/defun-dld.h, libinterp/interp-core/defun-int.h, libinterp/interp-core/display.cc, libinterp/interp-core/display.h, libinterp/interp-core/dynamic-ld.cc, libinterp/interp-core/dynamic-ld.h, libinterp/interp-core/event-queue.h, libinterp/interp-core/gl-render.cc, libinterp/interp-core/gl-render.h, libinterp/interp-core/gl2ps-renderer.cc, libinterp/interp-core/gl2ps-renderer.h, libinterp/interp-core/gl2ps.c, libinterp/interp-core/gl2ps.h, libinterp/interp-core/gripes.cc, libinterp/interp-core/gripes.h, libinterp/interp-core/jit-ir.cc, libinterp/interp-core/jit-ir.h, libinterp/interp-core/jit-typeinfo.cc, libinterp/interp-core/jit-typeinfo.h, libinterp/interp-core/jit-util.cc, libinterp/interp-core/jit-util.h, libinterp/interp-core/ls-ascii-helper.cc, libinterp/interp-core/ls-ascii-helper.h, libinterp/interp-core/ls-hdf5.cc, libinterp/interp-core/ls-hdf5.h, libinterp/interp-core/ls-mat-ascii.cc, libinterp/interp-core/ls-mat-ascii.h, libinterp/interp-core/ls-mat4.cc, libinterp/interp-core/ls-mat4.h, libinterp/interp-core/ls-mat5.cc, libinterp/interp-core/ls-mat5.h, libinterp/interp-core/ls-oct-binary.cc, libinterp/interp-core/ls-oct-binary.h, libinterp/interp-core/ls-utils.cc, libinterp/interp-core/ls-utils.h, libinterp/interp-core/matherr.c, libinterp/interp-core/mex.cc, libinterp/interp-core/mex.h, libinterp/interp-core/mexproto.h, libinterp/interp-core/module.mk, libinterp/interp-core/mxarray.in.h, libinterp/interp-core/oct-errno.h, libinterp/interp-core/oct-errno.in.cc, libinterp/interp-core/oct-fstrm.cc, libinterp/interp-core/oct-fstrm.h, libinterp/interp-core/oct-hdf5.h, libinterp/interp-core/oct-iostrm.cc, libinterp/interp-core/oct-iostrm.h, libinterp/interp-core/oct-lvalue.cc, libinterp/interp-core/oct-lvalue.h, libinterp/interp-core/oct-map.cc, libinterp/interp-core/oct-map.h, libinterp/interp-core/oct-obj.cc, libinterp/interp-core/oct-obj.h, libinterp/interp-core/oct-prcstrm.cc, libinterp/interp-core/oct-prcstrm.h, libinterp/interp-core/oct-procbuf.cc, libinterp/interp-core/oct-procbuf.h, libinterp/interp-core/oct-stdstrm.h, libinterp/interp-core/oct-stream.cc, libinterp/interp-core/oct-stream.h, libinterp/interp-core/oct-strstrm.cc, libinterp/interp-core/oct-strstrm.h, libinterp/interp-core/oct.h, libinterp/interp-core/procstream.cc, libinterp/interp-core/procstream.h, libinterp/interp-core/pt-jit.cc, libinterp/interp-core/pt-jit.h, libinterp/interp-core/siglist.c, libinterp/interp-core/siglist.h, libinterp/interp-core/sparse-xdiv.cc, libinterp/interp-core/sparse-xdiv.h, libinterp/interp-core/sparse-xpow.cc, libinterp/interp-core/sparse-xpow.h, libinterp/interp-core/txt-eng-ft.cc, libinterp/interp-core/txt-eng-ft.h, libinterp/interp-core/txt-eng.h, libinterp/interp-core/unwind-prot.cc, libinterp/interp-core/unwind-prot.h, libinterp/interp-core/xdiv.cc, libinterp/interp-core/xdiv.h, libinterp/interp-core/xgl2ps.c, libinterp/interp-core/xnorm.cc, libinterp/interp-core/xnorm.h, libinterp/interp-core/xpow.cc, libinterp/interp-core/xpow.h, libinterp/interp-core/zfstream.cc, libinterp/interp-core/zfstream.h, libinterp/interpfcn/data.cc, libinterp/interpfcn/data.h, libinterp/interpfcn/debug.cc, libinterp/interpfcn/debug.h, libinterp/interpfcn/defaults.cc, libinterp/interpfcn/defaults.in.h, libinterp/interpfcn/defun.cc, libinterp/interpfcn/defun.h, libinterp/interpfcn/dirfns.cc, libinterp/interpfcn/dirfns.h, libinterp/interpfcn/error.cc, libinterp/interpfcn/error.h, libinterp/interpfcn/file-io.cc, libinterp/interpfcn/file-io.h, libinterp/interpfcn/graphics.cc, libinterp/interpfcn/graphics.in.h, libinterp/interpfcn/help.cc, libinterp/interpfcn/help.h, libinterp/interpfcn/hook-fcn.cc, libinterp/interpfcn/hook-fcn.h, libinterp/interpfcn/input.cc, libinterp/interpfcn/input.h, libinterp/interpfcn/load-path.cc, libinterp/interpfcn/load-path.h, libinterp/interpfcn/load-save.cc, libinterp/interpfcn/load-save.h, libinterp/interpfcn/ls-oct-ascii.cc, libinterp/interpfcn/ls-oct-ascii.h, libinterp/interpfcn/module.mk, libinterp/interpfcn/oct-hist.cc, libinterp/interpfcn/oct-hist.h, libinterp/interpfcn/octave-link.cc, libinterp/interpfcn/octave-link.h, libinterp/interpfcn/pager.cc, libinterp/interpfcn/pager.h, libinterp/interpfcn/pr-output.cc, libinterp/interpfcn/pr-output.h, libinterp/interpfcn/profiler.cc, libinterp/interpfcn/profiler.h, libinterp/interpfcn/sighandlers.cc, libinterp/interpfcn/sighandlers.h, libinterp/interpfcn/symtab.cc, libinterp/interpfcn/symtab.h, libinterp/interpfcn/sysdep.cc, libinterp/interpfcn/sysdep.h, libinterp/interpfcn/toplev.cc, libinterp/interpfcn/toplev.h, libinterp/interpfcn/utils.cc, libinterp/interpfcn/utils.h, libinterp/interpfcn/variables.cc, libinterp/interpfcn/variables.h, libinterp/interpfcn/workspace-element.h: deleted files.
author Rik <rik@octave.org>
date Wed, 03 Jul 2013 17:43:48 -0700
parents libinterp/interpfcn/graphics.cc@359ac80ecb30
children 259c1f295a1e
comparison
equal deleted inserted replaced
16891:486c3e2731ff 16892:68fc671a9339
1 /*
2
3 Copyright (C) 2007-2012 John W. Eaton
4
5 This file is part of Octave.
6
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <cctype>
28 #include <cfloat>
29 #include <cstdlib>
30 #include <ctime>
31
32 #include <algorithm>
33 #include <list>
34 #include <map>
35 #include <set>
36 #include <string>
37 #include <sstream>
38
39 #include "cmd-edit.h"
40 #include "file-ops.h"
41 #include "file-stat.h"
42 #include "oct-locbuf.h"
43 #include "singleton-cleanup.h"
44
45 #include "builtins.h"
46 #include "cutils.h"
47 #include "defun.h"
48 #include "display.h"
49 #include "error.h"
50 #include "graphics.h"
51 #include "input.h"
52 #include "ov.h"
53 #include "oct-obj.h"
54 #include "oct-map.h"
55 #include "ov-fcn-handle.h"
56 #include "pager.h"
57 #include "parse.h"
58 #include "toplev.h"
59 #include "txt-eng-ft.h"
60 #include "unwind-prot.h"
61
62 // forward declarations
63 static octave_value xget (const graphics_handle& h, const caseless_str& name);
64
65 static void
66 gripe_set_invalid (const std::string& pname)
67 {
68 error ("set: invalid value for %s property", pname.c_str ());
69 }
70
71 // Check to see that PNAME matches just one of PNAMES uniquely.
72 // Return the full name of the match, or an empty caseless_str object
73 // if there is no match, or the match is ambiguous.
74
75 static caseless_str
76 validate_property_name (const std::string& who, const std::string& what,
77 const std::set<std::string>& pnames,
78 const caseless_str& pname)
79 {
80 size_t len = pname.length ();
81 std::set<std::string> matches;
82
83 for (std::set<std::string>::const_iterator p = pnames.begin ();
84 p != pnames.end (); p++)
85 {
86 if (pname.compare (*p, len))
87 {
88 if (len == p->length ())
89 {
90 // Exact match.
91 return pname;
92 }
93
94 matches.insert (*p);
95 }
96 }
97
98 size_t num_matches = matches.size ();
99
100 if (num_matches == 0)
101 {
102 error ("%s: unknown %s property %s",
103 who.c_str (), what.c_str (), pname.c_str ());
104 }
105 else if (num_matches > 1)
106 {
107 string_vector sv (matches);
108
109 std::ostringstream os;
110
111 sv.list_in_columns (os);
112
113 std::string match_list = os.str ();
114
115 error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
116 who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
117 }
118 else if (num_matches == 1)
119 {
120 // Exact match was handled above.
121
122 std::string possible_match = *(matches.begin ());
123
124 warning_with_id ("Octave:abbreviated-property-match",
125 "%s: allowing %s to match %s property %s",
126 who.c_str (), pname.c_str (), what.c_str (),
127 possible_match.c_str ());
128
129 return possible_match;
130 }
131
132 return caseless_str ();
133 }
134
135 static Matrix
136 jet_colormap (void)
137 {
138 Matrix cmap (64, 3, 0.0);
139
140 // Produce X in the same manner as linspace so that
141 // jet_colormap and jet.m produce *exactly* the same result.
142 double delta = 1.0 / 63.0;
143
144 for (octave_idx_type i = 0; i < 64; i++)
145 {
146 // This is the jet colormap. It would be nice to be able
147 // to feval the jet function but since there is a static
148 // property object that includes a colormap_property
149 // object, we need to initialize this before main is even
150 // called, so calling an interpreted function is not
151 // possible.
152
153 double x = i*delta;
154
155 if (x >= 3.0/8.0 && x < 5.0/8.0)
156 cmap(i,0) = 4.0 * x - 3.0/2.0;
157 else if (x >= 5.0/8.0 && x < 7.0/8.0)
158 cmap(i,0) = 1.0;
159 else if (x >= 7.0/8.0)
160 cmap(i,0) = -4.0 * x + 9.0/2.0;
161
162 if (x >= 1.0/8.0 && x < 3.0/8.0)
163 cmap(i,1) = 4.0 * x - 1.0/2.0;
164 else if (x >= 3.0/8.0 && x < 5.0/8.0)
165 cmap(i,1) = 1.0;
166 else if (x >= 5.0/8.0 && x < 7.0/8.0)
167 cmap(i,1) = -4.0 * x + 7.0/2.0;
168
169 if (x < 1.0/8.0)
170 cmap(i,2) = 4.0 * x + 1.0/2.0;
171 else if (x >= 1.0/8.0 && x < 3.0/8.0)
172 cmap(i,2) = 1.0;
173 else if (x >= 3.0/8.0 && x < 5.0/8.0)
174 cmap(i,2) = -4.0 * x + 5.0/2.0;
175 }
176
177 return cmap;
178 }
179
180 static double
181 default_screendepth (void)
182 {
183 return display_info::depth ();
184 }
185
186 static Matrix
187 default_screensize (void)
188 {
189 Matrix retval (1, 4, 1.0);
190
191 retval(2) = display_info::width ();
192 retval(3) = display_info::height ();
193
194 return retval;
195 }
196
197 static double
198 default_screenpixelsperinch (void)
199 {
200 return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
201 }
202
203 static Matrix
204 default_colororder (void)
205 {
206 Matrix retval (7, 3, 0.0);
207
208 retval(0,2) = 1.0;
209
210 retval(1,1) = 0.5;
211
212 retval(2,0) = 1.0;
213
214 retval(3,1) = 0.75;
215 retval(3,2) = 0.75;
216
217 retval(4,0) = 0.75;
218 retval(4,2) = 0.75;
219
220 retval(5,0) = 0.75;
221 retval(5,1) = 0.75;
222
223 retval(6,0) = 0.25;
224 retval(6,1) = 0.25;
225 retval(6,2) = 0.25;
226
227 return retval;
228 }
229
230 static Matrix
231 default_lim (bool logscale = false)
232 {
233 Matrix m (1, 2, 0);
234
235 if (logscale)
236 {
237 m(0) = 0.1;
238 m(1) = 1.0;
239 }
240 else
241 m(1) = 1;
242
243 return m;
244 }
245
246 static Matrix
247 default_data (void)
248 {
249 Matrix retval (1, 2);
250
251 retval(0) = 0;
252 retval(1) = 1;
253
254 return retval;
255 }
256
257 static Matrix
258 default_axes_position (void)
259 {
260 Matrix m (1, 4, 0.0);
261 m(0) = 0.13;
262 m(1) = 0.11;
263 m(2) = 0.775;
264 m(3) = 0.815;
265 return m;
266 }
267
268 static Matrix
269 default_axes_outerposition (void)
270 {
271 Matrix m (1, 4, 0.0);
272 m(2) = m(3) = 1.0;
273 return m;
274 }
275
276 static Matrix
277 default_axes_tick (void)
278 {
279 Matrix m (1, 6, 0.0);
280 m(0) = 0.0;
281 m(1) = 0.2;
282 m(2) = 0.4;
283 m(3) = 0.6;
284 m(4) = 0.8;
285 m(5) = 1.0;
286 return m;
287 }
288
289 static Matrix
290 default_axes_ticklength (void)
291 {
292 Matrix m (1, 2, 0.0);
293 m(0) = 0.01;
294 m(1) = 0.025;
295 return m;
296 }
297
298 static Matrix
299 default_figure_position (void)
300 {
301 Matrix m (1, 4, 0.0);
302 m(0) = 300;
303 m(1) = 200;
304 m(2) = 560;
305 m(3) = 420;
306 return m;
307 }
308
309 static Matrix
310 default_figure_papersize (void)
311 {
312 Matrix m (1, 2, 0.0);
313 m(0) = 8.5;
314 m(1) = 11.0;
315 return m;
316 }
317
318 static Matrix
319 default_figure_paperposition (void)
320 {
321 Matrix m (1, 4, 0.0);
322 m(0) = 0.25;
323 m(1) = 2.50;
324 m(2) = 8.00;
325 m(3) = 6.00;
326 return m;
327 }
328
329 static Matrix
330 default_control_position (void)
331 {
332 Matrix retval (1, 4, 0.0);
333
334 retval(0) = 0;
335 retval(1) = 0;
336 retval(2) = 80;
337 retval(3) = 30;
338
339 return retval;
340 }
341
342 static Matrix
343 default_control_sliderstep (void)
344 {
345 Matrix retval (1, 2, 0.0);
346
347 retval(0) = 0.01;
348 retval(1) = 0.1;
349
350 return retval;
351 }
352
353 static Matrix
354 default_panel_position (void)
355 {
356 Matrix retval (1, 4, 0.0);
357
358 retval(0) = 0;
359 retval(1) = 0;
360 retval(2) = 0.5;
361 retval(3) = 0.5;
362
363 return retval;
364 }
365
366 static double
367 convert_font_size (double font_size, const caseless_str& from_units,
368 const caseless_str& to_units, double parent_height = 0)
369 {
370 // Simple case where from_units == to_units
371
372 if (from_units.compare (to_units))
373 return font_size;
374
375 // Converts the given fontsize using the following transformation:
376 // <old_font_size> => points => <new_font_size>
377
378 double points_size = 0;
379 double res = 0;
380
381 if (from_units.compare ("points"))
382 points_size = font_size;
383 else
384 {
385 res = xget (0, "screenpixelsperinch").double_value ();
386
387 if (from_units.compare ("pixels"))
388 points_size = font_size * 72.0 / res;
389 else if (from_units.compare ("inches"))
390 points_size = font_size * 72.0;
391 else if (from_units.compare ("centimeters"))
392 points_size = font_size * 72.0 / 2.54;
393 else if (from_units.compare ("normalized"))
394 points_size = font_size * parent_height * 72.0 / res;
395 }
396
397 double new_font_size = 0;
398
399 if (to_units.compare ("points"))
400 new_font_size = points_size;
401 else
402 {
403 if (res <= 0)
404 res = xget (0, "screenpixelsperinch").double_value ();
405
406 if (to_units.compare ("pixels"))
407 new_font_size = points_size * res / 72.0;
408 else if (to_units.compare ("inches"))
409 new_font_size = points_size / 72.0;
410 else if (to_units.compare ("centimeters"))
411 new_font_size = points_size * 2.54 / 72.0;
412 else if (to_units.compare ("normalized"))
413 {
414 // Avoid setting font size to (0/0) = NaN
415
416 if (parent_height > 0)
417 new_font_size = points_size * res / (parent_height * 72.0);
418 }
419 }
420
421 return new_font_size;
422 }
423
424 static Matrix
425 convert_position (const Matrix& pos, const caseless_str& from_units,
426 const caseless_str& to_units, const Matrix& parent_dim)
427 {
428 Matrix retval (1, pos.numel ());
429 double res = 0;
430 bool is_rectangle = (pos.numel () == 4);
431 bool is_2d = (pos.numel () == 2);
432
433 if (from_units.compare ("pixels"))
434 retval = pos;
435 else if (from_units.compare ("normalized"))
436 {
437 retval(0) = pos(0) * parent_dim(0) + 1;
438 retval(1) = pos(1) * parent_dim(1) + 1;
439 if (is_rectangle)
440 {
441 retval(2) = pos(2) * parent_dim(0);
442 retval(3) = pos(3) * parent_dim(1);
443 }
444 else if (! is_2d)
445 retval(2) = 0;
446 }
447 else if (from_units.compare ("characters"))
448 {
449 if (res <= 0)
450 res = xget (0, "screenpixelsperinch").double_value ();
451
452 double f = 0.0;
453
454 // FIXME -- this assumes the system font is Helvetica 10pt
455 // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
456 f = 12.0 * res / 74.951;
457
458 if (f > 0)
459 {
460 retval(0) = 0.5 * pos(0) * f;
461 retval(1) = pos(1) * f;
462 if (is_rectangle)
463 {
464 retval(2) = 0.5 * pos(2) * f;
465 retval(3) = pos(3) * f;
466 }
467 else if (! is_2d)
468 retval(2) = 0;
469 }
470 }
471 else
472 {
473 if (res <= 0)
474 res = xget (0, "screenpixelsperinch").double_value ();
475
476 double f = 0.0;
477
478 if (from_units.compare ("points"))
479 f = res / 72.0;
480 else if (from_units.compare ("inches"))
481 f = res;
482 else if (from_units.compare ("centimeters"))
483 f = res / 2.54;
484
485 if (f > 0)
486 {
487 retval(0) = pos(0) * f + 1;
488 retval(1) = pos(1) * f + 1;
489 if (is_rectangle)
490 {
491 retval(2) = pos(2) * f;
492 retval(3) = pos(3) * f;
493 }
494 else if (! is_2d)
495 retval(2) = 0;
496 }
497 }
498
499 if (! to_units.compare ("pixels"))
500 {
501 if (to_units.compare ("normalized"))
502 {
503 retval(0) = (retval(0) - 1) / parent_dim(0);
504 retval(1) = (retval(1) - 1) / parent_dim(1);
505 if (is_rectangle)
506 {
507 retval(2) /= parent_dim(0);
508 retval(3) /= parent_dim(1);
509 }
510 else if (! is_2d)
511 retval(2) = 0;
512 }
513 else if (to_units.compare ("characters"))
514 {
515 if (res <= 0)
516 res = xget (0, "screenpixelsperinch").double_value ();
517
518 double f = 0.0;
519
520 f = 12.0 * res / 74.951;
521
522 if (f > 0)
523 {
524 retval(0) = 2 * retval(0) / f;
525 retval(1) = retval(1) / f;
526 if (is_rectangle)
527 {
528 retval(2) = 2 * retval(2) / f;
529 retval(3) = retval(3) / f;
530 }
531 else if (! is_2d)
532 retval(2) = 0;
533 }
534 }
535 else
536 {
537 if (res <= 0)
538 res = xget (0, "screenpixelsperinch").double_value ();
539
540 double f = 0.0;
541
542 if (to_units.compare ("points"))
543 f = res / 72.0;
544 else if (to_units.compare ("inches"))
545 f = res;
546 else if (to_units.compare ("centimeters"))
547 f = res / 2.54;
548
549 if (f > 0)
550 {
551 retval(0) = (retval(0) - 1) / f;
552 retval(1) = (retval(1) - 1) / f;
553 if (is_rectangle)
554 {
555 retval(2) /= f;
556 retval(3) /= f;
557 }
558 else if (! is_2d)
559 retval(2) = 0;
560 }
561 }
562 }
563 else if (! is_rectangle && ! is_2d)
564 retval(2) = 0;
565
566 return retval;
567 }
568
569 static Matrix
570 convert_text_position (const Matrix& pos, const text::properties& props,
571 const caseless_str& from_units,
572 const caseless_str& to_units)
573 {
574 graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
575 graphics_object ax = go.get_ancestor ("axes");
576
577 Matrix retval;
578
579 if (ax.valid_object ())
580 {
581 const axes::properties& ax_props =
582 dynamic_cast<const axes::properties&> (ax.get_properties ());
583 graphics_xform ax_xform = ax_props.get_transform ();
584 bool is_rectangle = (pos.numel () == 4);
585 Matrix ax_bbox = ax_props.get_boundingbox (true),
586 ax_size = ax_bbox.extract_n (0, 2, 1, 2);
587
588 if (from_units.compare ("data"))
589 {
590 if (is_rectangle)
591 {
592 ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
593 v2 = ax_xform.transform (pos(0) + pos(2),
594 pos(1) + pos(3), 0);
595
596 retval.resize (1, 4);
597
598 retval(0) = v1(0) - ax_bbox(0) + 1;
599 retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
600 retval(2) = v2(0) - v1(0);
601 retval(3) = v1(1) - v2(1);
602 }
603 else
604 {
605 ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
606
607 retval.resize (1, 3);
608
609 retval(0) = v(0) - ax_bbox(0) + 1;
610 retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
611 retval(2) = 0;
612 }
613 }
614 else
615 retval = convert_position (pos, from_units, "pixels", ax_size);
616
617 if (! to_units.compare ("pixels"))
618 {
619 if (to_units.compare ("data"))
620 {
621 if (is_rectangle)
622 {
623 ColumnVector v1 = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
624 ax_bbox(1) + ax_bbox(3) - retval(1) + 1),
625 v2 = ax_xform.untransform (retval(0) + retval(2) + ax_bbox(0) - 1,
626 ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
627
628 retval.resize (1, 4);
629
630 retval(0) = v1(0);
631 retval(1) = v1(1);
632 retval(2) = v2(0) - v1(0);
633 retval(3) = v2(1) - v1(1);
634 }
635 else
636 {
637 ColumnVector v = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
638 ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
639
640 retval.resize (1, 3);
641
642 retval(0) = v(0);
643 retval(1) = v(1);
644 retval(2) = v(2);
645 }
646 }
647 else
648 retval = convert_position (retval, "pixels", to_units, ax_size);
649 }
650 }
651
652 return retval;
653 }
654
655 // This function always returns the screensize in pixels
656 static Matrix
657 screen_size_pixels (void)
658 {
659 graphics_object obj = gh_manager::get_object (0);
660 Matrix sz = obj.get ("screensize").matrix_value ();
661 return convert_position (sz, obj.get ("units").string_value (), "pixels", sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
662 }
663
664 static void
665 convert_cdata_2 (bool is_scaled, double clim_0, double clim_1,
666 const double *cmapv, double x, octave_idx_type lda,
667 octave_idx_type nc, octave_idx_type i, double *av)
668 {
669 if (is_scaled)
670 x = xround ((nc - 1) * (x - clim_0) / (clim_1 - clim_0));
671 else
672 x = xround (x - 1);
673
674 if (xisnan (x))
675 {
676 av[i] = x;
677 av[i+lda] = x;
678 av[i+2*lda] = x;
679 }
680 else
681 {
682 if (x < 0)
683 x = 0;
684 else if (x >= nc)
685 x = (nc - 1);
686
687 octave_idx_type idx = static_cast<octave_idx_type> (x);
688
689 av[i] = cmapv[idx];
690 av[i+lda] = cmapv[idx+nc];
691 av[i+2*lda] = cmapv[idx+2*nc];
692 }
693 }
694
695 template <class T>
696 void
697 convert_cdata_1 (bool is_scaled, double clim_0, double clim_1,
698 const double *cmapv, const T *cv, octave_idx_type lda,
699 octave_idx_type nc, double *av)
700 {
701 for (octave_idx_type i = 0; i < lda; i++)
702 convert_cdata_2 (is_scaled, clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
703 }
704
705 static octave_value
706 convert_cdata (const base_properties& props, const octave_value& cdata,
707 bool is_scaled, int cdim)
708 {
709 dim_vector dv (cdata.dims ());
710
711 if (dv.length () == cdim && dv(cdim-1) == 3)
712 return cdata;
713
714 Matrix cmap (1, 3, 0.0);
715 Matrix clim (1, 2, 0.0);
716
717 graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
718 graphics_object fig = go.get_ancestor ("figure");
719
720 if (fig.valid_object ())
721 {
722 Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
723
724 if (! error_state)
725 cmap = _cmap;
726 }
727
728 if (is_scaled)
729 {
730 graphics_object ax = go.get_ancestor ("axes");
731
732 if (ax.valid_object ())
733 {
734 Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
735
736 if (! error_state)
737 clim = _clim;
738 }
739 }
740
741 dv.resize (cdim);
742 dv(cdim-1) = 3;
743
744 NDArray a (dv);
745
746 octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
747 octave_idx_type nc = cmap.rows ();
748
749 double *av = a.fortran_vec ();
750 const double *cmapv = cmap.data ();
751
752 double clim_0 = clim(0);
753 double clim_1 = clim(1);
754
755 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN) \
756 do \
757 { \
758 ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
759 \
760 convert_cdata_1 (is_scaled, clim_0, clim_1, cmapv, \
761 tmp.data (), lda, nc, av); \
762 } \
763 while (0)
764
765 if (cdata.is_uint8_type ())
766 CONVERT_CDATA_1 (uint8NDArray, uint8_);
767 else if (cdata.is_single_type ())
768 CONVERT_CDATA_1 (FloatNDArray, float_);
769 else if (cdata.is_double_type ())
770 CONVERT_CDATA_1 (NDArray, );
771 else
772 error ("unsupported type for cdata (= %s)", cdata.type_name ().c_str ());
773
774 #undef CONVERT_CDATA_1
775
776 return octave_value (a);
777 }
778
779 template<class T>
780 static void
781 get_array_limits (const Array<T>& m, double& emin, double& emax,
782 double& eminp, double& emaxp)
783 {
784 const T *data = m.data ();
785 octave_idx_type n = m.numel ();
786
787 for (octave_idx_type i = 0; i < n; i++)
788 {
789 double e = double (data[i]);
790
791 // Don't need to test for NaN here as NaN>x and NaN<x is always false
792 if (! xisinf (e))
793 {
794 if (e < emin)
795 emin = e;
796
797 if (e > emax)
798 emax = e;
799
800 if (e > 0 && e < eminp)
801 eminp = e;
802
803 if (e < 0 && e > emaxp)
804 emaxp = e;
805 }
806 }
807 }
808
809 static bool
810 lookup_object_name (const caseless_str& name, caseless_str& go_name,
811 caseless_str& rest)
812 {
813 int len = name.length ();
814 int offset = 0;
815 bool result = false;
816
817 if (len >= 4)
818 {
819 caseless_str pfx = name.substr (0, 4);
820
821 if (pfx.compare ("axes") || pfx.compare ("line")
822 || pfx.compare ("text"))
823 offset = 4;
824 else if (len >= 5)
825 {
826 pfx = name.substr (0, 5);
827
828 if (pfx.compare ("image") || pfx.compare ("patch"))
829 offset = 5;
830 else if (len >= 6)
831 {
832 pfx = name.substr (0, 6);
833
834 if (pfx.compare ("figure") || pfx.compare ("uimenu"))
835 offset = 6;
836 else if (len >= 7)
837 {
838 pfx = name.substr (0, 7);
839
840 if (pfx.compare ("surface") || pfx.compare ("hggroup")
841 || pfx.compare ("uipanel"))
842 offset = 7;
843 else if (len >= 9)
844 {
845 pfx = name.substr (0, 9);
846
847 if (pfx.compare ("uicontrol")
848 || pfx.compare ("uitoolbar"))
849 offset = 9;
850 else if (len >= 10)
851 {
852 pfx = name.substr (0, 10);
853
854 if (pfx.compare ("uipushtool"))
855 offset = 10;
856 else if (len >= 12)
857 {
858 pfx = name.substr (0, 12);
859
860 if (pfx.compare ("uitoggletool"))
861 offset = 12;
862 else if (len >= 13)
863 {
864 pfx = name.substr (0, 13);
865
866 if (pfx.compare ("uicontextmenu"))
867 offset = 13;
868 }
869 }
870 }
871 }
872 }
873 }
874 }
875
876 if (offset > 0)
877 {
878 go_name = pfx;
879 rest = name.substr (offset);
880 result = true;
881 }
882 }
883
884 return result;
885 }
886
887 static base_graphics_object*
888 make_graphics_object_from_type (const caseless_str& type,
889 const graphics_handle& h = graphics_handle (),
890 const graphics_handle& p = graphics_handle ())
891 {
892 base_graphics_object *go = 0;
893
894 if (type.compare ("figure"))
895 go = new figure (h, p);
896 else if (type.compare ("axes"))
897 go = new axes (h, p);
898 else if (type.compare ("line"))
899 go = new line (h, p);
900 else if (type.compare ("text"))
901 go = new text (h, p);
902 else if (type.compare ("image"))
903 go = new image (h, p);
904 else if (type.compare ("patch"))
905 go = new patch (h, p);
906 else if (type.compare ("surface"))
907 go = new surface (h, p);
908 else if (type.compare ("hggroup"))
909 go = new hggroup (h, p);
910 else if (type.compare ("uimenu"))
911 go = new uimenu (h, p);
912 else if (type.compare ("uicontrol"))
913 go = new uicontrol (h, p);
914 else if (type.compare ("uipanel"))
915 go = new uipanel (h, p);
916 else if (type.compare ("uicontextmenu"))
917 go = new uicontextmenu (h, p);
918 else if (type.compare ("uitoolbar"))
919 go = new uitoolbar (h, p);
920 else if (type.compare ("uipushtool"))
921 go = new uipushtool (h, p);
922 else if (type.compare ("uitoggletool"))
923 go = new uitoggletool (h, p);
924 return go;
925 }
926
927 // ---------------------------------------------------------------------
928
929 bool
930 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
931 {
932 if (do_set (v))
933 {
934
935 // Notify graphics toolkit.
936 if (id >= 0 && do_notify_toolkit)
937 {
938 graphics_object go = gh_manager::get_object (parent);
939 if (go)
940 go.update (id);
941 }
942
943 // run listeners
944 if (do_run && ! error_state)
945 run_listeners (POSTSET);
946
947 return true;
948 }
949
950 return false;
951 }
952
953
954 void
955 base_property::run_listeners (listener_mode mode)
956 {
957 const octave_value_list& l = listeners[mode];
958
959 for (int i = 0; i < l.length (); i++)
960 {
961 gh_manager::execute_listener (parent, l(i));
962
963 if (error_state)
964 break;
965 }
966 }
967
968 radio_values::radio_values (const std::string& opt_string)
969 : default_val (), possible_vals ()
970 {
971 size_t beg = 0;
972 size_t len = opt_string.length ();
973 bool done = len == 0;
974
975 while (! done)
976 {
977 size_t end = opt_string.find ('|', beg);
978
979 if (end == std::string::npos)
980 {
981 end = len;
982 done = true;
983 }
984
985 std::string t = opt_string.substr (beg, end-beg);
986
987 // Might want more error checking here...
988 if (t[0] == '{')
989 {
990 t = t.substr (1, t.length () - 2);
991 default_val = t;
992 }
993 else if (beg == 0) // ensure default value
994 default_val = t;
995
996 possible_vals.insert (t);
997
998 beg = end + 1;
999 }
1000 }
1001
1002 std::string
1003 radio_values::values_as_string (void) const
1004 {
1005 std::string retval;
1006 for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1007 it != possible_vals.end (); it++)
1008 {
1009 if (retval == "")
1010 {
1011 if (*it == default_value ())
1012 retval = "{" + *it + "}";
1013 else
1014 retval = *it;
1015 }
1016 else
1017 {
1018 if (*it == default_value ())
1019 retval += " | {" + *it + "}";
1020 else
1021 retval += " | " + *it;
1022 }
1023 }
1024 if (retval != "")
1025 retval = "[ " + retval + " ]";
1026 return retval;
1027 }
1028
1029 Cell
1030 radio_values::values_as_cell (void) const
1031 {
1032 octave_idx_type i = 0;
1033 Cell retval (nelem (), 1);
1034 for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1035 it != possible_vals.end (); it++)
1036 retval(i++) = std::string (*it);
1037 return retval;
1038 }
1039
1040 bool
1041 color_values::str2rgb (std::string str)
1042 {
1043 double tmp_rgb[3] = {0, 0, 0};
1044 bool retval = true;
1045 unsigned int len = str.length ();
1046
1047 std::transform (str.begin (), str.end (), str.begin (), tolower);
1048
1049 if (str.compare (0, len, "blue", 0, len) == 0)
1050 tmp_rgb[2] = 1;
1051 else if (str.compare (0, len, "black", 0, len) == 0
1052 || str.compare (0, len, "k", 0, len) == 0)
1053 tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1054 else if (str.compare (0, len, "red", 0, len) == 0)
1055 tmp_rgb[0] = 1;
1056 else if (str.compare (0, len, "green", 0, len) == 0)
1057 tmp_rgb[1] = 1;
1058 else if (str.compare (0, len, "yellow", 0, len) == 0)
1059 tmp_rgb[0] = tmp_rgb[1] = 1;
1060 else if (str.compare (0, len, "magenta", 0, len) == 0)
1061 tmp_rgb[0] = tmp_rgb[2] = 1;
1062 else if (str.compare (0, len, "cyan", 0, len) == 0)
1063 tmp_rgb[1] = tmp_rgb[2] = 1;
1064 else if (str.compare (0, len, "white", 0, len) == 0
1065 || str.compare (0, len, "w", 0, len) == 0)
1066 tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1067 else
1068 retval = false;
1069
1070 if (retval)
1071 {
1072 for (int i = 0; i < 3; i++)
1073 xrgb(i) = tmp_rgb[i];
1074 }
1075
1076 return retval;
1077 }
1078
1079 bool
1080 color_property::do_set (const octave_value& val)
1081 {
1082 if (val.is_string ())
1083 {
1084 std::string s = val.string_value ();
1085
1086 if (! s.empty ())
1087 {
1088 std::string match;
1089
1090 if (radio_val.contains (s, match))
1091 {
1092 if (current_type != radio_t || match != current_val)
1093 {
1094 if (s.length () != match.length ())
1095 warning_with_id ("Octave:abbreviated-property-match",
1096 "%s: allowing %s to match %s value %s",
1097 "set", s.c_str (), get_name ().c_str (),
1098 match.c_str ());
1099 current_val = match;
1100 current_type = radio_t;
1101 return true;
1102 }
1103 }
1104 else
1105 {
1106 color_values col (s);
1107 if (! error_state)
1108 {
1109 if (current_type != color_t || col != color_val)
1110 {
1111 color_val = col;
1112 current_type = color_t;
1113 return true;
1114 }
1115 }
1116 else
1117 error ("invalid value for color property \"%s\" (value = %s)",
1118 get_name ().c_str (), s.c_str ());
1119 }
1120 }
1121 else
1122 error ("invalid value for color property \"%s\"",
1123 get_name ().c_str ());
1124 }
1125 else if (val.is_numeric_type ())
1126 {
1127 Matrix m = val.matrix_value ();
1128
1129 if (m.numel () == 3)
1130 {
1131 color_values col (m(0), m(1), m(2));
1132 if (! error_state)
1133 {
1134 if (current_type != color_t || col != color_val)
1135 {
1136 color_val = col;
1137 current_type = color_t;
1138 return true;
1139 }
1140 }
1141 }
1142 else
1143 error ("invalid value for color property \"%s\"",
1144 get_name ().c_str ());
1145 }
1146 else
1147 error ("invalid value for color property \"%s\"",
1148 get_name ().c_str ());
1149
1150 return false;
1151 }
1152
1153 bool
1154 double_radio_property::do_set (const octave_value& val)
1155 {
1156 if (val.is_string ())
1157 {
1158 std::string s = val.string_value ();
1159 std::string match;
1160
1161 if (! s.empty () && radio_val.contains (s, match))
1162 {
1163 if (current_type != radio_t || match != current_val)
1164 {
1165 if (s.length () != match.length ())
1166 warning_with_id ("Octave:abbreviated-property-match",
1167 "%s: allowing %s to match %s value %s",
1168 "set", s.c_str (), get_name ().c_str (),
1169 match.c_str ());
1170 current_val = match;
1171 current_type = radio_t;
1172 return true;
1173 }
1174 }
1175 else
1176 error ("invalid value for double_radio property \"%s\"",
1177 get_name ().c_str ());
1178 }
1179 else if (val.is_scalar_type () && val.is_real_type ())
1180 {
1181 double new_dval = val.double_value ();
1182
1183 if (current_type != double_t || new_dval != dval)
1184 {
1185 dval = new_dval;
1186 current_type = double_t;
1187 return true;
1188 }
1189 }
1190 else
1191 error ("invalid value for double_radio property \"%s\"",
1192 get_name ().c_str ());
1193
1194 return false;
1195 }
1196
1197 bool
1198 array_property::validate (const octave_value& v)
1199 {
1200 bool xok = false;
1201
1202 // FIXME -- should we always support []?
1203 if (v.is_empty () && v.is_numeric_type ())
1204 return true;
1205
1206 // check value type
1207 if (type_constraints.size () > 0)
1208 {
1209 if(type_constraints.find (v.class_name()) != type_constraints.end())
1210 xok = true;
1211
1212 // check if complex is allowed (it's also of class "double", so
1213 // checking that alone is not enough to ensure real type)
1214 if (type_constraints.find ("real") != type_constraints.end ()
1215 && v.is_complex_type ())
1216 xok = false;
1217 }
1218 else
1219 xok = v.is_numeric_type ();
1220
1221 if (xok)
1222 {
1223 dim_vector vdims = v.dims ();
1224 int vlen = vdims.length ();
1225
1226 xok = false;
1227
1228 // check value size
1229 if (size_constraints.size () > 0)
1230 for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
1231 ! xok && it != size_constraints.end (); ++it)
1232 {
1233 dim_vector itdims = (*it);
1234
1235 if (itdims.length () == vlen)
1236 {
1237 xok = true;
1238
1239 for (int i = 0; xok && i < vlen; i++)
1240 if (itdims(i) >= 0 && itdims(i) != vdims(i))
1241 xok = false;
1242 }
1243 }
1244 else
1245 return true;
1246 }
1247
1248 return xok;
1249 }
1250
1251 bool
1252 array_property::is_equal (const octave_value& v) const
1253 {
1254 if (data.type_name () == v.type_name ())
1255 {
1256 if (data.dims () == v.dims ())
1257 {
1258
1259 #define CHECK_ARRAY_EQUAL(T,F,A) \
1260 { \
1261 if (data.numel () == 1) \
1262 return data.F ## scalar_value () == \
1263 v.F ## scalar_value (); \
1264 else \
1265 { \
1266 /* Keep copy of array_value to allow sparse/bool arrays */ \
1267 /* that are converted, to not be deallocated early */ \
1268 const A m1 = data.F ## array_value (); \
1269 const T* d1 = m1.data (); \
1270 const A m2 = v.F ## array_value (); \
1271 const T* d2 = m2.data ();\
1272 \
1273 bool flag = true; \
1274 \
1275 for (int i = 0; flag && i < data.numel (); i++) \
1276 if (d1[i] != d2[i]) \
1277 flag = false; \
1278 \
1279 return flag; \
1280 } \
1281 }
1282
1283 if (data.is_double_type () || data.is_bool_type ())
1284 CHECK_ARRAY_EQUAL (double, , NDArray)
1285 else if (data.is_single_type ())
1286 CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1287 else if (data.is_int8_type ())
1288 CHECK_ARRAY_EQUAL (octave_int8, int8_, int8NDArray)
1289 else if (data.is_int16_type ())
1290 CHECK_ARRAY_EQUAL (octave_int16, int16_, int16NDArray)
1291 else if (data.is_int32_type ())
1292 CHECK_ARRAY_EQUAL (octave_int32, int32_, int32NDArray)
1293 else if (data.is_int64_type ())
1294 CHECK_ARRAY_EQUAL (octave_int64, int64_, int64NDArray)
1295 else if (data.is_uint8_type ())
1296 CHECK_ARRAY_EQUAL (octave_uint8, uint8_, uint8NDArray)
1297 else if (data.is_uint16_type ())
1298 CHECK_ARRAY_EQUAL (octave_uint16, uint16_, uint16NDArray)
1299 else if (data.is_uint32_type ())
1300 CHECK_ARRAY_EQUAL (octave_uint32, uint32_, uint32NDArray)
1301 else if (data.is_uint64_type ())
1302 CHECK_ARRAY_EQUAL (octave_uint64, uint64_, uint64NDArray)
1303 }
1304 }
1305
1306 return false;
1307 }
1308
1309 void
1310 array_property::get_data_limits (void)
1311 {
1312 xmin = xminp = octave_Inf;
1313 xmax = xmaxp = -octave_Inf;
1314
1315 if (! data.is_empty ())
1316 {
1317 if (data.is_integer_type ())
1318 {
1319 if (data.is_int8_type ())
1320 get_array_limits (data.int8_array_value (), xmin, xmax, xminp, xmaxp);
1321 else if (data.is_uint8_type ())
1322 get_array_limits (data.uint8_array_value (), xmin, xmax, xminp, xmaxp);
1323 else if (data.is_int16_type ())
1324 get_array_limits (data.int16_array_value (), xmin, xmax, xminp, xmaxp);
1325 else if (data.is_uint16_type ())
1326 get_array_limits (data.uint16_array_value (), xmin, xmax, xminp, xmaxp);
1327 else if (data.is_int32_type ())
1328 get_array_limits (data.int32_array_value (), xmin, xmax, xminp, xmaxp);
1329 else if (data.is_uint32_type ())
1330 get_array_limits (data.uint32_array_value (), xmin, xmax, xminp, xmaxp);
1331 else if (data.is_int64_type ())
1332 get_array_limits (data.int64_array_value (), xmin, xmax, xminp, xmaxp);
1333 else if (data.is_uint64_type ())
1334 get_array_limits (data.uint64_array_value (), xmin, xmax, xminp, xmaxp);
1335 }
1336 else
1337 get_array_limits (data.array_value (), xmin, xmax, xminp, xmaxp);
1338 }
1339 }
1340
1341 bool
1342 handle_property::do_set (const octave_value& v)
1343 {
1344 double dv = v.double_value ();
1345
1346 if (! error_state)
1347 {
1348 graphics_handle gh = gh_manager::lookup (dv);
1349
1350 if (xisnan (gh.value ()) || gh.ok ())
1351 {
1352 if (current_val != gh)
1353 {
1354 current_val = gh;
1355 return true;
1356 }
1357 }
1358 else
1359 error ("set: invalid graphics handle (= %g) for property \"%s\"",
1360 dv, get_name ().c_str ());
1361 }
1362 else
1363 error ("set: invalid graphics handle for property \"%s\"",
1364 get_name ().c_str ());
1365
1366 return false;
1367 }
1368
1369 Matrix
1370 children_property::do_get_children (bool return_hidden) const
1371 {
1372 Matrix retval (children_list.size (), 1);
1373 octave_idx_type k = 0;
1374
1375 graphics_object go = gh_manager::get_object (0);
1376
1377 root_figure::properties& props =
1378 dynamic_cast<root_figure::properties&> (go.get_properties ());
1379
1380 if (! props.is_showhiddenhandles ())
1381 {
1382 for (const_children_list_iterator p = children_list.begin ();
1383 p != children_list.end (); p++)
1384 {
1385 graphics_handle kid = *p;
1386
1387 if (gh_manager::is_handle_visible (kid))
1388 {
1389 if (! return_hidden)
1390 retval(k++) = *p;
1391 }
1392 else if (return_hidden)
1393 retval(k++) = *p;
1394 }
1395
1396 retval.resize (k, 1);
1397 }
1398 else
1399 {
1400 for (const_children_list_iterator p = children_list.begin ();
1401 p != children_list.end (); p++)
1402 retval(k++) = *p;
1403 }
1404
1405 return retval;
1406 }
1407
1408 void
1409 children_property::do_delete_children (bool clear)
1410 {
1411 for (children_list_iterator p = children_list.begin ();
1412 p != children_list.end (); p++)
1413 {
1414 graphics_object go = gh_manager::get_object (*p);
1415
1416 if (go.valid_object ())
1417 gh_manager::free (*p);
1418
1419 }
1420
1421 if (clear)
1422 children_list.clear ();
1423 }
1424
1425 bool
1426 callback_property::validate (const octave_value& v) const
1427 {
1428 // case 1: function handle
1429 // case 2: cell array with first element being a function handle
1430 // case 3: string corresponding to known function name
1431 // case 4: evaluatable string
1432 // case 5: empty matrix
1433
1434 if (v.is_function_handle ())
1435 return true;
1436 else if (v.is_string ())
1437 // complete validation will be done at execution-time
1438 return true;
1439 else if (v.is_cell () && v.length () > 0
1440 && (v.rows () == 1 || v.columns () == 1)
1441 && v.cell_value ()(0).is_function_handle ())
1442 return true;
1443 else if (v.is_empty ())
1444 return true;
1445
1446 return false;
1447 }
1448
1449 // If TRUE, we are executing any callback function, or the functions it
1450 // calls. Used to determine handle visibility inside callback
1451 // functions.
1452 static bool executing_callback = false;
1453
1454 void
1455 callback_property::execute (const octave_value& data) const
1456 {
1457 unwind_protect frame;
1458
1459 // We are executing the callback function associated with this
1460 // callback property. When set to true, we avoid recursive calls to
1461 // callback routines.
1462 frame.protect_var (executing);
1463
1464 // We are executing a callback function, so allow handles that have
1465 // their handlevisibility property set to "callback" to be visible.
1466 frame.protect_var (executing_callback);
1467
1468 if (! executing)
1469 {
1470 executing = true;
1471 executing_callback = true;
1472
1473 if (callback.is_defined () && ! callback.is_empty ())
1474 gh_manager::execute_callback (get_parent (), callback, data);
1475 }
1476 }
1477
1478 // Used to cache dummy graphics objects from which dynamic
1479 // properties can be cloned.
1480 static std::map<caseless_str, graphics_object> dprop_obj_map;
1481
1482 property
1483 property::create (const std::string& name, const graphics_handle& h,
1484 const caseless_str& type, const octave_value_list& args)
1485 {
1486 property retval;
1487
1488 if (type.compare ("string"))
1489 {
1490 std::string val = (args.length () > 0 ? args(0).string_value () : "");
1491
1492 if (! error_state)
1493 retval = property (new string_property (name, h, val));
1494 }
1495 else if (type.compare ("any"))
1496 {
1497 octave_value val =
1498 (args.length () > 0 ? args(0) : octave_value (Matrix ()));
1499
1500 retval = property (new any_property (name, h, val));
1501 }
1502 else if (type.compare ("radio"))
1503 {
1504 if (args.length () > 0)
1505 {
1506 std::string vals = args(0).string_value ();
1507
1508 if (! error_state)
1509 {
1510 retval = property (new radio_property (name, h, vals));
1511
1512 if (args.length () > 1)
1513 retval.set (args(1));
1514 }
1515 else
1516 error ("addproperty: invalid argument for radio property, expected a string value");
1517 }
1518 else
1519 error ("addproperty: missing possible values for radio property");
1520 }
1521 else if (type.compare ("double"))
1522 {
1523 double d = (args.length () > 0 ? args(0).double_value () : 0);
1524
1525 if (! error_state)
1526 retval = property (new double_property (name, h, d));
1527 }
1528 else if (type.compare ("handle"))
1529 {
1530 double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
1531
1532 if (! error_state)
1533 {
1534 graphics_handle gh (hh);
1535
1536 retval = property (new handle_property (name, h, gh));
1537 }
1538 }
1539 else if (type.compare ("boolean"))
1540 {
1541 retval = property (new bool_property (name, h, false));
1542
1543 if (args.length () > 0)
1544 retval.set (args(0));
1545 }
1546 else if (type.compare ("data"))
1547 {
1548 retval = property (new array_property (name, h, Matrix ()));
1549
1550 if (args.length () > 0)
1551 {
1552 retval.set (args(0));
1553
1554 // FIXME -- additional argument could define constraints,
1555 // but is this really useful?
1556 }
1557 }
1558 else if (type.compare ("color"))
1559 {
1560 color_values cv (0, 0, 0);
1561 radio_values rv;
1562
1563 if (args.length () > 1)
1564 rv = radio_values (args(1).string_value ());
1565
1566 if (! error_state)
1567 {
1568 retval = property (new color_property (name, h, cv, rv));
1569
1570 if (! error_state)
1571 {
1572 if (args.length () > 0 && ! args(0).is_empty ())
1573 retval.set (args(0));
1574 else
1575 retval.set (rv.default_value ());
1576 }
1577 }
1578 }
1579 else
1580 {
1581 caseless_str go_name, go_rest;
1582
1583 if (lookup_object_name (type, go_name, go_rest))
1584 {
1585 graphics_object go;
1586
1587 std::map<caseless_str, graphics_object>::const_iterator it =
1588 dprop_obj_map.find (go_name);
1589
1590 if (it == dprop_obj_map.end ())
1591 {
1592 base_graphics_object *bgo =
1593 make_graphics_object_from_type (go_name);
1594
1595 if (bgo)
1596 {
1597 go = graphics_object (bgo);
1598
1599 dprop_obj_map[go_name] = go;
1600 }
1601 }
1602 else
1603 go = it->second;
1604
1605 if (go.valid_object ())
1606 {
1607 property prop = go.get_properties ().get_property (go_rest);
1608
1609 if (! error_state)
1610 {
1611 retval = prop.clone ();
1612
1613 retval.set_parent (h);
1614 retval.set_name (name);
1615
1616 if (args.length () > 0)
1617 retval.set (args(0));
1618 }
1619 }
1620 else
1621 error ("addproperty: invalid object type (= %s)",
1622 go_name.c_str ());
1623 }
1624 else
1625 error ("addproperty: unsupported type for dynamic property (= %s)",
1626 type.c_str ());
1627 }
1628
1629 return retval;
1630 }
1631
1632 static void
1633 finalize_r (const graphics_handle& h)
1634 {
1635 graphics_object go = gh_manager::get_object (h);
1636
1637 if (go)
1638 {
1639 Matrix children = go.get_properties ().get_all_children ();
1640
1641 for (int k = 0; k < children.numel (); k++)
1642 finalize_r (children(k));
1643
1644 go.finalize ();
1645 }
1646 }
1647
1648 static void
1649 initialize_r (const graphics_handle& h)
1650 {
1651 graphics_object go = gh_manager::get_object (h);
1652
1653 if (go)
1654 {
1655 Matrix children = go.get_properties ().get_all_children ();
1656
1657 go.initialize ();
1658
1659 for (int k = 0; k < children.numel (); k++)
1660 initialize_r (children(k));
1661 }
1662 }
1663
1664 void
1665 figure::properties::set_toolkit (const graphics_toolkit& b)
1666 {
1667 if (toolkit)
1668 finalize_r (get___myhandle__ ());
1669
1670 toolkit = b;
1671 __graphics_toolkit__ = b.get_name ();
1672 __plot_stream__ = Matrix ();
1673
1674 if (toolkit)
1675 initialize_r (get___myhandle__ ());
1676
1677 mark_modified ();
1678 }
1679
1680 // ---------------------------------------------------------------------
1681
1682 void
1683 property_list::set (const caseless_str& name, const octave_value& val)
1684 {
1685 size_t offset = 0;
1686
1687 size_t len = name.length ();
1688
1689 if (len > 4)
1690 {
1691 caseless_str pfx = name.substr (0, 4);
1692
1693 if (pfx.compare ("axes") || pfx.compare ("line")
1694 || pfx.compare ("text"))
1695 offset = 4;
1696 else if (len > 5)
1697 {
1698 pfx = name.substr (0, 5);
1699
1700 if (pfx.compare ("image") || pfx.compare ("patch"))
1701 offset = 5;
1702 else if (len > 6)
1703 {
1704 pfx = name.substr (0, 6);
1705
1706 if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1707 offset = 6;
1708 else if (len > 7)
1709 {
1710 pfx = name.substr (0, 7);
1711
1712 if (pfx.compare ("surface") || pfx.compare ("hggroup")
1713 || pfx.compare ("uipanel"))
1714 offset = 7;
1715 else if (len > 9)
1716 {
1717 pfx = name.substr (0, 9);
1718
1719 if (pfx.compare ("uicontrol")
1720 || pfx.compare ("uitoolbar"))
1721 offset = 9;
1722 else if (len > 10)
1723 {
1724 pfx = name.substr (0, 10);
1725
1726 if (pfx.compare ("uipushtool"))
1727 offset = 10;
1728 else if (len > 12)
1729 {
1730 pfx = name.substr (0, 12);
1731
1732 if (pfx.compare ("uitoogletool"))
1733 offset = 12;
1734 else if (len > 13)
1735 {
1736 pfx = name.substr (0, 13);
1737
1738 if (pfx.compare ("uicontextmenu"))
1739 offset = 13;
1740 }
1741 }
1742 }
1743 }
1744 }
1745 }
1746 }
1747
1748 if (offset > 0)
1749 {
1750 // FIXME -- should we validate property names and values here?
1751
1752 std::string pname = name.substr (offset);
1753
1754 std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1755 std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
1756
1757 bool has_property = false;
1758 if (pfx == "axes")
1759 has_property = axes::properties::has_core_property (pname);
1760 else if (pfx == "line")
1761 has_property = line::properties::has_core_property (pname);
1762 else if (pfx == "text")
1763 has_property = text::properties::has_core_property (pname);
1764 else if (pfx == "image")
1765 has_property = image::properties::has_core_property (pname);
1766 else if (pfx == "patch")
1767 has_property = patch::properties::has_core_property (pname);
1768 else if (pfx == "figure")
1769 has_property = figure::properties::has_core_property (pname);
1770 else if (pfx == "surface")
1771 has_property = surface::properties::has_core_property (pname);
1772 else if (pfx == "hggroup")
1773 has_property = hggroup::properties::has_core_property (pname);
1774 else if (pfx == "uimenu")
1775 has_property = uimenu::properties::has_core_property (pname);
1776 else if (pfx == "uicontrol")
1777 has_property = uicontrol::properties::has_core_property (pname);
1778 else if (pfx == "uipanel")
1779 has_property = uipanel::properties::has_core_property (pname);
1780 else if (pfx == "uicontextmenu")
1781 has_property = uicontextmenu::properties::has_core_property (pname);
1782 else if (pfx == "uitoolbar")
1783 has_property = uitoolbar::properties::has_core_property (pname);
1784 else if (pfx == "uipushtool")
1785 has_property = uipushtool::properties::has_core_property (pname);
1786
1787 if (has_property)
1788 {
1789 bool remove = false;
1790 if (val.is_string ())
1791 {
1792 std::string tval = val.string_value ();
1793
1794 remove = (tval.compare ("remove") == 0);
1795 }
1796
1797 pval_map_type& pval_map = plist_map[pfx];
1798
1799 if (remove)
1800 {
1801 pval_map_iterator p = pval_map.find (pname);
1802
1803 if (p != pval_map.end ())
1804 pval_map.erase (p);
1805 }
1806 else
1807 pval_map[pname] = val;
1808 }
1809 else
1810 error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
1811 }
1812 }
1813
1814 if (! error_state && offset == 0)
1815 error ("invalid default property specification");
1816 }
1817
1818 octave_value
1819 property_list::lookup (const caseless_str& name) const
1820 {
1821 octave_value retval;
1822
1823 size_t offset = 0;
1824
1825 size_t len = name.length ();
1826
1827 if (len > 4)
1828 {
1829 caseless_str pfx = name.substr (0, 4);
1830
1831 if (pfx.compare ("axes") || pfx.compare ("line")
1832 || pfx.compare ("text"))
1833 offset = 4;
1834 else if (len > 5)
1835 {
1836 pfx = name.substr (0, 5);
1837
1838 if (pfx.compare ("image") || pfx.compare ("patch"))
1839 offset = 5;
1840 else if (len > 6)
1841 {
1842 pfx = name.substr (0, 6);
1843
1844 if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1845 offset = 6;
1846 else if (len > 7)
1847 {
1848 pfx = name.substr (0, 7);
1849
1850 if (pfx.compare ("surface") || pfx.compare ("hggroup")
1851 || pfx.compare ("uipanel"))
1852 offset = 7;
1853 else if (len > 9)
1854 {
1855 pfx = name.substr (0, 9);
1856
1857 if (pfx.compare ("uicontrol")
1858 || pfx.compare ("uitoolbar"))
1859 offset = 9;
1860 else if (len > 10)
1861 {
1862 pfx = name.substr (0, 10);
1863
1864 if (pfx.compare ("uipushtool"))
1865 offset = 10;
1866 else if (len > 12)
1867 {
1868 pfx = name.substr (0, 12);
1869
1870 if (pfx.compare ("uitoggletool"))
1871 offset = 12;
1872 else if (len > 13)
1873 {
1874 pfx = name.substr (0, 13);
1875
1876 if (pfx.compare ("uicontextmenu"))
1877 offset = 13;
1878 }
1879 }
1880 }
1881 }
1882 }
1883 }
1884 }
1885
1886 if (offset > 0)
1887 {
1888 std::string pname = name.substr (offset);
1889
1890 std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1891 std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
1892
1893 plist_map_const_iterator p = find (pfx);
1894
1895 if (p != end ())
1896 {
1897 const pval_map_type& pval_map = p->second;
1898
1899 pval_map_const_iterator q = pval_map.find (pname);
1900
1901 if (q != pval_map.end ())
1902 retval = q->second;
1903 }
1904 }
1905 }
1906
1907 return retval;
1908 }
1909
1910 octave_scalar_map
1911 property_list::as_struct (const std::string& prefix_arg) const
1912 {
1913 octave_scalar_map m;
1914
1915 for (plist_map_const_iterator p = begin (); p != end (); p++)
1916 {
1917 std::string prefix = prefix_arg + p->first;
1918
1919 const pval_map_type pval_map = p->second;
1920
1921 for (pval_map_const_iterator q = pval_map.begin ();
1922 q != pval_map.end ();
1923 q++)
1924 m.assign (prefix + q->first, q->second);
1925 }
1926
1927 return m;
1928 }
1929
1930 graphics_handle::graphics_handle (const octave_value& a)
1931 : val (octave_NaN)
1932 {
1933 if (a.is_empty ())
1934 /* do nothing */;
1935 else
1936 {
1937 double tval = a.double_value ();
1938
1939 if (! error_state)
1940 val = tval;
1941 else
1942 error ("invalid graphics handle");
1943 }
1944 }
1945
1946 // Set properties given as a cs-list of name, value pairs.
1947
1948 void
1949 graphics_object::set (const octave_value_list& args)
1950 {
1951 int nargin = args.length ();
1952
1953 if (nargin == 0)
1954 error ("graphics_object::set: Nothing to set");
1955 else if (nargin % 2 == 0)
1956 {
1957 for (int i = 0; i < nargin; i += 2)
1958 {
1959 caseless_str name = args(i).string_value ();
1960
1961 if (! error_state)
1962 {
1963 octave_value val = args(i+1);
1964
1965 set_value_or_default (name, val);
1966
1967 if (error_state)
1968 break;
1969 }
1970 else
1971 error ("set: expecting argument %d to be a property name", i);
1972 }
1973 }
1974 else
1975 error ("set: invalid number of arguments");
1976 }
1977
1978 /*
1979 ## test set with name, value pairs
1980 %!test
1981 %! set (gcf, "visible", "off");
1982 %! h = plot (1:10, 10:-1:1);
1983 %! set (h, "linewidth", 10, "marker", "x");
1984 %! assert (get (h, "linewidth"), 10);
1985 %! assert (get (h, "marker"), "x");
1986 */
1987
1988 // Set properties given in two cell arrays containing names and values.
1989 void
1990 graphics_object::set (const Array<std::string>& names,
1991 const Cell& values, octave_idx_type row)
1992 {
1993 if (names.numel () != values.columns ())
1994 {
1995 error ("set: number of names must match number of value columns (%d != %d)",
1996 names.numel (), values.columns ());
1997 }
1998
1999 octave_idx_type k = names.columns ();
2000
2001 for (octave_idx_type column = 0; column < k; column++)
2002 {
2003 caseless_str name = names(column);
2004 octave_value val = values(row, column);
2005
2006 set_value_or_default (name, val);
2007
2008 if (error_state)
2009 break;
2010 }
2011 }
2012
2013 /*
2014 ## test set with cell array arguments
2015 %!test
2016 %! set (gcf, "visible", "off");
2017 %! h = plot (1:10, 10:-1:1);
2018 %! set (h, {"linewidth", "marker"}, {10, "x"});
2019 %! assert (get (h, "linewidth"), 10);
2020 %! assert (get (h, "marker"), "x");
2021
2022 ## test set with multiple handles and cell array arguments
2023 %!test
2024 %! set (gcf, "visible", "off");
2025 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2026 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2027 %! assert (get (h, "linewidth"), {10; 5});
2028 %! assert (get (h, "marker"), {"x"; "o"});
2029 %! set (h, {"linewidth", "marker"}, {10, "x"});
2030 %! assert (get (h, "linewidth"), {10; 10});
2031 %! assert (get (h, "marker"), {"x"; "x"});
2032
2033 %!error <set: number of graphics handles must match number of value rows>
2034 %! set (gcf, "visible", "off");
2035 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2036 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2037
2038 %!error <set: number of names must match number of value columns>
2039 %! set (gcf, "visible", "off");
2040 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2041 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2042 */
2043
2044 // Set properties given in a struct array
2045 void
2046 graphics_object::set (const octave_map& m)
2047 {
2048 for (octave_idx_type p = 0; p < m.nfields (); p++)
2049 {
2050 caseless_str name = m.keys ()[p];
2051
2052 octave_value val = octave_value (m.contents (name).elem (m.numel () - 1));
2053
2054 set_value_or_default (name, val);
2055
2056 if (error_state)
2057 break;
2058 }
2059 }
2060
2061 /*
2062 ## test set ticklabels for compatibility
2063 %!test
2064 %! set (gcf (), "visible", "off");
2065 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2066 %! xticklabel = get (gca (), "xticklabel");
2067 %! assert (class (xticklabel), "char");
2068 %! assert (size (xticklabel), [6, 3]);
2069 %!test
2070 %! set (gcf (), "visible", "off");
2071 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2072 %! xticklabel = get (gca (), "xticklabel");
2073 %! assert (class (xticklabel), "char");
2074 %! assert (size (xticklabel), [6, 3]);
2075 %!test
2076 %! set (gcf (), "visible", "off");
2077 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2078 %! xticklabel = get (gca (), "xticklabel");
2079 %! assert (class (xticklabel), "char");
2080 %! assert (size (xticklabel), [6, 3]);
2081 %!test
2082 %! set (gcf (), "visible", "off");
2083 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2084 %! xticklabel = get (gca (), "xticklabel");
2085 %! assert (class (xticklabel), "cell");
2086 %! assert (size (xticklabel), [6, 1]);
2087 */
2088
2089 /*
2090 ## test set with struct arguments
2091 %!test
2092 %! set (gcf, "visible", "off");
2093 %! h = plot (1:10, 10:-1:1);
2094 %! set (h, struct ("linewidth", 10, "marker", "x"));
2095 %! assert (get (h, "linewidth"), 10);
2096 %! assert (get (h, "marker"), "x");
2097 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2098 %! set (h, struct ("linewidth", {5, 10}));
2099 %! assert (get (h, "linewidth"), {10; 10});
2100 ## test ordering
2101 %!test
2102 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2103 %! figure (1, "visible", "off")
2104 %! clf ();
2105 %! h = line ();
2106 %! set (h, "userdata", {});
2107 %! addlistener (h, "color", {markchanged, "color"});
2108 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2109 %! # "linewidth" first
2110 %! props.linewidth = 2;
2111 %! props.color = "r";
2112 %! set (h, props);
2113 %! assert (get (h, "userdata"), fieldnames (props));
2114 %! clear props
2115 %! clf ();
2116 %! h = line ();
2117 %! set (h, "userdata", {});
2118 %! addlistener (h, "color", {markchanged, "color"});
2119 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2120 %! # "color" first
2121 %! props.color = "r";
2122 %! props.linewidth = 2;
2123 %! set (h, props);
2124 %! assert (get (h, "userdata"), fieldnames (props));
2125 %! close (1);
2126 */
2127
2128 // Set a property to a value or to its (factory) default value.
2129
2130 void
2131 graphics_object::set_value_or_default (const caseless_str& name,
2132 const octave_value& val)
2133 {
2134 if (val.is_string ())
2135 {
2136 std::string tval = val.string_value ();
2137
2138 octave_value default_val;
2139
2140 if (tval.compare ("default") == 0)
2141 {
2142 default_val = get_default (name);
2143
2144 if (error_state)
2145 return;
2146
2147 rep->set (name, default_val);
2148 }
2149 else if (tval.compare ("factory") == 0)
2150 {
2151 default_val = get_factory_default (name);
2152
2153 if (error_state)
2154 return;
2155
2156 rep->set (name, default_val);
2157 }
2158 else
2159 {
2160 // Matlab specifically uses "\default" to escape string setting
2161 if (tval.compare ("\\default") == 0)
2162 rep->set (name, "default");
2163 else if (tval.compare ("\\factory") == 0)
2164 rep->set (name, "factory");
2165 else
2166 rep->set (name, val);
2167 }
2168 }
2169 else
2170 rep->set (name, val);
2171 }
2172
2173 /*
2174 ## test setting of default values
2175 %!test
2176 %! set (gcf, "visible", "off");
2177 %! h = plot (1:10, 10:-1:1);
2178 %! set (0, "defaultlinelinewidth", 20);
2179 %! set (h, "linewidth", "default");
2180 %! assert (get (h, "linewidth"), 20);
2181 %! set (h, "linewidth", "factory");
2182 %! assert (get (h, "linewidth"), 0.5);
2183 */
2184
2185 static double
2186 make_handle_fraction (void)
2187 {
2188 static double maxrand = RAND_MAX + 2.0;
2189
2190 return (rand () + 1.0) / maxrand;
2191 }
2192
2193 graphics_handle
2194 gh_manager::do_get_handle (bool integer_figure_handle)
2195 {
2196 graphics_handle retval;
2197
2198 if (integer_figure_handle)
2199 {
2200 // Figure handles are positive integers corresponding to the
2201 // figure number.
2202
2203 // We always want the lowest unused figure number.
2204
2205 retval = 1;
2206
2207 while (handle_map.find (retval) != handle_map.end ())
2208 retval++;
2209 }
2210 else
2211 {
2212 // Other graphics handles are negative integers plus some random
2213 // fractional part. To avoid running out of integers, we
2214 // recycle the integer part but tack on a new random part each
2215 // time.
2216
2217 free_list_iterator p = handle_free_list.begin ();
2218
2219 if (p != handle_free_list.end ())
2220 {
2221 retval = *p;
2222 handle_free_list.erase (p);
2223 }
2224 else
2225 {
2226 retval = graphics_handle (next_handle);
2227
2228 next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2229 }
2230 }
2231
2232 return retval;
2233 }
2234
2235 void
2236 gh_manager::do_free (const graphics_handle& h)
2237 {
2238 if (h.ok ())
2239 {
2240 if (h.value () != 0)
2241 {
2242 iterator p = handle_map.find (h);
2243
2244 if (p != handle_map.end ())
2245 {
2246 base_properties& bp = p->second.get_properties ();
2247
2248 bp.set_beingdeleted (true);
2249
2250 bp.delete_children ();
2251
2252 octave_value val = bp.get_deletefcn ();
2253
2254 bp.execute_deletefcn ();
2255
2256 // Notify graphics toolkit.
2257 p->second.finalize ();
2258
2259 // Note: this will be valid only for first explicitly
2260 // deleted object. All its children will then have an
2261 // unknown graphics toolkit.
2262
2263 // Graphics handles for non-figure objects are negative
2264 // integers plus some random fractional part. To avoid
2265 // running out of integers, we recycle the integer part
2266 // but tack on a new random part each time.
2267
2268 handle_map.erase (p);
2269
2270 if (h.value () < 0)
2271 handle_free_list.insert (std::ceil (h.value ()) - make_handle_fraction ());
2272 }
2273 else
2274 error ("graphics_handle::free: invalid object %g", h.value ());
2275 }
2276 else
2277 error ("graphics_handle::free: can't delete root figure");
2278 }
2279 }
2280
2281 void
2282 gh_manager::do_renumber_figure (const graphics_handle& old_gh,
2283 const graphics_handle& new_gh)
2284 {
2285 iterator p = handle_map.find (old_gh);
2286
2287 if (p != handle_map.end ())
2288 {
2289 graphics_object go = p->second;
2290
2291 handle_map.erase (p);
2292
2293 handle_map[new_gh] = go;
2294
2295 if (old_gh.value () < 0)
2296 handle_free_list.insert (std::ceil (old_gh.value ())
2297 - make_handle_fraction ());
2298 }
2299 else
2300 error ("graphics_handle::free: invalid object %g", old_gh.value ());
2301
2302 for (figure_list_iterator q = figure_list.begin ();
2303 q != figure_list.end (); q++)
2304 {
2305 if (*q == old_gh)
2306 {
2307 *q = new_gh;
2308 break;
2309 }
2310 }
2311 }
2312
2313 gh_manager *gh_manager::instance = 0;
2314
2315 static void
2316 xset (const graphics_handle& h, const caseless_str& name,
2317 const octave_value& val)
2318 {
2319 graphics_object obj = gh_manager::get_object (h);
2320 obj.set (name, val);
2321 }
2322
2323 static void
2324 xset (const graphics_handle& h, const octave_value_list& args)
2325 {
2326 if (args.length () > 0)
2327 {
2328 graphics_object obj = gh_manager::get_object (h);
2329 obj.set (args);
2330 }
2331 }
2332
2333 static octave_value
2334 xget (const graphics_handle& h, const caseless_str& name)
2335 {
2336 graphics_object obj = gh_manager::get_object (h);
2337 return obj.get (name);
2338 }
2339
2340 static graphics_handle
2341 reparent (const octave_value& ov, const std::string& who,
2342 const std::string& property, const graphics_handle& new_parent,
2343 bool adopt = true)
2344 {
2345 graphics_handle h = octave_NaN;
2346
2347 double val = ov.double_value ();
2348
2349 if (! error_state)
2350 {
2351 h = gh_manager::lookup (val);
2352
2353 if (h.ok ())
2354 {
2355 graphics_object obj = gh_manager::get_object (h);
2356
2357 graphics_handle parent_h = obj.get_parent ();
2358
2359 graphics_object parent_obj = gh_manager::get_object (parent_h);
2360
2361 parent_obj.remove_child (h);
2362
2363 if (adopt)
2364 obj.set ("parent", new_parent.value ());
2365 else
2366 obj.reparent (new_parent);
2367 }
2368 else
2369 error ("%s: invalid graphics handle (= %g) for %s",
2370 who.c_str (), val, property.c_str ());
2371 }
2372 else
2373 error ("%s: expecting %s to be a graphics handle",
2374 who.c_str (), property.c_str ());
2375
2376 return h;
2377 }
2378
2379 // This function is NOT equivalent to the scripting language function gcf.
2380 graphics_handle
2381 gcf (void)
2382 {
2383 octave_value val = xget (0, "currentfigure");
2384
2385 return val.is_empty () ? octave_NaN : val.double_value ();
2386 }
2387
2388 // This function is NOT equivalent to the scripting language function gca.
2389 graphics_handle
2390 gca (void)
2391 {
2392 octave_value val = xget (gcf (), "currentaxes");
2393
2394 return val.is_empty () ? octave_NaN : val.double_value ();
2395 }
2396
2397 static void
2398 delete_graphics_object (const graphics_handle& h)
2399 {
2400 if (h.ok ())
2401 {
2402 graphics_object obj = gh_manager::get_object (h);
2403
2404 // Don't do recursive deleting, due to callbacks
2405 if (! obj.get_properties ().is_beingdeleted ())
2406 {
2407 graphics_handle parent_h = obj.get_parent ();
2408
2409 graphics_object parent_obj =
2410 gh_manager::get_object (parent_h);
2411
2412 // NOTE: free the handle before removing it from its
2413 // parent's children, such that the object's
2414 // state is correct when the deletefcn callback
2415 // is executed
2416
2417 gh_manager::free (h);
2418
2419 // A callback function might have already deleted
2420 // the parent
2421 if (parent_obj.valid_object ())
2422 parent_obj.remove_child (h);
2423
2424 Vdrawnow_requested = true;
2425 }
2426 }
2427 }
2428
2429 static void
2430 delete_graphics_object (double val)
2431 {
2432 delete_graphics_object (gh_manager::lookup (val));
2433 }
2434
2435 static void
2436 delete_graphics_objects (const NDArray vals)
2437 {
2438 for (octave_idx_type i = 0; i < vals.numel (); i++)
2439 delete_graphics_object (vals.elem (i));
2440 }
2441
2442 static void
2443 close_figure (const graphics_handle& handle)
2444 {
2445 octave_value closerequestfcn = xget (handle, "closerequestfcn");
2446
2447 OCTAVE_SAFE_CALL (gh_manager::execute_callback, (handle, closerequestfcn));
2448 }
2449
2450 static void
2451 force_close_figure (const graphics_handle& handle)
2452 {
2453 // Remove the deletefcn and closerequestfcn callbacks and delete the
2454 // object directly.
2455
2456 xset (handle, "deletefcn", Matrix ());
2457 xset (handle, "closerequestfcn", Matrix ());
2458
2459 delete_graphics_object (handle);
2460 }
2461
2462 void
2463 gh_manager::do_close_all_figures (void)
2464 {
2465 // FIXME -- should we process or discard pending events?
2466
2467 event_queue.clear ();
2468
2469 // Don't use figure_list_iterator because we'll be removing elements
2470 // from the list elsewhere.
2471
2472 Matrix hlist = do_figure_handle_list (true);
2473
2474 for (octave_idx_type i = 0; i < hlist.numel (); i++)
2475 {
2476 graphics_handle h = gh_manager::lookup (hlist(i));
2477
2478 if (h.ok ())
2479 close_figure (h);
2480 }
2481
2482 // They should all be closed now. If not, force them to close.
2483
2484 hlist = do_figure_handle_list (true);
2485
2486 for (octave_idx_type i = 0; i < hlist.numel (); i++)
2487 {
2488 graphics_handle h = gh_manager::lookup (hlist(i));
2489
2490 if (h.ok ())
2491 force_close_figure (h);
2492 }
2493
2494 // None left now, right?
2495
2496 hlist = do_figure_handle_list (true);
2497
2498 assert (hlist.numel () == 0);
2499
2500 // Clear all callback objects from our list.
2501
2502 callback_objects.clear ();
2503 }
2504
2505 static void
2506 adopt (const graphics_handle& p, const graphics_handle& h)
2507 {
2508 graphics_object parent_obj = gh_manager::get_object (p);
2509 parent_obj.adopt (h);
2510 }
2511
2512 static bool
2513 is_handle (const graphics_handle& h)
2514 {
2515 return h.ok ();
2516 }
2517
2518 static bool
2519 is_handle (double val)
2520 {
2521 graphics_handle h = gh_manager::lookup (val);
2522
2523 return h.ok ();
2524 }
2525
2526 static octave_value
2527 is_handle (const octave_value& val)
2528 {
2529 octave_value retval = false;
2530
2531 if (val.is_real_scalar () && is_handle (val.double_value ()))
2532 retval = true;
2533 else if (val.is_numeric_type () && val.is_real_type ())
2534 {
2535 const NDArray handles = val.array_value ();
2536
2537 if (! error_state)
2538 {
2539 boolNDArray result (handles.dims ());
2540
2541 for (octave_idx_type i = 0; i < handles.numel (); i++)
2542 result.xelem (i) = is_handle (handles (i));
2543
2544 retval = result;
2545 }
2546 }
2547
2548 return retval;
2549 }
2550
2551 static bool
2552 is_figure (double val)
2553 {
2554 graphics_object obj = gh_manager::get_object (val);
2555
2556 return obj && obj.isa ("figure");
2557 }
2558
2559 static void
2560 xcreatefcn (const graphics_handle& h)
2561 {
2562 graphics_object obj = gh_manager::get_object (h);
2563 obj.get_properties ().execute_createfcn ();
2564 }
2565
2566 static void
2567 xinitialize (const graphics_handle& h)
2568 {
2569 graphics_object go = gh_manager::get_object (h);
2570
2571 if (go)
2572 go.initialize ();
2573 }
2574
2575 // ---------------------------------------------------------------------
2576
2577 void
2578 base_graphics_toolkit::update (const graphics_handle& h, int id)
2579 {
2580 graphics_object go = gh_manager::get_object (h);
2581
2582 update (go, id);
2583 }
2584
2585 bool
2586 base_graphics_toolkit::initialize (const graphics_handle& h)
2587 {
2588 graphics_object go = gh_manager::get_object (h);
2589
2590 return initialize (go);
2591 }
2592
2593 void
2594 base_graphics_toolkit::finalize (const graphics_handle& h)
2595 {
2596 graphics_object go = gh_manager::get_object (h);
2597
2598 finalize (go);
2599 }
2600
2601 // ---------------------------------------------------------------------
2602
2603 void
2604 base_properties::set_from_list (base_graphics_object& obj,
2605 property_list& defaults)
2606 {
2607 std::string go_name = graphics_object_name ();
2608
2609 property_list::plist_map_const_iterator p = defaults.find (go_name);
2610
2611 if (p != defaults.end ())
2612 {
2613 const property_list::pval_map_type pval_map = p->second;
2614
2615 for (property_list::pval_map_const_iterator q = pval_map.begin ();
2616 q != pval_map.end ();
2617 q++)
2618 {
2619 std::string pname = q->first;
2620
2621 obj.set (pname, q->second);
2622
2623 if (error_state)
2624 {
2625 error ("error setting default property %s", pname.c_str ());
2626 break;
2627 }
2628 }
2629 }
2630 }
2631
2632 octave_value
2633 base_properties::get_dynamic (const caseless_str& name) const
2634 {
2635 octave_value retval;
2636
2637 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
2638
2639 if (it != all_props.end ())
2640 retval = it->second.get ();
2641 else
2642 error ("get: unknown property \"%s\"", name.c_str ());
2643
2644 return retval;
2645 }
2646
2647 octave_value
2648 base_properties::get_dynamic (bool all) const
2649 {
2650 octave_scalar_map m;
2651
2652 for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.begin ();
2653 it != all_props.end (); ++it)
2654 if (all || ! it->second.is_hidden ())
2655 m.assign (it->second.get_name (), it->second.get ());
2656
2657 return m;
2658 }
2659
2660 std::set<std::string>
2661 base_properties::dynamic_property_names (void) const
2662 {
2663 return dynamic_properties;
2664 }
2665
2666 bool
2667 base_properties::has_dynamic_property (const std::string& pname)
2668 {
2669 const std::set<std::string>& dynprops = dynamic_property_names ();
2670
2671 if (dynprops.find (pname) != dynprops.end ())
2672 return true;
2673 else
2674 return all_props.find (pname) != all_props.end ();
2675 }
2676
2677 void
2678 base_properties::set_dynamic (const caseless_str& pname,
2679 const octave_value& val)
2680 {
2681 std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
2682
2683 if (it != all_props.end ())
2684 it->second.set (val);
2685 else
2686 error ("set: unknown property \"%s\"", pname.c_str ());
2687
2688 if (! error_state)
2689 {
2690 dynamic_properties.insert (pname);
2691
2692 mark_modified ();
2693 }
2694 }
2695
2696 property
2697 base_properties::get_property_dynamic (const caseless_str& name)
2698 {
2699 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
2700
2701 if (it == all_props.end ())
2702 {
2703 error ("get_property: unknown property \"%s\"", name.c_str ());
2704 return property ();
2705 }
2706 else
2707 return it->second;
2708 }
2709
2710 void
2711 base_properties::set_parent (const octave_value& val)
2712 {
2713 double tmp = val.double_value ();
2714
2715 graphics_handle new_parent = octave_NaN;
2716
2717 if (! error_state)
2718 {
2719 new_parent = gh_manager::lookup (tmp);
2720
2721 if (new_parent.ok ())
2722 {
2723 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2724
2725 parent_obj.remove_child (__myhandle__);
2726
2727 parent = new_parent.as_octave_value ();
2728
2729 ::adopt (parent.handle_value (), __myhandle__);
2730 }
2731 else
2732 error ("set: invalid graphics handle (= %g) for parent", tmp);
2733 }
2734 else
2735 error ("set: expecting parent to be a graphics handle");
2736 }
2737
2738 void
2739 base_properties::mark_modified (void)
2740 {
2741 __modified__ = "on";
2742 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2743 if (parent_obj)
2744 parent_obj.mark_modified ();
2745 }
2746
2747 void
2748 base_properties::override_defaults (base_graphics_object& obj)
2749 {
2750 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2751
2752 if (parent_obj)
2753 parent_obj.override_defaults (obj);
2754 }
2755
2756 void
2757 base_properties::update_axis_limits (const std::string& axis_type) const
2758 {
2759 graphics_object obj = gh_manager::get_object (__myhandle__);
2760
2761 if (obj)
2762 obj.update_axis_limits (axis_type);
2763 }
2764
2765 void
2766 base_properties::update_axis_limits (const std::string& axis_type,
2767 const graphics_handle& h) const
2768 {
2769 graphics_object obj = gh_manager::get_object (__myhandle__);
2770
2771 if (obj)
2772 obj.update_axis_limits (axis_type, h);
2773 }
2774
2775 bool
2776 base_properties::is_handle_visible (void) const
2777 {
2778 return (handlevisibility.is ("on")
2779 || (executing_callback && ! handlevisibility.is ("off")));
2780 }
2781
2782 graphics_toolkit
2783 base_properties::get_toolkit (void) const
2784 {
2785 graphics_object go = gh_manager::get_object (get_parent ());
2786
2787 if (go)
2788 return go.get_toolkit ();
2789 else
2790 return graphics_toolkit ();
2791 }
2792
2793 void
2794 base_properties::update_boundingbox (void)
2795 {
2796 Matrix kids = get_children ();
2797
2798 for (int i = 0; i < kids.numel (); i++)
2799 {
2800 graphics_object go = gh_manager::get_object (kids(i));
2801
2802 if (go.valid_object ())
2803 go.get_properties ().update_boundingbox ();
2804 }
2805 }
2806
2807 void
2808 base_properties::update_autopos (const std::string& elem_type)
2809 {
2810 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2811
2812 if (parent_obj.valid_object ())
2813 parent_obj.get_properties ().update_autopos (elem_type);
2814 }
2815
2816 void
2817 base_properties::add_listener (const caseless_str& nm, const octave_value& v,
2818 listener_mode mode)
2819 {
2820 property p = get_property (nm);
2821
2822 if (! error_state && p.ok ())
2823 p.add_listener (v, mode);
2824 }
2825
2826 void
2827 base_properties::delete_listener (const caseless_str& nm,
2828 const octave_value& v, listener_mode mode)
2829 {
2830 property p = get_property (nm);
2831
2832 if (! error_state && p.ok ())
2833 p.delete_listener (v, mode);
2834 }
2835
2836 // ---------------------------------------------------------------------
2837
2838 void
2839 base_graphics_object::update_axis_limits (const std::string& axis_type)
2840 {
2841 if (valid_object ())
2842 {
2843 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2844
2845 if (parent_obj)
2846 parent_obj.update_axis_limits (axis_type);
2847 }
2848 else
2849 error ("base_graphics_object::update_axis_limits: invalid graphics object");
2850 }
2851
2852 void
2853 base_graphics_object::update_axis_limits (const std::string& axis_type,
2854 const graphics_handle& h)
2855 {
2856 if (valid_object ())
2857 {
2858 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2859
2860 if (parent_obj)
2861 parent_obj.update_axis_limits (axis_type, h);
2862 }
2863 else
2864 error ("base_graphics_object::update_axis_limits: invalid graphics object");
2865 }
2866
2867 void
2868 base_graphics_object::remove_all_listeners (void)
2869 {
2870 octave_map m = get (true).map_value ();
2871
2872 for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2873 {
2874 // FIXME -- there has to be a better way. I think we want to
2875 // ask whether it is OK to delete the listener for the given
2876 // property. How can we know in advance that it will be OK?
2877
2878 unwind_protect frame;
2879
2880 frame.protect_var (error_state);
2881 frame.protect_var (discard_error_messages);
2882 frame.protect_var (Vdebug_on_error);
2883 frame.protect_var (Vdebug_on_warning);
2884
2885 discard_error_messages = true;
2886 Vdebug_on_error = false;
2887 Vdebug_on_warning = false;
2888
2889 property p = get_properties ().get_property (pa->first);
2890
2891 if (! error_state && p.ok ())
2892 p.delete_listener ();
2893 }
2894 }
2895
2896 std::string
2897 base_graphics_object::values_as_string (void)
2898 {
2899 std::string retval;
2900
2901 if (valid_object ())
2902 {
2903 octave_map m = get ().map_value ();
2904
2905 for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2906 {
2907 if (pa->first != "children")
2908 {
2909 property p = get_properties ().get_property (pa->first);
2910
2911 if (p.ok () && ! p.is_hidden ())
2912 {
2913 retval += "\n\t" + std::string (pa->first) + ": ";
2914 if (p.is_radio ())
2915 retval += p.values_as_string ();
2916 }
2917 }
2918 }
2919 if (retval != "")
2920 retval += "\n";
2921 }
2922 else
2923 error ("base_graphics_object::values_as_string: invalid graphics object");
2924
2925 return retval;
2926 }
2927
2928 octave_scalar_map
2929 base_graphics_object::values_as_struct (void)
2930 {
2931 octave_scalar_map retval;
2932
2933 if (valid_object ())
2934 {
2935 octave_scalar_map m = get ().scalar_map_value ();
2936
2937 for (octave_scalar_map::const_iterator pa = m.begin ();
2938 pa != m.end (); pa++)
2939 {
2940 if (pa->first != "children")
2941 {
2942 property p = get_properties ().get_property (pa->first);
2943
2944 if (p.ok () && ! p.is_hidden ())
2945 {
2946 if (p.is_radio ())
2947 retval.assign (p.get_name (), p.values_as_cell ());
2948 else
2949 retval.assign (p.get_name (), Cell ());
2950 }
2951 }
2952 }
2953 }
2954 else
2955 error ("base_graphics_object::values_as_struct: invalid graphics object");
2956
2957 return retval;
2958 }
2959
2960 graphics_object
2961 graphics_object::get_ancestor (const std::string& obj_type) const
2962 {
2963 if (valid_object ())
2964 {
2965 if (isa (obj_type))
2966 return *this;
2967 else
2968 return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
2969 }
2970 else
2971 return graphics_object ();
2972 }
2973
2974 // ---------------------------------------------------------------------
2975
2976 #include "graphics-props.cc"
2977
2978 // ---------------------------------------------------------------------
2979
2980 void
2981 root_figure::properties::set_currentfigure (const octave_value& v)
2982 {
2983 graphics_handle val (v);
2984
2985 if (error_state)
2986 return;
2987
2988 if (xisnan (val.value ()) || is_handle (val))
2989 {
2990 currentfigure = val;
2991
2992 if (val.ok ())
2993 gh_manager::push_figure (val);
2994 }
2995 else
2996 gripe_set_invalid ("currentfigure");
2997 }
2998
2999 void
3000 root_figure::properties::set_callbackobject (const octave_value& v)
3001 {
3002 graphics_handle val (v);
3003
3004 if (error_state)
3005 return;
3006
3007 if (xisnan (val.value ()))
3008 {
3009 if (! cbo_stack.empty ())
3010 {
3011 val = cbo_stack.front ();
3012
3013 cbo_stack.pop_front ();
3014 }
3015
3016 callbackobject = val;
3017 }
3018 else if (is_handle (val))
3019 {
3020 if (get_callbackobject ().ok ())
3021 cbo_stack.push_front (get_callbackobject ());
3022
3023 callbackobject = val;
3024 }
3025 else
3026 gripe_set_invalid ("callbackobject");
3027 }
3028
3029 void
3030 figure::properties::set_integerhandle (const octave_value& val)
3031 {
3032 if (! error_state)
3033 {
3034 if (integerhandle.set (val, true))
3035 {
3036 bool int_fig_handle = integerhandle.is_on ();
3037
3038 graphics_object this_go = gh_manager::get_object (__myhandle__);
3039
3040 graphics_handle old_myhandle = __myhandle__;
3041
3042 __myhandle__ = gh_manager::get_handle (int_fig_handle);
3043
3044 gh_manager::renumber_figure (old_myhandle, __myhandle__);
3045
3046 graphics_object parent_go = gh_manager::get_object (get_parent ());
3047
3048 base_properties& props = parent_go.get_properties ();
3049
3050 props.renumber_child (old_myhandle, __myhandle__);
3051
3052 Matrix kids = get_children ();
3053
3054 for (octave_idx_type i = 0; i < kids.numel (); i++)
3055 {
3056 graphics_object kid = gh_manager::get_object (kids(i));
3057
3058 kid.get_properties ().renumber_parent (__myhandle__);
3059 }
3060
3061 graphics_handle cf = gh_manager::current_figure ();
3062
3063 if (__myhandle__ == cf)
3064 xset (0, "currentfigure", __myhandle__.value ());
3065
3066 this_go.update (integerhandle.get_id ());
3067
3068 mark_modified ();
3069 }
3070 }
3071 }
3072
3073 // FIXME This should update monitorpositions and pointerlocation, but
3074 // as these properties are yet used, and so it doesn't matter that they
3075 // aren't set yet.
3076 void
3077 root_figure::properties::update_units (void)
3078 {
3079 caseless_str xunits = get_units ();
3080
3081 Matrix ss = default_screensize ();
3082
3083 double dpi = get_screenpixelsperinch ();
3084
3085 if (xunits.compare ("inches"))
3086 {
3087 ss(0) = 0;
3088 ss(1) = 0;
3089 ss(2) /= dpi;
3090 ss(3) /= dpi;
3091 }
3092 else if (xunits.compare ("centimeters"))
3093 {
3094 ss(0) = 0;
3095 ss(1) = 0;
3096 ss(2) *= 2.54 / dpi;
3097 ss(3) *= 2.54 / dpi;
3098 }
3099 else if (xunits.compare ("normalized"))
3100 {
3101 ss = Matrix (1, 4, 1.0);
3102 ss(0) = 0;
3103 ss(1) = 0;
3104 }
3105 else if (xunits.compare ("points"))
3106 {
3107 ss(0) = 0;
3108 ss(1) = 0;
3109 ss(2) *= 72 / dpi;
3110 ss(3) *= 72 / dpi;
3111 }
3112
3113 set_screensize (ss);
3114 }
3115
3116 Matrix
3117 root_figure::properties::get_boundingbox (bool, const Matrix&) const
3118 {
3119 Matrix screen_size = screen_size_pixels ();
3120 Matrix pos = Matrix (1, 4, 0);
3121 pos(2) = screen_size(0);
3122 pos(3) = screen_size(1);
3123 return pos;
3124 }
3125
3126 /*
3127 %!test
3128 %! set (0, "units", "pixels");
3129 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3130 %! dpi = get (0, "screenpixelsperinch");
3131 %! set (0, "units", "inches");
3132 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3133 %! set (0, "units", "centimeters");
3134 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3135 %! set (0, "units", "points");
3136 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3137 %! set (0, "units", "normalized");
3138 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3139 %! set (0, "units", "pixels");
3140 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3141 */
3142
3143 void
3144 root_figure::properties::remove_child (const graphics_handle& gh)
3145 {
3146 gh_manager::pop_figure (gh);
3147
3148 graphics_handle cf = gh_manager::current_figure ();
3149
3150 xset (0, "currentfigure", cf.value ());
3151
3152 base_properties::remove_child (gh);
3153 }
3154
3155 property_list
3156 root_figure::factory_properties = root_figure::init_factory_properties ();
3157
3158 static void
3159 reset_default_properties (property_list& default_properties)
3160 {
3161 property_list new_defaults;
3162
3163 for (property_list::plist_map_const_iterator p = default_properties.begin ();
3164 p != default_properties.end (); p++)
3165 {
3166 const property_list::pval_map_type pval_map = p->second;
3167 std::string prefix = p->first;
3168
3169 for (property_list::pval_map_const_iterator q = pval_map.begin ();
3170 q != pval_map.end ();
3171 q++)
3172 {
3173 std::string s = q->first;
3174
3175 if (prefix == "axes" && (s == "position" || s == "units"))
3176 new_defaults.set (prefix + s, q->second);
3177 else if (prefix == "figure" && (s == "position" || s == "units"
3178 || s == "windowstyle"
3179 || s == "paperunits"))
3180 new_defaults.set (prefix + s, q->second);
3181 }
3182 }
3183
3184 default_properties = new_defaults;
3185 }
3186
3187 void
3188 root_figure::reset_default_properties (void)
3189 {
3190 ::reset_default_properties (default_properties);
3191 }
3192
3193 // ---------------------------------------------------------------------
3194
3195 void
3196 figure::properties::set_currentaxes (const octave_value& v)
3197 {
3198 graphics_handle val (v);
3199
3200 if (error_state)
3201 return;
3202
3203 if (xisnan (val.value ()) || is_handle (val))
3204 currentaxes = val;
3205 else
3206 gripe_set_invalid ("currentaxes");
3207 }
3208
3209 void
3210 figure::properties::remove_child (const graphics_handle& gh)
3211 {
3212 base_properties::remove_child (gh);
3213
3214 if (gh == currentaxes.handle_value ())
3215 {
3216 graphics_handle new_currentaxes;
3217
3218 Matrix kids = get_children ();
3219
3220 for (octave_idx_type i = 0; i < kids.numel (); i++)
3221 {
3222 graphics_handle kid = kids(i);
3223
3224 graphics_object go = gh_manager::get_object (kid);
3225
3226 if (go.isa ("axes"))
3227 {
3228 new_currentaxes = kid;
3229 break;
3230 }
3231 }
3232
3233 currentaxes = new_currentaxes;
3234 }
3235 }
3236
3237 void
3238 figure::properties::set_visible (const octave_value& val)
3239 {
3240 std::string s = val.string_value ();
3241
3242 if (! error_state)
3243 {
3244 if (s == "on")
3245 xset (0, "currentfigure", __myhandle__.value ());
3246
3247 visible = val;
3248 }
3249 }
3250
3251 Matrix
3252 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3253 {
3254 Matrix screen_size = screen_size_pixels ();
3255 Matrix pos = (internal ?
3256 get_position ().matrix_value () :
3257 get_outerposition ().matrix_value ());
3258
3259 pos = convert_position (pos, get_units (), "pixels", screen_size);
3260
3261 pos(0)--;
3262 pos(1)--;
3263 pos(1) = screen_size(1) - pos(1) - pos(3);
3264
3265 return pos;
3266 }
3267
3268 void
3269 figure::properties::set_boundingbox (const Matrix& bb, bool internal,
3270 bool do_notify_toolkit)
3271 {
3272 Matrix screen_size = screen_size_pixels ();
3273 Matrix pos = bb;
3274
3275 pos(1) = screen_size(1) - pos(1) - pos(3);
3276 pos(1)++;
3277 pos(0)++;
3278 pos = convert_position (pos, "pixels", get_units (), screen_size);
3279
3280 if (internal)
3281 set_position (pos, do_notify_toolkit);
3282 else
3283 set_outerposition (pos, do_notify_toolkit);
3284 }
3285
3286 Matrix
3287 figure::properties::map_from_boundingbox (double x, double y) const
3288 {
3289 Matrix bb = get_boundingbox (true);
3290 Matrix pos (1, 2, 0);
3291
3292 pos(0) = x;
3293 pos(1) = y;
3294
3295 pos(1) = bb(3) - pos(1);
3296 pos(0)++;
3297 pos = convert_position (pos, "pixels", get_units (),
3298 bb.extract_n (0, 2, 1, 2));
3299
3300 return pos;
3301 }
3302
3303 Matrix
3304 figure::properties::map_to_boundingbox (double x, double y) const
3305 {
3306 Matrix bb = get_boundingbox (true);
3307 Matrix pos (1, 2, 0);
3308
3309 pos(0) = x;
3310 pos(1) = y;
3311
3312 pos = convert_position (pos, get_units (), "pixels",
3313 bb.extract_n (0, 2, 1, 2));
3314 pos(0)--;
3315 pos(1) = bb(3) - pos(1);
3316
3317 return pos;
3318 }
3319
3320 void
3321 figure::properties::set_position (const octave_value& v,
3322 bool do_notify_toolkit)
3323 {
3324 if (! error_state)
3325 {
3326 Matrix old_bb, new_bb;
3327 bool modified = false;
3328
3329 old_bb = get_boundingbox (true);
3330 modified = position.set (v, false, do_notify_toolkit);
3331 new_bb = get_boundingbox (true);
3332
3333 if (old_bb != new_bb)
3334 {
3335 if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
3336 {
3337 execute_resizefcn ();
3338 update_boundingbox ();
3339 }
3340 }
3341
3342 if (modified)
3343 {
3344 position.run_listeners (POSTSET);
3345 mark_modified ();
3346 }
3347 }
3348 }
3349
3350 void
3351 figure::properties::set_outerposition (const octave_value& v,
3352 bool do_notify_toolkit)
3353 {
3354 if (! error_state)
3355 {
3356 if (outerposition.set (v, true, do_notify_toolkit))
3357 {
3358 mark_modified ();
3359 }
3360 }
3361 }
3362
3363 void
3364 figure::properties::set_paperunits (const octave_value& v)
3365 {
3366 if (! error_state)
3367 {
3368 caseless_str typ = get_papertype ();
3369 caseless_str punits = v.string_value ();
3370 if (! error_state)
3371 {
3372 if (punits.compare ("normalized") && typ.compare ("<custom>"))
3373 error ("set: can't set the paperunits to normalized when the papertype is custom");
3374 else
3375 {
3376 caseless_str old_paperunits = get_paperunits ();
3377 if (paperunits.set (v, true))
3378 {
3379 update_paperunits (old_paperunits);
3380 mark_modified ();
3381 }
3382 }
3383 }
3384 }
3385 }
3386
3387 void
3388 figure::properties::set_papertype (const octave_value& v)
3389 {
3390 if (! error_state)
3391 {
3392 caseless_str typ = v.string_value ();
3393 caseless_str punits = get_paperunits ();
3394 if (! error_state)
3395 {
3396 if (punits.compare ("normalized") && typ.compare ("<custom>"))
3397 error ("set: can't set the paperunits to normalized when the papertype is custom");
3398 else
3399 {
3400 if (papertype.set (v, true))
3401 {
3402 update_papertype ();
3403 mark_modified ();
3404 }
3405 }
3406 }
3407 }
3408 }
3409
3410 static Matrix
3411 papersize_from_type (const caseless_str punits, const caseless_str typ)
3412 {
3413 Matrix ret (1, 2, 1.0);
3414
3415 if (! punits.compare ("normalized"))
3416 {
3417 double in2units;
3418 double mm2units;
3419
3420 if (punits.compare ("inches"))
3421 {
3422 in2units = 1.0;
3423 mm2units = 1 / 25.4 ;
3424 }
3425 else if (punits.compare ("centimeters"))
3426 {
3427 in2units = 2.54;
3428 mm2units = 1 / 10.0;
3429 }
3430 else // points
3431 {
3432 in2units = 72.0;
3433 mm2units = 72.0 / 25.4;
3434 }
3435
3436 if (typ.compare ("usletter"))
3437 {
3438 ret (0) = 8.5 * in2units;
3439 ret (1) = 11.0 * in2units;
3440 }
3441 else if (typ.compare ("uslegal"))
3442 {
3443 ret (0) = 8.5 * in2units;
3444 ret (1) = 14.0 * in2units;
3445 }
3446 else if (typ.compare ("tabloid"))
3447 {
3448 ret (0) = 11.0 * in2units;
3449 ret (1) = 17.0 * in2units;
3450 }
3451 else if (typ.compare ("a0"))
3452 {
3453 ret (0) = 841.0 * mm2units;
3454 ret (1) = 1189.0 * mm2units;
3455 }
3456 else if (typ.compare ("a1"))
3457 {
3458 ret (0) = 594.0 * mm2units;
3459 ret (1) = 841.0 * mm2units;
3460 }
3461 else if (typ.compare ("a2"))
3462 {
3463 ret (0) = 420.0 * mm2units;
3464 ret (1) = 594.0 * mm2units;
3465 }
3466 else if (typ.compare ("a3"))
3467 {
3468 ret (0) = 297.0 * mm2units;
3469 ret (1) = 420.0 * mm2units;
3470 }
3471 else if (typ.compare ("a4"))
3472 {
3473 ret (0) = 210.0 * mm2units;
3474 ret (1) = 297.0 * mm2units;
3475 }
3476 else if (typ.compare ("a5"))
3477 {
3478 ret (0) = 148.0 * mm2units;
3479 ret (1) = 210.0 * mm2units;
3480 }
3481 else if (typ.compare ("b0"))
3482 {
3483 ret (0) = 1029.0 * mm2units;
3484 ret (1) = 1456.0 * mm2units;
3485 }
3486 else if (typ.compare ("b1"))
3487 {
3488 ret (0) = 728.0 * mm2units;
3489 ret (1) = 1028.0 * mm2units;
3490 }
3491 else if (typ.compare ("b2"))
3492 {
3493 ret (0) = 514.0 * mm2units;
3494 ret (1) = 728.0 * mm2units;
3495 }
3496 else if (typ.compare ("b3"))
3497 {
3498 ret (0) = 364.0 * mm2units;
3499 ret (1) = 514.0 * mm2units;
3500 }
3501 else if (typ.compare ("b4"))
3502 {
3503 ret (0) = 257.0 * mm2units;
3504 ret (1) = 364.0 * mm2units;
3505 }
3506 else if (typ.compare ("b5"))
3507 {
3508 ret (0) = 182.0 * mm2units;
3509 ret (1) = 257.0 * mm2units;
3510 }
3511 else if (typ.compare ("arch-a"))
3512 {
3513 ret (0) = 9.0 * in2units;
3514 ret (1) = 12.0 * in2units;
3515 }
3516 else if (typ.compare ("arch-b"))
3517 {
3518 ret (0) = 12.0 * in2units;
3519 ret (1) = 18.0 * in2units;
3520 }
3521 else if (typ.compare ("arch-c"))
3522 {
3523 ret (0) = 18.0 * in2units;
3524 ret (1) = 24.0 * in2units;
3525 }
3526 else if (typ.compare ("arch-d"))
3527 {
3528 ret (0) = 24.0 * in2units;
3529 ret (1) = 36.0 * in2units;
3530 }
3531 else if (typ.compare ("arch-e"))
3532 {
3533 ret (0) = 36.0 * in2units;
3534 ret (1) = 48.0 * in2units;
3535 }
3536 else if (typ.compare ("a"))
3537 {
3538 ret (0) = 8.5 * in2units;
3539 ret (1) = 11.0 * in2units;
3540 }
3541 else if (typ.compare ("b"))
3542 {
3543 ret (0) = 11.0 * in2units;
3544 ret (1) = 17.0 * in2units;
3545 }
3546 else if (typ.compare ("c"))
3547 {
3548 ret (0) = 17.0 * in2units;
3549 ret (1) = 22.0 * in2units;
3550 }
3551 else if (typ.compare ("d"))
3552 {
3553 ret (0) = 22.0 * in2units;
3554 ret (1) = 34.0 * in2units;
3555 }
3556 else if (typ.compare ("e"))
3557 {
3558 ret (0) = 34.0 * in2units;
3559 ret (1) = 43.0 * in2units;
3560 }
3561 }
3562
3563 return ret;
3564 }
3565
3566 void
3567 figure::properties::update_paperunits (const caseless_str& old_paperunits)
3568 {
3569 Matrix pos = get_paperposition ().matrix_value ();
3570 Matrix sz = get_papersize ().matrix_value ();
3571
3572 pos(0) /= sz(0);
3573 pos(1) /= sz(1);
3574 pos(2) /= sz(0);
3575 pos(3) /= sz(1);
3576
3577 std::string porient = get_paperorientation ();
3578 caseless_str punits = get_paperunits ();
3579 caseless_str typ = get_papertype ();
3580
3581 if (typ.compare ("<custom>"))
3582 {
3583 if (old_paperunits.compare ("centimeters"))
3584 {
3585 sz(0) /= 2.54;
3586 sz(1) /= 2.54;
3587 }
3588 else if (old_paperunits.compare ("points"))
3589 {
3590 sz(0) /= 72.0;
3591 sz(1) /= 72.0;
3592 }
3593
3594 if (punits.compare ("centimeters"))
3595 {
3596 sz(0) *= 2.54;
3597 sz(1) *= 2.54;
3598 }
3599 else if (punits.compare ("points"))
3600 {
3601 sz(0) *= 72.0;
3602 sz(1) *= 72.0;
3603 }
3604 }
3605 else
3606 {
3607 sz = papersize_from_type (punits, typ);
3608 if (porient == "landscape")
3609 std::swap (sz(0), sz(1));
3610 }
3611
3612 pos(0) *= sz(0);
3613 pos(1) *= sz(1);
3614 pos(2) *= sz(0);
3615 pos(3) *= sz(1);
3616
3617 papersize.set (octave_value (sz));
3618 paperposition.set (octave_value (pos));
3619 }
3620
3621 void
3622 figure::properties::update_papertype (void)
3623 {
3624 caseless_str typ = get_papertype ();
3625 if (! typ.compare ("<custom>"))
3626 {
3627 Matrix sz = papersize_from_type (get_paperunits (), typ);
3628 if (get_paperorientation () == "landscape")
3629 std::swap (sz(0), sz(1));
3630 // Call papersize.set rather than set_papersize to avoid loops
3631 // between update_papersize and update_papertype
3632 papersize.set (octave_value (sz));
3633 }
3634 }
3635
3636 void
3637 figure::properties::update_papersize (void)
3638 {
3639 Matrix sz = get_papersize ().matrix_value ();
3640 if (sz(0) > sz(1))
3641 {
3642 std::swap (sz(0), sz(1));
3643 papersize.set (octave_value (sz));
3644 paperorientation.set (octave_value ("landscape"));
3645 }
3646 else
3647 {
3648 paperorientation.set ("portrait");
3649 }
3650 std::string punits = get_paperunits ();
3651 if (punits == "centimeters")
3652 {
3653 sz(0) /= 2.54;
3654 sz(1) /= 2.54;
3655 }
3656 else if (punits == "points")
3657 {
3658 sz(0) /= 72.0;
3659 sz(1) /= 72.0;
3660 }
3661 if (punits == "normalized")
3662 {
3663 caseless_str typ = get_papertype ();
3664 if (get_papertype () == "<custom>")
3665 error ("set: can't set the papertype to <custom> when the paperunits is normalized");
3666 }
3667 else
3668 {
3669 // TODO - the papersizes info is also in papersize_from_type().
3670 // Both should be rewritten to avoid the duplication.
3671 std::string typ = "<custom>";
3672 const double mm2in = 1.0 / 25.4;
3673 const double tol = 0.01;
3674
3675 if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
3676 typ = "usletter";
3677 else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
3678 typ = "uslegal";
3679 else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
3680 typ = "tabloid";
3681 else if (std::abs (sz(0) - 841.0 * mm2in) + std::abs (sz(1) - 1198.0 * mm2in) < tol)
3682 typ = "a0";
3683 else if (std::abs (sz(0) - 594.0 * mm2in) + std::abs (sz(1) - 841.0 * mm2in) < tol)
3684 typ = "a1";
3685 else if (std::abs (sz(0) - 420.0 * mm2in) + std::abs (sz(1) - 594.0 * mm2in) < tol)
3686 typ = "a2";
3687 else if (std::abs (sz(0) - 297.0 * mm2in) + std::abs (sz(1) - 420.0 * mm2in) < tol)
3688 typ = "a3";
3689 else if (std::abs (sz(0) - 210.0 * mm2in) + std::abs (sz(1) - 297.0 * mm2in) < tol)
3690 typ = "a4";
3691 else if (std::abs (sz(0) - 148.0 * mm2in) + std::abs (sz(1) - 210.0 * mm2in) < tol)
3692 typ = "a5";
3693 else if (std::abs (sz(0) - 1029.0 * mm2in) + std::abs (sz(1) - 1456.0 * mm2in) < tol)
3694 typ = "b0";
3695 else if (std::abs (sz(0) - 728.0 * mm2in) + std::abs (sz(1) - 1028.0 * mm2in) < tol)
3696 typ = "b1";
3697 else if (std::abs (sz(0) - 514.0 * mm2in) + std::abs (sz(1) - 728.0 * mm2in) < tol)
3698 typ = "b2";
3699 else if (std::abs (sz(0) - 364.0 * mm2in) + std::abs (sz(1) - 514.0 * mm2in) < tol)
3700 typ = "b3";
3701 else if (std::abs (sz(0) - 257.0 * mm2in) + std::abs (sz(1) - 364.0 * mm2in) < tol)
3702 typ = "b4";
3703 else if (std::abs (sz(0) - 182.0 * mm2in) + std::abs (sz(1) - 257.0 * mm2in) < tol)
3704 typ = "b5";
3705 else if (std::abs (sz(0) - 9.0) + std::abs (sz(1) - 12.0) < tol)
3706 typ = "arch-a";
3707 else if (std::abs (sz(0) - 12.0) + std::abs (sz(1) - 18.0) < tol)
3708 typ = "arch-b";
3709 else if (std::abs (sz(0) - 18.0) + std::abs (sz(1) - 24.0) < tol)
3710 typ = "arch-c";
3711 else if (std::abs (sz(0) - 24.0) + std::abs (sz(1) - 36.0) < tol)
3712 typ = "arch-d";
3713 else if (std::abs (sz(0) - 36.0) + std::abs (sz(1) - 48.0) < tol)
3714 typ = "arch-e";
3715 else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
3716 typ = "a";
3717 else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
3718 typ = "b";
3719 else if (std::abs (sz(0) - 17.0) + std::abs (sz(1) - 22.0) < tol)
3720 typ = "c";
3721 else if (std::abs (sz(0) - 22.0) + std::abs (sz(1) - 34.0) < tol)
3722 typ = "d";
3723 else if (std::abs (sz(0) - 34.0) + std::abs (sz(1) - 43.0) < tol)
3724 typ = "e";
3725 // Call papertype.set rather than set_papertype to avoid loops between
3726 // update_papersize and update_papertype
3727 papertype.set (typ);
3728 }
3729 if (punits == "centimeters")
3730 {
3731 sz(0) *= 2.54;
3732 sz(1) *= 2.54;
3733 }
3734 else if (punits == "points")
3735 {
3736 sz(0) *= 72.0;
3737 sz(1) *= 72.0;
3738 }
3739 if (get_paperorientation () == "landscape")
3740 {
3741 std::swap (sz(0), sz(1));
3742 papersize.set (octave_value (sz));
3743 }
3744 }
3745
3746 /*
3747 %!test
3748 %! figure (1, "visible", "off");
3749 %! set (1, "paperunits", "inches");
3750 %! set (1, "papersize", [5, 4]);
3751 %! set (1, "paperunits", "points");
3752 %! assert (get (1, "papersize"), [5, 4] * 72, 1);
3753 %! papersize = get (gcf, "papersize");
3754 %! set (1, "papersize", papersize + 1);
3755 %! set (1, "papersize", papersize);
3756 %! assert (get (1, "papersize"), [5, 4] * 72, 1);
3757 %! close (1);
3758 %!test
3759 %! figure (1, "visible", "off");
3760 %! set (1, "paperunits", "inches");
3761 %! set (1, "papersize", [5, 4]);
3762 %! set (1, "paperunits", "centimeters");
3763 %! assert (get (1, "papersize"), [5, 4] * 2.54, 2.54/72);
3764 %! papersize = get (gcf, "papersize");
3765 %! set (1, "papersize", papersize + 1);
3766 %! set (1, "papersize", papersize);
3767 %! assert (get (1, "papersize"), [5, 4] * 2.54, 2.54/72);
3768 %! close (1);
3769 */
3770
3771 void
3772 figure::properties::update_paperorientation (void)
3773 {
3774 std::string porient = get_paperorientation ();
3775 Matrix sz = get_papersize ().matrix_value ();
3776 Matrix pos = get_paperposition ().matrix_value ();
3777 if ((sz(0) > sz(1) && porient == "portrait")
3778 || (sz(0) < sz(1) && porient == "landscape"))
3779 {
3780 std::swap (sz(0), sz(1));
3781 std::swap (pos(0), pos(1));
3782 std::swap (pos(2), pos(3));
3783 // Call papertype.set rather than set_papertype to avoid loops
3784 // between update_papersize and update_papertype
3785 papersize.set (octave_value (sz));
3786 paperposition.set (octave_value (pos));
3787 }
3788 }
3789
3790 /*
3791 %!test
3792 %! figure (1, "visible", false);
3793 %! tol = 100 * eps ();
3794 %! ## UPPER case and MiXed case is part of test and should not be changed.
3795 %! set (gcf (), "paperorientation", "PORTRAIT");
3796 %! set (gcf (), "paperunits", "inches");
3797 %! set (gcf (), "papertype", "USletter");
3798 %! assert (get (gcf (), "papersize"), [8.5, 11.0], tol);
3799 %! set (gcf (), "paperorientation", "Landscape");
3800 %! assert (get (gcf (), "papersize"), [11.0, 8.5], tol);
3801 %! set (gcf (), "paperunits", "centimeters");
3802 %! assert (get (gcf (), "papersize"), [11.0, 8.5] * 2.54, tol);
3803 %! set (gcf (), "papertype", "a4");
3804 %! assert (get (gcf (), "papersize"), [29.7, 21.0], tol);
3805 %! set (gcf (), "paperunits", "inches", "papersize", [8.5, 11.0]);
3806 %! assert (get (gcf (), "papertype"), "usletter");
3807 %! assert (get (gcf (), "paperorientation"), "portrait");
3808 %! set (gcf (), "papersize", [11.0, 8.5]);
3809 %! assert (get (gcf (), "papertype"), "usletter");
3810 %! assert (get (gcf (), "paperorientation"), "landscape");
3811 */
3812
3813 void
3814 figure::properties::set_units (const octave_value& v)
3815 {
3816 if (! error_state)
3817 {
3818 caseless_str old_units = get_units ();
3819 if (units.set (v, true))
3820 {
3821 update_units (old_units);
3822 mark_modified ();
3823 }
3824 }
3825 }
3826
3827 void
3828 figure::properties::update_units (const caseless_str& old_units)
3829 {
3830 position.set (convert_position (get_position ().matrix_value (), old_units,
3831 get_units (), screen_size_pixels ()), false);
3832 }
3833
3834 /*
3835 %!test
3836 %! figure (1, "visible", false);
3837 %! set (0, "units", "pixels");
3838 %! rsz = get (0, "screensize");
3839 %! set (gcf (), "units", "pixels");
3840 %! fsz = get (gcf (), "position");
3841 %! set (gcf (), "units", "normalized");
3842 %! assert (get (gcf (), "position"), (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
3843 */
3844
3845 std::string
3846 figure::properties::get_title (void) const
3847 {
3848 if (is_numbertitle ())
3849 {
3850 std::ostringstream os;
3851 std::string nm = get_name ();
3852
3853 os << "Figure " << __myhandle__.value ();
3854 if (! nm.empty ())
3855 os << ": " << get_name ();
3856
3857 return os.str ();
3858 }
3859 else
3860 return get_name ();
3861 }
3862
3863 octave_value
3864 figure::get_default (const caseless_str& name) const
3865 {
3866 octave_value retval = default_properties.lookup (name);
3867
3868 if (retval.is_undefined ())
3869 {
3870 graphics_handle parent = get_parent ();
3871 graphics_object parent_obj = gh_manager::get_object (parent);
3872
3873 retval = parent_obj.get_default (name);
3874 }
3875
3876 return retval;
3877 }
3878
3879 void
3880 figure::reset_default_properties (void)
3881 {
3882 ::reset_default_properties (default_properties);
3883 }
3884
3885 // ---------------------------------------------------------------------
3886
3887 void
3888 axes::properties::init (void)
3889 {
3890 position.add_constraint (dim_vector (1, 4));
3891 position.add_constraint (dim_vector (0, 0));
3892 outerposition.add_constraint (dim_vector (1, 4));
3893 colororder.add_constraint (dim_vector (-1, 3));
3894 dataaspectratio.add_constraint (dim_vector (1, 3));
3895 plotboxaspectratio.add_constraint (dim_vector (1, 3));
3896 xlim.add_constraint (2);
3897 ylim.add_constraint (2);
3898 zlim.add_constraint (2);
3899 clim.add_constraint (2);
3900 alim.add_constraint (2);
3901 xtick.add_constraint (dim_vector (1, -1));
3902 ytick.add_constraint (dim_vector (1, -1));
3903 ztick.add_constraint (dim_vector (1, -1));
3904 Matrix vw (1, 2, 0);
3905 vw(1) = 90;
3906 view = vw;
3907 view.add_constraint (dim_vector (1, 2));
3908 cameraposition.add_constraint (dim_vector (1, 3));
3909 Matrix upv (1, 3, 0.0);
3910 upv(2) = 1.0;
3911 cameraupvector = upv;
3912 cameraupvector.add_constraint (dim_vector (1, 3));
3913 currentpoint.add_constraint (dim_vector (2, 3));
3914 ticklength.add_constraint (dim_vector (1, 2));
3915 tightinset.add_constraint (dim_vector (1, 4));
3916 looseinset.add_constraint (dim_vector (1, 4));
3917 update_font ();
3918
3919 x_zlim.resize (1, 2);
3920
3921 sx = "linear";
3922 sy = "linear";
3923 sz = "linear";
3924
3925 calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
3926 calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
3927 calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
3928
3929 xset (xlabel.handle_value (), "handlevisibility", "off");
3930 xset (ylabel.handle_value (), "handlevisibility", "off");
3931 xset (zlabel.handle_value (), "handlevisibility", "off");
3932 xset (title.handle_value (), "handlevisibility", "off");
3933
3934 xset (xlabel.handle_value (), "horizontalalignment", "center");
3935 xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
3936 xset (ylabel.handle_value (), "horizontalalignment", "center");
3937 xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
3938 xset (zlabel.handle_value (), "horizontalalignment", "right");
3939 xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
3940 xset (title.handle_value (), "horizontalalignment", "center");
3941 xset (title.handle_value (), "horizontalalignmentmode", "auto");
3942
3943 xset (xlabel.handle_value (), "verticalalignment", "top");
3944 xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
3945 xset (ylabel.handle_value (), "verticalalignment", "bottom");
3946 xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
3947 xset (title.handle_value (), "verticalalignment", "bottom");
3948 xset (title.handle_value (), "verticalalignmentmode", "auto");
3949
3950 xset (ylabel.handle_value (), "rotation", 90.0);
3951 xset (ylabel.handle_value (), "rotationmode", "auto");
3952
3953 xset (zlabel.handle_value (), "visible", "off");
3954
3955 xset (xlabel.handle_value (), "clipping", "off");
3956 xset (ylabel.handle_value (), "clipping", "off");
3957 xset (zlabel.handle_value (), "clipping", "off");
3958 xset (title.handle_value (), "clipping", "off");
3959
3960 xset (xlabel.handle_value (), "autopos_tag", "xlabel");
3961 xset (ylabel.handle_value (), "autopos_tag", "ylabel");
3962 xset (zlabel.handle_value (), "autopos_tag", "zlabel");
3963 xset (title.handle_value (), "autopos_tag", "title");
3964
3965 adopt (xlabel.handle_value ());
3966 adopt (ylabel.handle_value ());
3967 adopt (zlabel.handle_value ());
3968 adopt (title.handle_value ());
3969
3970 Matrix tlooseinset = default_axes_position ();
3971 tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
3972 tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
3973 looseinset = tlooseinset;
3974 }
3975
3976 Matrix
3977 axes::properties::calc_tightbox (const Matrix& init_pos)
3978 {
3979 Matrix pos = init_pos;
3980 graphics_object obj = gh_manager::get_object (get_parent ());
3981 Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
3982 Matrix ext = get_extent (true, true);
3983 ext(1) = parent_bb(3) - ext(1) - ext(3);
3984 ext(0)++;
3985 ext(1)++;
3986 ext = convert_position (ext, "pixels", get_units (),
3987 parent_bb.extract_n (0, 2, 1, 2));
3988 if (ext(0) < pos(0))
3989 {
3990 pos(2) += pos(0)-ext(0);
3991 pos(0) = ext(0);
3992 }
3993 if (ext(0)+ext(2) > pos(0)+pos(2))
3994 pos(2) = ext(0)+ext(2)-pos(0);
3995
3996 if (ext(1) < pos(1))
3997 {
3998 pos(3) += pos(1)-ext(1);
3999 pos(1) = ext(1);
4000 }
4001 if (ext(1)+ext(3) > pos(1)+pos(3))
4002 pos(3) = ext(1)+ext(3)-pos(1);
4003 return pos;
4004 }
4005
4006 void
4007 axes::properties::sync_positions (void)
4008 {
4009 Matrix ref_linset = looseinset.get ().matrix_value ();
4010 if (autopos_tag_is ("subplot"))
4011 {
4012 graphics_object parent_obj = gh_manager::get_object (get_parent ());
4013 if (parent_obj.isa ("figure"))
4014 {
4015 // FIXME: temporarily changed units should be protected
4016 // from interrupts
4017 std::string fig_units = parent_obj.get ("units").string_value ();
4018 parent_obj.set ("units", "pixels");
4019
4020 Matrix ref_outbox = outerposition.get ().matrix_value ();
4021 ref_outbox(2) += ref_outbox(0);
4022 ref_outbox(3) += ref_outbox(1);
4023
4024 // Find those subplots that are left, right, bottom and top aligned
4025 // with the current subplot
4026 Matrix kids = parent_obj.get_properties ().get_children ();
4027 std::vector<octave_value> aligned;
4028 std::vector<bool> l_aligned, b_aligned, r_aligned, t_aligned;
4029 for (octave_idx_type i = 0; i < kids.numel (); i++)
4030 {
4031 graphics_object go = gh_manager::get_object (kids(i));
4032 if (go.isa ("axes"))
4033 {
4034 axes::properties& props =
4035 dynamic_cast<axes::properties&> (go.get_properties ());
4036 if (props.autopos_tag_is ("subplot"))
4037 {
4038 Matrix outpos = go.get ("outerposition").matrix_value ();
4039 bool l_align = (std::abs (outpos(0)-ref_outbox(0)) < 1e-15);
4040 bool b_align = (std::abs (outpos(1)-ref_outbox(1)) < 1e-15);
4041 bool r_align = (std::abs (outpos(0)+outpos(2)-ref_outbox(2)) < 1e-15);
4042 bool t_align = (std::abs (outpos(1)+outpos(3)-ref_outbox(3)) < 1e-15);
4043 if (l_align || b_align || r_align || t_align)
4044 {
4045 aligned.push_back (kids(i));
4046 l_aligned.push_back (l_align);
4047 b_aligned.push_back (b_align);
4048 r_aligned.push_back (r_align);
4049 t_aligned.push_back (t_align);
4050 // FIXME: the temporarily deleted tags should be
4051 // protected from interrupts
4052 props.set_autopos_tag ("none");
4053 }
4054 }
4055 }
4056 }
4057 // Determine a minimum box which aligns the subplots
4058 Matrix ref_box (1, 4, 0.);
4059 ref_box(2) = 1.;
4060 ref_box(3) = 1.;
4061 for (size_t i = 0; i < aligned.size (); i++)
4062 {
4063 graphics_object go = gh_manager::get_object (aligned[i]);
4064 axes::properties& props =
4065 dynamic_cast<axes::properties&> (go.get_properties ());
4066 Matrix linset = props.get_looseinset ().matrix_value ();
4067 if (l_aligned[i])
4068 linset(0) = std::min (0., linset(0)-0.01);
4069 if (b_aligned[i])
4070 linset(1) = std::min (0., linset(1)-0.01);
4071 if (r_aligned[i])
4072 linset(2) = std::min (0., linset(2)-0.01);
4073 if (t_aligned[i])
4074 linset(3) = std::min (0., linset(3)-0.01);
4075 props.set_looseinset (linset);
4076 Matrix pos = props.get_position ().matrix_value ();
4077 if (l_aligned[i])
4078 ref_box(0) = std::max (ref_box(0), pos(0));
4079 if (b_aligned[i])
4080 ref_box(1) = std::max (ref_box(1), pos(1));
4081 if (r_aligned[i])
4082 ref_box(2) = std::min (ref_box(2), pos(0)+pos(2));
4083 if (t_aligned[i])
4084 ref_box(3) = std::min (ref_box(3), pos(1)+pos(3));
4085 }
4086 // Set common looseinset values for all aligned subplots and
4087 // revert their tag values
4088 for (size_t i = 0; i < aligned.size (); i++)
4089 {
4090 graphics_object go = gh_manager::get_object (aligned[i]);
4091 axes::properties& props =
4092 dynamic_cast<axes::properties&> (go.get_properties ());
4093 Matrix outpos = props.get_outerposition ().matrix_value ();
4094 Matrix linset = props.get_looseinset ().matrix_value ();
4095 if (l_aligned[i])
4096 linset(0) = (ref_box(0)-outpos(0))/outpos(2);
4097 if (b_aligned[i])
4098 linset(1) = (ref_box(1)-outpos(1))/outpos(3);
4099 if (r_aligned[i])
4100 linset(2) = (outpos(0)+outpos(2)-ref_box(2))/outpos(2);
4101 if (t_aligned[i])
4102 linset(3) = (outpos(1)+outpos(3)-ref_box(3))/outpos(3);
4103 props.set_looseinset (linset);
4104 props.set_autopos_tag ("subplot");
4105 }
4106 parent_obj.set ("units", fig_units);
4107 }
4108 }
4109 else
4110 sync_positions (ref_linset);
4111 }
4112
4113 void
4114 axes::properties::sync_positions (const Matrix& linset)
4115 {
4116 Matrix pos = position.get ().matrix_value ();
4117 Matrix outpos = outerposition.get ().matrix_value ();
4118 double lratio = linset(0);
4119 double bratio = linset(1);
4120 double wratio = 1-linset(0)-linset(2);
4121 double hratio = 1-linset(1)-linset(3);
4122 if (activepositionproperty.is ("outerposition"))
4123 {
4124 pos = outpos;
4125 pos(0) = outpos(0)+lratio*outpos(2);
4126 pos(1) = outpos(1)+bratio*outpos(3);
4127 pos(2) = wratio*outpos(2);
4128 pos(3) = hratio*outpos(3);
4129
4130 position = pos;
4131 update_transform ();
4132 Matrix tightpos = calc_tightbox (pos);
4133
4134 double thrshldx = 0.005*outpos(2);
4135 double thrshldy = 0.005*outpos(3);
4136 double minsizex = 0.2*outpos(2);
4137 double minsizey = 0.2*outpos(3);
4138 bool updatex = true, updatey = true;
4139 for (int i = 0; i < 10; i++)
4140 {
4141 double dt;
4142 bool modified = false;
4143 dt = outpos(0)+outpos(2)-tightpos(0)-tightpos(2);
4144 if (dt < -thrshldx && updatex)
4145 {
4146 pos(2) += dt;
4147 modified = true;
4148 }
4149 dt = outpos(1)+outpos(3)-tightpos(1)-tightpos(3);
4150 if (dt < -thrshldy && updatey)
4151 {
4152 pos(3) += dt;
4153 modified = true;
4154 }
4155 dt = outpos(0)-tightpos(0);
4156 if (dt > thrshldx && updatex)
4157 {
4158 pos(0) += dt;
4159 pos(2) -= dt;
4160 modified = true;
4161 }
4162 dt = outpos(1)-tightpos(1);
4163 if (dt > thrshldy && updatey)
4164 {
4165 pos(1) += dt;
4166 pos(3) -= dt;
4167 modified = true;
4168 }
4169
4170 // Note: checking limit for minimum axes size
4171 if (pos(2) < minsizex)
4172 {
4173 pos(0) -= 0.5*(minsizex-pos(2));
4174 pos(2) = minsizex;
4175 updatex = false;
4176 }
4177 if (pos(3) < minsizey)
4178 {
4179 pos(1) -= 0.5*(minsizey-pos(3));
4180 pos(3) = minsizey;
4181 updatey = false;
4182 }
4183
4184 if (modified)
4185 {
4186 position = pos;
4187 update_transform ();
4188 tightpos = calc_tightbox (pos);
4189 }
4190 else
4191 break;
4192 }
4193 }
4194 else
4195 {
4196 update_transform ();
4197
4198 outpos(0) = pos(0)-pos(2)*lratio/wratio;
4199 outpos(1) = pos(1)-pos(3)*bratio/hratio;
4200 outpos(2) = pos(2)/wratio;
4201 outpos(3) = pos(3)/hratio;
4202
4203 outerposition = calc_tightbox (outpos);
4204 }
4205
4206 update_insets ();
4207 }
4208
4209 void
4210 axes::properties::update_insets (void)
4211 {
4212 Matrix pos = position.get ().matrix_value ();
4213 Matrix outpos = outerposition.get ().matrix_value ();
4214 Matrix tightpos = calc_tightbox (pos);
4215 // Determine the tightinset = axes_bbox - position
4216 Matrix inset (1, 4, 1.0);
4217 inset(0) = pos(0)-tightpos(0);
4218 inset(1) = pos(1)-tightpos(1);
4219 inset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4220 inset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4221 tightinset = inset;
4222
4223 // Determine the looseinset = outerposition - position
4224 inset(0) = pos(0)-outpos(0);
4225 inset(1) = pos(1)-outpos(1);
4226 inset(2) = outpos(0)+outpos(2)-pos(0)-pos(2);
4227 inset(3) = outpos(1)+outpos(3)-pos(1)-pos(3);
4228 looseinset = inset;
4229 }
4230
4231
4232 void
4233 axes::properties::set_text_child (handle_property& hp,
4234 const std::string& who,
4235 const octave_value& v)
4236 {
4237 graphics_handle val;
4238
4239 if (v.is_string ())
4240 {
4241 val = gh_manager::make_graphics_handle ("text", __myhandle__,
4242 false, false);
4243
4244 xset (val, "string", v);
4245 }
4246 else
4247 {
4248 graphics_object go = gh_manager::get_object (gh_manager::lookup (v));
4249
4250 if (go.isa ("text"))
4251 val = ::reparent (v, "set", who, __myhandle__, false);
4252 else
4253 {
4254 std::string cname = v.class_name ();
4255
4256 error ("set: expecting text graphics object or character string for %s property, found %s",
4257 who.c_str (), cname.c_str ());
4258 }
4259 }
4260
4261 if (! error_state)
4262 {
4263 xset (val, "handlevisibility", "off");
4264
4265 gh_manager::free (hp.handle_value ());
4266
4267 base_properties::remove_child (hp.handle_value ());
4268
4269 hp = val;
4270
4271 adopt (hp.handle_value ());
4272 }
4273 }
4274
4275 void
4276 axes::properties::set_xlabel (const octave_value& v)
4277 {
4278 set_text_child (xlabel, "xlabel", v);
4279 xset (xlabel.handle_value (), "positionmode", "auto");
4280 xset (xlabel.handle_value (), "rotationmode", "auto");
4281 xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4282 xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4283 xset (xlabel.handle_value (), "clipping", "off");
4284 xset (xlabel.handle_value (), "color", get_xcolor ());
4285 xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4286 update_xlabel_position ();
4287 }
4288
4289 void
4290 axes::properties::set_ylabel (const octave_value& v)
4291 {
4292 set_text_child (ylabel, "ylabel", v);
4293 xset (ylabel.handle_value (), "positionmode", "auto");
4294 xset (ylabel.handle_value (), "rotationmode", "auto");
4295 xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4296 xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4297 xset (ylabel.handle_value (), "clipping", "off");
4298 xset (ylabel.handle_value (), "color", get_ycolor ());
4299 xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4300 update_ylabel_position ();
4301 }
4302
4303 void
4304 axes::properties::set_zlabel (const octave_value& v)
4305 {
4306 set_text_child (zlabel, "zlabel", v);
4307 xset (zlabel.handle_value (), "positionmode", "auto");
4308 xset (zlabel.handle_value (), "rotationmode", "auto");
4309 xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4310 xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
4311 xset (zlabel.handle_value (), "clipping", "off");
4312 xset (zlabel.handle_value (), "color", get_zcolor ());
4313 xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4314 update_zlabel_position ();
4315 }
4316
4317 void
4318 axes::properties::set_title (const octave_value& v)
4319 {
4320 set_text_child (title, "title", v);
4321 xset (title.handle_value (), "positionmode", "auto");
4322 xset (title.handle_value (), "horizontalalignment", "center");
4323 xset (title.handle_value (), "horizontalalignmentmode", "auto");
4324 xset (title.handle_value (), "verticalalignment", "bottom");
4325 xset (title.handle_value (), "verticalalignmentmode", "auto");
4326 xset (title.handle_value (), "clipping", "off");
4327 xset (title.handle_value (), "autopos_tag", "title");
4328 update_title_position ();
4329 }
4330
4331 void
4332 axes::properties::set_defaults (base_graphics_object& obj,
4333 const std::string& mode)
4334 {
4335 box = "on";
4336 colororder = default_colororder ();
4337 dataaspectratio = Matrix (1, 3, 1.0);
4338 dataaspectratiomode = "auto";
4339 layer = "bottom";
4340
4341 Matrix tlim (1, 2, 0.0);
4342 tlim(1) = 1;
4343 xlim = tlim;
4344 ylim = tlim;
4345 zlim = tlim;
4346
4347 Matrix cl (1, 2, 0);
4348 cl(1) = 1;
4349 clim = cl;
4350
4351 xlimmode = "auto";
4352 ylimmode = "auto";
4353 zlimmode = "auto";
4354 climmode = "auto";
4355
4356 xgrid = "off";
4357 ygrid = "off";
4358 zgrid = "off";
4359 xminorgrid = "off";
4360 yminorgrid = "off";
4361 zminorgrid = "off";
4362 xtick = Matrix ();
4363 ytick = Matrix ();
4364 ztick = Matrix ();
4365 xtickmode = "auto";
4366 ytickmode = "auto";
4367 ztickmode = "auto";
4368 xticklabel = "";
4369 yticklabel = "";
4370 zticklabel = "";
4371 xticklabelmode = "auto";
4372 yticklabelmode = "auto";
4373 zticklabelmode = "auto";
4374 color = color_values ("white");
4375 xcolor = color_values ("black");
4376 ycolor = color_values ("black");
4377 zcolor = color_values ("black");
4378 xscale = "linear";
4379 yscale = "linear";
4380 zscale = "linear";
4381 xdir = "normal";
4382 ydir = "normal";
4383 zdir = "normal";
4384 yaxislocation = "left";
4385 xaxislocation = "bottom";
4386
4387 // Note: camera properties will be set through update_transform
4388 camerapositionmode = "auto";
4389 cameratargetmode = "auto";
4390 cameraupvectormode = "auto";
4391 cameraviewanglemode = "auto";
4392 plotboxaspectratio = Matrix (1, 3, 1.0);
4393 drawmode = "normal";
4394 gridlinestyle = ":";
4395 linestyleorder = "-";
4396 linewidth = 0.5;
4397 minorgridlinestyle = ":";
4398 // Note: plotboxaspectratio will be set through update_aspectratiors
4399 plotboxaspectratiomode = "auto";
4400 projection = "orthographic";
4401 tickdir = "in";
4402 tickdirmode = "auto";
4403 ticklength = default_axes_ticklength ();
4404 tightinset = Matrix (1, 4, 0.0);
4405
4406 sx = "linear";
4407 sy = "linear";
4408 sz = "linear";
4409
4410 Matrix tview (1, 2, 0.0);
4411 tview(1) = 90;
4412 view = tview;
4413
4414 visible = "on";
4415 nextplot = "replace";
4416
4417 if (mode != "replace")
4418 {
4419 fontangle = "normal";
4420 fontname = OCTAVE_DEFAULT_FONTNAME;
4421 fontsize = 10;
4422 fontunits = "points";
4423 fontweight = "normal";
4424
4425 outerposition = default_axes_outerposition ();
4426 position = default_axes_position ();
4427 activepositionproperty = "outerposition";
4428 }
4429
4430 delete_children (true);
4431
4432 xlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4433 false, false);
4434
4435 ylabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4436 false, false);
4437
4438 zlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4439 false, false);
4440
4441 title = gh_manager::make_graphics_handle ("text", __myhandle__,
4442 false, false);
4443
4444 xset (xlabel.handle_value (), "handlevisibility", "off");
4445 xset (ylabel.handle_value (), "handlevisibility", "off");
4446 xset (zlabel.handle_value (), "handlevisibility", "off");
4447 xset (title.handle_value (), "handlevisibility", "off");
4448
4449 xset (xlabel.handle_value (), "horizontalalignment", "center");
4450 xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4451 xset (ylabel.handle_value (), "horizontalalignment", "center");
4452 xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4453 xset (zlabel.handle_value (), "horizontalalignment", "right");
4454 xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4455 xset (title.handle_value (), "horizontalalignment", "center");
4456 xset (title.handle_value (), "horizontalalignmentmode", "auto");
4457
4458 xset (xlabel.handle_value (), "verticalalignment", "top");
4459 xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4460 xset (ylabel.handle_value (), "verticalalignment", "bottom");
4461 xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4462 xset (title.handle_value (), "verticalalignment", "bottom");
4463 xset (title.handle_value (), "verticalalignmentmode", "auto");
4464
4465 xset (ylabel.handle_value (), "rotation", 90.0);
4466 xset (ylabel.handle_value (), "rotationmode", "auto");
4467
4468 xset (zlabel.handle_value (), "visible", "off");
4469
4470 xset (xlabel.handle_value (), "clipping", "off");
4471 xset (ylabel.handle_value (), "clipping", "off");
4472 xset (zlabel.handle_value (), "clipping", "off");
4473 xset (title.handle_value (), "clipping", "off");
4474
4475 xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4476 xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4477 xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4478 xset (title.handle_value (), "autopos_tag", "title");
4479
4480 adopt (xlabel.handle_value ());
4481 adopt (ylabel.handle_value ());
4482 adopt (zlabel.handle_value ());
4483 adopt (title.handle_value ());
4484
4485 update_transform ();
4486 update_insets ();
4487 override_defaults (obj);
4488 }
4489
4490 void
4491 axes::properties::delete_text_child (handle_property& hp)
4492 {
4493 graphics_handle h = hp.handle_value ();
4494
4495 if (h.ok ())
4496 {
4497 graphics_object go = gh_manager::get_object (h);
4498
4499 if (go.valid_object ())
4500 gh_manager::free (h);
4501
4502 base_properties::remove_child (h);
4503 }
4504
4505 // FIXME -- is it necessary to check whether the axes object is
4506 // being deleted now? I think this function is only called when an
4507 // individual child object is delete and not when the parent axes
4508 // object is deleted.
4509
4510 if (! is_beingdeleted ())
4511 {
4512 hp = gh_manager::make_graphics_handle ("text", __myhandle__,
4513 false, false);
4514
4515 xset (hp.handle_value (), "handlevisibility", "off");
4516
4517 adopt (hp.handle_value ());
4518 }
4519 }
4520
4521 void
4522 axes::properties::remove_child (const graphics_handle& h)
4523 {
4524 if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
4525 delete_text_child (xlabel);
4526 else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
4527 delete_text_child (ylabel);
4528 else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
4529 delete_text_child (zlabel);
4530 else if (title.handle_value ().ok () && h == title.handle_value ())
4531 delete_text_child (title);
4532 else
4533 base_properties::remove_child (h);
4534 }
4535
4536 inline Matrix
4537 xform_matrix (void)
4538 {
4539 Matrix m (4, 4, 0.0);
4540 for (int i = 0; i < 4; i++)
4541 m(i,i) = 1;
4542 return m;
4543 }
4544
4545 inline ColumnVector
4546 xform_vector (void)
4547 {
4548 ColumnVector v (4, 0.0);
4549 v(3) = 1;
4550 return v;
4551 }
4552
4553 inline ColumnVector
4554 xform_vector (double x, double y, double z)
4555 {
4556 ColumnVector v (4, 1.0);
4557 v(0) = x; v(1) = y; v(2) = z;
4558 return v;
4559 }
4560
4561 inline ColumnVector
4562 transform (const Matrix& m, double x, double y, double z)
4563 {
4564 return (m * xform_vector (x, y, z));
4565 }
4566
4567 inline Matrix
4568 xform_scale (double x, double y, double z)
4569 {
4570 Matrix m (4, 4, 0.0);
4571 m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
4572 return m;
4573 }
4574
4575 inline Matrix
4576 xform_translate (double x, double y, double z)
4577 {
4578 Matrix m = xform_matrix ();
4579 m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
4580 return m;
4581 }
4582
4583 inline void
4584 scale (Matrix& m, double x, double y, double z)
4585 {
4586 m = m * xform_scale (x, y, z);
4587 }
4588
4589 inline void
4590 translate (Matrix& m, double x, double y, double z)
4591 {
4592 m = m * xform_translate (x, y, z);
4593 }
4594
4595 inline void
4596 xform (ColumnVector& v, const Matrix& m)
4597 {
4598 v = m*v;
4599 }
4600
4601 inline void
4602 scale (ColumnVector& v, double x, double y, double z)
4603 {
4604 v(0) *= x;
4605 v(1) *= y;
4606 v(2) *= z;
4607 }
4608
4609 inline void
4610 translate (ColumnVector& v, double x, double y, double z)
4611 {
4612 v(0) += x;
4613 v(1) += y;
4614 v(2) += z;
4615 }
4616
4617 inline void
4618 normalize (ColumnVector& v)
4619 {
4620 double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
4621 scale (v, fact, fact, fact);
4622 }
4623
4624 inline double
4625 dot (const ColumnVector& v1, const ColumnVector& v2)
4626 {
4627 return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
4628 }
4629
4630 inline double
4631 norm (const ColumnVector& v)
4632 {
4633 return sqrt (dot (v, v));
4634 }
4635
4636 inline ColumnVector
4637 cross (const ColumnVector& v1, const ColumnVector& v2)
4638 {
4639 ColumnVector r = xform_vector ();
4640 r(0) = v1(1)*v2(2)-v1(2)*v2(1);
4641 r(1) = v1(2)*v2(0)-v1(0)*v2(2);
4642 r(2) = v1(0)*v2(1)-v1(1)*v2(0);
4643 return r;
4644 }
4645
4646 inline Matrix
4647 unit_cube (void)
4648 {
4649 static double data[32] = {
4650 0,0,0,1,
4651 1,0,0,1,
4652 0,1,0,1,
4653 0,0,1,1,
4654 1,1,0,1,
4655 1,0,1,1,
4656 0,1,1,1,
4657 1,1,1,1};
4658 Matrix m (4, 8);
4659 memcpy (m.fortran_vec (), data, sizeof (double)*32);
4660 return m;
4661 }
4662
4663 inline ColumnVector
4664 cam2xform (const Array<double>& m)
4665 {
4666 ColumnVector retval (4, 1.0);
4667 memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
4668 return retval;
4669 }
4670
4671 inline RowVector
4672 xform2cam (const ColumnVector& v)
4673 {
4674 return v.extract_n (0, 3).transpose ();
4675 }
4676
4677 void
4678 axes::properties::update_camera (void)
4679 {
4680 double xd = (xdir_is ("normal") ? 1 : -1);
4681 double yd = (ydir_is ("normal") ? 1 : -1);
4682 double zd = (zdir_is ("normal") ? 1 : -1);
4683
4684 Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
4685 Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
4686 Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
4687
4688 double xo = xlimits(xd > 0 ? 0 : 1);
4689 double yo = ylimits(yd > 0 ? 0 : 1);
4690 double zo = zlimits(zd > 0 ? 0 : 1);
4691
4692 Matrix pb = get_plotboxaspectratio ().matrix_value ();
4693
4694 bool autocam = (camerapositionmode_is ("auto")
4695 && cameratargetmode_is ("auto")
4696 && cameraupvectormode_is ("auto")
4697 && cameraviewanglemode_is ("auto"));
4698 bool dowarp = (autocam && dataaspectratiomode_is ("auto")
4699 && plotboxaspectratiomode_is ("auto"));
4700
4701 ColumnVector c_eye (xform_vector ());
4702 ColumnVector c_center (xform_vector ());
4703 ColumnVector c_upv (xform_vector ());
4704
4705 if (cameratargetmode_is ("auto"))
4706 {
4707 c_center(0) = (xlimits(0)+xlimits(1))/2;
4708 c_center(1) = (ylimits(0)+ylimits(1))/2;
4709 c_center(2) = (zlimits(0)+zlimits(1))/2;
4710
4711 cameratarget = xform2cam (c_center);
4712 }
4713 else
4714 c_center = cam2xform (get_cameratarget ().matrix_value ());
4715
4716 if (camerapositionmode_is ("auto"))
4717 {
4718 Matrix tview = get_view ().matrix_value ();
4719 double az = tview(0), el = tview(1);
4720 double d = 5 * sqrt (pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
4721
4722 if (el == 90 || el == -90)
4723 c_eye(2) = d*signum (el);
4724 else
4725 {
4726 az *= M_PI/180.0;
4727 el *= M_PI/180.0;
4728 c_eye(0) = d * cos (el) * sin (az);
4729 c_eye(1) = -d* cos (el) * cos (az);
4730 c_eye(2) = d * sin (el);
4731 }
4732 c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
4733 c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
4734 c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
4735
4736 cameraposition = xform2cam (c_eye);
4737 }
4738 else
4739 c_eye = cam2xform (get_cameraposition ().matrix_value ());
4740
4741 if (cameraupvectormode_is ("auto"))
4742 {
4743 Matrix tview = get_view ().matrix_value ();
4744 double az = tview(0), el = tview(1);
4745
4746 if (el == 90 || el == -90)
4747 {
4748 c_upv(0) =
4749 -signum (el) *sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
4750 c_upv(1) =
4751 signum (el) * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
4752 }
4753 else
4754 c_upv(2) = 1;
4755
4756 cameraupvector = xform2cam (c_upv);
4757 }
4758 else
4759 c_upv = cam2xform (get_cameraupvector ().matrix_value ());
4760
4761 Matrix x_view = xform_matrix ();
4762 Matrix x_projection = xform_matrix ();
4763 Matrix x_viewport = xform_matrix ();
4764 Matrix x_normrender = xform_matrix ();
4765 Matrix x_pre = xform_matrix ();
4766
4767 x_render = xform_matrix ();
4768 x_render_inv = xform_matrix ();
4769
4770 scale (x_pre, pb(0), pb(1), pb(2));
4771 translate (x_pre, -0.5, -0.5, -0.5);
4772 scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4773 zd/(zlimits(1)-zlimits(0)));
4774 translate (x_pre, -xo, -yo, -zo);
4775
4776 xform (c_eye, x_pre);
4777 xform (c_center, x_pre);
4778 scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
4779 pb(2)/(zlimits(1)-zlimits(0)));
4780 translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
4781
4782 ColumnVector F (c_center), f (F), UP (c_upv);
4783 normalize (f);
4784 normalize (UP);
4785
4786 if (std::abs (dot (f, UP)) > 1e-15)
4787 {
4788 double fa = 1 / sqrt(1-f(2)*f(2));
4789 scale (UP, fa, fa, fa);
4790 }
4791
4792 ColumnVector s = cross (f, UP);
4793 ColumnVector u = cross (s, f);
4794
4795 scale (x_view, 1, 1, -1);
4796 Matrix l = xform_matrix ();
4797 l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
4798 l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
4799 l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
4800 x_view = x_view * l;
4801 translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
4802 scale (x_view, pb(0), pb(1), pb(2));
4803 translate (x_view, -0.5, -0.5, -0.5);
4804
4805 Matrix x_cube = x_view * unit_cube ();
4806 ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
4807 double xM = cmax(0)-cmin(0);
4808 double yM = cmax(1)-cmin(1);
4809
4810 Matrix bb = get_boundingbox (true);
4811
4812 double v_angle;
4813
4814 if (cameraviewanglemode_is ("auto"))
4815 {
4816 double af;
4817
4818 // FIXME -- was this really needed? When compared to Matlab, it
4819 // does not seem to be required. Need investigation with concrete
4820 // graphics toolkit to see results visually.
4821 if (false && dowarp)
4822 af = 1.0 / (xM > yM ? xM : yM);
4823 else
4824 {
4825 if ((bb(2)/bb(3)) > (xM/yM))
4826 af = 1.0 / yM;
4827 else
4828 af = 1.0 / xM;
4829 }
4830 v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
4831
4832 cameraviewangle = v_angle;
4833 }
4834 else
4835 v_angle = get_cameraviewangle ();
4836
4837 double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
4838 scale (x_projection, pf, pf, 1);
4839
4840 if (dowarp)
4841 {
4842 xM *= pf;
4843 yM *= pf;
4844 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
4845 scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
4846 }
4847 else
4848 {
4849 double pix = 1;
4850 if (autocam)
4851 {
4852 if ((bb(2)/bb(3)) > (xM/yM))
4853 pix = bb(3);
4854 else
4855 pix = bb(2);
4856 }
4857 else
4858 pix = (bb(2) < bb(3) ? bb(2) : bb(3));
4859 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
4860 scale (x_viewport, pix, -pix, 1);
4861 }
4862
4863 x_normrender = x_viewport * x_projection * x_view;
4864
4865 x_cube = x_normrender * unit_cube ();
4866 cmin = x_cube.row_min ();
4867 cmax = x_cube.row_max ();
4868 x_zlim.resize (1, 2);
4869 x_zlim(0) = cmin(2);
4870 x_zlim(1) = cmax(2);
4871
4872 x_render = x_normrender;
4873 scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4874 zd/(zlimits(1)-zlimits(0)));
4875 translate (x_render, -xo, -yo, -zo);
4876
4877 x_viewtransform = x_view;
4878 x_projectiontransform = x_projection;
4879 x_viewporttransform = x_viewport;
4880 x_normrendertransform = x_normrender;
4881 x_rendertransform = x_render;
4882
4883 x_render_inv = x_render.inverse ();
4884
4885 // Note: these matrices are a slight modified version of the regular
4886 // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
4887 // => x_gl_mat2)
4888 x_gl_mat1 = x_view;
4889 scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4890 zd/(zlimits(1)-zlimits(0)));
4891 translate (x_gl_mat1, -xo, -yo, -zo);
4892 x_gl_mat2 = x_viewport * x_projection;
4893 }
4894
4895 static bool updating_axes_layout = false;
4896
4897 void
4898 axes::properties::update_axes_layout (void)
4899 {
4900 if (updating_axes_layout)
4901 return;
4902
4903 graphics_xform xform = get_transform ();
4904
4905 double xd = (xdir_is ("normal") ? 1 : -1);
4906 double yd = (ydir_is ("normal") ? 1 : -1);
4907 double zd = (zdir_is ("normal") ? 1 : -1);
4908
4909 const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
4910 const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
4911 const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
4912 double x_min = xlims(0), x_max = xlims(1);
4913 double y_min = ylims(0), y_max = ylims(1);
4914 double z_min = zlims(0), z_max = zlims(1);
4915
4916 ColumnVector p1, p2, dir (3);
4917
4918 xstate = ystate = zstate = AXE_ANY_DIR;
4919
4920 p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
4921 p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
4922 dir(0) = xround (p2(0)-p1(0));
4923 dir(1) = xround (p2(1)-p1(1));
4924 dir(2) = (p2(2)-p1(2));
4925 if (dir(0) == 0 && dir(1) == 0)
4926 xstate = AXE_DEPTH_DIR;
4927 else if (dir(2) == 0)
4928 {
4929 if (dir(0) == 0)
4930 xstate = AXE_VERT_DIR;
4931 else if (dir(1) == 0)
4932 xstate = AXE_HORZ_DIR;
4933 }
4934
4935 if (dir(2) == 0)
4936 {
4937 if (dir(1) == 0)
4938 xPlane = (dir(0) > 0 ? x_max : x_min);
4939 else
4940 xPlane = (dir(1) < 0 ? x_max : x_min);
4941 }
4942 else
4943 xPlane = (dir(2) < 0 ? x_min : x_max);
4944
4945 xPlaneN = (xPlane == x_min ? x_max : x_min);
4946 fx = (x_max-x_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4947
4948 p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
4949 p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
4950 dir(0) = xround (p2(0)-p1(0));
4951 dir(1) = xround (p2(1)-p1(1));
4952 dir(2) = (p2(2)-p1(2));
4953 if (dir(0) == 0 && dir(1) == 0)
4954 ystate = AXE_DEPTH_DIR;
4955 else if (dir(2) == 0)
4956 {
4957 if (dir(0) == 0)
4958 ystate = AXE_VERT_DIR;
4959 else if (dir(1) == 0)
4960 ystate = AXE_HORZ_DIR;
4961 }
4962
4963 if (dir(2) == 0)
4964 {
4965 if (dir(1) == 0)
4966 yPlane = (dir(0) > 0 ? y_max : y_min);
4967 else
4968 yPlane = (dir(1) < 0 ? y_max : y_min);
4969 }
4970 else
4971 yPlane = (dir(2) < 0 ? y_min : y_max);
4972
4973 yPlaneN = (yPlane == y_min ? y_max : y_min);
4974 fy = (y_max-y_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4975
4976 p1 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
4977 p2 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
4978 dir(0) = xround (p2(0)-p1(0));
4979 dir(1) = xround (p2(1)-p1(1));
4980 dir(2) = (p2(2)-p1(2));
4981 if (dir(0) == 0 && dir(1) == 0)
4982 zstate = AXE_DEPTH_DIR;
4983 else if (dir(2) == 0)
4984 {
4985 if (dir(0) == 0)
4986 zstate = AXE_VERT_DIR;
4987 else if (dir(1) == 0)
4988 zstate = AXE_HORZ_DIR;
4989 }
4990
4991 if (dir(2) == 0)
4992 {
4993 if (dir(1) == 0)
4994 zPlane = (dir(0) > 0 ? z_min : z_max);
4995 else
4996 zPlane = (dir(1) < 0 ? z_min : z_max);
4997 }
4998 else
4999 zPlane = (dir(2) < 0 ? z_min : z_max);
5000
5001 zPlaneN = (zPlane == z_min ? z_max : z_min);
5002 fz = (z_max-z_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
5003
5004 unwind_protect frame;
5005 frame.protect_var (updating_axes_layout);
5006 updating_axes_layout = true;
5007
5008 xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
5009 zSign = (zd*(zPlane-zPlaneN) <= 0);
5010 xyzSym = zSign ? xySym : !xySym;
5011 xpTick = (zSign ? xPlaneN : xPlane);
5012 ypTick = (zSign ? yPlaneN : yPlane);
5013 zpTick = (zSign ? zPlane : zPlaneN);
5014 xpTickN = (zSign ? xPlane : xPlaneN);
5015 ypTickN = (zSign ? yPlane : yPlaneN);
5016 zpTickN = (zSign ? zPlaneN : zPlane);
5017
5018 /* 2D mode */
5019 x2Dtop = false;
5020 y2Dright = false;
5021 layer2Dtop = false;
5022 if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
5023 {
5024 if (xaxislocation_is ("top"))
5025 {
5026 double tmp = yPlane;
5027 yPlane = yPlaneN;
5028 yPlaneN = tmp;
5029 x2Dtop = true;
5030 }
5031 ypTick = yPlaneN;
5032 ypTickN = yPlane;
5033 if (yaxislocation_is ("right"))
5034 {
5035 double tmp = xPlane;
5036 xPlane = xPlaneN;
5037 xPlaneN = tmp;
5038 y2Dright = true;
5039 }
5040 xpTick = xPlaneN;
5041 xpTickN = xPlane;
5042 if (layer_is ("top"))
5043 {
5044 zpTick = zPlaneN;
5045 layer2Dtop = true;
5046 }
5047 else
5048 zpTick = zPlane;
5049 }
5050
5051 Matrix viewmat = get_view ().matrix_value ();
5052 nearhoriz = std::abs (viewmat(1)) <= 5;
5053
5054 update_ticklength ();
5055 }
5056
5057 void
5058 axes::properties::update_ticklength (void)
5059 {
5060 bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
5061 (ystate > AXE_DEPTH_DIR ? 1 : 0) +
5062 (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5063
5064 if (tickdirmode_is ("auto"))
5065 tickdir.set (mode2d ? "in" : "out", true);
5066
5067 double ticksign = (tickdir_is ("in") ? -1 : 1);
5068
5069 Matrix bbox = get_boundingbox (true);
5070 Matrix ticklen = get_ticklength ().matrix_value ();
5071 ticklen(0) = ticklen(0) * std::max (bbox(2), bbox(3));
5072 ticklen(1) = ticklen(1) * std::max (bbox(2), bbox(3));
5073
5074 xticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5075 yticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5076 zticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5077
5078 xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
5079 ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
5080 ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
5081
5082 update_xlabel_position ();
5083 update_ylabel_position ();
5084 update_zlabel_position ();
5085 update_title_position ();
5086 }
5087
5088 /*
5089 ## FIXME: A demo can't be called in a C++ file. This should be made a test
5090 ## or moved to a .m file where it can be called.
5091 %!demo
5092 %! clf;
5093 %! subplot (2,1,1);
5094 %! plot (rand (3));
5095 %! xlabel xlabel;
5096 %! ylabel ylabel;
5097 %! title title;
5098 %! subplot (2,1,2);
5099 %! plot (rand (3));
5100 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
5101 %! xlabel xlabel;
5102 %! ylabel ylabel;
5103 %! title title;
5104 */
5105
5106 static bool updating_xlabel_position = false;
5107
5108 void
5109 axes::properties::update_xlabel_position (void)
5110 {
5111 if (updating_xlabel_position)
5112 return;
5113
5114 text::properties& xlabel_props = reinterpret_cast<text::properties&>
5115 (gh_manager::get_object (get_xlabel ()).get_properties ());
5116
5117 bool is_empty = xlabel_props.get_string ().is_empty ();
5118
5119 unwind_protect frame;
5120 frame.protect_var (updating_xlabel_position);
5121 updating_xlabel_position = true;
5122
5123 if (! is_empty)
5124 {
5125 if (xlabel_props.horizontalalignmentmode_is ("auto"))
5126 {
5127 xlabel_props.set_horizontalalignment
5128 (xstate > AXE_DEPTH_DIR
5129 ? "center" : (xyzSym ? "left" : "right"));
5130
5131 xlabel_props.set_horizontalalignmentmode ("auto");
5132 }
5133
5134 if (xlabel_props.verticalalignmentmode_is ("auto"))
5135 {
5136 xlabel_props.set_verticalalignment
5137 (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
5138
5139 xlabel_props.set_verticalalignmentmode ("auto");
5140 }
5141 }
5142
5143 if (xlabel_props.positionmode_is ("auto")
5144 || xlabel_props.rotationmode_is ("auto"))
5145 {
5146 graphics_xform xform = get_transform ();
5147
5148 Matrix ext (1, 2, 0.0);
5149 ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5150 get_xticklabel ().all_strings (),
5151 get_xlim ().matrix_value ());
5152
5153 double wmax = ext(0), hmax = ext(1), angle = 0;
5154 ColumnVector p =
5155 graphics_xform::xform_vector ((xpTickN+xpTick)/2, ypTick, zpTick);
5156
5157 bool tick_along_z = nearhoriz || xisinf (fy);
5158 if (tick_along_z)
5159 p(2) += (signum (zpTick-zpTickN)*fz*xtickoffset);
5160 else
5161 p(1) += (signum (ypTick-ypTickN)*fy*xtickoffset);
5162
5163 p = xform.transform (p(0), p(1), p(2), false);
5164
5165 switch (xstate)
5166 {
5167 case AXE_ANY_DIR:
5168 p(0) += (xyzSym ? wmax : -wmax);
5169 p(1) += hmax;
5170 break;
5171
5172 case AXE_VERT_DIR:
5173 p(0) -= wmax;
5174 angle = 90;
5175 break;
5176
5177 case AXE_HORZ_DIR:
5178 p(1) += (x2Dtop ? -hmax : hmax);
5179 break;
5180 }
5181
5182 if (xlabel_props.positionmode_is ("auto"))
5183 {
5184 p = xform.untransform (p(0), p(1), p(2), true);
5185 xlabel_props.set_position (p.extract_n (0, 3).transpose ());
5186 xlabel_props.set_positionmode ("auto");
5187 }
5188
5189 if (! is_empty && xlabel_props.rotationmode_is ("auto"))
5190 {
5191 xlabel_props.set_rotation (angle);
5192 xlabel_props.set_rotationmode ("auto");
5193 }
5194 }
5195 }
5196
5197 static bool updating_ylabel_position = false;
5198
5199 void
5200 axes::properties::update_ylabel_position (void)
5201 {
5202 if (updating_ylabel_position)
5203 return;
5204
5205 text::properties& ylabel_props = reinterpret_cast<text::properties&>
5206 (gh_manager::get_object (get_ylabel ()).get_properties ());
5207
5208 bool is_empty = ylabel_props.get_string ().is_empty ();
5209
5210 unwind_protect frame;
5211 frame.protect_var (updating_ylabel_position);
5212 updating_ylabel_position = true;
5213
5214 if (! is_empty)
5215 {
5216 if (ylabel_props.horizontalalignmentmode_is ("auto"))
5217 {
5218 ylabel_props.set_horizontalalignment
5219 (ystate > AXE_DEPTH_DIR
5220 ? "center" : (!xyzSym ? "left" : "right"));
5221
5222 ylabel_props.set_horizontalalignmentmode ("auto");
5223 }
5224
5225 if (ylabel_props.verticalalignmentmode_is ("auto"))
5226 {
5227 ylabel_props.set_verticalalignment
5228 (ystate == AXE_VERT_DIR && !y2Dright ? "bottom" : "top");
5229
5230 ylabel_props.set_verticalalignmentmode ("auto");
5231 }
5232 }
5233
5234 if (ylabel_props.positionmode_is ("auto")
5235 || ylabel_props.rotationmode_is ("auto"))
5236 {
5237 graphics_xform xform = get_transform ();
5238
5239 Matrix ext (1, 2, 0.0);
5240
5241 // The underlying get_extents() from FreeType produces mismatched values.
5242 // x-extent accurately measures the width of the glyphs.
5243 // y-extent instead measures from baseline-to-baseline.
5244 // Pad x-extent (+4) so that it approximately matches y-extent.
5245 // This keeps ylabels about the same distance from y-axis as
5246 // xlabels are from x-axis.
5247 // ALWAYS use an even number for padding or horizontal alignment
5248 // will be off.
5249 ext = get_ticklabel_extents (get_ytick ().matrix_value (),
5250 get_yticklabel ().all_strings (),
5251 get_ylim ().matrix_value ());
5252
5253 double wmax = ext(0)+4, hmax = ext(1), angle = 0;
5254 ColumnVector p =
5255 graphics_xform::xform_vector (xpTick, (ypTickN+ypTick)/2, zpTick);
5256
5257 bool tick_along_z = nearhoriz || xisinf (fx);
5258 if (tick_along_z)
5259 p(2) += (signum (zpTick-zpTickN)*fz*ytickoffset);
5260 else
5261 p(0) += (signum (xpTick-xpTickN)*fx*ytickoffset);
5262
5263 p = xform.transform (p(0), p(1), p(2), false);
5264
5265 switch (ystate)
5266 {
5267 case AXE_ANY_DIR:
5268 p(0) += (!xyzSym ? wmax : -wmax);
5269 p(1) += hmax;
5270 break;
5271
5272 case AXE_VERT_DIR:
5273 p(0) += (y2Dright ? wmax : -wmax);
5274 angle = 90;
5275 break;
5276
5277 case AXE_HORZ_DIR:
5278 p(1) += hmax;
5279 break;
5280 }
5281
5282 if (ylabel_props.positionmode_is ("auto"))
5283 {
5284 p = xform.untransform (p(0), p(1), p(2), true);
5285 ylabel_props.set_position (p.extract_n (0, 3).transpose ());
5286 ylabel_props.set_positionmode ("auto");
5287 }
5288
5289 if (! is_empty && ylabel_props.rotationmode_is ("auto"))
5290 {
5291 ylabel_props.set_rotation (angle);
5292 ylabel_props.set_rotationmode ("auto");
5293 }
5294 }
5295 }
5296
5297 static bool updating_zlabel_position = false;
5298
5299 void
5300 axes::properties::update_zlabel_position (void)
5301 {
5302 if (updating_zlabel_position)
5303 return;
5304
5305 text::properties& zlabel_props = reinterpret_cast<text::properties&>
5306 (gh_manager::get_object (get_zlabel ()).get_properties ());
5307
5308 bool camAuto = cameraupvectormode_is ("auto");
5309 bool is_empty = zlabel_props.get_string ().is_empty ();
5310
5311 unwind_protect frame;
5312 frame.protect_var (updating_zlabel_position);
5313 updating_zlabel_position = true;
5314
5315 if (! is_empty)
5316 {
5317 if (zlabel_props.horizontalalignmentmode_is ("auto"))
5318 {
5319 zlabel_props.set_horizontalalignment
5320 ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
5321
5322 zlabel_props.set_horizontalalignmentmode ("auto");
5323 }
5324
5325 if (zlabel_props.verticalalignmentmode_is ("auto"))
5326 {
5327 zlabel_props.set_verticalalignment
5328 (zstate == AXE_VERT_DIR
5329 ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
5330
5331 zlabel_props.set_verticalalignmentmode ("auto");
5332 }
5333 }
5334
5335 if (zlabel_props.positionmode_is ("auto")
5336 || zlabel_props.rotationmode_is ("auto"))
5337 {
5338 graphics_xform xform = get_transform ();
5339
5340 Matrix ext (1, 2, 0.0);
5341 ext = get_ticklabel_extents (get_ztick ().matrix_value (),
5342 get_zticklabel ().all_strings (),
5343 get_zlim ().matrix_value ());
5344
5345 double wmax = ext(0), hmax = ext(1), angle = 0;
5346 ColumnVector p;
5347
5348 if (xySym)
5349 {
5350 p = graphics_xform::xform_vector (xPlaneN, yPlane,
5351 (zpTickN+zpTick)/2);
5352 if (xisinf (fy))
5353 p(0) += (signum (xPlaneN-xPlane)*fx*ztickoffset);
5354 else
5355 p(1) += (signum (yPlane-yPlaneN)*fy*ztickoffset);
5356 }
5357 else
5358 {
5359 p = graphics_xform::xform_vector (xPlane, yPlaneN,
5360 (zpTickN+zpTick)/2);
5361 if (xisinf (fx))
5362 p(1) += (signum (yPlaneN-yPlane)*fy*ztickoffset);
5363 else
5364 p(0) += (signum (xPlane-xPlaneN)*fx*ztickoffset);
5365 }
5366
5367 p = xform.transform (p(0), p(1), p(2), false);
5368
5369 switch (zstate)
5370 {
5371 case AXE_ANY_DIR:
5372 if (camAuto)
5373 {
5374 p(0) -= wmax;
5375 angle = 90;
5376 }
5377
5378 // FIXME -- what's the correct offset?
5379 //
5380 // p[0] += (!xySym ? wmax : -wmax);
5381 // p[1] += (zSign ? hmax : -hmax);
5382
5383 break;
5384
5385 case AXE_VERT_DIR:
5386 p(0) -= wmax;
5387 angle = 90;
5388 break;
5389
5390 case AXE_HORZ_DIR:
5391 p(1) += hmax;
5392 break;
5393 }
5394
5395 if (zlabel_props.positionmode_is ("auto"))
5396 {
5397 p = xform.untransform (p(0), p(1), p(2), true);
5398 zlabel_props.set_position (p.extract_n (0, 3).transpose ());
5399 zlabel_props.set_positionmode ("auto");
5400 }
5401
5402 if (! is_empty && zlabel_props.rotationmode_is ("auto"))
5403 {
5404 zlabel_props.set_rotation (angle);
5405 zlabel_props.set_rotationmode ("auto");
5406 }
5407 }
5408 }
5409
5410 static bool updating_title_position = false;
5411
5412 void
5413 axes::properties::update_title_position (void)
5414 {
5415 if (updating_title_position)
5416 return;
5417
5418 text::properties& title_props = reinterpret_cast<text::properties&>
5419 (gh_manager::get_object (get_title ()).get_properties ());
5420
5421 unwind_protect frame;
5422 frame.protect_var (updating_title_position);
5423 updating_title_position = true;
5424
5425 if (title_props.positionmode_is ("auto"))
5426 {
5427 graphics_xform xform = get_transform ();
5428
5429 // FIXME: bbox should be stored in axes::properties
5430 Matrix bbox = get_extent (false);
5431
5432 ColumnVector p =
5433 graphics_xform::xform_vector (bbox(0)+bbox(2)/2,
5434 bbox(1)-10,
5435 (x_zlim(0)+x_zlim(1))/2);
5436
5437 if (x2Dtop)
5438 {
5439 Matrix ext (1, 2, 0.0);
5440 ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5441 get_xticklabel ().all_strings (),
5442 get_xlim ().matrix_value ());
5443 p(1) -= ext(1);
5444 }
5445
5446 p = xform.untransform (p(0), p(1), p(2), true);
5447
5448 title_props.set_position (p.extract_n (0, 3).transpose ());
5449 title_props.set_positionmode ("auto");
5450 }
5451 }
5452
5453 void
5454 axes::properties::update_autopos (const std::string& elem_type)
5455 {
5456 if (elem_type == "xlabel")
5457 update_xlabel_position ();
5458 else if (elem_type == "ylabel")
5459 update_ylabel_position ();
5460 else if (elem_type == "zlabel")
5461 update_zlabel_position ();
5462 else if (elem_type == "title")
5463 update_title_position ();
5464 else if (elem_type == "sync")
5465 sync_positions ();
5466 }
5467
5468 static void
5469 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
5470 double xlength, double ylength, double zlength)
5471 {
5472 double xval = xlength/scalefactors(0);
5473 double yval = ylength/scalefactors(1);
5474 double zval = zlength/scalefactors(2);
5475
5476 double minval = xmin (xmin (xval, yval), zval);
5477
5478 aspectratios(0) = xval/minval;
5479 aspectratios(1) = yval/minval;
5480 aspectratios(2) = zval/minval;
5481 }
5482
5483 static void
5484 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
5485 double pbfactor, double dafactor, char limit_type, bool tight)
5486 {
5487 if (tight)
5488 {
5489 double minval = octave_Inf;
5490 double maxval = -octave_Inf;
5491 double min_pos = octave_Inf;
5492 double max_neg = -octave_Inf;
5493 get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
5494 if (!xisinf (minval) && !xisnan (minval)
5495 && !xisinf (maxval) && !xisnan (maxval))
5496 {
5497 limits(0) = minval;
5498 limits(1) = maxval;
5499 s = xmax(s, (maxval - minval) / (pbfactor * dafactor));
5500 }
5501 }
5502 else
5503 s = xmax(s, (limits(1) - limits(0)) / (pbfactor * dafactor));
5504 }
5505
5506 static bool updating_aspectratios = false;
5507
5508 void
5509 axes::properties::update_aspectratios (void)
5510 {
5511 if (updating_aspectratios)
5512 return;
5513
5514 Matrix xlimits = get_xlim ().matrix_value ();
5515 Matrix ylimits = get_ylim ().matrix_value ();
5516 Matrix zlimits = get_zlim ().matrix_value ();
5517
5518 double dx = (xlimits(1)-xlimits(0));
5519 double dy = (ylimits(1)-ylimits(0));
5520 double dz = (zlimits(1)-zlimits(0));
5521
5522 Matrix da = get_dataaspectratio ().matrix_value ();
5523 Matrix pba = get_plotboxaspectratio ().matrix_value ();
5524
5525 if (dataaspectratiomode_is ("auto"))
5526 {
5527 if (plotboxaspectratiomode_is ("auto"))
5528 {
5529 pba = Matrix (1, 3, 1.0);
5530 plotboxaspectratio.set (pba, false);
5531 }
5532
5533 normalized_aspectratios (da, pba, dx, dy, dz);
5534 dataaspectratio.set (da, false);
5535 }
5536 else if (plotboxaspectratiomode_is ("auto"))
5537 {
5538 normalized_aspectratios (pba, da, dx, dy, dz);
5539 plotboxaspectratio.set (pba, false);
5540 }
5541 else
5542 {
5543 double s = -octave_Inf;
5544 bool modified_limits = false;
5545 Matrix kids;
5546
5547 if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
5548 {
5549 modified_limits = true;
5550 kids = get_children ();
5551 max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
5552 max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
5553 max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
5554 }
5555 else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
5556 {
5557 modified_limits = true;
5558 max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
5559 }
5560 else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
5561 {
5562 modified_limits = true;
5563 max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
5564 }
5565 else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
5566 {
5567 modified_limits = true;
5568 max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
5569 }
5570
5571 if (modified_limits)
5572 {
5573
5574 unwind_protect frame;
5575 frame.protect_var (updating_aspectratios);
5576
5577 updating_aspectratios = true;
5578
5579 dx = pba(0) *da(0);
5580 dy = pba(1) *da(1);
5581 dz = pba(2) *da(2);
5582 if (xisinf (s))
5583 s = 1 / xmin (xmin (dx, dy), dz);
5584
5585 if (xlimmode_is ("auto"))
5586 {
5587 dx = s * dx;
5588 xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
5589 xlimits(1) = xlimits(0) + dx;
5590 set_xlim (xlimits);
5591 set_xlimmode ("auto");
5592 }
5593
5594 if (ylimmode_is ("auto"))
5595 {
5596 dy = s * dy;
5597 ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
5598 ylimits(1) = ylimits(0) + dy;
5599 set_ylim (ylimits);
5600 set_ylimmode ("auto");
5601 }
5602
5603 if (zlimmode_is ("auto"))
5604 {
5605 dz = s * dz;
5606 zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
5607 zlimits(1) = zlimits(0) + dz;
5608 set_zlim (zlimits);
5609 set_zlimmode ("auto");
5610 }
5611 }
5612 else
5613 {
5614 normalized_aspectratios (pba, da, dx, dy, dz);
5615 plotboxaspectratio.set (pba, false);
5616 }
5617 }
5618 }
5619
5620 void
5621 axes::properties::update_font (void)
5622 {
5623 #ifdef HAVE_FREETYPE
5624 #ifdef HAVE_FONTCONFIG
5625 text_renderer.set_font (get ("fontname").string_value (),
5626 get ("fontweight").string_value (),
5627 get ("fontangle").string_value (),
5628 get ("fontsize").double_value ());
5629 #endif
5630 #endif
5631 }
5632
5633 // The INTERNAL flag defines whether position or outerposition is used.
5634
5635 Matrix
5636 axes::properties::get_boundingbox (bool internal,
5637 const Matrix& parent_pix_size) const
5638 {
5639 Matrix pos = (internal ?
5640 get_position ().matrix_value ()
5641 : get_outerposition ().matrix_value ());
5642 Matrix parent_size (parent_pix_size);
5643
5644 if (parent_size.numel () == 0)
5645 {
5646 graphics_object obj = gh_manager::get_object (get_parent ());
5647
5648 parent_size =
5649 obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
5650 }
5651
5652 pos = convert_position (pos, get_units (), "pixels", parent_size);
5653
5654 pos(0)--;
5655 pos(1)--;
5656 pos(1) = parent_size(1) - pos(1) - pos(3);
5657
5658 return pos;
5659 }
5660
5661 Matrix
5662 axes::properties::get_extent (bool with_text, bool only_text_height) const
5663 {
5664 graphics_xform xform = get_transform ();
5665
5666 Matrix ext (1, 4, 0.0);
5667 ext(0) = octave_Inf;
5668 ext(1) = octave_Inf;
5669 ext(2) = -octave_Inf;
5670 ext(3) = -octave_Inf;
5671 for (int i = 0; i <= 1; i++)
5672 for (int j = 0; j <= 1; j++)
5673 for (int k = 0; k <= 1; k++)
5674 {
5675 ColumnVector p = xform.transform (i ? xPlaneN : xPlane,
5676 j ? yPlaneN : yPlane,
5677 k ? zPlaneN : zPlane, false);
5678 ext(0) = std::min (ext(0), p(0));
5679 ext(1) = std::min (ext(1), p(1));
5680 ext(2) = std::max (ext(2), p(0));
5681 ext(3) = std::max (ext(3), p(1));
5682 }
5683
5684 if (with_text)
5685 {
5686 for (int i = 0; i < 4; i++)
5687 {
5688 graphics_handle text_handle;
5689 if (i == 0)
5690 text_handle = get_title ();
5691 else if (i == 1)
5692 text_handle = get_xlabel ();
5693 else if (i == 2)
5694 text_handle = get_ylabel ();
5695 else if (i == 3)
5696 text_handle = get_zlabel ();
5697
5698 text::properties& text_props = reinterpret_cast<text::properties&>
5699 (gh_manager::get_object (text_handle).get_properties ());
5700
5701 Matrix text_pos = text_props.get_data_position ();
5702 text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
5703 if (text_props.get_string ().is_empty ())
5704 {
5705 ext(0) = std::min (ext(0), text_pos(0));
5706 ext(1) = std::min (ext(1), text_pos(1));
5707 ext(2) = std::max (ext(2), text_pos(0));
5708 ext(3) = std::max (ext(3), text_pos(1));
5709 }
5710 else
5711 {
5712 Matrix text_ext = text_props.get_extent_matrix ();
5713
5714 bool ignore_horizontal = false;
5715 bool ignore_vertical = false;
5716 if (only_text_height)
5717 {
5718 double text_rotation = text_props.get_rotation ();
5719 if (text_rotation == 0. || text_rotation == 180.)
5720 ignore_horizontal = true;
5721 else if (text_rotation == 90. || text_rotation == 270.)
5722 ignore_vertical = true;
5723 }
5724
5725 if (! ignore_horizontal)
5726 {
5727 ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
5728 ext(2) = std::max (ext(2), text_pos(0)+text_ext(0)+text_ext(2));
5729 }
5730
5731 if (! ignore_vertical)
5732 {
5733 ext(1) = std::min (ext(1), text_pos(1)-text_ext(1)-text_ext(3));
5734 ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
5735 }
5736 }
5737 }
5738 }
5739
5740 ext(2) = ext(2)-ext(0);
5741 ext(3) = ext(3)-ext(1);
5742
5743 return ext;
5744 }
5745
5746 static octave_value
5747 convert_ticklabel_string (const octave_value& val)
5748 {
5749 octave_value retval = val;
5750
5751 if (val.is_cellstr ())
5752 {
5753 // Always return a column vector for Matlab Compatibility
5754 if (val.columns () > 1)
5755 retval = val.reshape (dim_vector (val.numel (), 1));
5756 }
5757 else
5758 {
5759 string_vector sv;
5760 if (val.is_numeric_type ())
5761 {
5762 NDArray data = val.array_value ();
5763 std::ostringstream oss;
5764 oss.precision (5);
5765 for (octave_idx_type i = 0; i < val.numel (); i++)
5766 {
5767 oss.str ("");
5768 oss << data(i);
5769 sv.append (oss.str ());
5770 }
5771 }
5772 else if (val.is_string () && val.rows () == 1)
5773 {
5774 std::string valstr = val.string_value ();
5775 std::istringstream iss (valstr);
5776 std::string tmpstr;
5777
5778 // Split string with delimiter '|'
5779 while (std::getline (iss, tmpstr, '|'))
5780 sv.append (tmpstr);
5781
5782 // If string ends with '|' Matlab appends a null string
5783 if (*valstr.rbegin () == '|')
5784 sv.append (std::string (""));
5785 }
5786 else
5787 return retval;
5788
5789 charMatrix chmat (sv, ' ');
5790
5791 retval = octave_value (chmat);
5792 }
5793
5794 return retval;
5795 }
5796
5797 void
5798 axes::properties::set_xticklabel (const octave_value& v)
5799 {
5800 if (!error_state)
5801 {
5802 if (xticklabel.set (convert_ticklabel_string (v), false))
5803 {
5804 set_xticklabelmode ("manual");
5805 xticklabel.run_listeners (POSTSET);
5806 mark_modified ();
5807 }
5808 else
5809 set_xticklabelmode ("manual");
5810 }
5811 }
5812
5813 void
5814 axes::properties::set_yticklabel (const octave_value& v)
5815 {
5816 if (!error_state)
5817 {
5818 if (yticklabel.set (convert_ticklabel_string (v), false))
5819 {
5820 set_yticklabelmode ("manual");
5821 yticklabel.run_listeners (POSTSET);
5822 mark_modified ();
5823 }
5824 else
5825 set_yticklabelmode ("manual");
5826 }
5827 }
5828
5829 void
5830 axes::properties::set_zticklabel (const octave_value& v)
5831 {
5832 if (!error_state)
5833 {
5834 if (zticklabel.set (convert_ticklabel_string (v), false))
5835 {
5836 set_zticklabelmode ("manual");
5837 zticklabel.run_listeners (POSTSET);
5838 mark_modified ();
5839 }
5840 else
5841 set_zticklabelmode ("manual");
5842 }
5843 }
5844
5845 void
5846 axes::properties::set_units (const octave_value& v)
5847 {
5848 if (! error_state)
5849 {
5850 caseless_str old_units = get_units ();
5851 if (units.set (v, true))
5852 {
5853 update_units (old_units);
5854 mark_modified ();
5855 }
5856 }
5857 }
5858
5859 void
5860 axes::properties::update_units (const caseless_str& old_units)
5861 {
5862 graphics_object obj = gh_manager::get_object (get_parent ());
5863 Matrix parent_bb = obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
5864 caseless_str new_units = get_units ();
5865 position.set (octave_value (convert_position (get_position ().matrix_value (), old_units, new_units, parent_bb)), false);
5866 outerposition.set (octave_value (convert_position (get_outerposition ().matrix_value (), old_units, new_units, parent_bb)), false);
5867 tightinset.set (octave_value (convert_position (get_tightinset ().matrix_value (), old_units, new_units, parent_bb)), false);
5868 looseinset.set (octave_value (convert_position (get_looseinset ().matrix_value (), old_units, new_units, parent_bb)), false);
5869 }
5870
5871 void
5872 axes::properties::set_fontunits (const octave_value& v)
5873 {
5874 if (! error_state)
5875 {
5876 caseless_str old_fontunits = get_fontunits ();
5877 if (fontunits.set (v, true))
5878 {
5879 update_fontunits (old_fontunits);
5880 mark_modified ();
5881 }
5882 }
5883 }
5884
5885 void
5886 axes::properties::update_fontunits (const caseless_str& old_units)
5887 {
5888 caseless_str new_units = get_fontunits ();
5889 double parent_height = get_boundingbox (true).elem (3);
5890 double fsz = get_fontsize ();
5891
5892 fsz = convert_font_size (fsz, old_units, new_units, parent_height);
5893
5894 set_fontsize (octave_value (fsz));
5895 }
5896
5897 double
5898 axes::properties::get_fontsize_points (double box_pix_height) const
5899 {
5900 double fs = get_fontsize ();
5901 double parent_height = box_pix_height;
5902
5903 if (fontunits_is ("normalized") && parent_height <= 0)
5904 parent_height = get_boundingbox (true).elem (3);
5905
5906 return convert_font_size (fs, get_fontunits (), "points", parent_height);
5907 }
5908
5909 ColumnVector
5910 graphics_xform::xform_vector (double x, double y, double z)
5911 {
5912 return ::xform_vector (x, y, z);
5913 }
5914
5915 Matrix
5916 graphics_xform::xform_eye (void)
5917 {
5918 return ::xform_matrix ();
5919 }
5920
5921 ColumnVector
5922 graphics_xform::transform (double x, double y, double z,
5923 bool use_scale) const
5924 {
5925 if (use_scale)
5926 {
5927 x = sx.scale (x);
5928 y = sy.scale (y);
5929 z = sz.scale (z);
5930 }
5931
5932 return ::transform (xform, x, y, z);
5933 }
5934
5935 ColumnVector
5936 graphics_xform::untransform (double x, double y, double z,
5937 bool use_scale) const
5938 {
5939 ColumnVector v = ::transform (xform_inv, x, y, z);
5940
5941 if (use_scale)
5942 {
5943 v(0) = sx.unscale (v(0));
5944 v(1) = sy.unscale (v(1));
5945 v(2) = sz.unscale (v(2));
5946 }
5947
5948 return v;
5949 }
5950
5951 octave_value
5952 axes::get_default (const caseless_str& name) const
5953 {
5954 octave_value retval = default_properties.lookup (name);
5955
5956 if (retval.is_undefined ())
5957 {
5958 graphics_handle parent = get_parent ();
5959 graphics_object parent_obj = gh_manager::get_object (parent);
5960
5961 retval = parent_obj.get_default (name);
5962 }
5963
5964 return retval;
5965 }
5966
5967 // FIXME -- remove.
5968 // FIXME -- maybe this should go into array_property class?
5969 /*
5970 static void
5971 check_limit_vals (double& min_val, double& max_val,
5972 double& min_pos, double& max_neg,
5973 const array_property& data)
5974 {
5975 double val = data.min_val ();
5976 if (! (xisinf (val) || xisnan (val)) && val < min_val)
5977 min_val = val;
5978 val = data.max_val ();
5979 if (! (xisinf (val) || xisnan (val)) && val > max_val)
5980 max_val = val;
5981 val = data.min_pos ();
5982 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
5983 min_pos = val;
5984 val = data.max_neg ();
5985 if (! (xisinf (val) || xisnan (val)) && val < 0 && val > max_neg)
5986 max_neg = val;
5987 }
5988 */
5989
5990 static void
5991 check_limit_vals (double& min_val, double& max_val,
5992 double& min_pos, double& max_neg,
5993 const octave_value& data)
5994 {
5995 if (data.is_matrix_type ())
5996 {
5997 Matrix m = data.matrix_value ();
5998
5999 if (! error_state && m.numel () == 4)
6000 {
6001 double val;
6002
6003 val = m(0);
6004 if (! (xisinf (val) || xisnan (val)) && val < min_val)
6005 min_val = val;
6006
6007 val = m(1);
6008 if (! (xisinf (val) || xisnan (val)) && val > max_val)
6009 max_val = val;
6010
6011 val = m(2);
6012 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
6013 min_pos = val;
6014
6015 val = m(3);
6016 if (! (xisinf (val) || xisnan (val)) && val < 0 && val > max_neg)
6017 max_neg = val;
6018 }
6019 }
6020 }
6021
6022 // magform(x) Returns (a, b), where x = a * 10^b, abs (a) >= 1., and b is
6023 // integer.
6024
6025 static void
6026 magform (double x, double& a, int& b)
6027 {
6028 if (x == 0)
6029 {
6030 a = 0;
6031 b = 0;
6032 }
6033 else
6034 {
6035 b = static_cast<int> (gnulib::floor (std::log10 (std::abs (x))));
6036 a = x / std::pow (10.0, b);
6037 }
6038 }
6039
6040 // A translation from Tom Holoryd's python code at
6041 // http://kurage.nimh.nih.gov/tomh/tics.py
6042 // FIXME -- add log ticks
6043
6044 double
6045 axes::properties::calc_tick_sep (double lo, double hi)
6046 {
6047 int ticint = 5;
6048
6049 // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
6050 // SCALE3 for Determination of Scales on Computer Generated
6051 // Plots", Communications of the ACM, 10 (1973), 639-640.
6052 // Also cited as ACM Algorithm 463.
6053
6054 double a;
6055 int b, x;
6056
6057 magform ((hi-lo)/ticint, a, b);
6058
6059 static const double sqrt_2 = sqrt (2.0);
6060 static const double sqrt_10 = sqrt (10.0);
6061 static const double sqrt_50 = sqrt (50.0);
6062
6063 if (a < sqrt_2)
6064 x = 1;
6065 else if (a < sqrt_10)
6066 x = 2;
6067 else if (a < sqrt_50)
6068 x = 5;
6069 else
6070 x = 10;
6071
6072 return x * std::pow (10., b);
6073
6074 }
6075
6076 // Attempt to make "nice" limits from the actual max and min of the
6077 // data. For log plots, we will also use the smallest strictly positive
6078 // value.
6079
6080 Matrix
6081 axes::properties::get_axis_limits (double xmin, double xmax,
6082 double min_pos, double max_neg,
6083 bool logscale)
6084 {
6085 Matrix retval;
6086
6087 double min_val = xmin;
6088 double max_val = xmax;
6089
6090 if (xisinf (min_val) && min_val > 0 && xisinf (max_val) && max_val < 0)
6091 {
6092 retval = default_lim (logscale);
6093 return retval;
6094 }
6095 else if (! (xisinf (min_val) || xisinf (max_val)))
6096 {
6097 if (logscale)
6098 {
6099 if (xisinf (min_pos) && xisinf (max_neg))
6100 {
6101 // TODO -- max_neg is needed for "loglog ([0 -Inf])"
6102 // This is the only place where max_neg is needed.
6103 // Is there another way?
6104 retval = default_lim ();
6105 retval(0) = pow (10., retval(0));
6106 retval(1) = pow (10., retval(1));
6107 return retval;
6108 }
6109 if ((min_val <= 0 && max_val > 0))
6110 {
6111 warning ("axis: omitting non-positive data in log plot");
6112 min_val = min_pos;
6113 }
6114 // FIXME -- maybe this test should also be relative?
6115 if (std::abs (min_val - max_val) < sqrt (std::numeric_limits<double>::epsilon ()))
6116 {
6117 // Widen range when too small
6118 if (min_val >= 0)
6119 {
6120 min_val *= 0.9;
6121 max_val *= 1.1;
6122 }
6123 else
6124 {
6125 min_val *= 1.1;
6126 max_val *= 0.9;
6127 }
6128 }
6129 if (min_val > 0)
6130 {
6131 // Log plots with all positive data
6132 min_val = pow (10, gnulib::floor (log10 (min_val)));
6133 max_val = pow (10, std::ceil (log10 (max_val)));
6134 }
6135 else
6136 {
6137 // Log plots with all negative data
6138 min_val = -pow (10, std::ceil (log10 (-min_val)));
6139 max_val = -pow (10, gnulib::floor (log10 (-max_val)));
6140 }
6141 }
6142 else
6143 {
6144 if (min_val == 0 && max_val == 0)
6145 {
6146 min_val = -1;
6147 max_val = 1;
6148 }
6149 // FIXME -- maybe this test should also be relative?
6150 else if (std::abs (min_val - max_val) < sqrt (std::numeric_limits<double>::epsilon ()))
6151 {
6152 min_val -= 0.1 * std::abs (min_val);
6153 max_val += 0.1 * std::abs (max_val);
6154 }
6155
6156 double tick_sep = calc_tick_sep (min_val , max_val);
6157 double min_tick = gnulib::floor (min_val / tick_sep);
6158 double max_tick = std::ceil (max_val / tick_sep);
6159 // Prevent round-off from cropping ticks
6160 min_val = std::min (min_val, tick_sep * min_tick);
6161 max_val = std::max (max_val, tick_sep * max_tick);
6162 }
6163 }
6164
6165 retval.resize (1, 2);
6166
6167 retval(1) = max_val;
6168 retval(0) = min_val;
6169
6170 return retval;
6171 }
6172
6173 void
6174 axes::properties::calc_ticks_and_lims (array_property& lims,
6175 array_property& ticks,
6176 array_property& mticks,
6177 bool limmode_is_auto, bool is_logscale)
6178 {
6179 // FIXME -- add log ticks and lims
6180
6181 if (lims.get ().is_empty ())
6182 return;
6183
6184 double lo = (lims.get ().matrix_value ()) (0);
6185 double hi = (lims.get ().matrix_value ()) (1);
6186 bool is_negative = lo < 0 && hi < 0;
6187 double tmp;
6188 // FIXME should this be checked for somewhere else? (i.e. set{x,y,z}lim)
6189 if (hi < lo)
6190 {
6191 tmp = hi;
6192 hi = lo;
6193 lo = tmp;
6194 }
6195
6196 if (is_logscale)
6197 {
6198 if (is_negative)
6199 {
6200 tmp = hi;
6201 hi = std::log10 (-lo);
6202 lo = std::log10 (-tmp);
6203 }
6204 else
6205 {
6206 hi = std::log10 (hi);
6207 lo = std::log10 (lo);
6208 }
6209 }
6210
6211 double tick_sep = calc_tick_sep (lo , hi);
6212
6213 if (is_logscale && ! (xisinf (hi) || xisinf (lo)))
6214 {
6215 // FIXME - what if (hi-lo) < tick_sep?
6216 // ex: loglog ([1 1.1])
6217 tick_sep = std::max (tick_sep, 1.);
6218 tick_sep = std::ceil (tick_sep);
6219 }
6220
6221 int i1 = static_cast<int> (gnulib::floor (lo / tick_sep));
6222 int i2 = static_cast<int> (std::ceil (hi / tick_sep));
6223
6224 if (limmode_is_auto)
6225 {
6226 // adjust limits to include min and max tics
6227 Matrix tmp_lims (1,2);
6228 tmp_lims(0) = std::min (tick_sep * i1, lo);
6229 tmp_lims(1) = std::max (tick_sep * i2, hi);
6230
6231 if (is_logscale)
6232 {
6233 tmp_lims(0) = std::pow (10.,tmp_lims(0));
6234 tmp_lims(1) = std::pow (10.,tmp_lims(1));
6235 if (tmp_lims(0) <= 0)
6236 tmp_lims(0) = std::pow (10., lo);
6237 if (is_negative)
6238 {
6239 tmp = tmp_lims(0);
6240 tmp_lims(0) = -tmp_lims(1);
6241 tmp_lims(1) = -tmp;
6242 }
6243 }
6244 lims = tmp_lims;
6245 }
6246
6247 Matrix tmp_ticks (1, i2-i1+1);
6248 for (int i = 0; i <= i2-i1; i++)
6249 {
6250 tmp_ticks (i) = tick_sep * (i+i1);
6251 if (is_logscale)
6252 tmp_ticks (i) = std::pow (10., tmp_ticks (i));
6253 }
6254 if (is_logscale && is_negative)
6255 {
6256 Matrix rev_ticks (1, i2-i1+1);
6257 rev_ticks = -tmp_ticks;
6258 for (int i = 0; i <= i2-i1; i++)
6259 tmp_ticks (i) = rev_ticks (i2-i1-i);
6260 }
6261
6262 ticks = tmp_ticks;
6263
6264 int n = is_logscale ? 8 : 4;
6265 Matrix tmp_mticks (1, n * (tmp_ticks.numel () - 1));
6266
6267 for (int i = 0; i < tmp_ticks.numel ()-1; i++)
6268 {
6269 double d = (tmp_ticks (i+1) - tmp_ticks (i)) / (n+1);
6270 for (int j = 0; j < n; j++)
6271 {
6272 tmp_mticks (n*i+j) = tmp_ticks (i) + d * (j+1);
6273 }
6274 }
6275 mticks = tmp_mticks;
6276 }
6277
6278 void
6279 axes::properties::calc_ticklabels (const array_property& ticks,
6280 any_property& labels, bool logscale)
6281 {
6282 Matrix values = ticks.get ().matrix_value ();
6283 Cell c (values.dims ());
6284 std::ostringstream os;
6285
6286 if (logscale)
6287 {
6288 double significand;
6289 double exponent;
6290 double exp_max = 0.;
6291 double exp_min = 0.;
6292
6293 for (int i = 0; i < values.numel (); i++)
6294 {
6295 exp_max = std::max (exp_max, std::log10 (values(i)));
6296 exp_min = std::max (exp_min, std::log10 (values(i)));
6297 }
6298
6299 for (int i = 0; i < values.numel (); i++)
6300 {
6301 if (values(i) < 0.)
6302 exponent = gnulib::floor (std::log10 (-values(i)));
6303 else
6304 exponent = gnulib::floor (std::log10 (values(i)));
6305 significand = values(i) * std::pow (10., -exponent);
6306 os.str (std::string ());
6307 os << significand;
6308 if (exponent < 0.)
6309 {
6310 os << "e-";
6311 exponent = -exponent;
6312 }
6313 else
6314 os << "e+";
6315 if (exponent < 10. && (exp_max > 9 || exp_min < -9))
6316 os << "0";
6317 os << exponent;
6318 c(i) = os.str ();
6319 }
6320 }
6321 else
6322 {
6323 for (int i = 0; i < values.numel (); i++)
6324 {
6325 os.str (std::string ());
6326 os << values(i);
6327 c(i) = os.str ();
6328 }
6329 }
6330
6331 labels = c;
6332 }
6333
6334 Matrix
6335 axes::properties::get_ticklabel_extents (const Matrix& ticks,
6336 const string_vector& ticklabels,
6337 const Matrix& limits)
6338 {
6339 #ifndef HAVE_FREETYPE
6340 double fontsize = get ("fontsize").double_value ();
6341 #endif
6342
6343 Matrix ext (1, 2, 0.0);
6344 double wmax = 0., hmax = 0.;
6345 int n = std::min (ticklabels.numel (), ticks.numel ());
6346 for (int i = 0; i < n; i++)
6347 {
6348 double val = ticks(i);
6349 if (limits(0) <= val && val <= limits(1))
6350 {
6351 std::string label (ticklabels(i));
6352 label.erase (0, label.find_first_not_of (" "));
6353 label = label.substr (0, label.find_last_not_of (" ")+1);
6354 #ifdef HAVE_FREETYPE
6355 ext = text_renderer.get_extent (label);
6356 wmax = std::max (wmax, ext(0));
6357 hmax = std::max (hmax, ext(1));
6358 #else
6359 // FIXME: find a better approximation
6360 int len = label.length ();
6361 wmax = std::max (wmax, 0.5*fontsize*len);
6362 hmax = fontsize;
6363 #endif
6364 }
6365 }
6366
6367 ext(0) = wmax;
6368 ext(1) = hmax;
6369 return ext;
6370 }
6371
6372 void
6373 get_children_limits (double& min_val, double& max_val,
6374 double& min_pos, double& max_neg,
6375 const Matrix& kids, char limit_type)
6376 {
6377 octave_idx_type n = kids.numel ();
6378
6379 switch (limit_type)
6380 {
6381 case 'x':
6382 for (octave_idx_type i = 0; i < n; i++)
6383 {
6384 graphics_object obj = gh_manager::get_object (kids(i));
6385
6386 if (obj.is_xliminclude ())
6387 {
6388 octave_value lim = obj.get_xlim ();
6389
6390 check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6391 }
6392 }
6393 break;
6394
6395 case 'y':
6396 for (octave_idx_type i = 0; i < n; i++)
6397 {
6398 graphics_object obj = gh_manager::get_object (kids(i));
6399
6400 if (obj.is_yliminclude ())
6401 {
6402 octave_value lim = obj.get_ylim ();
6403
6404 check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6405 }
6406 }
6407 break;
6408
6409 case 'z':
6410 for (octave_idx_type i = 0; i < n; i++)
6411 {
6412 graphics_object obj = gh_manager::get_object (kids(i));
6413
6414 if (obj.is_zliminclude ())
6415 {
6416 octave_value lim = obj.get_zlim ();
6417
6418 check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6419 }
6420 }
6421 break;
6422
6423 case 'c':
6424 for (octave_idx_type i = 0; i < n; i++)
6425 {
6426 graphics_object obj = gh_manager::get_object (kids(i));
6427
6428 if (obj.is_climinclude ())
6429 {
6430 octave_value lim = obj.get_clim ();
6431
6432 check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6433 }
6434 }
6435 break;
6436
6437 case 'a':
6438 for (octave_idx_type i = 0; i < n; i++)
6439 {
6440 graphics_object obj = gh_manager::get_object (kids(i));
6441
6442 if (obj.is_aliminclude ())
6443 {
6444 octave_value lim = obj.get_alim ();
6445
6446 check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6447 }
6448 }
6449 break;
6450
6451 default:
6452 break;
6453 }
6454 }
6455
6456 static bool updating_axis_limits = false;
6457
6458 void
6459 axes::update_axis_limits (const std::string& axis_type,
6460 const graphics_handle& h)
6461 {
6462 if (updating_axis_limits)
6463 return;
6464
6465 Matrix kids = Matrix (1, 1, h.value ());
6466
6467 double min_val = octave_Inf;
6468 double max_val = -octave_Inf;
6469 double min_pos = octave_Inf;
6470 double max_neg = -octave_Inf;
6471
6472 char update_type = 0;
6473
6474 Matrix limits;
6475 double val;
6476
6477 #define FIX_LIMITS \
6478 if (limits.numel () == 4) \
6479 { \
6480 val = limits(0); \
6481 if (! (xisinf (val) || xisnan (val))) \
6482 min_val = val; \
6483 val = limits(1); \
6484 if (! (xisinf (val) || xisnan (val))) \
6485 max_val = val; \
6486 val = limits(2); \
6487 if (! (xisinf (val) || xisnan (val))) \
6488 min_pos = val; \
6489 val = limits(3); \
6490 if (! (xisinf (val) || xisnan (val))) \
6491 max_neg = val; \
6492 } \
6493 else \
6494 { \
6495 limits.resize (4, 1); \
6496 limits(0) = min_val; \
6497 limits(1) = max_val; \
6498 limits(2) = min_pos; \
6499 limits(3) = max_neg; \
6500 }
6501
6502 if (axis_type == "xdata" || axis_type == "xscale"
6503 || axis_type == "xlimmode" || axis_type == "xliminclude"
6504 || axis_type == "xlim")
6505 {
6506 if (xproperties.xlimmode_is ("auto"))
6507 {
6508 limits = xproperties.get_xlim ().matrix_value ();
6509 FIX_LIMITS ;
6510
6511 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
6512
6513 limits = xproperties.get_axis_limits (min_val, max_val,
6514 min_pos, max_neg,
6515 xproperties.xscale_is ("log"));
6516
6517 update_type = 'x';
6518 }
6519 }
6520 else if (axis_type == "ydata" || axis_type == "yscale"
6521 || axis_type == "ylimmode" || axis_type == "yliminclude"
6522 || axis_type == "ylim")
6523 {
6524 if (xproperties.ylimmode_is ("auto"))
6525 {
6526 limits = xproperties.get_ylim ().matrix_value ();
6527 FIX_LIMITS ;
6528
6529 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
6530
6531 limits = xproperties.get_axis_limits (min_val, max_val,
6532 min_pos, max_neg,
6533 xproperties.yscale_is ("log"));
6534
6535 update_type = 'y';
6536 }
6537 }
6538 else if (axis_type == "zdata" || axis_type == "zscale"
6539 || axis_type == "zlimmode" || axis_type == "zliminclude"
6540 || axis_type == "zlim")
6541 {
6542 if (xproperties.zlimmode_is ("auto"))
6543 {
6544 limits = xproperties.get_zlim ().matrix_value ();
6545 FIX_LIMITS ;
6546
6547 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
6548
6549 limits = xproperties.get_axis_limits (min_val, max_val,
6550 min_pos, max_neg,
6551 xproperties.zscale_is ("log"));
6552
6553 update_type = 'z';
6554 }
6555 }
6556 else if (axis_type == "cdata" || axis_type == "climmode"
6557 || axis_type == "cdatamapping" || axis_type == "climinclude"
6558 || axis_type == "clim")
6559 {
6560 if (xproperties.climmode_is ("auto"))
6561 {
6562 limits = xproperties.get_clim ().matrix_value ();
6563 FIX_LIMITS ;
6564
6565 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
6566
6567 if (min_val > max_val)
6568 {
6569 min_val = min_pos = 0;
6570 max_val = 1;
6571 }
6572 else if (min_val == max_val)
6573 {
6574 max_val = min_val + 1;
6575 min_val -= 1;
6576 }
6577
6578 limits.resize (1, 2);
6579
6580 limits(0) = min_val;
6581 limits(1) = max_val;
6582
6583 update_type = 'c';
6584 }
6585
6586 }
6587 else if (axis_type == "alphadata" || axis_type == "alimmode"
6588 || axis_type == "alphadatamapping" || axis_type == "aliminclude"
6589 || axis_type == "alim")
6590 {
6591 if (xproperties.alimmode_is ("auto"))
6592 {
6593 limits = xproperties.get_alim ().matrix_value ();
6594 FIX_LIMITS ;
6595
6596 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
6597
6598 if (min_val > max_val)
6599 {
6600 min_val = min_pos = 0;
6601 max_val = 1;
6602 }
6603 else if (min_val == max_val)
6604 max_val = min_val + 1;
6605
6606 limits.resize (1, 2);
6607
6608 limits(0) = min_val;
6609 limits(1) = max_val;
6610
6611 update_type = 'a';
6612 }
6613
6614 }
6615
6616 #undef FIX_LIMITS
6617
6618 unwind_protect frame;
6619 frame.protect_var (updating_axis_limits);
6620
6621 updating_axis_limits = true;
6622
6623 switch (update_type)
6624 {
6625 case 'x':
6626 xproperties.set_xlim (limits);
6627 xproperties.set_xlimmode ("auto");
6628 xproperties.update_xlim ();
6629 break;
6630
6631 case 'y':
6632 xproperties.set_ylim (limits);
6633 xproperties.set_ylimmode ("auto");
6634 xproperties.update_ylim ();
6635 break;
6636
6637 case 'z':
6638 xproperties.set_zlim (limits);
6639 xproperties.set_zlimmode ("auto");
6640 xproperties.update_zlim ();
6641 break;
6642
6643 case 'c':
6644 xproperties.set_clim (limits);
6645 xproperties.set_climmode ("auto");
6646 break;
6647
6648 case 'a':
6649 xproperties.set_alim (limits);
6650 xproperties.set_alimmode ("auto");
6651 break;
6652
6653 default:
6654 break;
6655 }
6656
6657 xproperties.update_transform ();
6658
6659 }
6660
6661 void
6662 axes::update_axis_limits (const std::string& axis_type)
6663 {
6664 if (updating_axis_limits || updating_aspectratios)
6665 return;
6666
6667 Matrix kids = xproperties.get_children ();
6668
6669 double min_val = octave_Inf;
6670 double max_val = -octave_Inf;
6671 double min_pos = octave_Inf;
6672 double max_neg = -octave_Inf;
6673
6674 char update_type = 0;
6675
6676 Matrix limits;
6677
6678 if (axis_type == "xdata" || axis_type == "xscale"
6679 || axis_type == "xlimmode" || axis_type == "xliminclude"
6680 || axis_type == "xlim")
6681 {
6682 if (xproperties.xlimmode_is ("auto"))
6683 {
6684 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
6685
6686 limits = xproperties.get_axis_limits (min_val, max_val,
6687 min_pos, max_neg,
6688 xproperties.xscale_is ("log"));
6689
6690 update_type = 'x';
6691 }
6692 }
6693 else if (axis_type == "ydata" || axis_type == "yscale"
6694 || axis_type == "ylimmode" || axis_type == "yliminclude"
6695 || axis_type == "ylim")
6696 {
6697 if (xproperties.ylimmode_is ("auto"))
6698 {
6699 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
6700
6701 limits = xproperties.get_axis_limits (min_val, max_val,
6702 min_pos, max_neg,
6703 xproperties.yscale_is ("log"));
6704
6705 update_type = 'y';
6706 }
6707 }
6708 else if (axis_type == "zdata" || axis_type == "zscale"
6709 || axis_type == "zlimmode" || axis_type == "zliminclude"
6710 || axis_type == "zlim")
6711 {
6712 if (xproperties.zlimmode_is ("auto"))
6713 {
6714 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
6715
6716 limits = xproperties.get_axis_limits (min_val, max_val,
6717 min_pos, max_neg,
6718 xproperties.zscale_is ("log"));
6719
6720 update_type = 'z';
6721 }
6722 }
6723 else if (axis_type == "cdata" || axis_type == "climmode"
6724 || axis_type == "cdatamapping" || axis_type == "climinclude"
6725 || axis_type == "clim")
6726 {
6727 if (xproperties.climmode_is ("auto"))
6728 {
6729 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
6730
6731 if (min_val > max_val)
6732 {
6733 min_val = min_pos = 0;
6734 max_val = 1;
6735 }
6736 else if (min_val == max_val)
6737 {
6738 max_val = min_val + 1;
6739 min_val -= 1;
6740 }
6741
6742 limits.resize (1, 2);
6743
6744 limits(0) = min_val;
6745 limits(1) = max_val;
6746
6747 update_type = 'c';
6748 }
6749
6750 }
6751 else if (axis_type == "alphadata" || axis_type == "alimmode"
6752 || axis_type == "alphadatamapping" || axis_type == "aliminclude"
6753 || axis_type == "alim")
6754 {
6755 if (xproperties.alimmode_is ("auto"))
6756 {
6757 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
6758
6759 if (min_val > max_val)
6760 {
6761 min_val = min_pos = 0;
6762 max_val = 1;
6763 }
6764 else if (min_val == max_val)
6765 max_val = min_val + 1;
6766
6767 limits.resize (1, 2);
6768
6769 limits(0) = min_val;
6770 limits(1) = max_val;
6771
6772 update_type = 'a';
6773 }
6774
6775 }
6776
6777 unwind_protect frame;
6778 frame.protect_var (updating_axis_limits);
6779
6780 updating_axis_limits = true;
6781
6782 switch (update_type)
6783 {
6784 case 'x':
6785 xproperties.set_xlim (limits);
6786 xproperties.set_xlimmode ("auto");
6787 xproperties.update_xlim ();
6788 break;
6789
6790 case 'y':
6791 xproperties.set_ylim (limits);
6792 xproperties.set_ylimmode ("auto");
6793 xproperties.update_ylim ();
6794 break;
6795
6796 case 'z':
6797 xproperties.set_zlim (limits);
6798 xproperties.set_zlimmode ("auto");
6799 xproperties.update_zlim ();
6800 break;
6801
6802 case 'c':
6803 xproperties.set_clim (limits);
6804 xproperties.set_climmode ("auto");
6805 break;
6806
6807 case 'a':
6808 xproperties.set_alim (limits);
6809 xproperties.set_alimmode ("auto");
6810 break;
6811
6812 default:
6813 break;
6814 }
6815
6816 xproperties.update_transform ();
6817 }
6818
6819 inline
6820 double force_in_range (const double x, const double lower, const double upper)
6821 {
6822 if (x < lower)
6823 { return lower; }
6824 else if (x > upper)
6825 { return upper; }
6826 else
6827 { return x; }
6828 }
6829
6830 static Matrix
6831 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
6832 {
6833 Matrix new_lims = lims;
6834
6835 double lo = lims(0);
6836 double hi = lims(1);
6837
6838 bool is_negative = lo < 0 && hi < 0;
6839
6840 if (is_logscale)
6841 {
6842 if (is_negative)
6843 {
6844 double tmp = hi;
6845 hi = std::log10 (-lo);
6846 lo = std::log10 (-tmp);
6847 val = std::log10 (-val);
6848 }
6849 else
6850 {
6851 hi = std::log10 (hi);
6852 lo = std::log10 (lo);
6853 val = std::log10 (val);
6854 }
6855 }
6856
6857 // Perform the zooming
6858 lo = val + factor * (lo - val);
6859 hi = val + factor * (hi - val);
6860
6861 if (is_logscale)
6862 {
6863 if (is_negative)
6864 {
6865 double tmp = -std::pow (10.0, hi);
6866 hi = -std::pow (10.0, lo);
6867 lo = tmp;
6868 }
6869 else
6870 {
6871 lo = std::pow (10.0, lo);
6872 hi = std::pow (10.0, hi);
6873 }
6874 }
6875
6876 new_lims(0) = lo;
6877 new_lims(1) = hi;
6878
6879 return new_lims;
6880 }
6881
6882 void
6883 axes::properties::zoom_about_point (double x, double y, double factor,
6884 bool push_to_zoom_stack)
6885 {
6886 // FIXME: Do we need error checking here?
6887 Matrix xlims = get_xlim ().matrix_value ();
6888 Matrix ylims = get_ylim ().matrix_value ();
6889
6890 // Get children axes limits
6891 Matrix kids = get_children ();
6892 double minx = octave_Inf;
6893 double maxx = -octave_Inf;
6894 double min_pos_x = octave_Inf;
6895 double max_neg_x = -octave_Inf;
6896 get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
6897
6898 double miny = octave_Inf;
6899 double maxy = -octave_Inf;
6900 double min_pos_y = octave_Inf;
6901 double max_neg_y = -octave_Inf;
6902 get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
6903
6904 xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
6905 ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
6906
6907 zoom (xlims, ylims, push_to_zoom_stack);
6908 }
6909
6910 void
6911 axes::properties::zoom (const Matrix& xl, const Matrix& yl, bool push_to_zoom_stack)
6912 {
6913 if (push_to_zoom_stack)
6914 {
6915 zoom_stack.push_front (xlimmode.get ());
6916 zoom_stack.push_front (xlim.get ());
6917 zoom_stack.push_front (ylimmode.get ());
6918 zoom_stack.push_front (ylim.get ());
6919 }
6920
6921 xlim = xl;
6922 xlimmode = "manual";
6923 ylim = yl;
6924 ylimmode = "manual";
6925
6926 update_transform ();
6927 update_xlim (false);
6928 update_ylim (false);
6929 }
6930
6931 static Matrix
6932 do_translate (double x0, double x1, const Matrix& lims, bool is_logscale)
6933 {
6934 Matrix new_lims = lims;
6935
6936 double lo = lims(0);
6937 double hi = lims(1);
6938
6939 bool is_negative = lo < 0 && hi < 0;
6940
6941 double delta;
6942
6943 if (is_logscale)
6944 {
6945 if (is_negative)
6946 {
6947 double tmp = hi;
6948 hi = std::log10 (-lo);
6949 lo = std::log10 (-tmp);
6950 x0 = -x0;
6951 x1 = -x1;
6952 }
6953 else
6954 {
6955 hi = std::log10 (hi);
6956 lo = std::log10 (lo);
6957 }
6958
6959 delta = std::log10 (x0) - std::log10 (x1);
6960 }
6961 else
6962 {
6963 delta = x0 - x1;
6964 }
6965
6966 // Perform the translation
6967 lo += delta;
6968 hi += delta;
6969
6970 if (is_logscale)
6971 {
6972 if (is_negative)
6973 {
6974 double tmp = -std::pow (10.0, hi);
6975 hi = -std::pow (10.0, lo);
6976 lo = tmp;
6977 }
6978 else
6979 {
6980 lo = std::pow (10.0, lo);
6981 hi = std::pow (10.0, hi);
6982 }
6983 }
6984
6985 new_lims(0) = lo;
6986 new_lims(1) = hi;
6987
6988 return new_lims;
6989 }
6990
6991 void
6992 axes::properties::translate_view (double x0, double x1, double y0, double y1)
6993 {
6994 // FIXME: Do we need error checking here?
6995 Matrix xlims = get_xlim ().matrix_value ();
6996 Matrix ylims = get_ylim ().matrix_value ();
6997
6998 // Get children axes limits
6999 Matrix kids = get_children ();
7000 double minx = octave_Inf;
7001 double maxx = -octave_Inf;
7002 double min_pos_x = octave_Inf;
7003 double max_neg_x = -octave_Inf;
7004 get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
7005
7006 double miny = octave_Inf;
7007 double maxy = -octave_Inf;
7008 double min_pos_y = octave_Inf;
7009 double max_neg_y = -octave_Inf;
7010 get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
7011
7012 xlims = do_translate (x0, x1, xlims, xscale_is ("log"));
7013 ylims = do_translate (y0, y1, ylims, yscale_is ("log"));
7014
7015 zoom (xlims, ylims, false);
7016 }
7017
7018 void
7019 axes::properties::rotate_view (double delta_el, double delta_az)
7020 {
7021 Matrix v = get_view ().matrix_value ();
7022
7023 v(1) += delta_el;
7024
7025 if (v(1) > 90)
7026 v(1) = 90;
7027 if (v(1) < -90)
7028 v(1) = -90;
7029
7030 v(0) = fmod (v(0) - delta_az + 720,360);
7031
7032 set_view (v);
7033 update_transform ();
7034 }
7035
7036 void
7037 axes::properties::unzoom (void)
7038 {
7039 if (zoom_stack.size () >= 4)
7040 {
7041 ylim = zoom_stack.front ();
7042 zoom_stack.pop_front ();
7043 ylimmode = zoom_stack.front ();
7044 zoom_stack.pop_front ();
7045 xlim = zoom_stack.front ();
7046 zoom_stack.pop_front ();
7047 xlimmode = zoom_stack.front ();
7048 zoom_stack.pop_front ();
7049
7050 update_transform ();
7051 update_xlim (false);
7052 update_ylim (false);
7053 }
7054 }
7055
7056 void
7057 axes::properties::clear_zoom_stack (void)
7058 {
7059 while (zoom_stack.size () > 4)
7060 zoom_stack.pop_front ();
7061
7062 unzoom ();
7063 }
7064
7065 void
7066 axes::reset_default_properties (void)
7067 {
7068 ::reset_default_properties (default_properties);
7069 }
7070
7071 void
7072 axes::initialize (const graphics_object& go)
7073 {
7074 base_graphics_object::initialize (go);
7075
7076 xinitialize (xproperties.get_title ());
7077 xinitialize (xproperties.get_xlabel ());
7078 xinitialize (xproperties.get_ylabel ());
7079 xinitialize (xproperties.get_zlabel ());
7080 }
7081
7082 // ---------------------------------------------------------------------
7083
7084 Matrix
7085 line::properties::compute_xlim (void) const
7086 {
7087 Matrix m (1, 4);
7088
7089 m(0) = xdata.min_val ();
7090 m(1) = xdata.max_val ();
7091 m(2) = xdata.min_pos ();
7092 m(3) = xdata.max_neg ();
7093
7094 return m;
7095 }
7096
7097 Matrix
7098 line::properties::compute_ylim (void) const
7099 {
7100 Matrix m (1, 4);
7101
7102 m(0) = ydata.min_val ();
7103 m(1) = ydata.max_val ();
7104 m(2) = ydata.min_pos ();
7105 m(3) = ydata.max_neg ();
7106
7107 return m;
7108 }
7109
7110 // ---------------------------------------------------------------------
7111
7112 Matrix
7113 text::properties::get_data_position (void) const
7114 {
7115 Matrix pos = get_position ().matrix_value ();
7116
7117 if (! units_is ("data"))
7118 pos = convert_text_position (pos, *this, get_units (), "data");
7119
7120 return pos;
7121 }
7122
7123 Matrix
7124 text::properties::get_extent_matrix (void) const
7125 {
7126 // FIXME: Should this function also add the (x,y) base position?
7127 return extent.get ().matrix_value ();
7128 }
7129
7130 octave_value
7131 text::properties::get_extent (void) const
7132 {
7133 // FIXME: This doesn't work right for 3D plots.
7134 // (It doesn't in Matlab either, at least not in version 6.5.)
7135 Matrix m = extent.get ().matrix_value ();
7136 Matrix pos = get_position ().matrix_value ();
7137 Matrix p = convert_text_position (pos, *this, get_units (), "pixels");
7138
7139 m(0) += p(0);
7140 m(1) += p(1);
7141
7142 return convert_text_position (m, *this, "pixels", get_units ());
7143 }
7144
7145 void
7146 text::properties::update_font (void)
7147 {
7148 #ifdef HAVE_FREETYPE
7149 #ifdef HAVE_FONTCONFIG
7150 renderer.set_font (get ("fontname").string_value (),
7151 get ("fontweight").string_value (),
7152 get ("fontangle").string_value (),
7153 get ("fontsize").double_value ());
7154 #endif
7155 renderer.set_color (get_color_rgb ());
7156 #endif
7157 }
7158
7159 void
7160 text::properties::update_text_extent (void)
7161 {
7162 #ifdef HAVE_FREETYPE
7163
7164 int halign = 0, valign = 0;
7165
7166 if (horizontalalignment_is ("center"))
7167 halign = 1;
7168 else if (horizontalalignment_is ("right"))
7169 halign = 2;
7170
7171 if (verticalalignment_is ("middle"))
7172 valign = 1;
7173 else if (verticalalignment_is ("top"))
7174 valign = 2;
7175 else if (verticalalignment_is ("baseline"))
7176 valign = 3;
7177 else if (verticalalignment_is ("cap"))
7178 valign = 4;
7179
7180 Matrix bbox;
7181
7182 // FIXME: string should be parsed only when modified, for efficiency
7183
7184 octave_value string_prop = get_string ();
7185
7186 string_vector sv = string_prop.all_strings ();
7187
7188 renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
7189 halign, valign, get_rotation ());
7190 /* The bbox is relative to the text's position.
7191 We'll leave it that way, because get_position () does not return
7192 valid results when the text is first constructed.
7193 Conversion to proper coordinates is performed in get_extent. */
7194 set_extent (bbox);
7195
7196 #endif
7197
7198 if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel") ||
7199 autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
7200 update_autopos ("sync");
7201 }
7202
7203 void
7204 text::properties::request_autopos (void)
7205 {
7206 if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel") ||
7207 autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
7208 update_autopos (get_autopos_tag ());
7209 }
7210
7211 void
7212 text::properties::update_units (void)
7213 {
7214 if (! units_is ("data"))
7215 {
7216 set_xliminclude ("off");
7217 set_yliminclude ("off");
7218 set_zliminclude ("off");
7219 }
7220
7221 Matrix pos = get_position ().matrix_value ();
7222
7223 pos = convert_text_position (pos, *this, cached_units, get_units ());
7224 // FIXME: if the current axes view is 2D, then one should
7225 // probably drop the z-component of "pos" and leave "zliminclude"
7226 // to "off".
7227 set_position (pos);
7228
7229 if (units_is ("data"))
7230 {
7231 set_xliminclude ("on");
7232 set_yliminclude ("on");
7233 // FIXME: see above
7234 set_zliminclude ("off");
7235 }
7236
7237 cached_units = get_units ();
7238 }
7239
7240 double
7241 text::properties::get_fontsize_points (double box_pix_height) const
7242 {
7243 double fs = get_fontsize ();
7244 double parent_height = box_pix_height;
7245
7246 if (fontunits_is ("normalized") && parent_height <= 0)
7247 {
7248 graphics_object go (gh_manager::get_object (get___myhandle__ ()));
7249 graphics_object ax (go.get_ancestor ("axes"));
7250
7251 parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
7252 }
7253
7254 return convert_font_size (fs, get_fontunits (), "points", parent_height);
7255 }
7256
7257 // ---------------------------------------------------------------------
7258
7259 octave_value
7260 image::properties::get_color_data (void) const
7261 {
7262 return convert_cdata (*this, get_cdata (),
7263 cdatamapping_is ("scaled"), 3);
7264 }
7265
7266 // ---------------------------------------------------------------------
7267
7268 octave_value
7269 patch::properties::get_color_data (void) const
7270 {
7271 octave_value fvc = get_facevertexcdata ();
7272 if (fvc.is_undefined () || fvc.is_empty ())
7273 return Matrix ();
7274 else
7275 return convert_cdata (*this, fvc,cdatamapping_is ("scaled"), 2);
7276 }
7277
7278 // ---------------------------------------------------------------------
7279
7280 octave_value
7281 surface::properties::get_color_data (void) const
7282 {
7283 return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
7284 }
7285
7286 inline void
7287 cross_product (double x1, double y1, double z1,
7288 double x2, double y2, double z2,
7289 double& x, double& y, double& z)
7290 {
7291 x += (y1 * z2 - z1 * y2);
7292 y += (z1 * x2 - x1 * z2);
7293 z += (x1 * y2 - y1 * x2);
7294 }
7295
7296 void
7297 surface::properties::update_normals (void)
7298 {
7299 if (normalmode_is ("auto"))
7300 {
7301 Matrix x = get_xdata ().matrix_value ();
7302 Matrix y = get_ydata ().matrix_value ();
7303 Matrix z = get_zdata ().matrix_value ();
7304
7305
7306 int p = z.columns (), q = z.rows ();
7307 int i1 = 0, i2 = 0, i3 = 0;
7308 int j1 = 0, j2 = 0, j3 = 0;
7309
7310 bool x_mat = (x.rows () == q);
7311 bool y_mat = (y.columns () == p);
7312
7313 NDArray n (dim_vector (q, p, 3), 0.0);
7314
7315 for (int i = 0; i < p; i++)
7316 {
7317 if (y_mat)
7318 {
7319 i1 = i - 1;
7320 i2 = i;
7321 i3 = i + 1;
7322 }
7323
7324 for (int j = 0; j < q; j++)
7325 {
7326 if (x_mat)
7327 {
7328 j1 = j - 1;
7329 j2 = j;
7330 j3 = j + 1;
7331 }
7332
7333 double& nx = n(j, i, 0);
7334 double& ny = n(j, i, 1);
7335 double& nz = n(j, i, 2);
7336
7337 if ((j > 0) && (i > 0))
7338 // upper left quadrangle
7339 cross_product (x(j1,i-1)-x(j2,i), y(j-1,i1)-y(j,i2), z(j-1,i-1)-z(j,i),
7340 x(j2,i-1)-x(j1,i), y(j,i1)-y(j-1,i2), z(j,i-1)-z(j-1,i),
7341 nx, ny, nz);
7342
7343 if ((j > 0) && (i < (p -1)))
7344 // upper right quadrangle
7345 cross_product (x(j1,i+1)-x(j2,i), y(j-1,i3)-y(j,i2), z(j-1,i+1)-z(j,i),
7346 x(j1,i)-x(j2,i+1), y(j-1,i2)-y(j,i3), z(j-1,i)-z(j,i+1),
7347 nx, ny, nz);
7348
7349 if ((j < (q - 1)) && (i > 0))
7350 // lower left quadrangle
7351 cross_product (x(j2,i-1)-x(j3,i), y(j,i1)-y(j+1,i2), z(j,i-1)-z(j+1,i),
7352 x(j3,i-1)-x(j2,i), y(j+1,i1)-y(j,i2), z(j+1,i-1)-z(j,i),
7353 nx, ny, nz);
7354
7355 if ((j < (q - 1)) && (i < (p -1)))
7356 // lower right quadrangle
7357 cross_product (x(j3,i)-x(j2,i+1), y(j+1,i2)-y(j,i3), z(j+1,i)-z(j,i+1),
7358 x(j3,i+1)-x(j2,i), y(j+1,i3)-y(j,i2), z(j+1,i+1)-z(j,i),
7359 nx, ny, nz);
7360
7361 double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
7362
7363 nx /= d;
7364 ny /= d;
7365 nz /= d;
7366 }
7367 }
7368 vertexnormals = n;
7369 }
7370 }
7371
7372 // ---------------------------------------------------------------------
7373
7374 void
7375 hggroup::properties::update_limits (void) const
7376 {
7377 graphics_object obj = gh_manager::get_object (__myhandle__);
7378
7379 if (obj)
7380 {
7381 obj.update_axis_limits ("xlim");
7382 obj.update_axis_limits ("ylim");
7383 obj.update_axis_limits ("zlim");
7384 obj.update_axis_limits ("clim");
7385 obj.update_axis_limits ("alim");
7386 }
7387 }
7388
7389 void
7390 hggroup::properties::update_limits (const graphics_handle& h) const
7391 {
7392 graphics_object obj = gh_manager::get_object (__myhandle__);
7393
7394 if (obj)
7395 {
7396 obj.update_axis_limits ("xlim", h);
7397 obj.update_axis_limits ("ylim", h);
7398 obj.update_axis_limits ("zlim", h);
7399 obj.update_axis_limits ("clim", h);
7400 obj.update_axis_limits ("alim", h);
7401 }
7402 }
7403
7404 static bool updating_hggroup_limits = false;
7405
7406 void
7407 hggroup::update_axis_limits (const std::string& axis_type,
7408 const graphics_handle& h)
7409 {
7410 if (updating_hggroup_limits)
7411 return;
7412
7413 Matrix kids = Matrix (1, 1, h.value ());
7414
7415 double min_val = octave_Inf;
7416 double max_val = -octave_Inf;
7417 double min_pos = octave_Inf;
7418 double max_neg = -octave_Inf;
7419
7420 Matrix limits;
7421 double val;
7422
7423 char update_type = 0;
7424
7425 if (axis_type == "xlim" || axis_type == "xliminclude")
7426 {
7427 limits = xproperties.get_xlim ().matrix_value ();
7428 update_type = 'x';
7429 }
7430 else if (axis_type == "ylim" || axis_type == "yliminclude")
7431 {
7432 limits = xproperties.get_ylim ().matrix_value ();
7433 update_type = 'y';
7434 }
7435 else if (axis_type == "zlim" || axis_type == "zliminclude")
7436 {
7437 limits = xproperties.get_zlim ().matrix_value ();
7438 update_type = 'z';
7439 }
7440 else if (axis_type == "clim" || axis_type == "climinclude")
7441 {
7442 limits = xproperties.get_clim ().matrix_value ();
7443 update_type = 'c';
7444 }
7445 else if (axis_type == "alim" || axis_type == "aliminclude")
7446 {
7447 limits = xproperties.get_alim ().matrix_value ();
7448 update_type = 'a';
7449 }
7450
7451 if (limits.numel () == 4)
7452 {
7453 val = limits(0);
7454 if (! (xisinf (val) || xisnan (val)))
7455 min_val = val;
7456 val = limits(1);
7457 if (! (xisinf (val) || xisnan (val)))
7458 max_val = val;
7459 val = limits(2);
7460 if (! (xisinf (val) || xisnan (val)))
7461 min_pos = val;
7462 val = limits(3);
7463 if (! (xisinf (val) || xisnan (val)))
7464 max_neg = val;
7465 }
7466 else
7467 {
7468 limits.resize (4,1);
7469 limits(0) = min_val;
7470 limits(1) = max_val;
7471 limits(2) = min_pos;
7472 limits(3) = max_neg;
7473 }
7474
7475 get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
7476
7477 unwind_protect frame;
7478 frame.protect_var (updating_hggroup_limits);
7479
7480 updating_hggroup_limits = true;
7481
7482 if (limits(0) != min_val || limits(1) != max_val
7483 || limits(2) != min_pos || limits(3) != max_neg)
7484 {
7485 limits(0) = min_val;
7486 limits(1) = max_val;
7487 limits(2) = min_pos;
7488 limits(3) = max_neg;
7489
7490 switch (update_type)
7491 {
7492 case 'x':
7493 xproperties.set_xlim (limits);
7494 break;
7495
7496 case 'y':
7497 xproperties.set_ylim (limits);
7498 break;
7499
7500 case 'z':
7501 xproperties.set_zlim (limits);
7502 break;
7503
7504 case 'c':
7505 xproperties.set_clim (limits);
7506 break;
7507
7508 case 'a':
7509 xproperties.set_alim (limits);
7510 break;
7511
7512 default:
7513 break;
7514 }
7515
7516 base_graphics_object::update_axis_limits (axis_type, h);
7517 }
7518 }
7519
7520 void
7521 hggroup::update_axis_limits (const std::string& axis_type)
7522 {
7523 if (updating_hggroup_limits)
7524 return;
7525
7526 Matrix kids = xproperties.get_children ();
7527
7528 double min_val = octave_Inf;
7529 double max_val = -octave_Inf;
7530 double min_pos = octave_Inf;
7531 double max_neg = -octave_Inf;
7532
7533 char update_type = 0;
7534
7535 if (axis_type == "xlim" || axis_type == "xliminclude")
7536 {
7537 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
7538
7539 update_type = 'x';
7540 }
7541 else if (axis_type == "ylim" || axis_type == "yliminclude")
7542 {
7543 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
7544
7545 update_type = 'y';
7546 }
7547 else if (axis_type == "zlim" || axis_type == "zliminclude")
7548 {
7549 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
7550
7551 update_type = 'z';
7552 }
7553 else if (axis_type == "clim" || axis_type == "climinclude")
7554 {
7555 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
7556
7557 update_type = 'c';
7558 }
7559 else if (axis_type == "alim" || axis_type == "aliminclude")
7560 {
7561 get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
7562
7563 update_type = 'a';
7564 }
7565
7566 unwind_protect frame;
7567 frame.protect_var (updating_hggroup_limits);
7568
7569 updating_hggroup_limits = true;
7570
7571 Matrix limits (1, 4, 0.0);
7572
7573 limits(0) = min_val;
7574 limits(1) = max_val;
7575 limits(2) = min_pos;
7576 limits(3) = max_neg;
7577
7578 switch (update_type)
7579 {
7580 case 'x':
7581 xproperties.set_xlim (limits);
7582 break;
7583
7584 case 'y':
7585 xproperties.set_ylim (limits);
7586 break;
7587
7588 case 'z':
7589 xproperties.set_zlim (limits);
7590 break;
7591
7592 case 'c':
7593 xproperties.set_clim (limits);
7594 break;
7595
7596 case 'a':
7597 xproperties.set_alim (limits);
7598 break;
7599
7600 default:
7601 break;
7602 }
7603
7604 base_graphics_object::update_axis_limits (axis_type);
7605 }
7606
7607 // ---------------------------------------------------------------------
7608
7609 octave_value
7610 uicontrol::properties::get_extent (void) const
7611 {
7612 Matrix m = extent.get ().matrix_value ();
7613
7614 graphics_object parent_obj =
7615 gh_manager::get_object (get_parent ());
7616 Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
7617 parent_size = parent_bbox.extract_n (0, 2, 1, 2);
7618
7619 return convert_position (m, "pixels", get_units (), parent_size);
7620 }
7621
7622 void
7623 uicontrol::properties::update_text_extent (void)
7624 {
7625 #ifdef HAVE_FREETYPE
7626
7627 text_element *elt;
7628 ft_render text_renderer;
7629 Matrix box;
7630
7631 // FIXME: parsed content should be cached for efficiency
7632 // FIXME: support multiline text
7633
7634 elt = text_parser_none ().parse (get_string_string ());
7635 #ifdef HAVE_FONTCONFIG
7636 text_renderer.set_font (get_fontname (),
7637 get_fontweight (),
7638 get_fontangle (),
7639 get_fontsize ());
7640 #endif
7641 box = text_renderer.get_extent (elt, 0);
7642
7643 Matrix ext (1, 4, 0.0);
7644
7645 // FIXME: also handle left and bottom components
7646
7647 ext(0) = ext(1) = 1;
7648 ext(2) = box(0);
7649 ext(3) = box(1);
7650
7651 set_extent (ext);
7652
7653 #endif
7654 }
7655
7656 void
7657 uicontrol::properties::update_units (void)
7658 {
7659 Matrix pos = get_position ().matrix_value ();
7660
7661 graphics_object parent_obj = gh_manager::get_object (get_parent ());
7662 Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
7663 parent_size = parent_bbox.extract_n (0, 2, 1, 2);
7664
7665 pos = convert_position (pos, cached_units, get_units (), parent_size);
7666 set_position (pos);
7667
7668 cached_units = get_units ();
7669 }
7670
7671 void
7672 uicontrol::properties::set_style (const octave_value& st)
7673 {
7674 if (get___object__ ().is_empty ())
7675 style = st;
7676 else
7677 error ("set: cannot change the style of a uicontrol object after creation.");
7678 }
7679
7680 Matrix
7681 uicontrol::properties::get_boundingbox (bool,
7682 const Matrix& parent_pix_size) const
7683 {
7684 Matrix pos = get_position ().matrix_value ();
7685 Matrix parent_size (parent_pix_size);
7686
7687 if (parent_size.numel () == 0)
7688 {
7689 graphics_object obj = gh_manager::get_object (get_parent ());
7690
7691 parent_size =
7692 obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7693 }
7694
7695 pos = convert_position (pos, get_units (), "pixels", parent_size);
7696
7697 pos(0)--;
7698 pos(1)--;
7699 pos(1) = parent_size(1) - pos(1) - pos(3);
7700
7701 return pos;
7702 }
7703
7704 void
7705 uicontrol::properties::set_fontunits (const octave_value& v)
7706 {
7707 if (! error_state)
7708 {
7709 caseless_str old_fontunits = get_fontunits ();
7710 if (fontunits.set (v, true))
7711 {
7712 update_fontunits (old_fontunits);
7713 mark_modified ();
7714 }
7715 }
7716 }
7717
7718 void
7719 uicontrol::properties::update_fontunits (const caseless_str& old_units)
7720 {
7721 caseless_str new_units = get_fontunits ();
7722 double parent_height = get_boundingbox (false).elem (3);
7723 double fsz = get_fontsize ();
7724
7725 fsz = convert_font_size (fsz, old_units, new_units, parent_height);
7726
7727 fontsize.set (octave_value (fsz), true);
7728 }
7729
7730 double
7731 uicontrol::properties::get_fontsize_points (double box_pix_height) const
7732 {
7733 double fs = get_fontsize ();
7734 double parent_height = box_pix_height;
7735
7736 if (fontunits_is ("normalized") && parent_height <= 0)
7737 parent_height = get_boundingbox (false).elem (3);
7738
7739 return convert_font_size (fs, get_fontunits (), "points", parent_height);
7740 }
7741
7742 // ---------------------------------------------------------------------
7743
7744 Matrix
7745 uipanel::properties::get_boundingbox (bool internal,
7746 const Matrix& parent_pix_size) const
7747 {
7748 Matrix pos = get_position ().matrix_value ();
7749 Matrix parent_size (parent_pix_size);
7750
7751 if (parent_size.numel () == 0)
7752 {
7753 graphics_object obj = gh_manager::get_object (get_parent ());
7754
7755 parent_size =
7756 obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7757 }
7758
7759 pos = convert_position (pos, get_units (), "pixels", parent_size);
7760
7761 pos(0)--;
7762 pos(1)--;
7763 pos(1) = parent_size(1) - pos(1) - pos(3);
7764
7765 if (internal)
7766 {
7767 double outer_height = pos(3);
7768
7769 pos(0) = pos(1) = 0;
7770
7771 if (! bordertype_is ("none"))
7772 {
7773 double bw = get_borderwidth ();
7774 double mul = 1.0;
7775
7776 if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
7777 mul = 2.0;
7778
7779 pos(0) += mul * bw;
7780 pos(1) += mul * bw;
7781 pos(2) -= 2 * mul * bw;
7782 pos(3) -= 2 * mul * bw;
7783 }
7784
7785 if (! get_title ().empty ())
7786 {
7787 double fs = get_fontsize ();
7788
7789 if (! fontunits_is ("pixels"))
7790 {
7791 double res = xget (0, "screenpixelsperinch").double_value ();
7792
7793 if (fontunits_is ("points"))
7794 fs *= (res / 72.0);
7795 else if (fontunits_is ("inches"))
7796 fs *= res;
7797 else if (fontunits_is ("centimeters"))
7798 fs *= (res / 2.54);
7799 else if (fontunits_is ("normalized"))
7800 fs *= outer_height;
7801 }
7802
7803 if (titleposition_is ("lefttop") || titleposition_is ("centertop")
7804 || titleposition_is ("righttop"))
7805 pos(1) += (fs / 2);
7806 pos(3) -= (fs / 2);
7807 }
7808 }
7809
7810 return pos;
7811 }
7812
7813 void
7814 uipanel::properties::set_units (const octave_value& v)
7815 {
7816 if (! error_state)
7817 {
7818 caseless_str old_units = get_units ();
7819 if (units.set (v, true))
7820 {
7821 update_units (old_units);
7822 mark_modified ();
7823 }
7824 }
7825 }
7826
7827 void
7828 uipanel::properties::update_units (const caseless_str& old_units)
7829 {
7830 Matrix pos = get_position ().matrix_value ();
7831
7832 graphics_object parent_obj = gh_manager::get_object (get_parent ());
7833 Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
7834 parent_size = parent_bbox.extract_n (0, 2, 1, 2);
7835
7836 pos = convert_position (pos, old_units, get_units (), parent_size);
7837 set_position (pos);
7838 }
7839
7840 void
7841 uipanel::properties::set_fontunits (const octave_value& v)
7842 {
7843 if (! error_state)
7844 {
7845 caseless_str old_fontunits = get_fontunits ();
7846 if (fontunits.set (v, true))
7847 {
7848 update_fontunits (old_fontunits);
7849 mark_modified ();
7850 }
7851 }
7852 }
7853
7854 void
7855 uipanel::properties::update_fontunits (const caseless_str& old_units)
7856 {
7857 caseless_str new_units = get_fontunits ();
7858 double parent_height = get_boundingbox (false).elem (3);
7859 double fsz = get_fontsize ();
7860
7861 fsz = convert_font_size (fsz, old_units, new_units, parent_height);
7862
7863 set_fontsize (octave_value (fsz));
7864 }
7865
7866 double
7867 uipanel::properties::get_fontsize_points (double box_pix_height) const
7868 {
7869 double fs = get_fontsize ();
7870 double parent_height = box_pix_height;
7871
7872 if (fontunits_is ("normalized") && parent_height <= 0)
7873 parent_height = get_boundingbox (false).elem (3);
7874
7875 return convert_font_size (fs, get_fontunits (), "points", parent_height);
7876 }
7877
7878 // ---------------------------------------------------------------------
7879
7880 octave_value
7881 uitoolbar::get_default (const caseless_str& name) const
7882 {
7883 octave_value retval = default_properties.lookup (name);
7884
7885 if (retval.is_undefined ())
7886 {
7887 graphics_handle parent = get_parent ();
7888 graphics_object parent_obj = gh_manager::get_object (parent);
7889
7890 retval = parent_obj.get_default (name);
7891 }
7892
7893 return retval;
7894 }
7895
7896 void
7897 uitoolbar::reset_default_properties (void)
7898 {
7899 ::reset_default_properties (default_properties);
7900 }
7901
7902 // ---------------------------------------------------------------------
7903
7904 octave_value
7905 base_graphics_object::get_default (const caseless_str& name) const
7906 {
7907 graphics_handle parent = get_parent ();
7908 graphics_object parent_obj = gh_manager::get_object (parent);
7909
7910 return parent_obj.get_default (type () + name);
7911 }
7912
7913 octave_value
7914 base_graphics_object::get_factory_default (const caseless_str& name) const
7915 {
7916 graphics_object parent_obj = gh_manager::get_object (0);
7917
7918 return parent_obj.get_factory_default (type () + name);
7919 }
7920
7921 // We use a random value for the handle to avoid issues with plots and
7922 // scalar values for the first argument.
7923 gh_manager::gh_manager (void)
7924 : handle_map (), handle_free_list (),
7925 next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
7926 figure_list (), graphics_lock (), event_queue (),
7927 callback_objects (), event_processing (0)
7928 {
7929 handle_map[0] = graphics_object (new root_figure ());
7930
7931 // Make sure the default graphics toolkit is registered.
7932 gtk_manager::default_toolkit ();
7933 }
7934
7935 void
7936 gh_manager::create_instance (void)
7937 {
7938 instance = new gh_manager ();
7939
7940 if (instance)
7941 singleton_cleanup_list::add (cleanup_instance);
7942 }
7943
7944 graphics_handle
7945 gh_manager::do_make_graphics_handle (const std::string& go_name,
7946 const graphics_handle& p,
7947 bool integer_figure_handle,
7948 bool do_createfcn,
7949 bool do_notify_toolkit)
7950 {
7951 graphics_handle h = get_handle (integer_figure_handle);
7952
7953 base_graphics_object *go = 0;
7954
7955 go = make_graphics_object_from_type (go_name, h, p);
7956
7957 if (go)
7958 {
7959 graphics_object obj (go);
7960
7961 handle_map[h] = obj;
7962 if (do_createfcn)
7963 go->get_properties ().execute_createfcn ();
7964
7965 // Notify graphics toolkit.
7966 if (do_notify_toolkit)
7967 obj.initialize ();
7968 }
7969 else
7970 error ("gh_manager::do_make_graphics_handle: invalid object type '%s'",
7971 go_name.c_str ());
7972
7973 return h;
7974 }
7975
7976 graphics_handle
7977 gh_manager::do_make_figure_handle (double val, bool do_notify_toolkit)
7978 {
7979 graphics_handle h = val;
7980
7981 base_graphics_object* go = new figure (h, 0);
7982 graphics_object obj (go);
7983
7984 handle_map[h] = obj;
7985
7986 // Notify graphics toolkit.
7987 if (do_notify_toolkit)
7988 obj.initialize ();
7989
7990 return h;
7991 }
7992
7993 void
7994 gh_manager::do_push_figure (const graphics_handle& h)
7995 {
7996 do_pop_figure (h);
7997
7998 figure_list.push_front (h);
7999 }
8000
8001 void
8002 gh_manager::do_pop_figure (const graphics_handle& h)
8003 {
8004 for (figure_list_iterator p = figure_list.begin ();
8005 p != figure_list.end ();
8006 p++)
8007 {
8008 if (*p == h)
8009 {
8010 figure_list.erase (p);
8011 break;
8012 }
8013 }
8014 }
8015
8016 class
8017 callback_event : public base_graphics_event
8018 {
8019 public:
8020 callback_event (const graphics_handle& h, const std::string& name,
8021 const octave_value& data = Matrix ())
8022 : base_graphics_event (), handle (h), callback_name (name),
8023 callback (), callback_data (data) { }
8024
8025 callback_event (const graphics_handle& h, const octave_value& cb,
8026 const octave_value& data = Matrix ())
8027 : base_graphics_event (), handle (h), callback_name (),
8028 callback (cb), callback_data (data) { }
8029
8030 void execute (void)
8031 {
8032 if (callback.is_defined ())
8033 gh_manager::execute_callback (handle, callback, callback_data);
8034 else
8035 gh_manager::execute_callback (handle, callback_name, callback_data);
8036 }
8037
8038 private:
8039 callback_event (void)
8040 : base_graphics_event (), handle (),
8041 callback_name (), callback_data ()
8042 { }
8043
8044 private:
8045 graphics_handle handle;
8046 std::string callback_name;
8047 octave_value callback;
8048 octave_value callback_data;
8049 };
8050
8051 class
8052 function_event : public base_graphics_event
8053 {
8054 public:
8055 function_event (graphics_event::event_fcn fcn, void* data = 0)
8056 : base_graphics_event (), function (fcn),
8057 function_data (data) { }
8058
8059 void execute (void)
8060 {
8061 function (function_data);
8062 }
8063
8064 private:
8065
8066 graphics_event::event_fcn function;
8067
8068 void* function_data;
8069
8070 // function_event objects must be created with at least a function.
8071 function_event (void);
8072
8073 // No copying!
8074
8075 function_event (const function_event &);
8076
8077 function_event & operator = (const function_event &);
8078 };
8079
8080 class
8081 set_event : public base_graphics_event
8082 {
8083 public:
8084 set_event (const graphics_handle& h, const std::string& name,
8085 const octave_value& value, bool do_notify_toolkit = true)
8086 : base_graphics_event (), handle (h), property_name (name),
8087 property_value (value), notify_toolkit (do_notify_toolkit) { }
8088
8089 void execute (void)
8090 {
8091 gh_manager::auto_lock guard;
8092
8093 graphics_object go = gh_manager::get_object (handle);
8094
8095 if (go)
8096 {
8097 property p = go.get_properties ().get_property (property_name);
8098
8099 if (p.ok ())
8100 p.set (property_value, true, notify_toolkit);
8101 }
8102 }
8103
8104 private:
8105 set_event (void)
8106 : base_graphics_event (), handle (), property_name (), property_value ()
8107 { }
8108
8109 private:
8110 graphics_handle handle;
8111 std::string property_name;
8112 octave_value property_value;
8113 bool notify_toolkit;
8114 };
8115
8116 graphics_event
8117 graphics_event::create_callback_event (const graphics_handle& h,
8118 const std::string& name,
8119 const octave_value& data)
8120 {
8121 graphics_event e;
8122
8123 e.rep = new callback_event (h, name, data);
8124
8125 return e;
8126 }
8127
8128 graphics_event
8129 graphics_event::create_callback_event (const graphics_handle& h,
8130 const octave_value& cb,
8131 const octave_value& data)
8132 {
8133 graphics_event e;
8134
8135 e.rep = new callback_event (h, cb, data);
8136
8137 return e;
8138 }
8139
8140 graphics_event
8141 graphics_event::create_function_event (graphics_event::event_fcn fcn,
8142 void *data)
8143 {
8144 graphics_event e;
8145
8146 e.rep = new function_event (fcn, data);
8147
8148 return e;
8149 }
8150
8151 graphics_event
8152 graphics_event::create_set_event (const graphics_handle& h,
8153 const std::string& name,
8154 const octave_value& data,
8155 bool notify_toolkit)
8156 {
8157 graphics_event e;
8158
8159 e.rep = new set_event (h, name, data, notify_toolkit);
8160
8161 return e;
8162 }
8163
8164 static void
8165 xset_gcbo (const graphics_handle& h)
8166 {
8167 graphics_object go = gh_manager::get_object (0);
8168 root_figure::properties& props =
8169 dynamic_cast<root_figure::properties&> (go.get_properties ());
8170
8171 props.set_callbackobject (h.as_octave_value ());
8172 }
8173
8174 void
8175 gh_manager::do_restore_gcbo (void)
8176 {
8177 gh_manager::auto_lock guard;
8178
8179 callback_objects.pop_front ();
8180
8181 xset_gcbo (callback_objects.empty ()
8182 ? graphics_handle ()
8183 : callback_objects.front ().get_handle ());
8184 }
8185
8186 void
8187 gh_manager::do_execute_listener (const graphics_handle& h,
8188 const octave_value& l)
8189 {
8190 if (octave_thread::is_octave_thread ())
8191 gh_manager::execute_callback (h, l, octave_value ());
8192 else
8193 {
8194 gh_manager::auto_lock guard;
8195
8196 do_post_event (graphics_event::create_callback_event (h, l));
8197 }
8198 }
8199
8200 void
8201 gh_manager::do_execute_callback (const graphics_handle& h,
8202 const octave_value& cb_arg,
8203 const octave_value& data)
8204 {
8205 if (cb_arg.is_defined () && ! cb_arg.is_empty ())
8206 {
8207 octave_value_list args;
8208 octave_function *fcn = 0;
8209
8210 args(0) = h.as_octave_value ();
8211 if (data.is_defined ())
8212 args(1) = data;
8213 else
8214 args(1) = Matrix ();
8215
8216 unwind_protect_safe frame;
8217 frame.add_fcn (gh_manager::restore_gcbo);
8218
8219 if (true)
8220 {
8221 gh_manager::auto_lock guard;
8222
8223 callback_objects.push_front (get_object (h));
8224 xset_gcbo (h);
8225 }
8226
8227 BEGIN_INTERRUPT_WITH_EXCEPTIONS;
8228
8229 // Copy CB because "function_value" method is non-const.
8230
8231 octave_value cb = cb_arg;
8232
8233 if (cb.is_function () || cb.is_function_handle ())
8234 fcn = cb.function_value ();
8235 else if (cb.is_string ())
8236 {
8237 int status;
8238 std::string s = cb.string_value ();
8239
8240 eval_string (s, false, status, 0);
8241 }
8242 else if (cb.is_cell () && cb.length () > 0
8243 && (cb.rows () == 1 || cb.columns () == 1)
8244 && (cb.cell_value ()(0).is_function ()
8245 || cb.cell_value ()(0).is_function_handle ()))
8246 {
8247 Cell c = cb.cell_value ();
8248
8249 fcn = c(0).function_value ();
8250 if (! error_state)
8251 {
8252 for (int i = 1; i < c.length () ; i++)
8253 args(1+i) = c(i);
8254 }
8255 }
8256 else
8257 {
8258 std::string nm = cb.class_name ();
8259 error ("trying to execute non-executable object (class = %s)",
8260 nm.c_str ());
8261 }
8262
8263 if (fcn && ! error_state)
8264 feval (fcn, args);
8265
8266 END_INTERRUPT_WITH_EXCEPTIONS;
8267 }
8268 }
8269
8270 void
8271 gh_manager::do_post_event (const graphics_event& e)
8272 {
8273 event_queue.push_back (e);
8274
8275 command_editor::add_event_hook (gh_manager::process_events);
8276 }
8277
8278 void
8279 gh_manager::do_post_callback (const graphics_handle& h, const std::string name,
8280 const octave_value& data)
8281 {
8282 gh_manager::auto_lock guard;
8283
8284 graphics_object go = get_object (h);
8285
8286 if (go.valid_object ())
8287 {
8288 if (callback_objects.empty ())
8289 do_post_event (graphics_event::create_callback_event (h, name, data));
8290 else
8291 {
8292 const graphics_object& current = callback_objects.front ();
8293
8294 if (current.get_properties ().is_interruptible ())
8295 do_post_event (graphics_event::create_callback_event (h, name, data));
8296 else
8297 {
8298 caseless_str busy_action (go.get_properties ().get_busyaction ());
8299
8300 if (busy_action.compare ("queue"))
8301 do_post_event (graphics_event::create_callback_event (h, name, data));
8302 else
8303 {
8304 caseless_str cname (name);
8305
8306 if (cname.compare ("deletefcn")
8307 || cname.compare ("createfcn")
8308 || (go.isa ("figure")
8309 && (cname.compare ("closerequestfcn")
8310 || cname.compare ("resizefcn"))))
8311 do_post_event (graphics_event::create_callback_event (h, name, data));
8312 }
8313 }
8314 }
8315 }
8316 }
8317
8318 void
8319 gh_manager::do_post_function (graphics_event::event_fcn fcn, void* fcn_data)
8320 {
8321 gh_manager::auto_lock guard;
8322
8323 do_post_event (graphics_event::create_function_event (fcn, fcn_data));
8324 }
8325
8326 void
8327 gh_manager::do_post_set (const graphics_handle& h, const std::string name,
8328 const octave_value& value, bool notify_toolkit)
8329 {
8330 gh_manager::auto_lock guard;
8331
8332 do_post_event (graphics_event::create_set_event (h, name, value,
8333 notify_toolkit));
8334 }
8335
8336 int
8337 gh_manager::do_process_events (bool force)
8338 {
8339 graphics_event e;
8340 bool old_Vdrawnow_requested = Vdrawnow_requested;
8341 bool events_executed = false;
8342
8343 do
8344 {
8345 e = graphics_event ();
8346
8347 gh_manager::lock ();
8348
8349 if (! event_queue.empty ())
8350 {
8351 if (callback_objects.empty () || force)
8352 {
8353 e = event_queue.front ();
8354
8355 event_queue.pop_front ();
8356 }
8357 else
8358 {
8359 const graphics_object& go = callback_objects.front ();
8360
8361 if (go.get_properties ().is_interruptible ())
8362 {
8363 e = event_queue.front ();
8364
8365 event_queue.pop_front ();
8366 }
8367 }
8368 }
8369
8370 gh_manager::unlock ();
8371
8372 if (e.ok ())
8373 {
8374 e.execute ();
8375 events_executed = true;
8376 }
8377 }
8378 while (e.ok ());
8379
8380 gh_manager::lock ();
8381
8382 if (event_queue.empty () && event_processing == 0)
8383 command_editor::remove_event_hook (gh_manager::process_events);
8384
8385 gh_manager::unlock ();
8386
8387 if (events_executed)
8388 flush_octave_stdout ();
8389
8390 if (Vdrawnow_requested && ! old_Vdrawnow_requested)
8391 {
8392 Fdrawnow ();
8393
8394 Vdrawnow_requested = false;
8395 }
8396
8397 return 0;
8398 }
8399
8400 void
8401 gh_manager::do_enable_event_processing (bool enable)
8402 {
8403 gh_manager::auto_lock guard;
8404
8405 if (enable)
8406 {
8407 event_processing++;
8408
8409 command_editor::add_event_hook (gh_manager::process_events);
8410 }
8411 else
8412 {
8413 event_processing--;
8414
8415 if (event_queue.empty () && event_processing == 0)
8416 command_editor::remove_event_hook (gh_manager::process_events);
8417 }
8418 }
8419
8420 property_list::plist_map_type
8421 root_figure::init_factory_properties (void)
8422 {
8423 property_list::plist_map_type plist_map;
8424
8425 plist_map["figure"] = figure::properties::factory_defaults ();
8426 plist_map["axes"] = axes::properties::factory_defaults ();
8427 plist_map["line"] = line::properties::factory_defaults ();
8428 plist_map["text"] = text::properties::factory_defaults ();
8429 plist_map["image"] = image::properties::factory_defaults ();
8430 plist_map["patch"] = patch::properties::factory_defaults ();
8431 plist_map["surface"] = surface::properties::factory_defaults ();
8432 plist_map["hggroup"] = hggroup::properties::factory_defaults ();
8433 plist_map["uimenu"] = uimenu::properties::factory_defaults ();
8434 plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
8435 plist_map["uipanel"] = uipanel::properties::factory_defaults ();
8436 plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
8437 plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
8438 plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
8439 plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
8440
8441 return plist_map;
8442 }
8443
8444 // ---------------------------------------------------------------------
8445
8446 DEFUN (ishandle, args, ,
8447 "-*- texinfo -*-\n\
8448 @deftypefn {Built-in Function} {} ishandle (@var{h})\n\
8449 Return true if @var{h} is a graphics handle and false otherwise.\n\
8450 @var{h} may also be a matrix of handles in which case a logical\n\
8451 array is returned that is true where the elements of @var{h} are\n\
8452 graphics handles and false where they are not.\n\
8453 @seealso{isfigure}\n\
8454 @end deftypefn")
8455 {
8456 gh_manager::auto_lock guard;
8457
8458 octave_value retval;
8459
8460 if (args.length () == 1)
8461 retval = is_handle (args(0));
8462 else
8463 print_usage ();
8464
8465 return retval;
8466 }
8467
8468 static bool
8469 is_handle_visible (const graphics_handle& h)
8470 {
8471 return h.ok () && gh_manager::is_handle_visible (h);
8472 }
8473
8474 static bool
8475 is_handle_visible (double val)
8476 {
8477 return is_handle_visible (gh_manager::lookup (val));
8478 }
8479
8480 static octave_value
8481 is_handle_visible (const octave_value& val)
8482 {
8483 octave_value retval = false;
8484
8485 if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
8486 retval = true;
8487 else if (val.is_numeric_type () && val.is_real_type ())
8488 {
8489 const NDArray handles = val.array_value ();
8490
8491 if (! error_state)
8492 {
8493 boolNDArray result (handles.dims ());
8494
8495 for (octave_idx_type i = 0; i < handles.numel (); i++)
8496 result.xelem (i) = is_handle_visible (handles (i));
8497
8498 retval = result;
8499 }
8500 }
8501
8502 return retval;
8503 }
8504
8505 DEFUN (__is_handle_visible__, args, ,
8506 "-*- texinfo -*-\n\
8507 @deftypefn {Built-in Function} __is_handle_visible__ (@var{h})\n\
8508 Undocumented internal function.\n\
8509 @end deftypefn")
8510 {
8511 octave_value retval;
8512
8513 if (args.length () == 1)
8514 retval = is_handle_visible (args(0));
8515 else
8516 print_usage ();
8517
8518 return retval;
8519 }
8520
8521 DEFUN (reset, args, ,
8522 "-*- texinfo -*-\n\
8523 @deftypefn {Built-in Function} {} reset (@var{h}, @var{property})\n\
8524 Remove any defaults set for the handle @var{h}. The default figure\n\
8525 properties of \"position\", \"units\", \"windowstyle\" and\n\
8526 \"paperunits\" and the default axes properties of \"position\" and \"units\"\n\
8527 are not reset.\n\
8528 @end deftypefn")
8529 {
8530 int nargin = args.length ();
8531
8532 if (nargin != 1)
8533 print_usage ();
8534 else
8535 {
8536 // get vector of graphics handles
8537 ColumnVector hcv (args(0).vector_value ());
8538
8539 if (! error_state)
8540 {
8541 // loop over graphics objects
8542 for (octave_idx_type n = 0; n < hcv.length (); n++)
8543 gh_manager::get_object (hcv(n)).reset_default_properties ();
8544 }
8545 }
8546
8547 return octave_value ();
8548 }
8549
8550 DEFUN (set, args, nargout,
8551 "-*- texinfo -*-\n\
8552 @deftypefn {Built-in Function} {} set (@var{h}, @var{property}, @var{value}, @dots{})\n\
8553 @deftypefnx {Built-in Function} {} set (@var{h}, @var{properties}, @var{values})\n\
8554 @deftypefnx {Built-in Function} {} set (@var{h}, @var{pv})\n\
8555 Set named property values for the graphics handle (or vector of graphics\n\
8556 handles) @var{h}.\n\
8557 There are three ways how to give the property names and values:\n\
8558 \n\
8559 @itemize\n\
8560 @item as a comma separated list of @var{property}, @var{value} pairs\n\
8561 \n\
8562 Here, each @var{property} is a string containing the property name, each\n\
8563 @var{value} is a value of the appropriate type for the property.\n\
8564 \n\
8565 @item as a cell array of strings @var{properties} containing property names\n\
8566 and a cell array @var{values} containing property values.\n\
8567 \n\
8568 In this case, the number of columns of @var{values} must match the number of\n\
8569 elements in @var{properties}. The first column of @var{values} contains\n\
8570 values for the first entry in @var{properties}, etc. The number of rows of\n\
8571 @var{values} must be 1 or match the number of elements of @var{h}. In the\n\
8572 first case, each handle in @var{h} will be assigned the same values. In the\n\
8573 latter case, the first handle in @var{h} will be assigned the values from\n\
8574 the first row of @var{values} and so on.\n\
8575 \n\
8576 @item as a structure array @var{pv}\n\
8577 \n\
8578 Here, the field names of @var{pv} represent the property names, and the field\n\
8579 values give the property values. In contrast to the previous case, all\n\
8580 elements of @var{pv} will be set in all handles in @var{h} independent of\n\
8581 the dimensions of @var{pv}.\n\
8582 @end itemize\n\
8583 @seealso{get}\n\
8584 @end deftypefn")
8585 {
8586 gh_manager::auto_lock guard;
8587
8588 octave_value retval;
8589
8590 int nargin = args.length ();
8591
8592 if (nargin > 0)
8593 {
8594 // get vector of graphics handles
8595 ColumnVector hcv (args(0).vector_value ());
8596
8597 if (! error_state)
8598 {
8599 bool request_drawnow = false;
8600
8601 // loop over graphics objects
8602 for (octave_idx_type n = 0; n < hcv.length (); n++)
8603 {
8604 graphics_object obj = gh_manager::get_object (hcv(n));
8605
8606 if (obj)
8607 {
8608 if (nargin == 3 && args(1).is_cellstr ()
8609 && args(2).is_cell ())
8610 {
8611 if (args(2).cell_value ().rows () == 1)
8612 {
8613 obj.set (args(1).cellstr_value (),
8614 args(2).cell_value (), 0);
8615 }
8616 else if (hcv.length () == args(2).cell_value ().rows ())
8617 {
8618 obj.set (args(1).cellstr_value (),
8619 args(2).cell_value (), n);
8620 }
8621 else
8622 {
8623 error ("set: number of graphics handles must match number of value rows (%d != %d)",
8624 hcv.length (), args(2).cell_value ().rows ());
8625 break;
8626
8627 }
8628 }
8629 else if (nargin == 2 && args(1).is_map ())
8630 {
8631 obj.set (args(1).map_value ());
8632 }
8633 else if (nargin == 1)
8634 {
8635 if (nargout != 0)
8636 retval = obj.values_as_struct ();
8637 else
8638 {
8639 std::string s = obj.values_as_string ();
8640 if (! error_state)
8641 octave_stdout << s;
8642 }
8643 }
8644 else
8645 {
8646 obj.set (args.splice (0, 1));
8647 request_drawnow = true;
8648 }
8649 }
8650 else
8651 {
8652 error ("set: invalid handle (= %g)", hcv(n));
8653 break;
8654 }
8655
8656 if (error_state)
8657 break;
8658
8659 request_drawnow = true;
8660 }
8661
8662 if (! error_state && request_drawnow)
8663 Vdrawnow_requested = true;
8664 }
8665 else
8666 error ("set: expecting graphics handle as first argument");
8667 }
8668 else
8669 print_usage ();
8670
8671 return retval;
8672 }
8673
8674 static std::string
8675 get_graphics_object_type (const double val)
8676 {
8677 std::string retval;
8678
8679 graphics_object obj = gh_manager::get_object (val);
8680
8681 if (obj)
8682 retval = obj.type ();
8683 else
8684 error ("get: invalid handle (= %g)", val);
8685
8686 return retval;
8687 }
8688
8689 DEFUN (get, args, ,
8690 "-*- texinfo -*-\n\
8691 @deftypefn {Built-in Function} {@var{val} =} get (@var{h})\n\
8692 @deftypefnx {Built-in Function} {@var{val} =} get (@var{h}, @var{p})\n\
8693 Return the value of the named property @var{p} from the graphics handle\n\
8694 @var{h}. If @var{p} is omitted, return the complete property list for\n\
8695 @var{h}. If @var{h} is a vector, return a cell array including the property\n\
8696 values or lists respectively.\n\
8697 @seealso{set}\n\
8698 @end deftypefn")
8699 {
8700 gh_manager::auto_lock guard;
8701
8702 octave_value retval;
8703
8704 Cell vals;
8705
8706 int nargin = args.length ();
8707
8708 bool use_cell_format = false;
8709
8710 if (nargin == 1 || nargin == 2)
8711 {
8712 if (args(0).is_empty ())
8713 {
8714 retval = Matrix ();
8715 return retval;
8716 }
8717
8718 ColumnVector hcv (args(0).vector_value ());
8719
8720 if (! error_state)
8721 {
8722 octave_idx_type len = hcv.length ();
8723
8724 if (nargin == 1 && len > 1)
8725 {
8726 std::string t0 = get_graphics_object_type (hcv(0));
8727
8728 if (! error_state)
8729 {
8730 for (octave_idx_type n = 1; n < len; n++)
8731 {
8732 std::string t = get_graphics_object_type (hcv(n));
8733
8734 if (error_state)
8735 break;
8736
8737 if (t != t0)
8738 {
8739 error ("get: vector of handles must all have same type");
8740 break;
8741 }
8742 }
8743
8744 }
8745 }
8746
8747 if (! error_state)
8748 {
8749 if (nargin > 1 && args(1).is_cellstr ())
8750 {
8751 Array<std::string> plist = args(1).cellstr_value ();
8752
8753 if (! error_state)
8754 {
8755 octave_idx_type plen = plist.numel ();
8756
8757 use_cell_format = true;
8758
8759 vals.resize (dim_vector (len, plen));
8760
8761 for (octave_idx_type n = 0; ! error_state && n < len; n++)
8762 {
8763 graphics_object obj = gh_manager::get_object (hcv(n));
8764
8765 if (obj)
8766 {
8767 for (octave_idx_type m = 0; ! error_state && m < plen; m++)
8768 {
8769 caseless_str property = plist(m);
8770
8771 vals(n, m) = obj.get (property);
8772 }
8773 }
8774 else
8775 {
8776 error ("get: invalid handle (= %g)", hcv(n));
8777 break;
8778 }
8779 }
8780 }
8781 else
8782 error ("get: expecting property name or cell array of property names as second argument");
8783 }
8784 else
8785 {
8786 caseless_str property;
8787
8788 if (nargin > 1)
8789 {
8790 property = args(1).string_value ();
8791
8792 if (error_state)
8793 error ("get: expecting property name or cell array of property names as second argument");
8794 }
8795
8796 vals.resize (dim_vector (len, 1));
8797
8798 if (! error_state)
8799 {
8800 for (octave_idx_type n = 0; ! error_state && n < len; n++)
8801 {
8802 graphics_object obj = gh_manager::get_object (hcv(n));
8803
8804 if (obj)
8805 {
8806 if (nargin == 1)
8807 vals(n) = obj.get ();
8808 else
8809 vals(n) = obj.get (property);
8810 }
8811 else
8812 {
8813 error ("get: invalid handle (= %g)", hcv(n));
8814 break;
8815 }
8816 }
8817 }
8818 }
8819 }
8820 }
8821 else
8822 error ("get: expecting graphics handle as first argument");
8823 }
8824 else
8825 print_usage ();
8826
8827 if (! error_state)
8828 {
8829 if (use_cell_format)
8830 retval = vals;
8831 else
8832 {
8833 octave_idx_type len = vals.numel ();
8834
8835 if (len == 0)
8836 retval = Matrix ();
8837 else if (len == 1)
8838 retval = vals(0);
8839 else if (len > 1 && nargin == 1)
8840 {
8841 OCTAVE_LOCAL_BUFFER (octave_scalar_map, tmp, len);
8842
8843 for (octave_idx_type n = 0; n < len; n++)
8844 tmp[n] = vals(n).scalar_map_value ();
8845
8846 retval = octave_map::cat (0, len, tmp);
8847 }
8848 else
8849 retval = vals;
8850 }
8851 }
8852
8853 return retval;
8854 }
8855
8856 /*
8857 %!assert (get (findobj (0, "Tag", "nonexistenttag"), "nonexistentproperty"), [])
8858 */
8859
8860 // Return all properties from the graphics handle @var{h}.
8861 // If @var{h} is a vector, return a cell array including the
8862 // property values or lists respectively.
8863
8864 DEFUN (__get__, args, ,
8865 "-*- texinfo -*-\n\
8866 @deftypefn {Built-in Function} {} __get__ (@var{h})\n\
8867 Undocumented internal function.\n\
8868 @end deftypefn")
8869 {
8870 gh_manager::auto_lock guard;
8871
8872 octave_value retval;
8873
8874 Cell vals;
8875
8876 int nargin = args.length ();
8877
8878 if (nargin == 1)
8879 {
8880 ColumnVector hcv (args(0).vector_value ());
8881
8882 if (! error_state)
8883 {
8884 octave_idx_type len = hcv.length ();
8885
8886 vals.resize (dim_vector (len, 1));
8887
8888 for (octave_idx_type n = 0; n < len; n++)
8889 {
8890 graphics_object obj = gh_manager::get_object (hcv(n));
8891
8892 if (obj)
8893 vals(n) = obj.get (true);
8894 else
8895 {
8896 error ("get: invalid handle (= %g)", hcv(n));
8897 break;
8898 }
8899 }
8900 }
8901 else
8902 error ("get: expecting graphics handle as first argument");
8903 }
8904 else
8905 print_usage ();
8906
8907 if (! error_state)
8908 {
8909 octave_idx_type len = vals.numel ();
8910
8911 if (len > 1)
8912 retval = vals;
8913 else if (len == 1)
8914 retval = vals(0);
8915 }
8916
8917 return retval;
8918 }
8919
8920 static octave_value
8921 make_graphics_object (const std::string& go_name,
8922 bool integer_figure_handle,
8923 const octave_value_list& args)
8924 {
8925 octave_value retval;
8926
8927 double val = octave_NaN;
8928
8929 octave_value_list xargs = args.splice (0, 1);
8930
8931 caseless_str p ("parent");
8932
8933 for (int i = 0; i < xargs.length (); i++)
8934 if (xargs(i).is_string ()
8935 && p.compare (xargs(i).string_value ()))
8936 {
8937 if (i < (xargs.length () - 1))
8938 {
8939 val = xargs(i+1).double_value ();
8940
8941 if (! error_state)
8942 {
8943 xargs = xargs.splice (i, 2);
8944 break;
8945 }
8946 }
8947 else
8948 error ("__go_%s__: missing value for parent property",
8949 go_name.c_str ());
8950 }
8951
8952 if (! error_state && xisnan (val))
8953 val = args(0).double_value ();
8954
8955 if (! error_state)
8956 {
8957 graphics_handle parent = gh_manager::lookup (val);
8958
8959 if (parent.ok ())
8960 {
8961 graphics_handle h
8962 = gh_manager::make_graphics_handle (go_name, parent,
8963 integer_figure_handle,
8964 false, false);
8965
8966 if (! error_state)
8967 {
8968 adopt (parent, h);
8969
8970 xset (h, xargs);
8971 xcreatefcn (h);
8972 xinitialize (h);
8973
8974 retval = h.value ();
8975
8976 if (! error_state)
8977 Vdrawnow_requested = true;
8978 }
8979 else
8980 error ("__go%s__: unable to create graphics handle",
8981 go_name.c_str ());
8982 }
8983 else
8984 error ("__go_%s__: invalid parent", go_name.c_str ());
8985 }
8986 else
8987 error ("__go_%s__: invalid parent", go_name.c_str ());
8988
8989 return retval;
8990 }
8991
8992 DEFUN (__go_figure__, args, ,
8993 "-*- texinfo -*-\n\
8994 @deftypefn {Built-in Function} {} __go_figure__ (@var{fignum})\n\
8995 Undocumented internal function.\n\
8996 @end deftypefn")
8997 {
8998 gh_manager::auto_lock guard;
8999
9000 octave_value retval;
9001
9002 if (args.length () > 0)
9003 {
9004 double val = args(0).double_value ();
9005
9006 if (! error_state)
9007 {
9008 if (is_figure (val))
9009 {
9010 graphics_handle h = gh_manager::lookup (val);
9011
9012 xset (h, args.splice (0, 1));
9013
9014 retval = h.value ();
9015 }
9016 else
9017 {
9018 bool int_fig_handle = true;
9019
9020 octave_value_list xargs = args.splice (0, 1);
9021
9022 graphics_handle h = octave_NaN;
9023
9024 if (xisnan (val))
9025 {
9026 caseless_str p ("integerhandle");
9027
9028 for (int i = 0; i < xargs.length (); i++)
9029 {
9030 if (xargs(i).is_string ()
9031 && p.compare (xargs(i).string_value ()))
9032 {
9033 if (i < (xargs.length () - 1))
9034 {
9035 std::string pval = xargs(i+1).string_value ();
9036
9037 if (! error_state)
9038 {
9039 caseless_str on ("on");
9040 int_fig_handle = on.compare (pval);
9041 xargs = xargs.splice (i, 2);
9042 break;
9043 }
9044 }
9045 }
9046 }
9047
9048 h = gh_manager::make_graphics_handle ("figure", 0,
9049 int_fig_handle,
9050 false, false);
9051
9052 if (! int_fig_handle)
9053 {
9054 // We need to intiailize the integerhandle
9055 // property without calling the set_integerhandle
9056 // method, because doing that will generate a new
9057 // handle value...
9058
9059 graphics_object go = gh_manager::get_object (h);
9060 go.get_properties ().init_integerhandle ("off");
9061 }
9062 }
9063 else if (val > 0 && D_NINT (val) == val)
9064 h = gh_manager::make_figure_handle (val, false);
9065
9066 if (! error_state && h.ok ())
9067 {
9068 adopt (0, h);
9069
9070 gh_manager::push_figure (h);
9071
9072 xset (h, xargs);
9073 xcreatefcn (h);
9074 xinitialize (h);
9075
9076 retval = h.value ();
9077 }
9078 else
9079 error ("__go_figure__: failed to create figure handle");
9080 }
9081 }
9082 else
9083 error ("__go_figure__: expecting figure number to be double value");
9084 }
9085 else
9086 print_usage ();
9087
9088 return retval;
9089 }
9090
9091 #define GO_BODY(TYPE) \
9092 gh_manager::auto_lock guard; \
9093 \
9094 octave_value retval; \
9095 \
9096 if (args.length () > 0) \
9097 retval = make_graphics_object (#TYPE, false, args); \
9098 else \
9099 print_usage (); \
9100 \
9101 return retval
9102
9103 int
9104 calc_dimensions (const graphics_object& go)
9105 {
9106
9107 int nd = 2;
9108
9109 if (go.isa ("surface"))
9110 nd = 3;
9111
9112 if ((go.isa ("line") || go.isa ("patch")) && ! go.get("zdata").is_empty ())
9113 nd = 3;
9114
9115 Matrix kids = go.get_properties ().get_children ();
9116
9117 for (octave_idx_type i = 0; i < kids.length (); i++)
9118 {
9119 graphics_handle hnd = gh_manager::lookup (kids(i));
9120
9121 if (hnd.ok ())
9122 {
9123 const graphics_object& kid = gh_manager::get_object (hnd);
9124
9125 if (kid.valid_object ())
9126 nd = calc_dimensions (kid);
9127
9128 if (nd == 3)
9129 break;
9130 }
9131 }
9132
9133 return nd;
9134 }
9135
9136 DEFUN (__calc_dimensions__, args, ,
9137 "-*- texinfo -*-\n\
9138 @deftypefn {Built-in Function} {} __calc_dimensions__ (@var{axes})\n\
9139 Internal function. Determine the number of dimensions in a graphics\n\
9140 object, whether 2 or 3.\n\
9141 @end deftypefn")
9142 {
9143 gh_manager::auto_lock guard;
9144
9145 octave_value retval;
9146
9147 int nargin = args.length ();
9148
9149 if (nargin == 1)
9150 {
9151 double h = args(0).double_value ();
9152
9153 if (! error_state)
9154 retval = calc_dimensions (gh_manager::get_object (h));
9155 else
9156 error ("__calc_dimensions__: expecting graphics handle as only argument");
9157 }
9158 else
9159 print_usage ();
9160
9161 return retval;
9162 }
9163
9164 DEFUN (__go_axes__, args, ,
9165 "-*- texinfo -*-\n\
9166 @deftypefn {Built-in Function} {} __go_axes__ (@var{parent})\n\
9167 Undocumented internal function.\n\
9168 @end deftypefn")
9169 {
9170 GO_BODY (axes);
9171 }
9172
9173 DEFUN (__go_line__, args, ,
9174 "-*- texinfo -*-\n\
9175 @deftypefn {Built-in Function} {} __go_line__ (@var{parent})\n\
9176 Undocumented internal function.\n\
9177 @end deftypefn")
9178 {
9179 GO_BODY (line);
9180 }
9181
9182 DEFUN (__go_text__, args, ,
9183 "-*- texinfo -*-\n\
9184 @deftypefn {Built-in Function} {} __go_text__ (@var{parent})\n\
9185 Undocumented internal function.\n\
9186 @end deftypefn")
9187 {
9188 GO_BODY (text);
9189 }
9190
9191 DEFUN (__go_image__, args, ,
9192 "-*- texinfo -*-\n\
9193 @deftypefn {Built-in Function} {} __go_image__ (@var{parent})\n\
9194 Undocumented internal function.\n\
9195 @end deftypefn")
9196 {
9197 GO_BODY (image);
9198 }
9199
9200 DEFUN (__go_surface__, args, ,
9201 "-*- texinfo -*-\n\
9202 @deftypefn {Built-in Function} {} __go_surface__ (@var{parent})\n\
9203 Undocumented internal function.\n\
9204 @end deftypefn")
9205 {
9206 GO_BODY (surface);
9207 }
9208
9209 DEFUN (__go_patch__, args, ,
9210 "-*- texinfo -*-\n\
9211 @deftypefn {Built-in Function} {} __go_patch__ (@var{parent})\n\
9212 Undocumented internal function.\n\
9213 @end deftypefn")
9214 {
9215 GO_BODY (patch);
9216 }
9217
9218 DEFUN (__go_hggroup__, args, ,
9219 "-*- texinfo -*-\n\
9220 @deftypefn {Built-in Function} {} __go_hggroup__ (@var{parent})\n\
9221 Undocumented internal function.\n\
9222 @end deftypefn")
9223 {
9224 GO_BODY (hggroup);
9225 }
9226
9227 DEFUN (__go_uimenu__, args, ,
9228 "-*- texinfo -*-\n\
9229 @deftypefn {Built-in Function} {} __go_uimenu__ (@var{parent})\n\
9230 Undocumented internal function.\n\
9231 @end deftypefn")
9232 {
9233 GO_BODY (uimenu);
9234 }
9235
9236 DEFUN (__go_uicontrol__, args, ,
9237 "-*- texinfo -*-\n\
9238 @deftypefn {Built-in Function} {} __go_uicontrol__ (@var{parent})\n\
9239 Undocumented internal function.\n\
9240 @end deftypefn")
9241 {
9242 GO_BODY (uicontrol);
9243 }
9244
9245 DEFUN (__go_uipanel__, args, ,
9246 "-*- texinfo -*-\n\
9247 @deftypefn {Built-in Function} {} __go_uipanel__ (@var{parent})\n\
9248 Undocumented internal function.\n\
9249 @end deftypefn")
9250 {
9251 GO_BODY (uipanel);
9252 }
9253
9254 DEFUN (__go_uicontextmenu__, args, ,
9255 "-*- texinfo -*-\n\
9256 @deftypefn {Built-in Function} {} __go_uicontextmenu__ (@var{parent})\n\
9257 Undocumented internal function.\n\
9258 @end deftypefn")
9259 {
9260 GO_BODY (uicontextmenu);
9261 }
9262
9263 DEFUN (__go_uitoolbar__, args, ,
9264 "-*- texinfo -*-\n\
9265 @deftypefn {Built-in Function} {} __go_uitoolbar__ (@var{parent})\n\
9266 Undocumented internal function.\n\
9267 @end deftypefn")
9268 {
9269 GO_BODY (uitoolbar);
9270 }
9271
9272 DEFUN (__go_uipushtool__, args, ,
9273 "-*- texinfo -*-\n\
9274 @deftypefn {Built-in Function} {} __go_uipushtool__ (@var{parent})\n\
9275 Undocumented internal function.\n\
9276 @end deftypefn")
9277 {
9278 GO_BODY (uipushtool);
9279 }
9280
9281 DEFUN (__go_uitoggletool__, args, ,
9282 "-*- texinfo -*-\n\
9283 @deftypefn {Built-in Function} {} __go_uitoggletool__ (@var{parent})\n\
9284 Undocumented internal function.\n\
9285 @end deftypefn")
9286 {
9287 GO_BODY (uitoggletool);
9288 }
9289
9290 DEFUN (__go_delete__, args, ,
9291 "-*- texinfo -*-\n\
9292 @deftypefn {Built-in Function} {} __go_delete__ (@var{h})\n\
9293 Undocumented internal function.\n\
9294 @end deftypefn")
9295 {
9296 gh_manager::auto_lock guard;
9297
9298 octave_value_list retval;
9299
9300 if (args.length () == 1)
9301 {
9302 graphics_handle h = octave_NaN;
9303
9304 const NDArray vals = args (0).array_value ();
9305
9306 if (! error_state)
9307 {
9308 // Check is all the handles to delete are valid first
9309 // as callbacks might delete one of the handles we
9310 // later want to delete
9311 for (octave_idx_type i = 0; i < vals.numel (); i++)
9312 {
9313 h = gh_manager::lookup (vals.elem (i));
9314
9315 if (! h.ok ())
9316 {
9317 error ("delete: invalid graphics object (= %g)",
9318 vals.elem (i));
9319 break;
9320 }
9321 }
9322
9323 if (! error_state)
9324 delete_graphics_objects (vals);
9325 }
9326 else
9327 error ("delete: invalid graphics object");
9328 }
9329 else
9330 print_usage ();
9331
9332 return retval;
9333 }
9334
9335 DEFUN (__go_axes_init__, args, ,
9336 "-*- texinfo -*-\n\
9337 @deftypefn {Built-in Function} {} __go_axes_init__ (@var{h}, @var{mode})\n\
9338 Undocumented internal function.\n\
9339 @end deftypefn")
9340 {
9341 gh_manager::auto_lock guard;
9342
9343 octave_value retval;
9344
9345 int nargin = args.length ();
9346
9347 std::string mode = "";
9348
9349 if (nargin == 2)
9350 {
9351 mode = args(1).string_value ();
9352
9353 if (error_state)
9354 return retval;
9355 }
9356
9357 if (nargin == 1 || nargin == 2)
9358 {
9359 graphics_handle h = octave_NaN;
9360
9361 double val = args(0).double_value ();
9362
9363 if (! error_state)
9364 {
9365 h = gh_manager::lookup (val);
9366
9367 if (h.ok ())
9368 {
9369 graphics_object obj = gh_manager::get_object (h);
9370
9371 obj.set_defaults (mode);
9372
9373 h = gh_manager::lookup (val);
9374 if (! h.ok ())
9375 error ("__go_axes_init__: axis deleted during initialization (= %g)", val);
9376 }
9377 else
9378 error ("__go_axes_init__: invalid graphics object (= %g)", val);
9379 }
9380 else
9381 error ("__go_axes_init__: invalid graphics object");
9382 }
9383 else
9384 print_usage ();
9385
9386 return retval;
9387 }
9388
9389 DEFUN (__go_handles__, args, ,
9390 "-*- texinfo -*-\n\
9391 @deftypefn {Built-in Function} {} __go_handles__ (@var{show_hidden})\n\
9392 Undocumented internal function.\n\
9393 @end deftypefn")
9394 {
9395 gh_manager::auto_lock guard;
9396
9397 bool show_hidden = false;
9398
9399 if (args.length () > 0)
9400 show_hidden = args(0).bool_value ();
9401
9402 return octave_value (gh_manager::handle_list (show_hidden));
9403 }
9404
9405 DEFUN (__go_figure_handles__, args, ,
9406 "-*- texinfo -*-\n\
9407 @deftypefn {Built-in Function} {} __go_figure_handles__ (@var{show_hidden})\n\
9408 Undocumented internal function.\n\
9409 @end deftypefn")
9410 {
9411 gh_manager::auto_lock guard;
9412
9413 bool show_hidden = false;
9414
9415 if (args.length () > 0)
9416 show_hidden = args(0).bool_value ();
9417
9418 return octave_value (gh_manager::figure_handle_list (show_hidden));
9419 }
9420
9421 DEFUN (__go_execute_callback__, args, ,
9422 "-*- texinfo -*-\n\
9423 @deftypefn {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name})\n\
9424 @deftypefnx {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})\n\
9425 Undocumented internal function.\n\
9426 @end deftypefn")
9427 {
9428 octave_value retval;
9429
9430 int nargin = args.length ();
9431
9432 if (nargin == 2 || nargin == 3)
9433 {
9434 double val = args(0).double_value ();
9435
9436 if (! error_state)
9437 {
9438 graphics_handle h = gh_manager::lookup (val);
9439
9440 if (h.ok ())
9441 {
9442 std::string name = args(1).string_value ();
9443
9444 if (! error_state)
9445 {
9446 if (nargin == 2)
9447 gh_manager::execute_callback (h, name);
9448 else
9449 gh_manager::execute_callback (h, name, args(2));
9450 }
9451 else
9452 error ("__go_execute_callback__: invalid callback name");
9453 }
9454 else
9455 error ("__go_execute_callback__: invalid graphics object (= %g)",
9456 val);
9457 }
9458 else
9459 error ("__go_execute_callback__: invalid graphics object");
9460 }
9461 else
9462 print_usage ();
9463
9464 return retval;
9465 }
9466
9467 DEFUN (__image_pixel_size__, args, ,
9468 "-*- texinfo -*-\n\
9469 @deftypefn {Built-in Function} {@var{px}, @var{py}} __image_pixel_size__ (@var{h})\n\
9470 Internal function: returns the pixel size of the image in normalized units.\n\
9471 @end deftypefn")
9472 {
9473 octave_value retval;
9474
9475 int nargin = args.length ();
9476
9477 if (nargin == 1)
9478 {
9479 double h = args(0).double_value ();
9480
9481 if (! error_state)
9482 {
9483 graphics_object fobj = gh_manager::get_object (h);
9484 if (fobj && fobj.isa ("image"))
9485 {
9486 image::properties& ip =
9487 dynamic_cast<image::properties&> (fobj.get_properties ());
9488
9489 Matrix dp = Matrix (1, 2, 0);
9490 dp(0, 0) = ip.pixel_xsize ();
9491 dp(0, 1) = ip.pixel_ysize ();
9492 retval = dp;
9493 }
9494 else
9495 error ("__image_pixel_size__: object is not an image");
9496 }
9497 else
9498 error ("__image_pixel_size__: argument is not a handle");
9499 }
9500 else
9501 print_usage ();
9502
9503 return retval;
9504 }
9505
9506 gtk_manager *gtk_manager::instance = 0;
9507
9508 void
9509 gtk_manager::create_instance (void)
9510 {
9511 instance = new gtk_manager ();
9512
9513 if (instance)
9514 singleton_cleanup_list::add (cleanup_instance);
9515 }
9516
9517 graphics_toolkit
9518 gtk_manager::do_get_toolkit (void) const
9519 {
9520 graphics_toolkit retval;
9521
9522 const_loaded_toolkits_iterator pl = loaded_toolkits.find (dtk);
9523
9524 if (pl == loaded_toolkits.end ())
9525 {
9526 const_available_toolkits_iterator pa = available_toolkits.find (dtk);
9527
9528 if (pa != available_toolkits.end ())
9529 {
9530 octave_value_list args;
9531 args(0) = dtk;
9532 feval ("graphics_toolkit", args);
9533
9534 if (! error_state)
9535 pl = loaded_toolkits.find (dtk);
9536
9537 if (error_state || pl == loaded_toolkits.end ())
9538 error ("failed to load %s graphics toolkit", dtk.c_str ());
9539 else
9540 retval = pl->second;
9541 }
9542 else
9543 error ("default graphics toolkit '%s' is not available!",
9544 dtk.c_str ());
9545 }
9546 else
9547 retval = pl->second;
9548
9549 return retval;
9550 }
9551
9552 DEFUN (available_graphics_toolkits, , ,
9553 "-*- texinfo -*-\n\
9554 @deftypefn {Built-in Function} {} available_graphics_toolkits ()\n\
9555 Return a cell array of registered graphics toolkits.\n\
9556 @seealso{graphics_toolkit, register_graphics_toolkit}\n\
9557 @end deftypefn")
9558 {
9559 gh_manager::auto_lock guard;
9560
9561 return octave_value (gtk_manager::available_toolkits_list ());
9562 }
9563
9564 DEFUN (register_graphics_toolkit, args, ,
9565 "-*- texinfo -*-\n\
9566 @deftypefn {Built-in Function} {} register_graphics_toolkit (@var{toolkit})\n\
9567 List @var{toolkit} as an available graphics toolkit.\n\
9568 @seealso{available_graphics_toolkits}\n\
9569 @end deftypefn")
9570 {
9571 octave_value retval;
9572
9573 gh_manager::auto_lock guard;
9574
9575 if (args.length () == 1)
9576 {
9577 std::string name = args(0).string_value ();
9578
9579 if (! error_state)
9580 gtk_manager::register_toolkit (name);
9581 else
9582 error ("register_graphics_toolkit: expecting character string");
9583 }
9584 else
9585 print_usage ();
9586
9587 return retval;
9588 }
9589
9590 DEFUN (loaded_graphics_toolkits, , ,
9591 "-*- texinfo -*-\n\
9592 @deftypefn {Built-in Function} {} loaded_graphics_toolkits ()\n\
9593 Return a cell array of the currently loaded graphics toolkits.\n\
9594 @seealso{available_graphics_toolkits}\n\
9595 @end deftypefn")
9596 {
9597 gh_manager::auto_lock guard;
9598
9599 return octave_value (gtk_manager::loaded_toolkits_list ());
9600 }
9601
9602 DEFUN (drawnow, args, ,
9603 "-*- texinfo -*-\n\
9604 @deftypefn {Built-in Function} {} drawnow ()\n\
9605 @deftypefnx {Built-in Function} {} drawnow (\"expose\")\n\
9606 @deftypefnx {Built-in Function} {} drawnow (@var{term}, @var{file}, @var{mono}, @var{debug_file})\n\
9607 Update figure windows and their children. The event queue is flushed and\n\
9608 any callbacks generated are executed. With the optional argument\n\
9609 @code{\"expose\"}, only graphic objects are updated and no other events or\n\
9610 callbacks are processed.\n\
9611 The third calling form of @code{drawnow} is for debugging and is\n\
9612 undocumented.\n\
9613 @end deftypefn")
9614 {
9615 static int drawnow_executing = 0;
9616
9617 octave_value retval;
9618
9619 gh_manager::lock ();
9620
9621 unwind_protect frame;
9622 frame.protect_var (Vdrawnow_requested, false);
9623
9624 frame.protect_var (drawnow_executing);
9625
9626 if (++drawnow_executing <= 1)
9627 {
9628 if (args.length () == 0 || args.length () == 1)
9629 {
9630 Matrix hlist = gh_manager::figure_handle_list (true);
9631
9632 for (int i = 0; ! error_state && i < hlist.length (); i++)
9633 {
9634 graphics_handle h = gh_manager::lookup (hlist(i));
9635
9636 if (h.ok () && h != 0)
9637 {
9638 graphics_object go = gh_manager::get_object (h);
9639 figure::properties& fprops = dynamic_cast <figure::properties&> (go.get_properties ());
9640
9641 if (fprops.is_modified ())
9642 {
9643 if (fprops.is_visible ())
9644 {
9645 gh_manager::unlock ();
9646
9647 fprops.get_toolkit ().redraw_figure (go);
9648
9649 gh_manager::lock ();
9650 }
9651
9652 fprops.set_modified (false);
9653 }
9654 }
9655 }
9656
9657 bool do_events = true;
9658
9659 if (args.length () == 1)
9660 {
9661 caseless_str val (args(0).string_value ());
9662
9663 if (! error_state && val.compare ("expose"))
9664 do_events = false;
9665 else
9666 {
9667 error ("drawnow: invalid argument, expected 'expose' as argument");
9668 return retval;
9669 }
9670 }
9671
9672 if (do_events)
9673 {
9674 gh_manager::unlock ();
9675
9676 gh_manager::process_events ();
9677
9678 gh_manager::lock ();
9679 }
9680 }
9681 else if (args.length () >= 2 && args.length () <= 4)
9682 {
9683 std::string term, file, debug_file;
9684 bool mono;
9685
9686 term = args(0).string_value ();
9687
9688 if (! error_state)
9689 {
9690 file = args(1).string_value ();
9691
9692 if (! error_state)
9693 {
9694 size_t pos = file.find_first_not_of ("|");
9695 if (pos > 0)
9696 file = file.substr (pos);
9697 else
9698 {
9699 pos = file.find_last_of (file_ops::dir_sep_chars ());
9700
9701 if (pos != std::string::npos)
9702 {
9703 std::string dirname = file.substr (0, pos+1);
9704
9705 file_stat fs (dirname);
9706
9707 if (! (fs && fs.is_dir ()))
9708 {
9709 error ("drawnow: nonexistent directory '%s'",
9710 dirname.c_str ());
9711
9712 return retval;
9713 }
9714 }
9715 }
9716
9717 mono = (args.length () >= 3 ? args(2).bool_value () : false);
9718
9719 if (! error_state)
9720 {
9721 debug_file = (args.length () > 3 ? args(3).string_value ()
9722 : "");
9723
9724 if (! error_state)
9725 {
9726 graphics_handle h = gcf ();
9727
9728 if (h.ok ())
9729 {
9730 graphics_object go = gh_manager::get_object (h);
9731
9732 gh_manager::unlock ();
9733
9734 go.get_toolkit ()
9735 .print_figure (go, term, file, mono, debug_file);
9736
9737 gh_manager::lock ();
9738 }
9739 else
9740 error ("drawnow: nothing to draw");
9741 }
9742 else
9743 error ("drawnow: invalid DEBUG_FILE, expected a string value");
9744 }
9745 else
9746 error ("drawnow: invalid colormode MONO, expected a boolean value");
9747 }
9748 else
9749 error ("drawnow: invalid FILE, expected a string value");
9750 }
9751 else
9752 error ("drawnow: invalid terminal TERM, expected a string value");
9753 }
9754 else
9755 print_usage ();
9756 }
9757
9758 gh_manager::unlock ();
9759
9760 return retval;
9761 }
9762
9763 DEFUN (addlistener, args, ,
9764 "-*- texinfo -*-\n\
9765 @deftypefn {Built-in Function} {} addlistener (@var{h}, @var{prop}, @var{fcn})\n\
9766 Register @var{fcn} as listener for the property @var{prop} of the graphics\n\
9767 object @var{h}. Property listeners are executed (in order of registration)\n\
9768 when the property is set. The new value is already available when the\n\
9769 listeners are executed.\n\
9770 \n\
9771 @var{prop} must be a string naming a valid property in @var{h}.\n\
9772 \n\
9773 @var{fcn} can be a function handle, a string or a cell array whose first\n\
9774 element is a function handle. If @var{fcn} is a function handle, the\n\
9775 corresponding function should accept at least 2 arguments, that will be\n\
9776 set to the object handle and the empty matrix respectively. If @var{fcn}\n\
9777 is a string, it must be any valid octave expression. If @var{fcn} is a cell\n\
9778 array, the first element must be a function handle with the same signature\n\
9779 as described above. The next elements of the cell array are passed\n\
9780 as additional arguments to the function.\n\
9781 \n\
9782 Example:\n\
9783 \n\
9784 @example\n\
9785 @group\n\
9786 function my_listener (h, dummy, p1)\n\
9787 fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
9788 endfunction\n\
9789 \n\
9790 addlistener (gcf, \"position\", @{@@my_listener, \"my string\"@})\n\
9791 @end group\n\
9792 @end example\n\
9793 \n\
9794 @end deftypefn")
9795 {
9796 gh_manager::auto_lock guard;
9797
9798 octave_value retval;
9799
9800 if (args.length () >= 3 && args.length () <= 4)
9801 {
9802 double h = args(0).double_value ();
9803
9804 if (! error_state)
9805 {
9806 std::string pname = args(1).string_value ();
9807
9808 if (! error_state)
9809 {
9810 graphics_handle gh = gh_manager::lookup (h);
9811
9812 if (gh.ok ())
9813 {
9814 graphics_object go = gh_manager::get_object (gh);
9815
9816 go.add_property_listener (pname, args(2), POSTSET);
9817
9818 if (args.length () == 4)
9819 {
9820 caseless_str persistent = args(3).string_value ();
9821 if (persistent.compare ("persistent"))
9822 go.add_property_listener (pname, args(2), PERSISTENT);
9823 }
9824 }
9825 else
9826 error ("addlistener: invalid graphics object (= %g)",
9827 h);
9828 }
9829 else
9830 error ("addlistener: invalid property name, expected a string value");
9831 }
9832 else
9833 error ("addlistener: invalid handle");
9834 }
9835 else
9836 print_usage ();
9837
9838 return retval;
9839 }
9840
9841 DEFUN (dellistener, args, ,
9842 "-*- texinfo -*-\n\
9843 @deftypefn {Built-in Function} {} dellistener (@var{h}, @var{prop}, @var{fcn})\n\
9844 Remove the registration of @var{fcn} as a listener for the property\n\
9845 @var{prop} of the graphics object @var{h}. The function @var{fcn} must\n\
9846 be the same variable (not just the same value), as was passed to the\n\
9847 original call to @code{addlistener}.\n\
9848 \n\
9849 If @var{fcn} is not defined then all listener functions of @var{prop}\n\
9850 are removed.\n\
9851 \n\
9852 Example:\n\
9853 \n\
9854 @example\n\
9855 @group\n\
9856 function my_listener (h, dummy, p1)\n\
9857 fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
9858 endfunction\n\
9859 \n\
9860 c = @{@@my_listener, \"my string\"@};\n\
9861 addlistener (gcf, \"position\", c);\n\
9862 dellistener (gcf, \"position\", c);\n\
9863 @end group\n\
9864 @end example\n\
9865 \n\
9866 @end deftypefn")
9867 {
9868 gh_manager::auto_lock guard;
9869
9870 octave_value retval;
9871
9872 if (args.length () == 3 || args.length () == 2)
9873 {
9874 double h = args(0).double_value ();
9875
9876 if (! error_state)
9877 {
9878 std::string pname = args(1).string_value ();
9879
9880 if (! error_state)
9881 {
9882 graphics_handle gh = gh_manager::lookup (h);
9883
9884 if (gh.ok ())
9885 {
9886 graphics_object go = gh_manager::get_object (gh);
9887
9888 if (args.length () == 2)
9889 go.delete_property_listener (pname, octave_value (), POSTSET);
9890 else
9891 {
9892 caseless_str persistent = args(2).string_value ();
9893 if (persistent.compare ("persistent"))
9894 {
9895 go.delete_property_listener (pname, octave_value (), PERSISTENT);
9896 go.delete_property_listener (pname, octave_value (), POSTSET);
9897 }
9898 else
9899 go.delete_property_listener (pname, args(2), POSTSET);
9900 }
9901 }
9902 else
9903 error ("dellistener: invalid graphics object (= %g)",
9904 h);
9905 }
9906 else
9907 error ("dellistener: invalid property name, expected a string value");
9908 }
9909 else
9910 error ("dellistener: invalid handle");
9911 }
9912 else
9913 print_usage ();
9914
9915 return retval;
9916 }
9917
9918 DEFUN (addproperty, args, ,
9919 "-*- texinfo -*-\n\
9920 @deftypefn {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type})\n\
9921 @deftypefnx {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})\n\
9922 Create a new property named @var{name} in graphics object @var{h}.\n\
9923 @var{type} determines the type of the property to create. @var{args}\n\
9924 usually contains the default value of the property, but additional\n\
9925 arguments might be given, depending on the type of the property.\n\
9926 \n\
9927 The supported property types are:\n\
9928 \n\
9929 @table @code\n\
9930 @item string\n\
9931 A string property. @var{arg} contains the default string value.\n\
9932 \n\
9933 @item any\n\
9934 An @nospell{un-typed} property. This kind of property can hold any octave\n\
9935 value. @var{args} contains the default value.\n\
9936 \n\
9937 @item radio\n\
9938 A string property with a limited set of accepted values. The first\n\
9939 argument must be a string with all accepted values separated by\n\
9940 a vertical bar ('|'). The default value can be marked by enclosing\n\
9941 it with a '@{' '@}' pair. The default value may also be given as\n\
9942 an optional second string argument.\n\
9943 \n\
9944 @item boolean\n\
9945 A boolean property. This property type is equivalent to a radio\n\
9946 property with \"on|off\" as accepted values. @var{arg} contains\n\
9947 the default property value.\n\
9948 \n\
9949 @item double\n\
9950 A scalar double property. @var{arg} contains the default value.\n\
9951 \n\
9952 @item handle\n\
9953 A handle property. This kind of property holds the handle of a\n\
9954 graphics object. @var{arg} contains the default handle value.\n\
9955 When no default value is given, the property is initialized to\n\
9956 the empty matrix.\n\
9957 \n\
9958 @item data\n\
9959 A data (matrix) property. @var{arg} contains the default data\n\
9960 value. When no default value is given, the data is initialized to\n\
9961 the empty matrix.\n\
9962 \n\
9963 @item color\n\
9964 A color property. @var{arg} contains the default color value.\n\
9965 When no default color is given, the property is set to black.\n\
9966 An optional second string argument may be given to specify an\n\
9967 additional set of accepted string values (like a radio property).\n\
9968 @end table\n\
9969 \n\
9970 @var{type} may also be the concatenation of a core object type and\n\
9971 a valid property name for that object type. The property created\n\
9972 then has the same characteristics as the referenced property (type,\n\
9973 possible values, hidden state@dots{}). This allows to clone an existing\n\
9974 property into the graphics object @var{h}.\n\
9975 \n\
9976 Examples:\n\
9977 \n\
9978 @example\n\
9979 @group\n\
9980 addproperty (\"my_property\", gcf, \"string\", \"a string value\");\n\
9981 addproperty (\"my_radio\", gcf, \"radio\", \"val_1|val_2|@{val_3@}\");\n\
9982 addproperty (\"my_style\", gcf, \"linelinestyle\", \"--\");\n\
9983 @end group\n\
9984 @end example\n\
9985 \n\
9986 @end deftypefn")
9987 {
9988 gh_manager::auto_lock guard;
9989
9990 octave_value retval;
9991
9992 if (args.length () >= 3)
9993 {
9994 std::string name = args(0).string_value ();
9995
9996 if (! error_state)
9997 {
9998 double h = args(1).double_value ();
9999
10000 if (! error_state)
10001 {
10002 graphics_handle gh = gh_manager::lookup (h);
10003
10004 if (gh.ok ())
10005 {
10006 graphics_object go = gh_manager::get_object (gh);
10007
10008 std::string type = args(2).string_value ();
10009
10010 if (! error_state)
10011 {
10012 if (! go.get_properties ().has_property (name))
10013 {
10014 property p = property::create (name, gh, type,
10015 args.splice (0, 3));
10016
10017 if (! error_state)
10018 go.get_properties ().insert_property (name, p);
10019 }
10020 else
10021 error ("addproperty: a '%s' property already exists in the graphics object",
10022 name.c_str ());
10023 }
10024 else
10025 error ("addproperty: invalid property TYPE, expected a string value");
10026 }
10027 else
10028 error ("addproperty: invalid graphics object (= %g)", h);
10029 }
10030 else
10031 error ("addproperty: invalid handle value");
10032 }
10033 else
10034 error ("addproperty: invalid property NAME, expected a string value");
10035 }
10036 else
10037 print_usage ();
10038
10039 return retval;
10040 }
10041
10042 octave_value
10043 get_property_from_handle (double handle, const std::string& property,
10044 const std::string& func)
10045 {
10046 gh_manager::auto_lock guard;
10047
10048 graphics_object obj = gh_manager::get_object (handle);
10049 octave_value retval;
10050
10051 if (obj)
10052 retval = obj.get (caseless_str (property));
10053 else
10054 error ("%s: invalid handle (= %g)", func.c_str (), handle);
10055
10056 return retval;
10057 }
10058
10059 bool
10060 set_property_in_handle (double handle, const std::string& property,
10061 const octave_value& arg, const std::string& func)
10062 {
10063 gh_manager::auto_lock guard;
10064
10065 graphics_object obj = gh_manager::get_object (handle);
10066 int ret = false;
10067
10068 if (obj)
10069 {
10070 obj.set (caseless_str (property), arg);
10071
10072 if (! error_state)
10073 ret = true;
10074 }
10075 else
10076 error ("%s: invalid handle (= %g)", func.c_str (), handle);
10077
10078 return ret;
10079 }
10080
10081 static bool
10082 compare_property_values (const octave_value& o1, const octave_value& o2)
10083 {
10084 octave_value_list args (2);
10085
10086 args(0) = o1;
10087 args(1) = o2;
10088
10089 octave_value_list result = feval ("isequal", args, 1);
10090
10091 if (! error_state && result.length () > 0)
10092 return result(0).bool_value ();
10093
10094 return false;
10095 }
10096
10097 static std::map<uint32_t, bool> waitfor_results;
10098
10099 static void
10100 cleanup_waitfor_id (uint32_t id)
10101 {
10102 waitfor_results.erase (id);
10103 }
10104
10105 static void
10106 do_cleanup_waitfor_listener (const octave_value& listener,
10107 listener_mode mode = POSTSET)
10108 {
10109 Cell c = listener.cell_value ();
10110
10111 if (c.numel () >= 4)
10112 {
10113 double h = c(2).double_value ();
10114
10115 if (! error_state)
10116 {
10117 caseless_str pname = c(3).string_value ();
10118
10119 if (! error_state)
10120 {
10121 gh_manager::auto_lock guard;
10122
10123 graphics_handle handle = gh_manager::lookup (h);
10124
10125 if (handle.ok ())
10126 {
10127 graphics_object go = gh_manager::get_object (handle);
10128
10129 if (go.get_properties ().has_property (pname))
10130 {
10131 go.get_properties ()
10132 .delete_listener (pname, listener, mode);
10133 if (mode == POSTSET)
10134 go.get_properties ()
10135 .delete_listener (pname, listener, PERSISTENT);
10136 }
10137 }
10138 }
10139 }
10140 }
10141 }
10142
10143 static void
10144 cleanup_waitfor_postset_listener (const octave_value& listener)
10145 { do_cleanup_waitfor_listener (listener, POSTSET); }
10146
10147 static void
10148 cleanup_waitfor_predelete_listener (const octave_value& listener)
10149 { do_cleanup_waitfor_listener (listener, PREDELETE); }
10150
10151 static octave_value_list
10152 waitfor_listener (const octave_value_list& args, int)
10153 {
10154 if (args.length () > 3)
10155 {
10156 uint32_t id = args(2).uint32_scalar_value ().value ();
10157
10158 if (! error_state)
10159 {
10160 if (args.length () > 5)
10161 {
10162 double h = args(0).double_value ();
10163
10164 if (! error_state)
10165 {
10166 caseless_str pname = args(4).string_value ();
10167
10168 if (! error_state)
10169 {
10170 gh_manager::auto_lock guard;
10171
10172 graphics_handle handle = gh_manager::lookup (h);
10173
10174 if (handle.ok ())
10175 {
10176 graphics_object go = gh_manager::get_object (handle);
10177 octave_value pvalue = go.get (pname);
10178
10179 if (compare_property_values (pvalue, args(5)))
10180 waitfor_results[id] = true;
10181 }
10182 }
10183 }
10184 }
10185 else
10186 waitfor_results[id] = true;
10187 }
10188 }
10189
10190 return octave_value_list ();
10191 }
10192
10193 static octave_value_list
10194 waitfor_del_listener (const octave_value_list& args, int)
10195 {
10196 if (args.length () > 2)
10197 {
10198 uint32_t id = args(2).uint32_scalar_value ().value ();
10199
10200 if (! error_state)
10201 waitfor_results[id] = true;
10202 }
10203
10204 return octave_value_list ();
10205 }
10206
10207 DEFUN (waitfor, args, ,
10208 "-*- texinfo -*-\n\
10209 @deftypefn {Built-in Function} {} waitfor (@var{h})\n\
10210 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop})\n\
10211 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop}, @var{value})\n\
10212 @deftypefnx {Built-in Function} {} waitfor (@dots{}, \"timeout\", @var{timeout})\n\
10213 Suspend the execution of the current program until a condition is\n\
10214 satisfied on the graphics handle @var{h}. While the program is suspended\n\
10215 graphics events are still being processed normally, allowing callbacks to\n\
10216 modify the state of graphics objects. This function is reentrant and can be\n\
10217 called from a callback, while another @code{waitfor} call is pending at\n\
10218 top-level.\n\
10219 \n\
10220 In the first form, program execution is suspended until the graphics object\n\
10221 @var{h} is destroyed. If the graphics handle is invalid, the function\n\
10222 returns immediately.\n\
10223 \n\
10224 In the second form, execution is suspended until the graphics object is\n\
10225 destroyed or the property named @var{prop} is modified. If the graphics\n\
10226 handle is invalid or the property does not exist, the function returns\n\
10227 immediately.\n\
10228 \n\
10229 In the third form, execution is suspended until the graphics object is\n\
10230 destroyed or the property named @var{prop} is set to @var{value}. The\n\
10231 function @code{isequal} is used to compare property values. If the graphics\n\
10232 handle is invalid, the property does not exist or the property is already\n\
10233 set to @var{value}, the function returns immediately.\n\
10234 \n\
10235 An optional timeout can be specified using the property @code{timeout}.\n\
10236 This timeout value is the number of seconds to wait for the condition to be\n\
10237 true. @var{timeout} must be at least 1. If a smaller value is specified, a\n\
10238 warning is issued and a value of 1 is used instead. If the timeout value is\n\
10239 not an integer, it is truncated towards 0.\n\
10240 \n\
10241 To define a condition on a property named @code{timeout}, use the string\n\
10242 @code{\\timeout} instead.\n\
10243 \n\
10244 In all cases, typing CTRL-C stops program execution immediately.\n\
10245 @seealso{isequal}\n\
10246 @end deftypefn")
10247 {
10248 if (args.length () > 0)
10249 {
10250 double h = args(0).double_value ();
10251
10252 if (! error_state)
10253 {
10254 caseless_str pname;
10255
10256 unwind_protect frame;
10257
10258 static uint32_t id_counter = 0;
10259 uint32_t id = 0;
10260
10261 int max_arg_index = 0;
10262 int timeout_index = -1;
10263
10264 int timeout = 0;
10265
10266 if (args.length () > 1)
10267 {
10268 pname = args(1).string_value ();
10269 if (! error_state
10270 && ! pname.empty ()
10271 && ! pname.compare ("timeout"))
10272 {
10273 if (pname.compare ("\\timeout"))
10274 pname = "timeout";
10275
10276 static octave_value wf_listener;
10277
10278 if (! wf_listener.is_defined ())
10279 wf_listener =
10280 octave_value (new octave_builtin (waitfor_listener,
10281 "waitfor_listener"));
10282
10283 max_arg_index++;
10284 if (args.length () > 2)
10285 {
10286 if (args(2).is_string ())
10287 {
10288 caseless_str s = args(2).string_value ();
10289
10290 if (! error_state)
10291 {
10292 if (s.compare ("timeout"))
10293 timeout_index = 2;
10294 else
10295 max_arg_index++;
10296 }
10297 }
10298 else
10299 max_arg_index++;
10300 }
10301
10302 Cell listener (1, max_arg_index >= 2 ? 5 : 4);
10303
10304 id = id_counter++;
10305 frame.add_fcn (cleanup_waitfor_id, id);
10306 waitfor_results[id] = false;
10307
10308 listener(0) = wf_listener;
10309 listener(1) = octave_uint32 (id);
10310 listener(2) = h;
10311 listener(3) = pname;
10312
10313 if (max_arg_index >= 2)
10314 listener(4) = args(2);
10315
10316 octave_value ov_listener (listener);
10317
10318 gh_manager::auto_lock guard;
10319
10320 graphics_handle handle = gh_manager::lookup (h);
10321
10322 if (handle.ok ())
10323 {
10324 graphics_object go = gh_manager::get_object (handle);
10325
10326 if (max_arg_index >= 2
10327 && compare_property_values (go.get (pname),
10328 args(2)))
10329 waitfor_results[id] = true;
10330 else
10331 {
10332
10333 frame.add_fcn (cleanup_waitfor_postset_listener,
10334 ov_listener);
10335 go.add_property_listener (pname, ov_listener,
10336 POSTSET);
10337 go.add_property_listener (pname, ov_listener,
10338 PERSISTENT);
10339
10340 if (go.get_properties ()
10341 .has_dynamic_property (pname))
10342 {
10343 static octave_value wf_del_listener;
10344
10345 if (! wf_del_listener.is_defined ())
10346 wf_del_listener =
10347 octave_value (new octave_builtin
10348 (waitfor_del_listener,
10349 "waitfor_del_listener"));
10350
10351 Cell del_listener (1, 4);
10352
10353 del_listener(0) = wf_del_listener;
10354 del_listener(1) = octave_uint32 (id);
10355 del_listener(2) = h;
10356 del_listener(3) = pname;
10357
10358 octave_value ov_del_listener (del_listener);
10359
10360 frame.add_fcn (cleanup_waitfor_predelete_listener,
10361 ov_del_listener);
10362 go.add_property_listener (pname, ov_del_listener,
10363 PREDELETE);
10364 }
10365 }
10366 }
10367 }
10368 else if (error_state || pname.empty ())
10369 error ("waitfor: invalid property name, expected a non-empty string value");
10370 }
10371
10372 if (! error_state
10373 && timeout_index < 0
10374 && args.length () > (max_arg_index + 1))
10375 {
10376 caseless_str s = args(max_arg_index + 1).string_value ();
10377
10378 if (! error_state)
10379 {
10380 if (s.compare ("timeout"))
10381 timeout_index = max_arg_index + 1;
10382 else
10383 error ("waitfor: invalid parameter '%s'", s.c_str ());
10384 }
10385 else
10386 error ("waitfor: invalid parameter, expected 'timeout'");
10387 }
10388
10389 if (! error_state && timeout_index >= 0)
10390 {
10391 if (args.length () > (timeout_index + 1))
10392 {
10393 timeout = static_cast<int>
10394 (args(timeout_index + 1).scalar_value ());
10395
10396 if (! error_state)
10397 {
10398 if (timeout < 1)
10399 {
10400 warning ("waitfor: the timeout value must be >= 1, using 1 instead");
10401 timeout = 1;
10402 }
10403 }
10404 else
10405 error ("waitfor: invalid timeout value, expected a value >= 1");
10406 }
10407 else
10408 error ("waitfor: missing timeout value");
10409 }
10410
10411 // FIXME: There is still a "hole" in the following loop. The code
10412 // assumes that an object handle is unique, which is a fair
10413 // assumptions, except for figures. If a figure is destroyed
10414 // then recreated with the same figure ID, within the same
10415 // run of event hooks, then the figure destruction won't be
10416 // caught and the loop will not stop. This is an unlikely
10417 // possibility in practice, though.
10418 //
10419 // Using deletefcn callback is also unreliable as it could be
10420 // modified during a callback execution and the waitfor loop
10421 // would not stop.
10422 //
10423 // The only "good" implementation would require object
10424 // listeners, similar to property listeners.
10425
10426 time_t start = 0;
10427
10428 if (timeout > 0)
10429 start = time (0);
10430
10431 while (! error_state)
10432 {
10433 if (true)
10434 {
10435 gh_manager::auto_lock guard;
10436
10437 graphics_handle handle = gh_manager::lookup (h);
10438
10439 if (handle.ok ())
10440 {
10441 if (! pname.empty () && waitfor_results[id])
10442 break;
10443 }
10444 else
10445 break;
10446 }
10447
10448 octave_usleep (100000);
10449
10450 OCTAVE_QUIT;
10451
10452 command_editor::run_event_hooks ();
10453
10454 if (timeout > 0)
10455 {
10456 if (start + timeout < time (0))
10457 break;
10458 }
10459 }
10460 }
10461 else
10462 error ("waitfor: invalid handle value.");
10463 }
10464 else
10465 print_usage ();
10466
10467 return octave_value ();
10468 }