GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
graphics.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2018 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
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License 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 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <cctype>
28 #include <cmath>
29 #include <cstdint>
30 #include <cstdlib>
31 
32 #include <algorithm>
33 #include <iostream>
34 #include <limits>
35 #include <list>
36 #include <map>
37 #include <set>
38 #include <string>
39 #include <sstream>
40 
41 #include "cmd-edit.h"
42 #include "file-ops.h"
43 #include "file-stat.h"
44 #include "oct-locbuf.h"
45 #include "oct-time.h"
46 #include "singleton-cleanup.h"
47 
48 #include "builtin-defun-decls.h"
49 #include "defun.h"
50 #include "display.h"
51 #include "error.h"
52 #include "graphics.h"
53 #include "input.h"
54 #include "interpreter-private.h"
55 #include "interpreter.h"
56 #include "ov.h"
57 #include "ovl.h"
58 #include "oct-map.h"
59 #include "ov-fcn-handle.h"
60 #include "pager.h"
61 #include "parse.h"
62 #include "text-renderer.h"
63 #include "unwind-prot.h"
64 #include "utils.h"
65 #include "octave-default-image.h"
66 
67 // forward declarations
68 static octave_value xget (const graphics_handle& h, const caseless_str& name);
69 
70 OCTAVE_NORETURN static
71 void
73 {
74  error ("set: invalid value for %s property", pname.c_str ());
75 }
76 
77 // Check to see that PNAME matches just one of PNAMES uniquely.
78 // Return the full name of the match, or an empty caseless_str object
79 // if there is no match, or the match is ambiguous.
80 
81 static caseless_str
83  const std::set<std::string>& pnames,
84  const caseless_str& pname)
85 {
86  size_t len = pname.length ();
87  std::set<std::string> matches;
88 
89  // Find exact or partial matches to property name
90  for (const auto& propnm : pnames)
91  {
92  if (pname.compare (propnm, len))
93  {
94  if (len == propnm.length ())
95  return pname; // Exact match.
96 
97  matches.insert (propnm);
98  }
99  }
100 
101  size_t num_matches = matches.size ();
102 
103  if (num_matches == 0)
104  error ("%s: unknown %s property %s",
105  who.c_str (), what.c_str (), pname.c_str ());
106  else if (num_matches > 1)
107  {
108  string_vector sv (matches);
109 
110  std::ostringstream os;
111 
112  sv.list_in_columns (os);
113 
114  std::string match_list = os.str ();
115 
116  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
117  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
118  }
119  else if (num_matches == 1)
120  {
121  // Exact match was handled above.
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
137 {
138  // The values below have been produced by viridis (64)(:)
139  // It would be nice to be able to feval the
140  // viridis function but since there is a static property object that includes
141  // a colormap_property object, we need to initialize this before main is
142  // even called, so calling an interpreted function is not possible.
143 
144  const double cmapv[] =
145  {
146  2.67004010000000e-01, 2.72651720952381e-01, 2.77106307619048e-01,
147  2.80356151428571e-01, 2.82390045238095e-01, 2.83204606666667e-01,
148  2.82809341428571e-01, 2.81230763333333e-01, 2.78516153333333e-01,
149  2.74735528571429e-01, 2.69981791904762e-01, 2.64368580952381e-01,
150  2.58026184285714e-01, 2.51098684761905e-01, 2.43732853333333e-01,
151  2.36073294285714e-01, 2.28263191428571e-01, 2.20424955714286e-01,
152  2.12666598571429e-01, 2.05079113809524e-01, 1.97721880952381e-01,
153  1.90631350000000e-01, 1.83819438571429e-01, 1.77272360952381e-01,
154  1.70957518571429e-01, 1.64832915714286e-01, 1.58845368095238e-01,
155  1.52951235714286e-01, 1.47131626666667e-01, 1.41402210952381e-01,
156  1.35832975714286e-01, 1.30582113809524e-01, 1.25898377619048e-01,
157  1.22163105714286e-01, 1.19872409523810e-01, 1.19626570000000e-01,
158  1.22045948571429e-01, 1.27667691904762e-01, 1.36834947142857e-01,
159  1.49643331428571e-01, 1.65967274285714e-01, 1.85538397142857e-01,
160  2.08030450000000e-01, 2.33127309523809e-01, 2.60531475238095e-01,
161  2.90000730000000e-01, 3.21329971428571e-01, 3.54355250000000e-01,
162  3.88930322857143e-01, 4.24933143333333e-01, 4.62246770476190e-01,
163  5.00753620000000e-01, 5.40336957142857e-01, 5.80861172380952e-01,
164  6.22170772857143e-01, 6.64087320476191e-01, 7.06403823333333e-01,
165  7.48885251428571e-01, 7.91273132857143e-01, 8.33302102380952e-01,
166  8.74717527142857e-01, 9.15296319047619e-01, 9.54839555238095e-01,
167  9.93247890000000e-01, 4.87433000000000e-03, 2.58456800000000e-02,
168  5.09139004761905e-02, 7.42014957142857e-02, 9.59536042857143e-02,
169  1.16893314761905e-01, 1.37350195714286e-01, 1.57479940000000e-01,
170  1.77347967619048e-01, 1.96969168571429e-01, 2.16330337619048e-01,
171  2.35404660952381e-01, 2.54161735714286e-01, 2.72573219047619e-01,
172  2.90619516666667e-01, 3.08291041428571e-01, 3.25586450952381e-01,
173  3.42517215238095e-01, 3.59102207142857e-01, 3.75366067142857e-01,
174  3.91340913333333e-01, 4.07061480000000e-01, 4.22563764285714e-01,
175  4.37885543809524e-01, 4.53062984285714e-01, 4.68129543809524e-01,
176  4.83117059523810e-01, 4.98052961428571e-01, 5.12959473333333e-01,
177  5.27854311428571e-01, 5.42750087142857e-01, 5.57652481904762e-01,
178  5.72563073333333e-01, 5.87476284285714e-01, 6.02382410952381e-01,
179  6.17265840000000e-01, 6.32106955714286e-01, 6.46881817142857e-01,
180  6.61562926190476e-01, 6.76119717142857e-01, 6.90518987142857e-01,
181  7.04725181904762e-01, 7.18700950000000e-01, 7.32406441904762e-01,
182  7.45802021904762e-01, 7.58846480000000e-01, 7.71497934761905e-01,
183  7.83714033809524e-01, 7.95453081428571e-01, 8.06673890000000e-01,
184  8.17337565714286e-01, 8.27409135714286e-01, 8.36858167619048e-01,
185  8.45663399523809e-01, 8.53815582857143e-01, 8.61321019047619e-01,
186  8.68206316666667e-01, 8.74522215714286e-01, 8.80346158571429e-01,
187  8.85780083333333e-01, 8.90945338571429e-01, 8.95973498571429e-01,
188  9.01005800000000e-01, 9.06156570000000e-01, 3.29415190000000e-01,
189  3.53367293333333e-01, 3.76236064761905e-01, 3.97901482857143e-01,
190  4.18250757142857e-01, 4.37178920000000e-01, 4.54595888571429e-01,
191  4.70433883333333e-01, 4.84653865714286e-01, 4.97250492857143e-01,
192  5.08254501428571e-01, 5.17731949047619e-01, 5.25780221428571e-01,
193  5.32522206190476e-01, 5.38097133333333e-01, 5.42651800000000e-01,
194  5.46335411904762e-01, 5.49287148571429e-01, 5.51635008571429e-01,
195  5.53493173333333e-01, 5.54953478571429e-01, 5.56089070000000e-01,
196  5.56952166666667e-01, 5.57576145714286e-01, 5.57974025714286e-01,
197  5.58142745238095e-01, 5.58058673809524e-01, 5.57684744285714e-01,
198  5.56973310000000e-01, 5.55864478571429e-01, 5.54288677142857e-01,
199  5.52175699047619e-01, 5.49445382857143e-01, 5.46023368571429e-01,
200  5.41830633809524e-01, 5.36795616666667e-01, 5.30847985714286e-01,
201  5.23924198571429e-01, 5.15966779523810e-01, 5.06924262857143e-01,
202  4.96751861428571e-01, 4.85412122857143e-01, 4.72873300000000e-01,
203  4.59105875238095e-01, 4.44095883333333e-01, 4.27825852857143e-01,
204  4.10292713809524e-01, 3.91487632857143e-01, 3.71420688571429e-01,
205  3.50098750000000e-01, 3.27544678571429e-01, 3.03798967142857e-01,
206  2.78916748571429e-01, 2.53000856190476e-01, 2.26223670000000e-01,
207  1.98879439523810e-01, 1.71494930000000e-01, 1.45037631428572e-01,
208  1.21291048571429e-01, 1.03326155238095e-01, 9.53507900000000e-02,
209  1.00469958095238e-01, 1.17876387142857e-01, 1.43936200000000e-01
210  };
211 
212  // It would be nice if Matrix had a ctor allowing to do the
213  // following without a copy
214  Matrix cmap (64, 3, 0.0);
215  std::copy (cmapv, cmapv + (64*3), cmap.fortran_vec ());
216  return cmap;
217 }
218 
219 static double
221 {
222  return octave::display_info::depth ();
223 }
224 
225 static Matrix
227 {
228  Matrix retval (1, 4);
229 
230  retval(0) = 1.0;
231  retval(1) = 1.0;
234 
235  return retval;
236 }
237 
238 static double
240 {
242 }
243 
244 static Matrix
246 {
247  Matrix retval (7, 3, 0.0);
248 
249  retval(0,1) = 0.447;
250  retval(0,2) = 0.741;
251 
252  retval(1,0) = 0.850;
253  retval(1,1) = 0.325;
254  retval(1,2) = 0.098;
255 
256  retval(2,0) = 0.929;
257  retval(2,1) = 0.694;
258  retval(2,2) = 0.125;
259 
260  retval(3,0) = 0.494;
261  retval(3,1) = 0.184;
262  retval(3,2) = 0.556;
263 
264  retval(4,0) = 0.466;
265  retval(4,1) = 0.674;
266  retval(4,2) = 0.188;
267 
268  retval(5,0) = 0.301;
269  retval(5,1) = 0.745;
270  retval(5,2) = 0.933;
271 
272  retval(6,0) = 0.635;
273  retval(6,1) = 0.078;
274  retval(6,2) = 0.184;
275 
276  return retval;
277 }
278 
279 static Matrix
280 default_lim (bool logscale = false)
281 {
282  Matrix m (1, 2);
283 
284  if (logscale)
285  {
286  m(0) = 0.1;
287  m(1) = 1.0;
288  }
289  else
290  {
291  m(0) = 0.0;
292  m(1) = 1.0;
293  }
294 
295  return m;
296 }
297 
298 static Matrix
300 {
301  Matrix retval (1, 2);
302 
303  retval(0) = 0;
304  retval(1) = 1;
305 
306  return retval;
307 }
308 
309 static Matrix
311 {
312  Matrix m (64, 64);
313 
314  int i = 0;
315  for (int col = 0; col < 64; col++)
316  for (int row = 0; row < 64; row++)
317  {
318  m(col,row) = static_cast<double> (default_im_data[i]);
319  i++;
320  }
321 
322  return m;
323 }
324 
325 static Matrix
327 {
328  Matrix m (3, 3);
329 
330  for (int col = 0; col < 3; col++)
331  for (int row = 0; row < 3; row++)
332  m(row,col) = col+1;
333 
334  return m;
335 }
336 
337 static Matrix
339 {
340  Matrix m (3, 3);
341 
342  for (int row = 0; row < 3; row++)
343  for (int col = 0; col < 3; col++)
344  m(row,col) = row+1;
345 
346  return m;
347 }
348 
349 static Matrix
351 {
352  Matrix m (3, 3, 0.0);
353 
354  for (int row = 0; row < 3; row++)
355  m(row,row) = 1.0;
356 
357  return m;
358 }
359 
360 static Matrix
362 {
363  return default_surface_zdata ();
364 }
365 
366 static Matrix
368 {
369  Matrix m (1, 3);
370 
371  m(0) = 1.0;
372  m(1) = 2.0;
373  m(2) = 3.0;
374 
375  return m;
376 }
377 
378 static Matrix
380 {
381  Matrix m (3, 2, 0.0);
382 
383  m(1) = 1.0;
384  m(3) = 1.0;
385  m(4) = 1.0;
386 
387  return m;
388 }
389 
390 static Matrix
392 {
393  Matrix m (3, 1, 0.0);
394 
395  m(1) = 1.0;
396 
397  return m;
398 }
399 
400 static Matrix
402 {
403  Matrix m (3, 1, 1.0);
404 
405  m(2) = 0.0;
406 
407  return m;
408 }
409 
410 static Matrix
412 {
413  Matrix m (1, 4);
414 
415  m(0) = 0.13;
416  m(1) = 0.11;
417  m(2) = 0.775;
418  m(3) = 0.815;
419 
420  return m;
421 }
422 
423 static Matrix
425 {
426  Matrix m (1, 4);
427 
428  m(0) = 0.0;
429  m(1) = 0.0;
430  m(2) = 1.0;
431  m(3) = 1.0;
432 
433  return m;
434 }
435 
436 static Matrix
438 {
439  Matrix m (1, 2);
440 
441  m(0) = 0.0;
442  m(1) = 90.0;
443 
444  return m;
445 }
446 
447 static Matrix
449 {
450  Matrix m (1, 6);
451 
452  m(0) = 0.0;
453  m(1) = 0.2;
454  m(2) = 0.4;
455  m(3) = 0.6;
456  m(4) = 0.8;
457  m(5) = 1.0;
458 
459  return m;
460 }
461 
462 static Matrix
464 {
465  Matrix m (1, 2);
466 
467  m(0) = 0.01;
468  m(1) = 0.025;
469 
470  return m;
471 }
472 
473 static Matrix
475 {
476  Matrix m (1, 4);
477 
478  m(0) = 300;
479  m(1) = 200;
480  m(2) = 560;
481  m(3) = 420;
482 
483  return m;
484 }
485 
486 static Matrix
488 {
489  Matrix m (1, 2);
490 
491  m(0) = 8.5;
492  m(1) = 11.0;
493 
494  return m;
495 }
496 
497 static Matrix
499 {
500  Matrix m (1, 4);
501 
502  m(0) = 0.25;
503  m(1) = 2.50;
504  m(2) = 8.00;
505  m(3) = 6.00;
506 
507  return m;
508 }
509 
510 static std::string
512 {
513  octave::gtk_manager& gtk_mgr
514  = octave::__get_gtk_manager__ ("default_graphics_toolkit");
515 
516  return gtk_mgr.default_toolkit ();
517 }
518 
519 static Matrix
521 {
522  Matrix retval (1, 4);
523 
524  retval(0) = 0;
525  retval(1) = 0;
526  retval(2) = 80;
527  retval(3) = 30;
528 
529  return retval;
530 }
531 
532 static Matrix
534 {
535  Matrix retval (1, 2);
536 
537  retval(0) = 0.01;
538  retval(1) = 0.1;
539 
540  return retval;
541 }
542 
543 static Matrix
545 {
546  Matrix retval (1, 4);
547 
548  retval(0) = 0;
549  retval(1) = 0;
550  retval(2) = 1;
551  retval(3) = 1;
552 
553  return retval;
554 }
555 
556 static Matrix
558 {
559  Matrix m (1, 3);
560 
561  m(0) = 1.0;
562  m(1) = 0.0;
563  m(2) = 1.0;
564 
565  return m;
566 }
567 
568 static double
569 convert_font_size (double font_size, const caseless_str& from_units,
570  const caseless_str& to_units, double parent_height = 0)
571 {
572  // Simple case where from_units == to_units
573 
574  if (from_units.compare (to_units))
575  return font_size;
576 
577  // Converts the given fontsize using the following transformation:
578  // <old_font_size> => points => <new_font_size>
579 
580  double points_size = 0;
581  double res = 0;
582 
583  if (from_units.compare ("points"))
584  points_size = font_size;
585  else
586  {
587  res = xget (0, "screenpixelsperinch").double_value ();
588 
589  if (from_units.compare ("pixels"))
590  points_size = font_size * 72.0 / res;
591  else if (from_units.compare ("inches"))
592  points_size = font_size * 72.0;
593  else if (from_units.compare ("centimeters"))
594  points_size = font_size * 72.0 / 2.54;
595  else if (from_units.compare ("normalized"))
596  points_size = font_size * parent_height * 72.0 / res;
597  }
598 
599  double new_font_size = 0;
600 
601  if (to_units.compare ("points"))
602  new_font_size = points_size;
603  else
604  {
605  if (res <= 0)
606  res = xget (0, "screenpixelsperinch").double_value ();
607 
608  if (to_units.compare ("pixels"))
609  new_font_size = points_size * res / 72.0;
610  else if (to_units.compare ("inches"))
611  new_font_size = points_size / 72.0;
612  else if (to_units.compare ("centimeters"))
613  new_font_size = points_size * 2.54 / 72.0;
614  else if (to_units.compare ("normalized"))
615  {
616  // Avoid setting font size to (0/0) = NaN
617 
618  if (parent_height > 0)
619  new_font_size = points_size * res / (parent_height * 72.0);
620  }
621  }
622 
623  return new_font_size;
624 }
625 
626 static Matrix
627 convert_position (const Matrix& pos, const caseless_str& from_units,
628  const caseless_str& to_units, const Matrix& parent_dim)
629 {
630  Matrix retval (1, pos.numel ());
631  double res = 0;
632  bool is_rectangle = (pos.numel () == 4);
633  bool is_2d = (pos.numel () == 2);
634 
635  if (from_units.compare ("pixels"))
636  retval = pos;
637  else if (from_units.compare ("normalized"))
638  {
639  retval(0) = pos(0) * parent_dim(0) + 1;
640  retval(1) = pos(1) * parent_dim(1) + 1;
641  if (is_rectangle)
642  {
643  retval(2) = pos(2) * parent_dim(0);
644  retval(3) = pos(3) * parent_dim(1);
645  }
646  else if (! is_2d)
647  retval(2) = 0;
648  }
649  else if (from_units.compare ("characters"))
650  {
651  if (res <= 0)
652  res = xget (0, "screenpixelsperinch").double_value ();
653 
654  double f = 0.0;
655 
656  // FIXME: this assumes the system font is Helvetica 10pt
657  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
658  f = 12.0 * res / 74.951;
659 
660  if (f > 0)
661  {
662  retval(0) = 0.5 * pos(0) * f;
663  retval(1) = pos(1) * f;
664  if (is_rectangle)
665  {
666  retval(2) = 0.5 * pos(2) * f;
667  retval(3) = pos(3) * f;
668  }
669  else if (! is_2d)
670  retval(2) = 0;
671  }
672  }
673  else
674  {
675  if (res <= 0)
676  res = xget (0, "screenpixelsperinch").double_value ();
677 
678  double f = 0.0;
679 
680  if (from_units.compare ("points"))
681  f = res / 72.0;
682  else if (from_units.compare ("inches"))
683  f = res;
684  else if (from_units.compare ("centimeters"))
685  f = res / 2.54;
686 
687  if (f > 0)
688  {
689  retval(0) = pos(0) * f + 1;
690  retval(1) = pos(1) * f + 1;
691  if (is_rectangle)
692  {
693  retval(2) = pos(2) * f;
694  retval(3) = pos(3) * f;
695  }
696  else if (! is_2d)
697  retval(2) = 0;
698  }
699  }
700 
701  if (! to_units.compare ("pixels"))
702  {
703  if (to_units.compare ("normalized"))
704  {
705  retval(0) = (retval(0) - 1) / parent_dim(0);
706  retval(1) = (retval(1) - 1) / parent_dim(1);
707  if (is_rectangle)
708  {
709  retval(2) /= parent_dim(0);
710  retval(3) /= parent_dim(1);
711  }
712  else if (! is_2d)
713  retval(2) = 0;
714  }
715  else if (to_units.compare ("characters"))
716  {
717  if (res <= 0)
718  res = xget (0, "screenpixelsperinch").double_value ();
719 
720  double f = 0.0;
721 
722  f = 12.0 * res / 74.951;
723 
724  if (f > 0)
725  {
726  retval(0) = 2 * retval(0) / f;
727  retval(1) = retval(1) / f;
728  if (is_rectangle)
729  {
730  retval(2) = 2 * retval(2) / f;
731  retval(3) = retval(3) / f;
732  }
733  else if (! is_2d)
734  retval(2) = 0;
735  }
736  }
737  else
738  {
739  if (res <= 0)
740  res = xget (0, "screenpixelsperinch").double_value ();
741 
742  double f = 0.0;
743 
744  if (to_units.compare ("points"))
745  f = res / 72.0;
746  else if (to_units.compare ("inches"))
747  f = res;
748  else if (to_units.compare ("centimeters"))
749  f = res / 2.54;
750 
751  if (f > 0)
752  {
753  retval(0) = (retval(0) - 1) / f;
754  retval(1) = (retval(1) - 1) / f;
755  if (is_rectangle)
756  {
757  retval(2) /= f;
758  retval(3) /= f;
759  }
760  else if (! is_2d)
761  retval(2) = 0;
762  }
763  }
764  }
765  else if (! is_rectangle && ! is_2d)
766  retval(2) = 0;
767 
768  return retval;
769 }
770 
771 static Matrix
773  const caseless_str& from_units,
774  const caseless_str& to_units)
775 {
776  graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
777  graphics_object ax = go.get_ancestor ("axes");
778 
779  Matrix retval;
780 
781  if (ax.valid_object ())
782  {
783  const axes::properties& ax_props =
784  dynamic_cast<const axes::properties&> (ax.get_properties ());
785  graphics_xform ax_xform = ax_props.get_transform ();
786  bool is_rectangle = (pos.numel () == 4);
787  Matrix ax_bbox = ax_props.get_boundingbox (true),
788  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
789 
790  if (from_units.compare ("data"))
791  {
792  if (is_rectangle)
793  {
794  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
795  v2 = ax_xform.transform (pos(0) + pos(2),
796  pos(1) + pos(3), 0);
797 
798  retval.resize (1, 4);
799 
800  retval(0) = v1(0) - ax_bbox(0) + 1;
801  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
802  retval(2) = v2(0) - v1(0);
803  retval(3) = v1(1) - v2(1);
804  }
805  else
806  {
807  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
808 
809  retval.resize (1, 3);
810 
811  retval(0) = v(0) - ax_bbox(0) + 1;
812  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
813  retval(2) = 0;
814  }
815  }
816  else
817  retval = convert_position (pos, from_units, "pixels", ax_size);
818 
819  if (! to_units.compare ("pixels"))
820  {
821  if (to_units.compare ("data"))
822  {
823  if (is_rectangle)
824  {
825  ColumnVector v1, v2;
826  v1 = ax_xform.untransform (
827  retval(0) + ax_bbox(0) - 1,
828  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
829  v2 = ax_xform.untransform (
830  retval(0) + retval(2) + ax_bbox(0) - 1,
831  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
832 
833  retval.resize (1, 4);
834 
835  retval(0) = v1(0);
836  retval(1) = v1(1);
837  retval(2) = v2(0) - v1(0);
838  retval(3) = v2(1) - v1(1);
839  }
840  else
841  {
842  ColumnVector v;
843  v = ax_xform.untransform (
844  retval(0) + ax_bbox(0) - 1,
845  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
846 
847  retval.resize (1, 3);
848 
849  retval(0) = v(0);
850  retval(1) = v(1);
851  retval(2) = v(2);
852  }
853  }
854  else
855  retval = convert_position (retval, "pixels", to_units, ax_size);
856  }
857  }
858 
859  return retval;
860 }
861 
862 // This function always returns the screensize in pixels
863 static Matrix
865 {
867  Matrix sz = obj.get ("screensize").matrix_value ();
868  return convert_position (sz, obj.get ("units").string_value (), "pixels",
869  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
870 }
871 
872 static void
873 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
874  const double *cmapv, double x, octave_idx_type lda,
875  octave_idx_type nc, octave_idx_type i, double *av)
876 {
877  if (is_scaled)
878  x = octave::math::fix (nc * (x - clim_0) / (clim_1 - clim_0));
879  else if (is_real)
880  x = octave::math::fix (x - 1);
881 
882  if (octave::math::isnan (x))
883  {
884  av[i] = x;
885  av[i+lda] = x;
886  av[i+2*lda] = x;
887  }
888  else
889  {
890  if (x < 0)
891  x = 0;
892  else if (x >= nc)
893  x = (nc - 1);
894 
895  octave_idx_type idx = static_cast<octave_idx_type> (x);
896 
897  av[i] = cmapv[idx];
898  av[i+lda] = cmapv[idx+nc];
899  av[i+2*lda] = cmapv[idx+2*nc];
900  }
901 }
902 
903 template <typename T>
904 void
905 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
906  const double *cmapv, const T *cv, octave_idx_type lda,
907  octave_idx_type nc, double *av)
908 {
909  for (octave_idx_type i = 0; i < lda; i++)
910  convert_cdata_2 (is_scaled, is_real,
911  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
912 }
913 
914 static octave_value
915 convert_cdata (const base_properties& props, const octave_value& cdata,
916  bool is_scaled, int cdim)
917 {
918  dim_vector dv (cdata.dims ());
919 
920  // TrueColor data doesn't require conversion
921  if (dv.ndims () == cdim && dv(cdim-1) == 3)
922  return cdata;
923 
924  Matrix cmap (1, 3, 0.0);
925  Matrix clim (1, 2, 0.0);
926 
927  graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
928  graphics_object ax = go.get_ancestor ("axes");
929 
930  if (ax.valid_object ())
931  {
932  Matrix _cmap = ax.get (caseless_str ("colormap")).matrix_value ();
933 
934  cmap = _cmap;
935 
936  if (is_scaled)
937  {
938  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
939 
940  clim = _clim;
941  }
942  }
943 
944  dv.resize (cdim);
945  dv(cdim-1) = 3;
946 
947  NDArray a (dv);
948 
949  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
950  octave_idx_type nc = cmap.rows ();
951 
952  double *av = a.fortran_vec ();
953  const double *cmapv = cmap.data ();
954 
955  double clim_0 = clim(0);
956  double clim_1 = clim(1);
957 
958  // FIXME: There is a lot of processing time spent just on data conversion
959  // both here in graphics.cc and again in gl-render.cc. There must
960  // be room for improvement! Here a macro expands to a templated
961  // function which in turn calls another function (covert_cdata_2).
962  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
963  // is supported anyways so there is another double for loop across
964  // height and width to convert all of the input data to GLfloat.
965 
966 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
967  do \
968  { \
969  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
970  \
971  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
972  tmp.data (), lda, nc, av); \
973  } \
974  while (0)
975 
976  if (cdata.is_uint8_type ())
977  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
978  else if (cdata.is_uint16_type ())
979  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
980  else if (cdata.is_double_type ())
981  CONVERT_CDATA_1 (NDArray, , true);
982  else if (cdata.is_single_type ())
983  CONVERT_CDATA_1 (FloatNDArray, float_, true);
984  else if (cdata.islogical ())
985  CONVERT_CDATA_1 (boolNDArray, bool_, false);
986  else
987  {
988  // Don't throw an error; leads to an incomplete FLTK object (bug #46933).
989  warning ("unsupported type for cdata (= %s). "
990  "Valid types are uint8, uint16, double, single, and bool.",
991  cdata.type_name ().c_str ());
992  a = NDArray (dv, 0); // return 0 instead
993  }
994 
995 #undef CONVERT_CDATA_1
996 
997  return octave_value (a);
998 }
999 
1000 template <typename T>
1001 static void
1002 get_array_limits (const Array<T>& m, double& emin, double& emax,
1003  double& eminp, double& emaxp)
1004 {
1005  const T *data = m.data ();
1006  octave_idx_type n = m.numel ();
1007 
1008  for (octave_idx_type i = 0; i < n; i++)
1009  {
1010  double e = double (data[i]);
1011 
1012  // Don't need to test for NaN here as NaN>x and NaN<x is always false
1013  if (! octave::math::isinf (e))
1014  {
1015  if (e < emin)
1016  emin = e;
1017 
1018  if (e > emax)
1019  emax = e;
1020 
1021  if (e > 0 && e < eminp)
1022  eminp = e;
1023 
1024  if (e < 0 && e > emaxp)
1025  emaxp = e;
1026  }
1027  }
1028 }
1029 
1030 static bool
1032  caseless_str& rest)
1033 {
1034  int len = name.length ();
1035  int offset = 0;
1036  bool result = false;
1037 
1038  if (len >= 4)
1039  {
1040  caseless_str pfx = name.substr (0, 4);
1041 
1042  if (pfx.compare ("axes") || pfx.compare ("line")
1043  || pfx.compare ("text"))
1044  offset = 4;
1045  else if (len >= 5)
1046  {
1047  pfx = name.substr (0, 5);
1048 
1049  if (pfx.compare ("image") || pfx.compare ("patch"))
1050  offset = 5;
1051  else if (len >= 6)
1052  {
1053  pfx = name.substr (0, 6);
1054 
1055  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1056  offset = 6;
1057  else if (len >= 7)
1058  {
1059  pfx = name.substr (0, 7);
1060 
1061  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1062  || pfx.compare ("uipanel"))
1063  offset = 7;
1064  else if (len >= 9)
1065  {
1066  pfx = name.substr (0, 9);
1067 
1068  if (pfx.compare ("uicontrol")
1069  || pfx.compare ("uitoolbar"))
1070  offset = 9;
1071  else if (len >= 10)
1072  {
1073  pfx = name.substr (0, 10);
1074 
1075  if (pfx.compare ("uipushtool"))
1076  offset = 10;
1077  else if (len >= 12)
1078  {
1079  pfx = name.substr (0, 12);
1080 
1081  if (pfx.compare ("uitoggletool"))
1082  offset = 12;
1083  else if (len >= 13)
1084  {
1085  pfx = name.substr (0, 13);
1086 
1087  if (pfx.compare ("uicontextmenu") ||
1088  pfx.compare ("uibuttongroup"))
1089  offset = 13;
1090  }
1091  }
1092  }
1093  }
1094  }
1095  }
1096  }
1097 
1098  if (offset > 0)
1099  {
1100  go_name = pfx;
1101  rest = name.substr (offset);
1102  result = true;
1103  }
1104  }
1105 
1106  return result;
1107 }
1108 
1109 static base_graphics_object*
1111  const graphics_handle& h = graphics_handle (),
1112  const graphics_handle& p = graphics_handle ())
1113 {
1114  base_graphics_object *go = nullptr;
1115 
1116  if (type.compare ("figure"))
1117  go = new figure (h, p);
1118  else if (type.compare ("axes"))
1119  go = new axes (h, p);
1120  else if (type.compare ("line"))
1121  go = new line (h, p);
1122  else if (type.compare ("text"))
1123  go = new text (h, p);
1124  else if (type.compare ("image"))
1125  go = new image (h, p);
1126  else if (type.compare ("light"))
1127  go = new light (h, p);
1128  else if (type.compare ("patch"))
1129  go = new patch (h, p);
1130  else if (type.compare ("surface"))
1131  go = new surface (h, p);
1132  else if (type.compare ("hggroup"))
1133  go = new hggroup (h, p);
1134  else if (type.compare ("uimenu"))
1135  go = new uimenu (h, p);
1136  else if (type.compare ("uicontrol"))
1137  go = new uicontrol (h, p);
1138  else if (type.compare ("uipanel"))
1139  go = new uipanel (h, p);
1140  else if (type.compare ("uibuttongroup"))
1141  go = new uibuttongroup (h, p);
1142  else if (type.compare ("uicontextmenu"))
1143  go = new uicontextmenu (h, p);
1144  else if (type.compare ("uitoolbar"))
1145  go = new uitoolbar (h, p);
1146  else if (type.compare ("uipushtool"))
1147  go = new uipushtool (h, p);
1148  else if (type.compare ("uitoggletool"))
1149  go = new uitoggletool (h, p);
1150  return go;
1151 }
1152 
1153 // ---------------------------------------------------------------------
1154 
1155 bool
1156 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1157 {
1158  if (do_set (v))
1159  {
1160  // Notify graphics toolkit.
1161  if (id >= 0 && do_notify_toolkit)
1162  {
1164  if (go)
1165  go.update (id);
1166  }
1167 
1168  // run listeners
1169  if (do_run)
1171 
1172  return true;
1173  }
1174 
1175  return false;
1176 }
1177 
1178 void
1180 {
1181  const octave_value_list& l = listeners[mode];
1182 
1183  for (int i = 0; i < l.length (); i++)
1185 }
1186 
1188  : default_val (), possible_vals ()
1189 {
1190  size_t beg = 0;
1191  size_t len = opt_string.length ();
1192  bool done = len == 0;
1193 
1194  while (! done)
1195  {
1196  size_t end = opt_string.find ('|', beg);
1197 
1198  if (end == std::string::npos)
1199  {
1200  end = len;
1201  done = true;
1202  }
1203 
1204  std::string t = opt_string.substr (beg, end-beg);
1205 
1206  // Might want more error checking here...
1207  if (t[0] == '{')
1208  {
1209  t = t.substr (1, t.length () - 2);
1210  default_val = t;
1211  }
1212  else if (beg == 0) // ensure default value
1213  default_val = t;
1214 
1215  possible_vals.insert (t);
1216 
1217  beg = end + 1;
1218  }
1219 }
1220 
1223 {
1225 
1226  for (const auto& val : possible_vals)
1227  {
1228  if (retval.empty ())
1229  {
1230  if (val == default_value ())
1231  retval = '{' + val + '}';
1232  else
1233  retval = val;
1234  }
1235  else
1236  {
1237  if (val == default_value ())
1238  retval += " | {" + val + '}';
1239  else
1240  retval += " | " + val;
1241  }
1242  }
1243 
1244  if (! retval.empty ())
1245  retval = "[ " + retval + " ]";
1246 
1247  return retval;
1248 }
1249 
1250 Cell
1252 {
1253  octave_idx_type i = 0;
1254  Cell retval (nelem (), 1);
1255 
1256  for (const auto& val : possible_vals)
1257  retval(i++) = std::string (val);
1258 
1259  return retval;
1260 }
1261 
1262 bool
1264 {
1265  bool retval = true;
1266 
1267  double tmp_rgb[3] = {0, 0, 0};
1268 
1269  std::string str = str_arg;
1270  unsigned int len = str.length ();
1271 
1272  std::transform (str.begin (), str.end (), str.begin (), tolower);
1273 
1274  if (str.compare (0, len, "blue", 0, len) == 0)
1275  tmp_rgb[2] = 1;
1276  else if (str.compare (0, len, "black", 0, len) == 0
1277  || str.compare (0, len, "k", 0, len) == 0)
1278  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1279  else if (str.compare (0, len, "red", 0, len) == 0)
1280  tmp_rgb[0] = 1;
1281  else if (str.compare (0, len, "green", 0, len) == 0)
1282  tmp_rgb[1] = 1;
1283  else if (str.compare (0, len, "yellow", 0, len) == 0)
1284  tmp_rgb[0] = tmp_rgb[1] = 1;
1285  else if (str.compare (0, len, "magenta", 0, len) == 0)
1286  tmp_rgb[0] = tmp_rgb[2] = 1;
1287  else if (str.compare (0, len, "cyan", 0, len) == 0)
1288  tmp_rgb[1] = tmp_rgb[2] = 1;
1289  else if (str.compare (0, len, "white", 0, len) == 0
1290  || str.compare (0, len, "w", 0, len) == 0)
1291  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1292  else
1293  retval = false;
1294 
1295  if (retval)
1296  {
1297  for (int i = 0; i < 3; i++)
1298  xrgb(i) = tmp_rgb[i];
1299  }
1300 
1301  return retval;
1302 }
1303 
1304 bool
1306 {
1307  if (val.is_string ())
1308  {
1309  std::string s = val.string_value ();
1310 
1311  if (s.empty ())
1312  error (R"(invalid value for color property "%s")",
1313  get_name ().c_str ());
1314 
1315  std::string match;
1316 
1317  if (radio_val.contains (s, match))
1318  {
1319  if (current_type != radio_t || match != current_val)
1320  {
1321  if (s.length () != match.length ())
1322  warning_with_id ("Octave:abbreviated-property-match",
1323  "%s: allowing %s to match %s value %s",
1324  "set", s.c_str (), get_name ().c_str (),
1325  match.c_str ());
1326  current_val = match;
1328  return true;
1329  }
1330  }
1331  else
1332  {
1333  try
1334  {
1335  color_values col (s);
1336 
1337  if (current_type != color_t || col != color_val)
1338  {
1339  color_val = col;
1341  return true;
1342  }
1343  }
1344  catch (octave::execution_exception& e)
1345  {
1346  error (e, R"(invalid value for color property "%s" (value = %s))",
1347  get_name ().c_str (), s.c_str ());
1348  }
1349  }
1350  }
1351  else if (val.isnumeric ())
1352  {
1353  Matrix m = val.matrix_value ();
1354 
1355  if (m.numel () != 3)
1356  error (R"(invalid value for color property "%s")",
1357  get_name ().c_str ());
1358 
1359  color_values col (m(0), m(1), m(2));
1360 
1361  if (current_type != color_t || col != color_val)
1362  {
1363  color_val = col;
1365  return true;
1366  }
1367  }
1368  else
1369  error (R"(invalid value for color property "%s")",
1370  get_name ().c_str ());
1371 
1372  return false;
1373 }
1374 
1375 bool
1377 {
1378  if (val.is_string ())
1379  {
1380  std::string s = val.string_value ();
1381  std::string match;
1382 
1383  if (s.empty () || ! radio_val.contains (s, match))
1384  error (R"(invalid value for double_radio property "%s")",
1385  get_name ().c_str ());
1386 
1387  if (current_type != radio_t || match != current_val)
1388  {
1389  if (s.length () != match.length ())
1390  warning_with_id ("Octave:abbreviated-property-match",
1391  "%s: allowing %s to match %s value %s",
1392  "set", s.c_str (), get_name ().c_str (),
1393  match.c_str ());
1394  current_val = match;
1396  return true;
1397  }
1398  }
1399  else if (val.is_scalar_type () && val.isreal ())
1400  {
1401  double new_dval = val.double_value ();
1402 
1403  if (current_type != double_t || new_dval != dval)
1404  {
1405  dval = new_dval;
1407  return true;
1408  }
1409  }
1410  else
1411  error (R"(invalid value for double_radio property "%s")",
1412  get_name ().c_str ());
1413 
1414  return false;
1415 }
1416 
1417 bool
1419 {
1420  bool xok = false;
1421 
1422  // check value type
1423  if (type_constraints.size () > 0)
1424  {
1425  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1426  xok = true;
1427 
1428  // check if complex is allowed (it's also of class "double", so
1429  // checking that alone is not enough to ensure real type)
1430  if (type_constraints.find ("real") != type_constraints.end ()
1431  && v.iscomplex ())
1432  xok = false;
1433  }
1434  else
1435  xok = v.isnumeric () || v.is_bool_scalar ();
1436 
1437  if (xok && size_constraints.size () > 0)
1438  {
1439  dim_vector vdims = v.dims ();
1440  int vlen = vdims.ndims ();
1441 
1442  xok = false;
1443 
1444  // check dimensional size constraints until a match is found
1445  for (auto it = size_constraints.cbegin ();
1446  ! xok && it != size_constraints.cend ();
1447  ++it)
1448  {
1449  dim_vector itdims = (*it);
1450 
1451  if (itdims.ndims () == vlen)
1452  {
1453  xok = true;
1454 
1455  for (int i = 0; xok && i < vlen; i++)
1456  {
1457  if (itdims(i) > 0)
1458  {
1459  if (itdims(i) != vdims(i))
1460  xok = false;
1461  }
1462  else if (v.isempty ())
1463  break;
1464  }
1465  }
1466  }
1467  }
1468 
1469  if (xok)
1470  {
1471  NDArray v_mat = v.array_value ();
1472  // Check min and max
1473  if (! octave::math::isnan (minval.first))
1474  {
1475  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1476  if (minval.second && minval.first > v_mat(i))
1477  error (R"(set: "%s" must be greater than or equal to %g)",
1478  get_name ().c_str (), minval.first);
1479  else if (! minval.second && minval.first >= v_mat(i))
1480  error (R"(set: "%s" must be greater than %g)",
1481  get_name ().c_str (), minval.first);
1482  }
1483 
1484  if (! octave::math::isnan (maxval.first))
1485  {
1486  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1487  if (maxval.second && maxval.first < v_mat(i))
1488  error (R"(set: "%s" must be less than or equal to %g)",
1489  get_name ().c_str (), maxval.first);
1490  else if (! maxval.second && maxval.first <= v_mat(i))
1491  error (R"(set: "%s" must be less than %g)",
1492  get_name ().c_str (), maxval.first);
1493  }
1494 
1495  if (finite_constraint == NO_CHECK) { /* do nothing */ }
1496  else if (finite_constraint == FINITE)
1497  {
1498  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1499  if (! octave::math::isfinite (v_mat(i)))
1500  error (R"(set: "%s" must be finite)", get_name ().c_str ());
1501  }
1502  else if (finite_constraint == NOT_NAN)
1503  {
1504  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1505  if (octave::math::isnan (v_mat(i)))
1506  error (R"(set: "%s" must not be nan)", get_name ().c_str ());
1507  }
1508  else if (finite_constraint == NOT_INF)
1509  {
1510  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1511  if (octave::math::isinf (v_mat(i)))
1512  error (R"(set: "%s" must not be infinite)", get_name ().c_str ());
1513  }
1514 
1515  }
1516 
1517  return xok;
1518 }
1519 
1520 bool
1522 {
1523  if (data.type_name () == v.type_name ())
1524  {
1525  if (data.dims () == v.dims ())
1526  {
1527 
1528 #define CHECK_ARRAY_EQUAL(T, F, A) \
1529  { \
1530  if (data.numel () == 1) \
1531  return data.F ## scalar_value () == \
1532  v.F ## scalar_value (); \
1533  else \
1534  { \
1535  /* Keep copy of array_value to allow */ \
1536  /* sparse/bool arrays that are converted, to */ \
1537  /* not be deallocated early */ \
1538  const A m1 = data.F ## array_value (); \
1539  const T *d1 = m1.data (); \
1540  const A m2 = v.F ## array_value (); \
1541  const T *d2 = m2.data (); \
1542  \
1543  bool flag = true; \
1544  \
1545  for (int i = 0; flag && i < data.numel (); i++) \
1546  if (d1[i] != d2[i]) \
1547  flag = false; \
1548  \
1549  return flag; \
1550  } \
1551  }
1552 
1553  if (data.is_double_type () || data.islogical ())
1554  CHECK_ARRAY_EQUAL (double, , NDArray)
1555  else if (data.is_single_type ())
1556  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1557  else if (data.is_int8_type ())
1559  else if (data.is_int16_type ())
1561  else if (data.is_int32_type ())
1563  else if (data.is_int64_type ())
1565  else if (data.is_uint8_type ())
1567  else if (data.is_uint16_type ())
1569  else if (data.is_uint32_type ())
1571  else if (data.is_uint64_type ())
1573  }
1574  }
1575 
1576  return false;
1577 }
1578 
1579 void
1581 {
1584 
1585  if (! data.isempty ())
1586  {
1587  if (data.isinteger ())
1588  {
1589  if (data.is_int8_type ())
1591  xmin, xmax, xminp, xmaxp);
1592  else if (data.is_uint8_type ())
1594  xmin, xmax, xminp, xmaxp);
1595  else if (data.is_int16_type ())
1597  xmin, xmax, xminp, xmaxp);
1598  else if (data.is_uint16_type ())
1600  xmin, xmax, xminp, xmaxp);
1601  else if (data.is_int32_type ())
1603  xmin, xmax, xminp, xmaxp);
1604  else if (data.is_uint32_type ())
1606  xmin, xmax, xminp, xmaxp);
1607  else if (data.is_int64_type ())
1609  xmin, xmax, xminp, xmaxp);
1610  else if (data.is_uint64_type ())
1612  xmin, xmax, xminp, xmaxp);
1613  }
1614  else
1616  }
1617 }
1618 
1619 bool
1621 {
1622  // Users may want to use empty matrix to reset a handle property
1623  if (v.isempty ())
1624  {
1625  if (! get ().isempty ())
1626  {
1628  return true;
1629  }
1630  else
1631  return false;
1632  }
1633 
1634  double dv = v.xdouble_value (R"(set: invalid graphics handle for property "%s")",
1635  get_name ().c_str ());
1636 
1638 
1639  // Check the object type if necessary
1640  bool type_ok = true;
1641  if (gh.ok () && ! type_constraints.empty ())
1642  {
1643  type_ok = false;
1645 
1646  for (const auto& type : type_constraints)
1647  if (obj.isa (type))
1648  {
1649  type_ok = true;
1650  break;
1651  }
1652  }
1653 
1654  if (! octave::math::isnan (gh.value ()) && ! (gh.ok () && type_ok))
1655  {
1656  if (type_ok)
1657  error (R"(set: invalid graphics handle (= %g) for property "%s")",
1658  dv, get_name ().c_str ());
1659  else
1660  error (R"(set: invalid graphics object type for property "%s")",
1661  get_name ().c_str ());
1662  }
1663 
1664  if (current_val != gh)
1665  {
1666  current_val = gh;
1667  return true;
1668  }
1669 
1670  return false;
1671 }
1672 
1673 /*
1674 ## Test validation of uicontextmenu property
1675 %!test
1676 %! hf = figure ("visible", "off");
1677 %! unwind_protect
1678 %! hax = axes ("parent", hf);
1679 %! hpa = patch ("parent", hax);
1680 %! try
1681 %! set (hax, "uicontextmenu", hpa);
1682 %! catch
1683 %! err = lasterr ();
1684 %! end_try_catch
1685 %! assert (err, 'set: invalid graphics object type for property "uicontextmenu"');
1686 %! unwind_protect_cleanup
1687 %! delete (hf);
1688 %! end_unwind_protect
1689 */
1690 
1691 Matrix
1692 children_property::do_get_children (bool return_hidden) const
1693 {
1694  Matrix retval (children_list.size (), 1);
1695  octave_idx_type k = 0;
1696 
1698 
1699  root_figure::properties& props =
1700  dynamic_cast<root_figure::properties&> (go.get_properties ());
1701 
1702  if (! props.is_showhiddenhandles ())
1703  {
1704  for (const auto& hchild : children_list)
1705  {
1706  graphics_handle kid = hchild;
1707 
1709  {
1710  if (! return_hidden)
1711  retval(k++) = hchild;
1712  }
1713  else if (return_hidden)
1714  retval(k++) = hchild;
1715  }
1716 
1717  retval.resize (k, 1);
1718  }
1719  else
1720  {
1721  for (const auto& hchild : children_list)
1722  retval(k++) = hchild;
1723  }
1724 
1725  return retval;
1726 }
1727 
1728 void
1730 {
1731  for (auto& hchild : children_list)
1732  {
1734 
1735  if (go.valid_object () && ! go.get_properties ().is_beingdeleted ())
1736  gh_manager::free (hchild);
1737  }
1738 
1739  if (clear)
1740  children_list.clear ();
1741 }
1742 
1743 bool
1745 {
1746  // case 1: empty matrix
1747  // case 2: function handle
1748  // case 3: string corresponding to known function name
1749  // case 4: string that can be eval()'ed
1750  // case 5: cell array with first element being a function handle
1751 
1752  if (v.isempty ())
1753  return true;
1754  else if (v.is_function_handle ())
1755  return true;
1756  else if (v.is_string ())
1757  // complete validation will be done at execution-time
1758  return true;
1759  else if (v.iscell () && (v.rows () == 1 || v.columns () == 1)
1760  && v.cell_value ()(0).is_function_handle ())
1761  return true;
1762 
1763  return false;
1764 }
1765 
1767 {
1768 public:
1769 
1770  callback_props (void) : m_set () { }
1771 
1772  callback_props (const callback_props&) = delete;
1773 
1774  callback_props& operator = (const callback_props&) = delete;
1775 
1776  ~callback_props (void) = default;
1777 
1778  bool empty (void) const { return m_set.empty (); }
1779 
1780  void insert (const callback_property *ptr)
1781  {
1782  m_set.insert (reinterpret_cast<intptr_t> (ptr));
1783  }
1784 
1785  void erase (const callback_property *ptr)
1786  {
1787  m_set.erase (reinterpret_cast<intptr_t> (ptr));
1788  }
1789 
1790  bool contains (const callback_property *ptr) const
1791  {
1792  return m_set.find (reinterpret_cast<intptr_t> (ptr)) != m_set.end ();
1793  }
1794 
1795 private:
1796 
1797  std::set<intptr_t> m_set;
1798 };
1799 
1800 // Elements of this set are pointers to currently executing
1801 // callback_property objects. Used to determine handle visibility
1802 // inside callback functions.
1803 
1805 
1806 void
1808 {
1810 
1811  // We are executing a callback function, so allow handles that have
1812  // their handlevisibility property set to "callback" to be visible.
1813 
1815 
1816  if (! executing_callbacks.contains (this))
1817  {
1818  executing_callbacks.insert (this);
1819 
1820  if (callback.is_defined () && ! callback.isempty ())
1822  }
1823 }
1824 
1825 // Used to cache dummy graphics objects from which dynamic properties can be
1826 // cloned.
1827 static std::map<caseless_str, graphics_object> dprop_obj_map;
1828 
1829 property
1831  const caseless_str& type, const octave_value_list& args)
1832 {
1833  property retval;
1834 
1835  if (type.compare ("string"))
1836  {
1837  std::string sv = (args.length () > 0 ? args(0).string_value () : "");
1838 
1839  retval = property (new string_property (name, h, sv));
1840  }
1841  else if (type.compare ("any"))
1842  {
1843  octave_value ov = (args.length () > 0 ? args(0)
1844  : octave_value (Matrix ()));
1845 
1846  retval = property (new any_property (name, h, ov));
1847  }
1848  else if (type.compare ("radio"))
1849  {
1850  if (args.length () < 1)
1851  error ("addproperty: missing possible values for radio property");
1852 
1853  std::string sv = args(0).xstring_value ("addproperty: argument for radio property must be a string");
1854 
1855  retval = property (new radio_property (name, h, sv));
1856 
1857  if (args.length () > 1)
1858  retval.set (args(1));
1859  }
1860  else if (type.compare ("double"))
1861  {
1862  double dv = (args.length () > 0 ? args(0).double_value () : 0.0);
1863 
1864  retval = property (new double_property (name, h, dv));
1865  }
1866  else if (type.compare ("handle"))
1867  {
1868  double hv = (args.length () > 0 ? args(0).double_value ()
1870 
1871  graphics_handle gh (hv);
1872 
1873  retval = property (new handle_property (name, h, gh));
1874  }
1875  else if (type.compare ("boolean"))
1876  {
1877  retval = property (new bool_property (name, h, false));
1878 
1879  if (args.length () > 0)
1880  retval.set (args(0));
1881  }
1882  else if (type.compare ("data"))
1883  {
1884  retval = property (new array_property (name, h, Matrix ()));
1885 
1886  if (args.length () > 0)
1887  {
1888  retval.set (args(0));
1889  // FIXME: additional argument could define constraints,
1890  // but is this really useful?
1891  }
1892  }
1893  else if (type.compare ("color"))
1894  {
1895  color_values cv (0, 0, 0);
1896  radio_values rv;
1897 
1898  if (args.length () > 1)
1899  rv = radio_values (args(1).string_value ());
1900 
1901  retval = property (new color_property (name, h, cv, rv));
1902 
1903  if (args.length () > 0 && ! args(0).isempty ())
1904  retval.set (args(0));
1905  else
1906  retval.set (rv.default_value ());
1907  }
1908  else
1909  {
1910  caseless_str go_name, go_rest;
1911 
1912  if (! lookup_object_name (type, go_name, go_rest))
1913  error ("addproperty: unsupported type for dynamic property (= %s)",
1914  type.c_str ());
1915 
1916  graphics_object go;
1917 
1918  std::map<caseless_str, graphics_object>::const_iterator it =
1919  dprop_obj_map.find (go_name);
1920 
1921  if (it == dprop_obj_map.end ())
1922  {
1923  base_graphics_object *bgo =
1925 
1926  if (bgo)
1927  {
1928  go = graphics_object (bgo);
1929 
1930  dprop_obj_map[go_name] = go;
1931  }
1932  }
1933  else
1934  go = it->second;
1935 
1936  if (! go.valid_object ())
1937  error ("addproperty: invalid object type (= %s)",
1938  go_name.c_str ());
1939 
1940  property prop = go.get_properties ().get_property (go_rest);
1941 
1942  retval = prop.clone ();
1943 
1944  retval.set_parent (h);
1945  retval.set_name (name);
1946 
1947  if (args.length () > 0)
1948  retval.set (args(0));
1949  }
1950 
1951  return retval;
1952 }
1953 
1954 static void
1956 {
1958 
1959  if (go)
1960  {
1961  Matrix children = go.get_properties ().get_all_children ();
1962 
1963  for (int k = 0; k < children.numel (); k++)
1964  finalize_r (children(k));
1965 
1966  go.finalize ();
1967  }
1968 }
1969 
1970 static void
1972 {
1974 
1975  if (go)
1976  {
1977  Matrix children = go.get_properties ().get_all_children ();
1978 
1979  go.initialize ();
1980 
1981  for (int k = 0; k < children.numel (); k++)
1982  initialize_r (children(k));
1983  }
1984 }
1985 
1986 void
1988 {
1989  if (toolkit)
1990  finalize_r (get___myhandle__ ());
1991 
1992  toolkit = b;
1993  __graphics_toolkit__ = b.get_name ();
1994  __plot_stream__ = Matrix ();
1995 
1996  if (toolkit)
1997  initialize_r (get___myhandle__ ());
1998 
1999  mark_modified ();
2000 }
2001 
2002 void
2003 figure::properties::set___mouse_mode__ (const octave_value& val_arg)
2004 {
2005  std::string direction = "in";
2006 
2007  octave_value val = val_arg;
2008 
2009  if (val.is_string ())
2010  {
2011  std::string modestr = val.string_value ();
2012 
2013  if (modestr == "zoom in")
2014  {
2015  val = modestr = "zoom";
2016  direction = "in";
2017  }
2018  else if (modestr == "zoom out")
2019  {
2020  val = modestr = "zoom";
2021  direction = "out";
2022  }
2023 
2024  if (__mouse_mode__.set (val, true))
2025  {
2026  std::string mode = __mouse_mode__.current_value ();
2027 
2028  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
2029  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
2030  set___pan_mode__ (pm);
2031 
2032  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
2033  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
2034  set___rotate_mode__ (rm);
2035 
2036  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2037  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
2038  zm.setfield ("Direction", direction);
2039  set___zoom_mode__ (zm);
2040 
2041  mark_modified ();
2042  }
2043  else if (modestr == "zoom")
2044  {
2045  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2046  std::string curr_direction
2047  = zm.getfield ("Direction").string_value ();
2048 
2049  if (direction != curr_direction)
2050  {
2051  zm.setfield ("Direction", direction);
2052  set___zoom_mode__ (zm);
2053 
2054  mark_modified ();
2055  }
2056  }
2057  }
2058 }
2059 
2060 void
2061 figure::properties::update_handlevisibility (void)
2062 {
2063  if (! is_handle_visible ())
2064  {
2065  octave_value cf = gh_manager::get_object (0).get ("currentfigure");
2066  if (! cf.isempty () && cf.double_value () == __myhandle__)
2067  {
2068  gh_manager::auto_lock guard;
2069  octave_value kids = gh_manager::get_object (0).get ("children");
2070  if (kids.isempty ())
2071  gh_manager::get_object (0).set ("currentfigure", Matrix ());
2072  else
2073  {
2074  NDArray kidsarray = kids.array_value ();
2075  gh_manager::get_object (0).set ("currentfigure", kidsarray(0));
2076  }
2077  }
2078  }
2079 
2080  base_properties::update_handlevisibility ();
2081 }
2082 // ---------------------------------------------------------------------
2083 
2084 void
2086 {
2087  size_t offset = 0;
2088 
2089  size_t len = name.length ();
2090 
2091  if (len > 4)
2092  {
2093  caseless_str pfx = name.substr (0, 4);
2094 
2095  if (pfx.compare ("axes") || pfx.compare ("line")
2096  || pfx.compare ("text"))
2097  offset = 4;
2098  else if (len > 5)
2099  {
2100  pfx = name.substr (0, 5);
2101 
2102  if (pfx.compare ("image") || pfx.compare ("patch"))
2103  offset = 5;
2104  else if (len > 6)
2105  {
2106  pfx = name.substr (0, 6);
2107 
2108  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2109  offset = 6;
2110  else if (len > 7)
2111  {
2112  pfx = name.substr (0, 7);
2113 
2114  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2115  || pfx.compare ("uipanel"))
2116  offset = 7;
2117  else if (len > 9)
2118  {
2119  pfx = name.substr (0, 9);
2120 
2121  if (pfx.compare ("uicontrol")
2122  || pfx.compare ("uitoolbar"))
2123  offset = 9;
2124  else if (len > 10)
2125  {
2126  pfx = name.substr (0, 10);
2127 
2128  if (pfx.compare ("uipushtool"))
2129  offset = 10;
2130  else if (len > 12)
2131  {
2132  pfx = name.substr (0, 12);
2133 
2134  if (pfx.compare ("uitoogletool"))
2135  offset = 12;
2136  else if (len > 13)
2137  {
2138  pfx = name.substr (0, 13);
2139 
2140  if (pfx.compare ("uicontextmenu")
2141  || pfx.compare ("uibuttongroup"))
2142  offset = 13;
2143  }
2144  }
2145  }
2146  }
2147  }
2148  }
2149  }
2150 
2151  if (offset > 0)
2152  {
2153  // FIXME: should we validate property names and values here?
2154 
2155  std::string pname = name.substr (offset);
2156 
2157  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2158  std::transform (pname.begin (), pname.end (), pname.begin (),
2159  tolower);
2160 
2161  bool has_property = false;
2162  if (pfx == "axes")
2163  has_property = axes::properties::has_core_property (pname);
2164  else if (pfx == "figure")
2165  has_property = figure::properties::has_core_property (pname);
2166  else if (pfx == "line")
2167  has_property = line::properties::has_core_property (pname);
2168  else if (pfx == "text")
2169  has_property = text::properties::has_core_property (pname);
2170  else if (pfx == "image")
2171  has_property = image::properties::has_core_property (pname);
2172  else if (pfx == "patch")
2173  has_property = patch::properties::has_core_property (pname);
2174  else if (pfx == "surface")
2175  has_property = surface::properties::has_core_property (pname);
2176  else if (pfx == "hggroup")
2177  has_property = hggroup::properties::has_core_property (pname);
2178  else if (pfx == "uimenu")
2179  has_property = uimenu::properties::has_core_property (pname);
2180  else if (pfx == "uicontrol")
2181  has_property = uicontrol::properties::has_core_property (pname);
2182  else if (pfx == "uibuttongroup")
2183  has_property = uibuttongroup::properties::has_core_property (pname);
2184  else if (pfx == "uipanel")
2185  has_property = uipanel::properties::has_core_property (pname);
2186  else if (pfx == "uicontextmenu")
2187  has_property = uicontextmenu::properties::has_core_property (pname);
2188  else if (pfx == "uitoolbar")
2189  has_property = uitoolbar::properties::has_core_property (pname);
2190  else if (pfx == "uipushtool")
2191  has_property = uipushtool::properties::has_core_property (pname);
2192 
2193  if (! has_property)
2194  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2195 
2196  bool remove = false;
2197  if (val.is_string ())
2198  {
2199  std::string sval = val.string_value ();
2200 
2201  remove = (sval == "remove");
2202  }
2203 
2204  pval_map_type& pval_map = plist_map[pfx];
2205 
2206  if (remove)
2207  {
2208  pval_map_iterator p = pval_map.find (pname);
2209 
2210  if (p != pval_map.end ())
2211  pval_map.erase (p);
2212  }
2213  else
2214  pval_map[pname] = val;
2215  }
2216  }
2217 
2218  if (offset == 0)
2219  error ("invalid default property specification");
2220 }
2221 
2224 {
2226 
2227  size_t offset = 0;
2228 
2229  size_t len = name.length ();
2230 
2231  if (len > 4)
2232  {
2233  caseless_str pfx = name.substr (0, 4);
2234 
2235  if (pfx.compare ("axes") || pfx.compare ("line")
2236  || pfx.compare ("text"))
2237  offset = 4;
2238  else if (len > 5)
2239  {
2240  pfx = name.substr (0, 5);
2241 
2242  if (pfx.compare ("image") || pfx.compare ("patch"))
2243  offset = 5;
2244  else if (len > 6)
2245  {
2246  pfx = name.substr (0, 6);
2247 
2248  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2249  offset = 6;
2250  else if (len > 7)
2251  {
2252  pfx = name.substr (0, 7);
2253 
2254  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2255  || pfx.compare ("uipanel"))
2256  offset = 7;
2257  else if (len > 9)
2258  {
2259  pfx = name.substr (0, 9);
2260 
2261  if (pfx.compare ("uicontrol")
2262  || pfx.compare ("uitoolbar"))
2263  offset = 9;
2264  else if (len > 10)
2265  {
2266  pfx = name.substr (0, 10);
2267 
2268  if (pfx.compare ("uipushtool"))
2269  offset = 10;
2270  else if (len > 12)
2271  {
2272  pfx = name.substr (0, 12);
2273 
2274  if (pfx.compare ("uitoggletool"))
2275  offset = 12;
2276  else if (len > 13)
2277  {
2278  pfx = name.substr (0, 13);
2279 
2280  if (pfx.compare ("uicontextmenu")
2281  || pfx.compare ("uibuttongroup"))
2282  offset = 13;
2283  }
2284  }
2285  }
2286  }
2287  }
2288  }
2289  }
2290 
2291  if (offset > 0)
2292  {
2293  std::string pname = name.substr (offset);
2294 
2295  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2296  std::transform (pname.begin (), pname.end (), pname.begin (),
2297  tolower);
2298 
2300 
2301  if (p != end ())
2302  {
2303  const pval_map_type& pval_map = p->second;
2304 
2305  pval_map_const_iterator q = pval_map.find (pname);
2306 
2307  if (q != pval_map.end ())
2308  retval = q->second;
2309  }
2310  }
2311  }
2312 
2313  return retval;
2314 }
2315 
2317 property_list::as_struct (const std::string& prefix_arg) const
2318 {
2320 
2321  for (plist_map_const_iterator p = begin (); p != end (); p++)
2322  {
2323  std::string prefix = prefix_arg + p->first;
2324 
2325  for (const auto& prop_val : p->second)
2326  m.assign (prefix + prop_val.first, prop_val.second);
2327  }
2328 
2329  return m;
2330 }
2331 
2332 // Set properties given as a cs-list of name, value pairs.
2333 
2334 void
2336 {
2337  int nargin = args.length ();
2338 
2339  if (nargin == 0)
2340  error ("graphics_object::set: Nothing to set");
2341 
2342  for (int i = 0; i < nargin; )
2343  {
2344  if (args(i).isstruct () )
2345  {
2346  set (args(i).map_value ());
2347  i++;
2348  }
2349  else if (i < nargin - 1)
2350  {
2351  caseless_str pname = args(i).xstring_value ("set: argument %d must be a property name", i);
2352  octave_value val = args(i+1);
2353  set_value_or_default (pname, val);
2354  i += 2;
2355  }
2356  else
2357  error ("set: invalid number of arguments");
2358  }
2359 }
2360 
2361 /*
2362 ## test set with name, value pairs
2363 %!test
2364 %! hf = figure ("visible", "off");
2365 %! h = plot (1:10, 10:-1:1);
2366 %! set (h, "linewidth", 10, "marker", "x");
2367 %! lw = get (h, "linewidth");
2368 %! mk = get (h, "marker");
2369 %! close (hf);
2370 %! assert (lw, 10);
2371 %! assert (mk, "x");
2372 */
2373 
2374 // Set properties given in two cell arrays containing names and values.
2375 void
2377  const Cell& values, octave_idx_type row)
2378 {
2379  if (pnames.numel () != values.columns ())
2380  error ("set: number of names must match number of value columns (%d != %d)",
2381  pnames.numel (), values.columns ());
2382 
2383  octave_idx_type k = pnames.columns ();
2384 
2385  for (octave_idx_type column = 0; column < k; column++)
2386  {
2387  caseless_str pname = pnames(column);
2389 
2390  set_value_or_default (pname, val);
2391  }
2392 }
2393 
2394 /*
2395 ## test set with cell array arguments
2396 %!test
2397 %! hf = figure ("visible", "off");
2398 %! h = plot (1:10, 10:-1:1);
2399 %! set (h, {"linewidth", "marker"}, {10, "x"});
2400 %! lw = get (h, "linewidth");
2401 %! mk = get (h, "marker");
2402 %! close (hf);
2403 %! assert (lw, 10);
2404 %! assert (mk, "x");
2405 
2406 ## test set with multiple handles and cell array arguments
2407 %!test
2408 %! hf = figure ("visible", "off");
2409 %! unwind_protect
2410 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2411 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2412 %! assert (get (h, "linewidth"), {10; 5});
2413 %! assert (get (h, "marker"), {"x"; "o"});
2414 %! set (h, {"linewidth", "marker"}, {10, "x"});
2415 %! assert (get (h, "linewidth"), {10; 10});
2416 %! assert (get (h, "marker"), {"x"; "x"});
2417 %! unwind_protect_cleanup
2418 %! close (hf);
2419 %! end_unwind_protect;
2420 
2421 %!error <set: number of graphics handles must match number of value rows>
2422 %! hf = figure ("visible", "off");
2423 %! unwind_protect
2424 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2425 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2426 %! unwind_protect_cleanup
2427 %! close (hf);
2428 %! end_unwind_protect
2429 
2430 %!error <set: number of names must match number of value columns>
2431 %! hf = figure ("visible", "off");
2432 %! unwind_protect
2433 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2434 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2435 %! unwind_protect_cleanup
2436 %! close (hf);
2437 %! end_unwind_protect
2438 */
2439 
2440 // Set properties given in a struct array
2441 void
2443 {
2444  for (octave_idx_type p = 0; p < m.nfields (); p++)
2445  {
2446  // FIXME: Would it be better to extract all the keys at once rather than
2447  // repeatedly call keys() inside a for loop?
2448  caseless_str pname = m.keys ()[p];
2449 
2450  octave_value val = octave_value (m.contents (pname).elem (m.numel () - 1));
2451 
2452  set_value_or_default (pname, val);
2453  }
2454 }
2455 
2456 /*
2457 ## test set ticklabels for compatibility
2458 %!test
2459 %! hf = figure ("visible", "off");
2460 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2461 %! xticklabel = get (gca (), "xticklabel");
2462 %! close (hf);
2463 %! assert (class (xticklabel), "char");
2464 %! assert (size (xticklabel), [6, 3]);
2465 
2466 %!test
2467 %! hf = figure ("visible", "off");
2468 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2469 %! xticklabel = get (gca (), "xticklabel");
2470 %! close (hf);
2471 %! assert (class (xticklabel), "char");
2472 %! assert (size (xticklabel), [6, 3]);
2473 
2474 %!test
2475 %! hf = figure ("visible", "off");
2476 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2477 %! xticklabel = get (gca (), "xticklabel");
2478 %! close (hf);
2479 %! assert (class (xticklabel), "char");
2480 %! assert (size (xticklabel), [6, 3]);
2481 
2482 %!test
2483 %! hf = figure ("visible", "off");
2484 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2485 %! xticklabel = get (gca (), "xticklabel");
2486 %! close (hf);
2487 %! assert (class (xticklabel), "cell");
2488 %! assert (size (xticklabel), [6, 1]);
2489 */
2490 
2491 /*
2492 ## test set with struct arguments
2493 %!test
2494 %! hf = figure ("visible", "off");
2495 %! unwind_protect
2496 %! h = plot (1:10, 10:-1:1);
2497 %! set (h, struct ("linewidth", 10, "marker", "x"));
2498 %! assert (get (h, "linewidth"), 10);
2499 %! assert (get (h, "marker"), "x");
2500 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2501 %! set (h, struct ("linewidth", {5, 10}));
2502 %! assert (get (h, "linewidth"), {10; 10});
2503 %! unwind_protect_cleanup
2504 %! close (hf);
2505 %! end_unwind_protect
2506 
2507 ## test ordering
2508 %!test
2509 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2510 %! hf = figure ("visible", "off");
2511 %! unwind_protect
2512 %! h = line ();
2513 %! set (h, "userdata", {});
2514 %! addlistener (h, "color", {markchanged, "color"});
2515 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2516 %! ## "linewidth" first
2517 %! props.linewidth = 2;
2518 %! props.color = "r";
2519 %! set (h, props);
2520 %! assert (get (h, "userdata"), fieldnames (props));
2521 %! clear props;
2522 %! clf ();
2523 %! h = line ();
2524 %! set (h, "userdata", {});
2525 %! addlistener (h, "color", {markchanged, "color"});
2526 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2527 %! ## "color" first
2528 %! props.color = "r";
2529 %! props.linewidth = 2;
2530 %! set (h, props);
2531 %! assert (get (h, "userdata"), fieldnames (props));
2532 %! unwind_protect_cleanup
2533 %! close (hf);
2534 %! end_unwind_protect
2535 */
2536 
2537 // Set a property to a value or to its (factory) default value.
2538 
2539 void
2541  const octave_value& val)
2542 {
2543  if (val.is_string ())
2544  {
2545  std::string sval = val.string_value ();
2546 
2547  octave_value default_val;
2548 
2549  if (sval == "default")
2550  {
2551  default_val = get_default (pname);
2552 
2553  rep->set (pname, default_val);
2554  }
2555  else if (sval == "factory")
2556  {
2557  default_val = get_factory_default (pname);
2558 
2559  rep->set (pname, default_val);
2560  }
2561  else
2562  {
2563  // Matlab specifically uses "\default" to escape string setting
2564  if (sval == R"(\default)")
2565  rep->set (pname, "default");
2566  else if (sval == R"(\factory)")
2567  rep->set (pname, "factory");
2568  else
2569  rep->set (pname, val);
2570  }
2571  }
2572  else
2573  rep->set (pname, val);
2574 }
2575 
2576 /*
2577 ## test setting of default values
2578 %!test
2579 %! old_lw = get (0, "defaultlinelinewidth");
2580 %! unwind_protect
2581 %! hf = figure ("visible", "off");
2582 %! h = plot (1:10, 10:-1:1);
2583 %! set (0, "defaultlinelinewidth", 20);
2584 %! set (h, "linewidth", "default");
2585 %! assert (get (h, "linewidth"), 20);
2586 %! set (h, "linewidth", "factory");
2587 %! assert (get (h, "linewidth"), 0.5);
2588 %! unwind_protect_cleanup
2589 %! close (hf);
2590 %! set (0, "defaultlinelinewidth", old_lw);
2591 %! end_unwind_protect
2592 */
2593 
2594 static double
2596 {
2597  static double maxrand = RAND_MAX + 2.0;
2598 
2599  return (rand () + 1.0) / maxrand;
2600 }
2601 
2603 gh_manager::do_get_handle (bool integer_figure_handle)
2604 {
2606 
2607  if (integer_figure_handle)
2608  {
2609  // Figure handles are positive integers corresponding
2610  // to the figure number.
2611 
2612  // We always want the lowest unused figure number.
2613 
2614  retval = 1;
2615 
2616  while (handle_map.find (retval) != handle_map.end ())
2617  retval++;
2618  }
2619  else
2620  {
2621  // Other graphics handles are negative integers plus some random
2622  // fractional part. To avoid running out of integers, we recycle the
2623  // integer part but tack on a new random part each time.
2624 
2625  free_list_iterator p = handle_free_list.begin ();
2626 
2627  if (p != handle_free_list.end ())
2628  {
2629  retval = *p;
2630  handle_free_list.erase (p);
2631  }
2632  else
2633  {
2634  retval = graphics_handle (next_handle);
2635 
2636  next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2637  }
2638  }
2639 
2640  return retval;
2641 }
2642 
2643 void
2645 {
2646  if (h.ok ())
2647  {
2648  if (h.value () == 0)
2649  error ("graphics_handle::free: can't delete root figure");
2650 
2651  iterator p = handle_map.find (h);
2652 
2653  if (p == handle_map.end ())
2654  error ("graphics_handle::free: invalid object %g", h.value ());
2655 
2656  base_properties& bp = p->second.get_properties ();
2657 
2658  bp.set_beingdeleted (true);
2659 
2660  bp.delete_children ();
2661 
2662  octave_value val = bp.get_deletefcn ();
2663 
2664  bp.execute_deletefcn ();
2665 
2666  // Notify graphics toolkit.
2667  p->second.finalize ();
2668 
2669  // Note: this will be valid only for first explicitly deleted
2670  // object. All its children will then have an
2671  // unknown graphics toolkit.
2672 
2673  // Graphics handles for non-figure objects are negative
2674  // integers plus some random fractional part. To avoid
2675  // running out of integers, we recycle the integer part
2676  // but tack on a new random part each time.
2677 
2678  handle_map.erase (p);
2679 
2680  if (h.value () < 0)
2681  handle_free_list.insert
2682  (std::ceil (h.value ()) - make_handle_fraction ());
2683  }
2684 }
2685 
2686 void
2688  const graphics_handle& new_gh)
2689 {
2690  iterator p = handle_map.find (old_gh);
2691 
2692  if (p == handle_map.end ())
2693  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2694 
2695  graphics_object go = p->second;
2696 
2697  handle_map.erase (p);
2698 
2699  handle_map[new_gh] = go;
2700 
2701  if (old_gh.value () < 0)
2702  handle_free_list.insert (std::ceil (old_gh.value ())
2703  - make_handle_fraction ());
2704 
2705  for (auto& hfig : figure_list)
2706  {
2707  if (hfig == old_gh)
2708  {
2709  hfig = new_gh;
2710  break;
2711  }
2712  }
2713 }
2714 
2715 gh_manager *gh_manager::instance = nullptr;
2716 
2717 static void
2719  const octave_value& val)
2720 {
2722  go.set (pname, val);
2723 }
2724 
2725 static void
2727 {
2728  if (args.length () > 0)
2729  {
2731  go.set (args);
2732  }
2733 }
2734 
2735 static octave_value
2737 {
2739  return go.get (pname);
2740 }
2741 
2742 static graphics_handle
2743 reparent (const octave_value& ov, const std::string& who,
2744  const std::string& pname, const graphics_handle& new_parent,
2745  bool adopt = true)
2746 {
2748 
2749  double hv = ov.xdouble_value ("%s: %s must be a graphics handle",
2750  who.c_str (), pname.c_str ());
2751 
2752  h = gh_manager::lookup (hv);
2753 
2754  if (! h.ok ())
2755  error ("%s: invalid graphics handle (= %g) for %s",
2756  who.c_str (), hv, pname.c_str ());
2757 
2759 
2760  graphics_handle parent_h = go.get_parent ();
2761 
2762  graphics_object parent_go = gh_manager::get_object (parent_h);
2763 
2764  parent_go.remove_child (h);
2765 
2766  if (adopt)
2767  go.set ("parent", new_parent.value ());
2768  else
2769  go.reparent (new_parent);
2770 
2771  return h;
2772 }
2773 
2774 // This function is NOT equivalent to the scripting language function gcf.
2776 gcf (void)
2777 {
2778  octave_value val = xget (0, "currentfigure");
2779 
2780  return val.isempty () ? octave::numeric_limits<double>::NaN ()
2781  : val.double_value ();
2782 }
2783 
2784 // This function is NOT equivalent to the scripting language function gca.
2786 gca (void)
2787 {
2788  octave_value val = xget (gcf (), "currentaxes");
2789 
2790  return val.isempty () ? octave::numeric_limits<double>::NaN ()
2791  : val.double_value ();
2792 }
2793 
2794 static void
2796 {
2797  if (h.ok ())
2798  {
2800 
2801  // Don't do recursive deleting, due to callbacks
2802  if (! go.get_properties ().is_beingdeleted ())
2803  {
2804  graphics_handle parent_h = go.get_parent ();
2805 
2806  graphics_object parent_go = gh_manager::get_object (parent_h);
2807 
2808  // NOTE: free the handle before removing it from its parent's
2809  // children, such that the object's state is correct when the
2810  // deletefcn callback is executed
2811 
2812  gh_manager::free (h);
2813 
2814  // A callback function might have already deleted the parent
2815  if (parent_go.valid_object ())
2816  parent_go.remove_child (h);
2817 
2818  Vdrawnow_requested = true;
2819  }
2820  }
2821 }
2822 
2823 static void
2825 {
2827 }
2828 
2829 // Flag to stop redraws due to callbacks while deletion is in progress.
2830 static bool delete_executing = false;
2831 
2832 static void
2834 {
2835  // Prevent redraw of partially deleted objects.
2838  delete_executing = true;
2839 
2840  for (octave_idx_type i = 0; i < vals.numel (); i++)
2841  delete_graphics_object (vals.elem (i));
2842 }
2843 
2844 static void
2846 {
2847  octave_value closerequestfcn = xget (h, "closerequestfcn");
2848 
2849  gh_manager::execute_callback (h, closerequestfcn);
2850 }
2851 
2852 static void
2854 {
2855  // Remove the deletefcn and closerequestfcn callbacks
2856  // and delete the object directly.
2857 
2858  xset (h, "deletefcn", Matrix ());
2859  xset (h, "closerequestfcn", Matrix ());
2860 
2862 }
2863 
2864 void
2866 {
2867  // FIXME: should we process or discard pending events?
2868 
2869  event_queue.clear ();
2870 
2871  // Don't use figure_list_iterator because we'll be removing elements
2872  // from the list elsewhere.
2873 
2874  Matrix hlist = do_figure_handle_list (true);
2875 
2876  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2877  {
2879 
2880  if (h.ok ())
2881  close_figure (h);
2882  }
2883 
2884  // They should all be closed now. If not, force them to close.
2885 
2886  hlist = do_figure_handle_list (true);
2887 
2888  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2889  {
2891 
2892  if (h.ok ())
2894  }
2895 
2896  // None left now, right?
2897 
2898  hlist = do_figure_handle_list (true);
2899 
2900  if (hlist.numel () != 0)
2901  warning ("gh_manager::do_close_all_figures: some graphics elements failed to close.");
2902 
2903  // Clear all callback objects from our list.
2904 
2905  callback_objects.clear ();
2906 }
2907 
2908 static void
2909 adopt (const graphics_handle& parent_h, const graphics_handle& h)
2910 {
2911  graphics_object parent_go = gh_manager::get_object (parent_h);
2912  parent_go.adopt (h);
2913 }
2914 
2915 static bool
2917 {
2918  return h.ok ();
2919 }
2920 
2921 static bool
2923 {
2925 
2926  return h.ok ();
2927 }
2928 
2929 static octave_value
2931 {
2932  octave_value retval = false;
2933 
2934  if (val.is_real_scalar () && is_hghandle (val.double_value ()))
2935  retval = true;
2936  else if (val.isnumeric () && val.isreal ())
2937  {
2938  const NDArray handles = val.array_value ();
2939 
2940  boolNDArray result (handles.dims ());
2941 
2942  for (octave_idx_type i = 0; i < handles.numel (); i++)
2943  result.xelem (i) = is_hghandle (handles(i));
2944 
2945  retval = result;
2946  }
2947 
2948  return retval;
2949 }
2950 
2951 static bool
2952 is_figure (double val)
2953 {
2955 
2956  return go && go.isa ("figure");
2957 }
2958 
2959 static void
2961 {
2963  go.get_properties ().execute_createfcn ();
2964 }
2965 
2966 static void
2968 {
2970 
2971  if (go)
2972  go.initialize ();
2973 }
2974 
2975 // ---------------------------------------------------------------------
2976 
2977 void
2979 {
2981 
2982  finalize (go);
2983 }
2984 
2985 static int
2986 toggle_warn (std::string id, bool on, int state = -1)
2987 {
2988  if (! on)
2989  {
2990  state = warning_enabled (id);
2991  disable_warning (id);
2992  }
2993  else
2994  {
2995  if (state == 1)
2996  set_warning_state (id, "on");
2997  else if (state == 2)
2998  set_warning_state (id, "error");
2999  }
3000  return state;
3001 }
3002 
3003 static void
3005  property_list::pval_map_type factory_pval)
3006 {
3008 
3009  // Replace factory defaults by user defined ones
3010  std::string go_name = go.get_properties ().graphics_object_name ();
3012  go.build_user_defaults_map (pval, go_name);
3013 
3014  for (const auto& p : pval)
3015  factory_pval[p.first] = p.second;
3016 
3017  // Save warning state of "Octave:deprecated-property"
3018  int state = toggle_warn ("Octave:deprecated-property", false);
3019 
3020  // Reset defaults
3021  for (const auto& p : factory_pval)
3022  {
3023  std::string pname = p.first;
3024 
3025  // Don't reset internal properties and handle_properties
3026  if (! go.has_readonly_property (pname)
3027  && pname.find ("__") != 0 && pname.find ("current") != 0
3028  && pname != "uicontextmenu" && pname != "parent")
3029  {
3030  // Store *mode prop/val in order to set them last
3031  if (pname.find ("mode") == (pname.length () - 4))
3032  pval[pname] = p.second;
3033  else
3034  go.set (pname, p.second);
3035  }
3036  }
3037 
3038  // set *mode properties
3039  for (const auto& p : pval)
3040  go.set (p.first, p.second);
3041 
3042  toggle_warn ("Octave:deprecated-property", true, state);
3043 }
3044 
3045 // ---------------------------------------------------------------------
3046 
3047 void
3050 {
3051  std::string go_name = graphics_object_name ();
3052 
3053  property_list::plist_map_const_iterator plist = defaults.find (go_name);
3054 
3055  if (plist != defaults.end ())
3056  {
3057  const property_list::pval_map_type pval_map = plist->second;
3058 
3059  for (const auto& prop_val : pval_map)
3060  {
3061  std::string pname = prop_val.first;
3062 
3063  try
3064  {
3065  bgo.set (pname, prop_val.second);
3066  }
3067  catch (octave::execution_exception& e)
3068  {
3069  error (e, "error setting default property %s", pname.c_str ());
3070  }
3071  }
3072  }
3073 }
3074 
3075 /*
3076 ## test defaults are set in the order they were stored
3077 %!test
3078 %! set(0, "defaultfigureunits", "normalized");
3079 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
3080 %! hf = figure ("visible", "off");
3081 %! tol = 20 * eps;
3082 %! unwind_protect
3083 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
3084 %! unwind_protect_cleanup
3085 %! close (hf);
3086 %! set(0, "defaultfigureunits", "remove");
3087 %! set(0, "defaultfigureposition", "remove");
3088 %! end_unwind_protect
3089 */
3090 
3093 {
3094  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
3095  all_props.find (pname);
3096 
3097  if (it == all_props.end ())
3098  error (R"(get: unknown property "%s")", pname.c_str ());
3099 
3100  return it->second.get ();
3101 }
3102 
3105 {
3107 
3108  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
3109  it = all_props.begin (); it != all_props.end (); ++it)
3110  if (all || ! it->second.is_hidden ())
3111  m.assign (it->second.get_name (), it->second.get ());
3112 
3113  return m;
3114 }
3115 
3116 std::set<std::string>
3118 {
3119  return dynamic_properties;
3120 }
3121 
3122 bool
3124 {
3125  const std::set<std::string>& dynprops = dynamic_property_names ();
3126 
3127  if (dynprops.find (pname) != dynprops.end ())
3128  return true;
3129  else
3130  return all_props.find (pname) != all_props.end ();
3131 }
3132 
3133 void
3135  const octave_value& val)
3136 {
3137  std::map<caseless_str, property, cmp_caseless_str>::iterator it =
3138  all_props.find (pname);
3139 
3140  if (it == all_props.end ())
3141  error (R"(set: unknown property "%s")", pname.c_str ());
3142 
3143  it->second.set (val);
3144 
3145  dynamic_properties.insert (pname);
3146 
3147  mark_modified ();
3148 }
3149 
3150 property
3152 {
3153  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
3154  all_props.find (pname);
3155 
3156  if (it == all_props.end ())
3157  error (R"(get_property: unknown property "%s")", pname.c_str ());
3158 
3159  return it->second;
3160 }
3161 
3162 void
3164 {
3165  double hp = val.xdouble_value ("set: parent must be a graphics handle");
3166 
3168 
3169  if (hp == __myhandle__)
3170  error ("set: can not set object parent to be object itself");
3171 
3172  new_parent = gh_manager::lookup (hp);
3173 
3174  if (! new_parent.ok ())
3175  error ("set: invalid graphics handle (= %g) for parent", hp);
3176 
3177  // Remove child from current parent
3178  graphics_object old_parent_go;
3179  old_parent_go = gh_manager::get_object (get_parent ());
3180 
3181  if (old_parent_go.get_handle () != hp)
3182  old_parent_go.remove_child (__myhandle__);
3183  else
3184  return; // Do nothing more
3185 
3186  // Check new parent's parent is not this child to avoid recursion
3187  graphics_object new_parent_go;
3188  new_parent_go = gh_manager::get_object (new_parent);
3189  if (new_parent_go.get_parent () == __myhandle__)
3190  {
3191  // new parent's parent gets child's original parent
3192  new_parent_go.get_properties ().set_parent (get_parent ().as_octave_value ());
3193  }
3194 
3195  // Set parent property to new_parent and do adoption
3196  parent = new_parent.as_octave_value ();
3197  ::adopt (parent.handle_value (), __myhandle__);
3198 }
3199 
3200 /*
3201 %!test
3202 %! hf = figure ("visible", "off");
3203 %! unwind_protect
3204 %! hax = gca ();
3205 %! set (hax, "parent", gcf ());
3206 %! assert (gca (), hax);
3207 %! unwind_protect_cleanup
3208 %! close (hf);
3209 %! end_unwind_protect
3210 */
3211 
3212 void
3214 {
3215  // Mark existing object as modified
3216  __modified__ = "on";
3217  // Attempt to mark parent object as modified if it exists
3219  if (parent_go)
3220  parent_go.mark_modified ();
3221 }
3222 
3223 void
3225 {
3227 
3228  if (parent_go)
3229  parent_go.override_defaults (obj);
3230 }
3231 
3232 void
3234 {
3235  graphics_object go = gh_manager::get_object (__myhandle__);
3236 
3237  if (go)
3238  go.update_axis_limits (axis_type);
3239 }
3240 
3241 void
3243  const graphics_handle& h) const
3244 {
3245  graphics_object go = gh_manager::get_object (__myhandle__);
3246 
3247  if (go)
3248  go.update_axis_limits (axis_type, h);
3249 }
3250 
3251 void
3253 {
3254  if (uicontextmenu.get ().isempty ())
3255  return;
3256 
3258  if (go && go.isa ("uicontextmenu"))
3259  {
3260  uicontextmenu::properties& props =
3261  reinterpret_cast<uicontextmenu::properties&> (go.get_properties ());
3262  props.add_dependent_obj (__myhandle__);
3263  }
3264 }
3265 
3266 bool
3268 {
3269  return (handlevisibility.is ("on")
3270  || (! executing_callbacks.empty () && ! handlevisibility.is ("off")));
3271 }
3272 
3275 {
3277 
3278  if (go)
3279  return go.get_toolkit ();
3280  else
3281  return graphics_toolkit ();
3282 }
3283 
3284 void
3286 {
3287  Matrix kids = get_children ();
3288 
3289  for (int i = 0; i < kids.numel (); i++)
3290  {
3292 
3293  if (go.valid_object ())
3295  }
3296 }
3297 
3298 void
3300 {
3302 
3303  if (parent_go.valid_object ())
3304  parent_go.get_properties ().update_autopos (elem_type);
3305 }
3306 
3307 void
3308 base_properties::update_handlevisibility (void)
3309 {
3310  if (is_handle_visible ())
3311  return;
3312 
3313  // This object should not be the root "callbackobject"
3315  octave_value cbo = rt.get ("callbackobject");
3316  if (! cbo.isempty () && cbo.double_value () == __myhandle__)
3317  {
3318  gh_manager::auto_lock guard;
3319  auto& root_props =
3320  dynamic_cast<root_figure::properties&> (rt.get_properties ());
3321  root_props.set_callbackobject (Matrix ());
3322  }
3323 
3324  // This object should not be the figure "currentobject"
3325  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
3326  graphics_object fig (go.get_ancestor ("figure"));
3327  if (fig.valid_object ())
3328  {
3329  octave_value co = fig.get ("currentobject");
3330  if (! co.isempty () && co.double_value () == __myhandle__)
3331  {
3332  gh_manager::auto_lock guard;
3333  auto& fig_props =
3334  dynamic_cast<figure::properties&> (fig.get_properties ());
3335  fig_props.set_currentobject (Matrix ());
3336  }
3337  }
3338 }
3339 
3340 /*
3341 ## test current figure and current axes have visible handles
3342 %!test
3343 %! hf1 = figure ("visible", "off");
3344 %! hf2 = figure ("visible", "off");
3345 %! hax1 = axes ();
3346 %! hax2 = axes ();
3347 %! unwind_protect
3348 %! assert (get (0, "currentfigure"), hf2);
3349 %! assert (get (hf2, "currentaxes"), hax2);
3350 %! set (hf2, "handlevisibility", "off");
3351 %! assert (get (0, "currentfigure"), hf1);
3352 %! set (hax2, "handlevisibility", "off");
3353 %! assert (get (hf2, "currentaxes"), hax1);
3354 %! assert (get (hf2, "currentobject"), []);
3355 %! unwind_protect_cleanup
3356 %! close ([hf1, hf2]);
3357 %! end_unwind_protect;
3358 */
3359 
3360 /*
3361 ## test current callback object have visible handle
3362 %!test
3363 %! hf = figure ("visible", "off");
3364 %! hax = axes ();
3365 %! unwind_protect
3366 %! fcn = @(h) assert (gcbo (), h);
3367 %! addlistener (hax, "color", fcn);
3368 %! set (hax, "color", "r");
3369 %! dellistener (hax, "color", fcn);
3370 %! set (hax, "handlevisibility", "off");
3371 %! fcn = @() assert (gcbo (), []);
3372 %! addlistener (hax, "color", fcn);
3373 %! set (hax, "color", "b");
3374 %! unwind_protect_cleanup
3375 %! close (hf);
3376 %! end_unwind_protect;
3377 */
3378 
3379 void
3381  const octave_value& val,
3383 {
3384  property p = get_property (pname);
3385 
3386  if (p.ok ())
3387  p.add_listener (val, mode);
3388 }
3389 
3390 void
3392  const octave_value& val,
3394 {
3395  property p = get_property (pname);
3396 
3397  if (p.ok ())
3398  p.delete_listener (val, mode);
3399 }
3400 
3401 // ---------------------------------------------------------------------
3402 
3403 void
3405 {
3406  if (! valid_object ())
3407  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3408 
3410 
3411  if (parent_go)
3412  parent_go.update_axis_limits (axis_type);
3413 }
3414 
3415 void
3417  const graphics_handle& h)
3418 {
3419  if (! valid_object ())
3420  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3421 
3423 
3424  if (parent_go)
3425  parent_go.update_axis_limits (axis_type, h);
3426 }
3427 
3428 void
3430 {
3431  int state = toggle_warn ("Octave:deprecated-property", false);
3432  octave_map m = get (true).map_value ();
3433  toggle_warn ("Octave:deprecated-property", true, state);
3434 
3435  for (const auto& pm : m)
3436  {
3437  // FIXME: there has to be a better way. I think we want to
3438  // ask whether it is OK to delete the listener for the given
3439  // property. How can we know in advance that it will be OK?
3440 
3442 
3446 
3447  discard_error_messages = true;
3448  Vdebug_on_error = false;
3449  Vdebug_on_warning = false;
3450 
3451  try
3452  {
3453  property p = get_properties ().get_property (pm.first);
3454 
3455  if (p.ok ())
3456  p.delete_listener ();
3457  }
3458  catch (const octave::execution_exception&)
3459  {
3461  }
3462  }
3463 }
3464 
3465 void
3467 {
3468  property_list local_defaults = get_defaults_list ();
3469  const auto it = local_defaults.find (go_name);
3470 
3471  if (it != local_defaults.end ())
3472  {
3473  property_list::pval_map_type pval_lst = it->second;
3474  for (const auto& prop_val : pval_lst)
3475  {
3476  std::string pname = prop_val.first;
3477  if (def.find (pname) == def.end ())
3478  def[pname] = prop_val.second;
3479  }
3480  }
3481 
3483 
3484  if (parent_go)
3485  parent_go.build_user_defaults_map (def, go_name);
3486 }
3487 
3488 void
3490 {
3491  if (valid_object ())
3492  {
3493  property_list::pval_map_type factory_pval =
3495  .find (type ())->second;
3496 
3498  xreset_default_properties (get_handle (), factory_pval);
3499  }
3500 }
3501 
3504 {
3505  if (! valid_object ())
3506  error ("base_graphics_object::values_as_string: invalid graphics object");
3507 
3509  octave_map m = get ().map_value ();
3511 
3512  for (const auto& pm : m)
3513  {
3514  const auto& pname = pm.first;
3515  if (pname != "children" && ! go.has_readonly_property (pname))
3516  {
3517  property p = get_properties ().get_property (pname);
3518 
3519  if (p.ok () && ! p.is_hidden ())
3520  {
3521  retval += "\n\t" + std::string (pname) + ": ";
3522  if (p.is_radio ())
3523  retval += p.values_as_string ();
3524  }
3525  }
3526  }
3527 
3528  if (! retval.empty ())
3529  retval += '\n';
3530 
3531  return retval;
3532 }
3533 
3536 {
3538 
3539  if (! valid_object ())
3540  error ("base_graphics_object::value_as_string: invalid graphics object");
3541 
3543 
3544  if (prop != "children" && ! go.has_readonly_property (prop))
3545  {
3546  property p = get_properties ().get_property (prop);
3547 
3548  if (p.ok () && ! p.is_hidden ())
3549  {
3550  if (p.is_radio ())
3551  retval += p.values_as_string ();
3552  }
3553  }
3554 
3555  if (! retval.empty ())
3556  retval += '\n';
3557 
3558  return retval;
3559 }
3560 
3563 {
3565 
3566  if (! valid_object ())
3567  error ("base_graphics_object::values_as_struct: invalid graphics object");
3568 
3569  octave_scalar_map m = get ().scalar_map_value ();
3571 
3572  for (const auto& pm : m)
3573  {
3574  const auto& pname = pm.first;
3575  if (pname != "children" && ! go.has_readonly_property (pname))
3576  {
3577  property p = get_properties ().get_property (pname);
3578 
3579  if (p.ok () && ! p.is_hidden ())
3580  {
3581  if (p.is_radio ())
3582  retval.assign (p.get_name (), p.values_as_cell ());
3583  else
3584  retval.assign (p.get_name (), Cell ());
3585  }
3586  }
3587  }
3588 
3589  return retval;
3590 }
3591 
3592 /*
3593 %!test
3594 %! hfig = figure ("visible", "off");
3595 %! unwind_protect
3596 %! hax = axes ();
3597 %! ret = set (hax, "tightinset");
3598 %! assert (isempty (ret));
3599 %! ret = set (hax, "type");
3600 %! assert (isempty (ret));
3601 %! ret = set (hfig, "tag");
3602 %! assert (isempty (ret));
3603 %! ret = set (0, "commandwindowsize");
3604 %! assert (isempty (ret));
3605 %! ret = set (0);
3606 %! assert (! isfield (ret, "commandwindowsize"));
3607 %! unwind_protect_cleanup
3608 %! close (hfig);
3609 %! end_unwind_protect
3610 */
3611 
3614 {
3615  if (valid_object ())
3616  {
3617  if (isa (obj_type))
3618  return *this;
3619  else
3620  return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
3621  }
3622  else
3623  return graphics_object ();
3624 }
3625 
3626 // ---------------------------------------------------------------------
3627 
3628 #include "graphics-props.cc"
3629 
3630 // ---------------------------------------------------------------------
3631 
3632 void
3633 root_figure::properties::set_callbackobject (const octave_value& v)
3634 {
3635  graphics_handle val (v);
3636 
3637  if (octave::math::isnan (val.value ()))
3638  {
3639  if (! cbo_stack.empty ())
3640  {
3641  val = cbo_stack.front ();
3642 
3643  cbo_stack.pop_front ();
3644  }
3645 
3646  callbackobject = val;
3647  }
3648  else if (is_hghandle (val))
3649  {
3650  if (get_callbackobject ().ok ())
3651  cbo_stack.push_front (get_callbackobject ());
3652 
3653  callbackobject = val;
3654  }
3655  else
3656  err_set_invalid ("callbackobject");
3657 }
3658 
3659 void
3660 root_figure::properties::set_currentfigure (const octave_value& v)
3661 {
3662  graphics_handle val (v);
3663 
3664  if (octave::math::isnan (val.value ()) || is_hghandle (val))
3665  {
3666  currentfigure = val;
3667 
3668  if (val.ok ())
3670  }
3671  else
3672  err_set_invalid ("currentfigure");
3673 }
3674 
3675 void
3676 figure::properties::set_integerhandle (const octave_value& val)
3677 {
3678  if (integerhandle.set (val, true))
3679  {
3680  bool int_fig_handle = integerhandle.is_on ();
3681 
3682  graphics_object this_go = gh_manager::get_object (__myhandle__);
3683 
3684  graphics_handle old_myhandle = __myhandle__;
3685 
3686  __myhandle__ = gh_manager::get_handle (int_fig_handle);
3687 
3688  gh_manager::renumber_figure (old_myhandle, __myhandle__);
3689 
3691 
3692  base_properties& props = parent_go.get_properties ();
3693 
3694  props.renumber_child (old_myhandle, __myhandle__);
3695 
3696  Matrix kids = get_children ();
3697 
3698  for (octave_idx_type i = 0; i < kids.numel (); i++)
3699  {
3701 
3702  kid.get_properties ().renumber_parent (__myhandle__);
3703  }
3704 
3706 
3707  if (__myhandle__ == cf)
3708  xset (0, "currentfigure", __myhandle__.value ());
3709 
3710  this_go.update (integerhandle.get_id ());
3711 
3712  mark_modified ();
3713  }
3714 }
3715 
3716 // FIXME: This should update monitorpositions and pointerlocation, but as these
3717 // properties aren't yet used, it doesn't matter that they aren't set either.
3718 void
3719 root_figure::properties::update_units (void)
3720 {
3721  std::string xunits = get_units ();
3722 
3723  Matrix scrn_sz = default_screensize ();
3724 
3725  double dpi = get_screenpixelsperinch ();
3726 
3727  if (xunits == "inches")
3728  {
3729  scrn_sz(0) = 0;
3730  scrn_sz(1) = 0;
3731  scrn_sz(2) /= dpi;
3732  scrn_sz(3) /= dpi;
3733  }
3734  else if (xunits == "centimeters")
3735  {
3736  scrn_sz(0) = 0;
3737  scrn_sz(1) = 0;
3738  scrn_sz(2) *= 2.54 / dpi;
3739  scrn_sz(3) *= 2.54 / dpi;
3740  }
3741  else if (xunits == "normalized")
3742  {
3743  scrn_sz = Matrix (1, 4, 1.0);
3744  scrn_sz(0) = 0;
3745  scrn_sz(1) = 0;
3746  }
3747  else if (xunits == "points")
3748  {
3749  scrn_sz(0) = 0;
3750  scrn_sz(1) = 0;
3751  scrn_sz(2) *= 72 / dpi;
3752  scrn_sz(3) *= 72 / dpi;
3753  }
3754 
3755  set_screensize (scrn_sz);
3756 }
3757 
3758 Matrix
3760 {
3761  Matrix screen_size = screen_size_pixels ();
3762  Matrix pos = Matrix (1, 4, 0.0);
3763 
3764  pos(2) = screen_size(0);
3765  pos(3) = screen_size(1);
3766 
3767  return pos;
3768 }
3769 
3770 /*
3771 %!test
3772 %! old_units = get (0, "units");
3773 %! unwind_protect
3774 %! set (0, "units", "pixels");
3775 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3776 %! dpi = get (0, "screenpixelsperinch");
3777 %! set (0, "units", "inches");
3778 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3779 %! set (0, "units", "centimeters");
3780 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3781 %! set (0, "units", "points");
3782 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3783 %! set (0, "units", "normalized");
3784 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3785 %! set (0, "units", "pixels");
3786 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3787 %! unwind_protect_cleanup
3788 %! set (0, "units", old_units);
3789 %! end_unwind_protect
3790 */
3791 
3792 void
3794 {
3796 
3798 
3799  xset (0, "currentfigure", cf.value ());
3800 
3802 }
3803 
3804 void
3806 {
3807  // empty list of local defaults
3809 
3813 }
3814 
3815 // ---------------------------------------------------------------------
3816 
3817 void
3818 figure::properties::set_currentaxes (const octave_value& val)
3819 {
3820  graphics_handle hax (val);
3821 
3822  if (octave::math::isnan (hax.value ()) || is_hghandle (hax))
3823  currentaxes = hax;
3824  else
3825  err_set_invalid ("currentaxes");
3826 }
3827 
3828 void
3830 {
3832 
3833  if (h == currentaxes.handle_value ())
3834  {
3835  graphics_handle new_currentaxes;
3836 
3837  Matrix kids = get_children ();
3838 
3839  for (octave_idx_type i = 0; i < kids.numel (); i++)
3840  {
3841  graphics_handle kid = kids(i);
3842 
3844 
3845  if (go.isa ("axes"))
3846  {
3847  new_currentaxes = kid;
3848  break;
3849  }
3850  }
3851 
3852  currentaxes = new_currentaxes;
3853  }
3854 }
3855 
3858 {
3859  if (! toolkit)
3860  {
3861  octave::gtk_manager& gtk_mgr
3862  = octave::__get_gtk_manager__ ("figure::properties::get_toolkit");
3863 
3864  toolkit = gtk_mgr.get_toolkit ();
3865  }
3866 
3867  return toolkit;
3868 }
3869 
3870 void
3872 {
3873  if (! val.is_string ())
3874  error ("set___graphics_toolkit__ must be a string");
3875 
3876  std::string nm = val.string_value ();
3877 
3878  octave::gtk_manager& gtk_mgr
3879  = octave::__get_gtk_manager__ ("figure::properties::set___graphics_toolkit__");
3880 
3881  graphics_toolkit b = gtk_mgr.find_toolkit (nm);
3882 
3883  if (b.get_name () != nm)
3884  error ("set___graphics_toolkit__: invalid graphics toolkit");
3885 
3886  if (nm != get___graphics_toolkit__ ())
3887  {
3888  set_toolkit (b);
3889  mark_modified ();
3890  }
3891 }
3892 
3893 void
3895 {
3897 
3898  if (! get_currentaxes ().ok ())
3899  {
3901 
3902  if (go.type () == "axes")
3903  set_currentaxes (h.as_octave_value ());
3904  }
3905 }
3906 
3907 /*
3908 %!test
3909 %! hf1 = figure ("visible", "off");
3910 %! ax1 = subplot (1,2,1);
3911 %! ax2 = subplot (1,2,2);
3912 %! hf2 = figure ("visible", "off");
3913 %! unwind_protect
3914 %! set (ax2, "parent", hf2);
3915 %! assert (get (hf2, "currentaxes"), ax2);
3916 %! assert (get (hf1, "currentaxes"), ax1);
3917 %! set (ax1, "parent", hf2);
3918 %! assert (get (hf2, "currentaxes"), ax2);
3919 %! unwind_protect_cleanup
3920 %! close (hf1);
3921 %! close (hf2);
3922 %! end_unwind_protect
3923 */
3924 
3925 void
3927 {
3928  std::string sval = val.string_value ();
3929 
3930  if (sval == "on")
3931  xset (0, "currentfigure", __myhandle__.value ());
3932 
3933  visible = val;
3934 }
3935 
3936 Matrix
3937 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3938 {
3939  Matrix screen_size = screen_size_pixels ();
3940  Matrix pos = (internal ?
3941  get_position ().matrix_value () :
3942  get_outerposition ().matrix_value ());
3943 
3944  pos = convert_position (pos, get_units (), "pixels", screen_size);
3945 
3946  pos(0)--;
3947  pos(1)--;
3948  pos(1) = screen_size(1) - pos(1) - pos(3);
3949 
3950  return pos;
3951 }
3952 
3953 void
3955  bool do_notify_toolkit)
3956 {
3957  Matrix screen_size = screen_size_pixels ();
3958  Matrix pos = bb;
3959 
3960  pos(1) = screen_size(1) - pos(1) - pos(3);
3961  pos(1)++;
3962  pos(0)++;
3963  pos = convert_position (pos, "pixels", get_units (), screen_size);
3964 
3965  if (internal)
3966  set_position (pos, do_notify_toolkit);
3967  else
3968  set_outerposition (pos, do_notify_toolkit);
3969 }
3970 
3971 Matrix
3973 {
3974  Matrix bb = get_boundingbox (true);
3975  Matrix pos (1, 2, 0.0);
3976 
3977  pos(0) = x;
3978  pos(1) = y;
3979 
3980  pos(1) = bb(3) - pos(1);
3981  pos(0)++;
3982  pos = convert_position (pos, "pixels", get_units (),
3983  bb.extract_n (0, 2, 1, 2));
3984 
3985  return pos;
3986 }
3987 
3988 Matrix
3990 {
3991  Matrix bb = get_boundingbox (true);
3992  Matrix pos (1, 2, 0.0);
3993 
3994  pos(0) = x;
3995  pos(1) = y;
3996 
3997  pos = convert_position (pos, get_units (), "pixels",
3998  bb.extract_n (0, 2, 1, 2));
3999  pos(0)--;
4000  pos(1) = bb(3) - pos(1);
4001 
4002  return pos;
4003 }
4004 
4005 void
4007  bool do_notify_toolkit)
4008 {
4009  Matrix old_bb, new_bb;
4010  bool modified = false;
4011 
4012  old_bb = get_boundingbox (true);
4013  modified = position.set (v, false, do_notify_toolkit);
4014  new_bb = get_boundingbox (true);
4015 
4016  if (old_bb != new_bb)
4017  {
4018  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
4019  {
4020  execute_resizefcn ();
4021  update_boundingbox ();
4022  }
4023  }
4024 
4025  if (modified)
4026  {
4027  position.run_listeners (POSTSET);
4028  mark_modified ();
4029  }
4030 
4031  if (paperpositionmode.is ("auto"))
4032  paperposition.set (get_auto_paperposition ());
4033 }
4034 
4035 void
4037  bool do_notify_toolkit)
4038 {
4039  if (outerposition.set (v, true, do_notify_toolkit))
4040  mark_modified ();
4041 }
4042 
4043 void
4044 figure::properties::set_paperunits (const octave_value& val)
4045 {
4046  caseless_str punits = val.string_value ();
4047  caseless_str ptype = get_papertype ();
4048 
4049  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4050  error ("set: can't set paperunits to normalized when papertype is custom");
4051 
4052  caseless_str old_paperunits = get_paperunits ();
4053  if (paperunits.set (val, true))
4054  {
4055  update_paperunits (old_paperunits);
4056  mark_modified ();
4057  }
4058 }
4059 
4060 void
4061 figure::properties::set_papertype (const octave_value& val)
4062 {
4063  caseless_str ptype = val.string_value ();
4064  caseless_str punits = get_paperunits ();
4065 
4066  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4067  error ("set: can't set paperunits to normalized when papertype is custom");
4068 
4069  if (papertype.set (val, true))
4070  {
4071  update_papertype ();
4072  mark_modified ();
4073  }
4074 }
4075 
4076 static Matrix
4077 papersize_from_type (const caseless_str punits, const caseless_str ptype)
4078 {
4079  Matrix retval (1, 2, 1.0);
4080 
4081  if (! punits.compare ("normalized"))
4082  {
4083  double in2units;
4084  double mm2units;
4085 
4086  if (punits.compare ("inches"))
4087  {
4088  in2units = 1.0;
4089  mm2units = 1 / 25.4;
4090  }
4091  else if (punits.compare ("centimeters"))
4092  {
4093  in2units = 2.54;
4094  mm2units = 1 / 10.0;
4095  }
4096  else // points
4097  {
4098  in2units = 72.0;
4099  mm2units = 72.0 / 25.4;
4100  }
4101 
4102  if (ptype.compare ("usletter"))
4103  {
4104  retval(0) = 8.5 * in2units;
4105  retval(1) = 11.0 * in2units;
4106  }
4107  else if (ptype.compare ("uslegal"))
4108  {
4109  retval(0) = 8.5 * in2units;
4110  retval(1) = 14.0 * in2units;
4111  }
4112  else if (ptype.compare ("tabloid"))
4113  {
4114  retval(0) = 11.0 * in2units;
4115  retval(1) = 17.0 * in2units;
4116  }
4117  else if (ptype.compare ("a0"))
4118  {
4119  retval(0) = 841.0 * mm2units;
4120  retval(1) = 1189.0 * mm2units;
4121  }
4122  else if (ptype.compare ("a1"))
4123  {
4124  retval(0) = 594.0 * mm2units;
4125  retval(1) = 841.0 * mm2units;
4126  }
4127  else if (ptype.compare ("a2"))
4128  {
4129  retval(0) = 420.0 * mm2units;
4130  retval(1) = 594.0 * mm2units;
4131  }
4132  else if (ptype.compare ("a3"))
4133  {
4134  retval(0) = 297.0 * mm2units;
4135  retval(1) = 420.0 * mm2units;
4136  }
4137  else if (ptype.compare ("a4"))
4138  {
4139  retval(0) = 210.0 * mm2units;
4140  retval(1) = 297.0 * mm2units;
4141  }
4142  else if (ptype.compare ("a5"))
4143  {
4144  retval(0) = 148.0 * mm2units;
4145  retval(1) = 210.0 * mm2units;
4146  }
4147  else if (ptype.compare ("b0"))
4148  {
4149  retval(0) = 1029.0 * mm2units;
4150  retval(1) = 1456.0 * mm2units;
4151  }
4152  else if (ptype.compare ("b1"))
4153  {
4154  retval(0) = 728.0 * mm2units;
4155  retval(1) = 1028.0 * mm2units;
4156  }
4157  else if (ptype.compare ("b2"))
4158  {
4159  retval(0) = 514.0 * mm2units;
4160  retval(1) = 728.0 * mm2units;
4161  }
4162  else if (ptype.compare ("b3"))
4163  {
4164  retval(0) = 364.0 * mm2units;
4165  retval(1) = 514.0 * mm2units;
4166  }
4167  else if (ptype.compare ("b4"))
4168  {
4169  retval(0) = 257.0 * mm2units;
4170  retval(1) = 364.0 * mm2units;
4171  }
4172  else if (ptype.compare ("b5"))
4173  {
4174  retval(0) = 182.0 * mm2units;
4175  retval(1) = 257.0 * mm2units;
4176  }
4177  else if (ptype.compare ("arch-a"))
4178  {
4179  retval(0) = 9.0 * in2units;
4180  retval(1) = 12.0 * in2units;
4181  }
4182  else if (ptype.compare ("arch-b"))
4183  {
4184  retval(0) = 12.0 * in2units;
4185  retval(1) = 18.0 * in2units;
4186  }
4187  else if (ptype.compare ("arch-c"))
4188  {
4189  retval(0) = 18.0 * in2units;
4190  retval(1) = 24.0 * in2units;
4191  }
4192  else if (ptype.compare ("arch-d"))
4193  {
4194  retval(0) = 24.0 * in2units;
4195  retval(1) = 36.0 * in2units;
4196  }
4197  else if (ptype.compare ("arch-e"))
4198  {
4199  retval(0) = 36.0 * in2units;
4200  retval(1) = 48.0 * in2units;
4201  }
4202  else if (ptype.compare ("a"))
4203  {
4204  retval(0) = 8.5 * in2units;
4205  retval(1) = 11.0 * in2units;
4206  }
4207  else if (ptype.compare ("b"))
4208  {
4209  retval(0) = 11.0 * in2units;
4210  retval(1) = 17.0 * in2units;
4211  }
4212  else if (ptype.compare ("c"))
4213  {
4214  retval(0) = 17.0 * in2units;
4215  retval(1) = 22.0 * in2units;
4216  }
4217  else if (ptype.compare ("d"))
4218  {
4219  retval(0) = 22.0 * in2units;
4220  retval(1) = 34.0 * in2units;
4221  }
4222  else if (ptype.compare ("e"))
4223  {
4224  retval(0) = 34.0 * in2units;
4225  retval(1) = 43.0 * in2units;
4226  }
4227  }
4228 
4229  return retval;
4230 }
4231 
4232 Matrix
4233 figure::properties::get_auto_paperposition (void)
4234 {
4235  Matrix pos = get_position ().matrix_value ();
4236  Matrix sz;
4237 
4238  caseless_str funits = get_units ();
4239  caseless_str punits = get_paperunits ();
4240 
4241  // Convert position from figure units to paperunits
4242  if (funits == "normalized" || punits == "normalized")
4243  {
4244  sz = screen_size_pixels ();
4245  pos = convert_position (pos, funits, "inches", sz);
4246 
4247  if (punits == "normalized")
4248  sz = papersize_from_type ("points", get_papertype ());
4249 
4250  pos = convert_position (pos, "inches", punits, sz);
4251  }
4252  else
4253  pos = convert_position (pos, funits, punits, sz);
4254 
4255  // Center the figure on the page
4256  sz = get_papersize ().matrix_value ();
4257 
4258  pos(0) = sz(0)/2 - pos(2)/2;
4259  pos(1) = sz(1)/2 - pos(3)/2;
4260 
4261  return pos;
4262 }
4263 
4264 /*
4265 %!test
4266 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4267 %! in_pos = [0 0 4 5];
4268 %! tol = 20 * eps ();
4269 %! unwind_protect
4270 %! ## paperpositionmode "auto" converts figure size to paper units
4271 %! set (hf, "units", "inches");
4272 %! set (hf, "position", in_pos);
4273 %! set (hf, "paperunits", "centimeters");
4274 %! psz = get (hf, "papersize");
4275 %! fsz = in_pos(3:4) * 2.54;
4276 %! pos = [(psz/2 .- fsz/2) fsz];
4277 %! set (hf, "paperpositionmode", "auto");
4278 %! assert (get (hf, "paperposition"), pos, tol);
4279 %! unwind_protect_cleanup
4280 %! close (hf);
4281 %! end_unwind_protect
4282 
4283 %!test
4284 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4285 %! in_pos = [0 0 4 5];
4286 %! tol = 20 * eps ();
4287 %! unwind_protect
4288 %! ## likewise with normalized units
4289 %! set (hf, "units", "inches");
4290 %! set (hf, "position", in_pos);
4291 %! psz = get (hf, "papersize");
4292 %! set (hf, "paperunits", "normalized");
4293 %! fsz = in_pos(3:4) ./ psz;
4294 %! pos = [([0.5 0.5] .- fsz/2) fsz];
4295 %! assert (get (hf, "paperposition"), pos, tol);
4296 %! unwind_protect_cleanup
4297 %! close (hf);
4298 %! end_unwind_protect
4299 
4300 %!test
4301 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4302 %! in_pos = [0 0 4 5];
4303 %! tol = 20 * eps ();
4304 %! unwind_protect
4305 %! ## changing papertype updates paperposition
4306 %! set (hf, "units", "inches");
4307 %! set (hf, "position", in_pos);
4308 %! set (hf, "papertype", "a4");
4309 %! psz = get (hf, "papersize");
4310 %! fsz = in_pos(3:4);
4311 %! pos = [(psz/2 .- fsz/2) fsz];
4312 %! assert (get (hf, "paperposition"), pos, tol);
4313 %! unwind_protect_cleanup
4314 %! close (hf);
4315 %! end_unwind_protect
4316 
4317 %!test
4318 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4319 %! in_pos = [0 0 4 5];
4320 %! tol = 20 * eps ();
4321 %! unwind_protect
4322 %! ## lanscape updates paperposition
4323 %! set (hf, "units", "inches");
4324 %! set (hf, "position", in_pos);
4325 %! set (hf, "paperorientation", "landscape");
4326 %! psz = get (hf, "papersize");
4327 %! fsz = in_pos(3:4);
4328 %! pos = [(psz/2 .- fsz/2) fsz];
4329 %! assert (get (hf, "paperposition"), pos, tol);
4330 %! unwind_protect_cleanup
4331 %! close (hf);
4332 %! end_unwind_protect
4333 
4334 %!test
4335 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4336 %! in_pos = [0 0 4 5];
4337 %! unwind_protect
4338 %! ## back to manual mode
4339 %! set (hf, "paperposition", in_pos * 1.1);
4340 %! assert (get (hf, "paperpositionmode"), "manual");
4341 %! assert (get (hf, "paperposition"), in_pos * 1.1);
4342 %! unwind_protect_cleanup
4343 %! close (hf);
4344 %! end_unwind_protect
4345 */
4346 
4347 void
4349 {
4350  Matrix pos = get_paperposition ().matrix_value ();
4351  Matrix sz = get_papersize ().matrix_value ();
4352 
4353  pos(0) /= sz(0);
4354  pos(1) /= sz(1);
4355  pos(2) /= sz(0);
4356  pos(3) /= sz(1);
4357 
4358  std::string porient = get_paperorientation ();
4359  caseless_str punits = get_paperunits ();
4360  caseless_str ptype = get_papertype ();
4361 
4362  if (ptype.compare ("<custom>"))
4363  {
4364  if (old_paperunits.compare ("centimeters"))
4365  {
4366  sz(0) /= 2.54;
4367  sz(1) /= 2.54;
4368  }
4369  else if (old_paperunits.compare ("points"))
4370  {
4371  sz(0) /= 72.0;
4372  sz(1) /= 72.0;
4373  }
4374 
4375  if (punits.compare ("centimeters"))
4376  {
4377  sz(0) *= 2.54;
4378  sz(1) *= 2.54;
4379  }
4380  else if (punits.compare ("points"))
4381  {
4382  sz(0) *= 72.0;
4383  sz(1) *= 72.0;
4384  }
4385  }
4386  else
4387  {
4388  sz = papersize_from_type (punits, ptype);
4389  if (porient == "landscape")
4390  std::swap (sz(0), sz(1));
4391  }
4392 
4393  pos(0) *= sz(0);
4394  pos(1) *= sz(1);
4395  pos(2) *= sz(0);
4396  pos(3) *= sz(1);
4397 
4398  papersize.set (octave_value (sz));
4399  paperposition.set (octave_value (pos));
4400 }
4401 
4402 void
4403 figure::properties::update_papertype (void)
4404 {
4405  std::string typ = get_papertype ();
4406  if (typ != "<custom>")
4407  {
4408  Matrix sz = papersize_from_type (get_paperunits (), typ);
4409  if (get_paperorientation () == "landscape")
4410  std::swap (sz(0), sz(1));
4411  // Call papersize.set rather than set_papersize to avoid loops
4412  // between update_papersize and update_papertype.
4413  papersize.set (octave_value (sz));
4414  }
4415 
4416  if (paperpositionmode.is ("auto"))
4417  paperposition.set (get_auto_paperposition ());
4418 }
4419 
4420 void
4421 figure::properties::update_papersize (void)
4422 {
4423  Matrix sz = get_papersize ().matrix_value ();
4424  if (sz(0) > sz(1))
4425  {
4426  std::swap (sz(0), sz(1));
4427  papersize.set (octave_value (sz));
4428  paperorientation.set (octave_value ("landscape"));
4429  }
4430  else
4431  {
4432  paperorientation.set ("portrait");
4433  }
4434 
4435  std::string punits = get_paperunits ();
4436  if (punits == "centimeters")
4437  {
4438  sz(0) /= 2.54;
4439  sz(1) /= 2.54;
4440  }
4441  else if (punits == "points")
4442  {
4443  sz(0) /= 72.0;
4444  sz(1) /= 72.0;
4445  }
4446  if (punits == "normalized")
4447  {
4448  if (get_papertype () == "<custom>")
4449  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4450  }
4451  else
4452  {
4453  // FIXME: The papersizes info is also in papersize_from_type().
4454  // Both should be rewritten to avoid the duplication.
4455  // Don't Repeat Yourself (DRY) principle.
4456  std::string ptype = "<custom>";
4457  const double mm2in = 1.0 / 25.4;
4458  const double tol = 0.01;
4459 
4460  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4461  ptype = "usletter";
4462  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4463  ptype = "uslegal";
4464  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4465  ptype = "tabloid";
4466  else if (std::abs (sz(0) - 841.0 * mm2in)
4467  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4468  ptype = "a0";
4469  else if (std::abs (sz(0) - 594.0 * mm2in)
4470  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4471  ptype = "a1";
4472  else if (std::abs (sz(0) - 420.0 * mm2in)
4473  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4474  ptype = "a2";
4475  else if (std::abs (sz(0) - 297.0 * mm2in)
4476  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4477  ptype = "a3";
4478  else if (std::abs (sz(0) - 210.0 * mm2in)
4479  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4480  ptype = "a4";
4481  else if (std::abs (sz(0) - 148.0 * mm2in)
4482  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4483  ptype = "a5";
4484  else if (std::abs (sz(0) - 1029.0 * mm2in)
4485  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4486  ptype = "b0";
4487  else if (std::abs (sz(0) - 728.0 * mm2in)
4488  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4489  ptype = "b1";
4490  else if (std::abs (sz(0) - 514.0 * mm2in)
4491  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4492  ptype = "b2";
4493  else if (std::abs (sz(0) - 364.0 * mm2in)
4494  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4495  ptype = "b3";
4496  else if (std::abs (sz(0) - 257.0 * mm2in)
4497  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4498  ptype = "b4";
4499  else if (std::abs (sz(0) - 182.0 * mm2in)
4500  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4501  ptype = "b5";
4502  else if (std::abs (sz(0) - 9.0)
4503  + std::abs (sz(1) - 12.0) < tol)
4504  ptype = "arch-a";
4505  else if (std::abs (sz(0) - 12.0)
4506  + std::abs (sz(1) - 18.0) < tol)
4507  ptype = "arch-b";
4508  else if (std::abs (sz(0) - 18.0)
4509  + std::abs (sz(1) - 24.0) < tol)
4510  ptype = "arch-c";
4511  else if (std::abs (sz(0) - 24.0)
4512  + std::abs (sz(1) - 36.0) < tol)
4513  ptype = "arch-d";
4514  else if (std::abs (sz(0) - 36.0)
4515  + std::abs (sz(1) - 48.0) < tol)
4516  ptype = "arch-e";
4517  else if (std::abs (sz(0) - 8.5)
4518  + std::abs (sz(1) - 11.0) < tol)
4519  ptype = "a";
4520  else if (std::abs (sz(0) - 11.0)
4521  + std::abs (sz(1) - 17.0) < tol)
4522  ptype = "b";
4523  else if (std::abs (sz(0) - 17.0)
4524  + std::abs (sz(1) - 22.0) < tol)
4525  ptype = "c";
4526  else if (std::abs (sz(0) - 22.0)
4527  + std::abs (sz(1) - 34.0) < tol)
4528  ptype = "d";
4529  else if (std::abs (sz(0) - 34.0)
4530  + std::abs (sz(1) - 43.0) < tol)
4531  ptype = "e";
4532  // Call papertype.set rather than set_papertype to avoid loops between
4533  // update_papersize and update_papertype
4534  papertype.set (ptype);
4535  }
4536  if (punits == "centimeters")
4537  {
4538  sz(0) *= 2.54;
4539  sz(1) *= 2.54;
4540  }
4541  else if (punits == "points")
4542  {
4543  sz(0) *= 72.0;
4544  sz(1) *= 72.0;
4545  }
4546  if (get_paperorientation () == "landscape")
4547  {
4548  std::swap (sz(0), sz(1));
4549  papersize.set (octave_value (sz));
4550  }
4551 
4552  if (paperpositionmode.is ("auto"))
4553  paperposition.set (get_auto_paperposition ());
4554 }
4555 
4556 /*
4557 %!test
4558 %! hf = figure ("visible", "off");
4559 %! unwind_protect
4560 %! set (hf, "paperunits", "inches");
4561 %! set (hf, "papersize", [5, 4]);
4562 %! set (hf, "paperunits", "points");
4563 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4564 %! papersize = get (hf, "papersize");
4565 %! set (hf, "papersize", papersize + 1);
4566 %! set (hf, "papersize", papersize);
4567 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4568 %! unwind_protect_cleanup
4569 %! close (hf);
4570 %! end_unwind_protect
4571 
4572 %!test
4573 %! hf = figure ("visible", "off");
4574 %! unwind_protect
4575 %! set (hf, "paperunits", "inches");
4576 %! set (hf, "papersize", [5, 4]);
4577 %! set (hf, "paperunits", "centimeters");
4578 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4579 %! papersize = get (hf, "papersize");
4580 %! set (hf, "papersize", papersize + 1);
4581 %! set (hf, "papersize", papersize);
4582 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4583 %! unwind_protect_cleanup
4584 %! close (hf);
4585 %! end_unwind_protect
4586 */
4587 
4588 void
4589 figure::properties::update_paperorientation (void)
4590 {
4591  std::string porient = get_paperorientation ();
4592  Matrix sz = get_papersize ().matrix_value ();
4593  if ((sz(0) > sz(1) && porient == "portrait")
4594  || (sz(0) < sz(1) && porient == "landscape"))
4595  {
4596  std::swap (sz(0), sz(1));
4597  // Call papertype.set rather than set_papertype to avoid loops
4598  // between update_papersize and update_papertype
4599  papersize.set (octave_value (sz));
4600  }
4601 
4602  if (paperpositionmode.is ("auto"))
4603  paperposition.set (get_auto_paperposition ());
4604 }
4605 
4606 /*
4607 %!test
4608 %! hf = figure ("visible", "off");
4609 %! unwind_protect
4610 %! tol = 100 * eps ();
4611 %! ## UPPER case and MiXed case is part of test and should not be changed.
4612 %! set (hf, "paperorientation", "PORTRAIT");
4613 %! set (hf, "paperunits", "inches");
4614 %! set (hf, "papertype", "USletter");
4615 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4616 %! set (hf, "paperorientation", "Landscape");
4617 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4618 %! set (hf, "paperunits", "centimeters");
4619 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4620 %! set (hf, "papertype", "a4");
4621 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4622 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4623 %! assert (get (hf, "papertype"), "usletter");
4624 %! assert (get (hf, "paperorientation"), "portrait");
4625 %! set (hf, "papersize", [11.0, 8.5]);
4626 %! assert (get (hf, "papertype"), "usletter");
4627 %! assert (get (hf, "paperorientation"), "landscape");
4628 %! unwind_protect_cleanup
4629 %! close (hf);
4630 %! end_unwind_protect
4631 */
4632 
4633 void
4634 figure::properties::set_units (const octave_value& val)
4635 {
4636  caseless_str old_units = get_units ();
4637 
4638  if (units.set (val, true))
4639  {
4640  update_units (old_units);
4641  mark_modified ();
4642  }
4643 }
4644 
4645 void
4647 {
4648  position.set (convert_position (get_position ().matrix_value (), old_units,
4649  get_units (), screen_size_pixels ()), false);
4650 }
4651 
4652 /*
4653 %!test
4654 %! hf = figure ("visible", "off");
4655 %! old_units = get (0, "units");
4656 %! unwind_protect
4657 %! set (0, "units", "pixels");
4658 %! rsz = get (0, "screensize");
4659 %! set (gcf (), "units", "pixels");
4660 %! fsz = get (gcf (), "position");
4661 %! set (gcf (), "units", "normalized");
4662 %! pos = get (gcf (), "position");
4663 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
4664 %! unwind_protect_cleanup
4665 %! close (hf);
4666 %! set (0, "units", old_units);
4667 %! end_unwind_protect
4668 */
4669 
4672 {
4673  if (is_numbertitle ())
4674  {
4675  std::ostringstream os;
4676  std::string nm = get_name ();
4677 
4678  os << "Figure " << __myhandle__.value ();
4679  if (! nm.empty ())
4680  os << ": " << get_name ();
4681 
4682  return os.str ();
4683  }
4684  else
4685  return get_name ();
4686 }
4687 
4690 {
4692 
4693  if (retval.is_undefined ())
4694  {
4695  graphics_handle parent_h = get_parent ();
4696  graphics_object parent_go = gh_manager::get_object (parent_h);
4697 
4698  retval = parent_go.get_default (name);
4699  }
4700 
4701  return retval;
4702 }
4703 
4704 void
4706 {
4707  // empty list of local defaults
4710 
4711  plist.erase ("units");
4712  plist.erase ("position");
4713  plist.erase ("outerposition");
4714  plist.erase ("paperunits");
4715  plist.erase ("paperposition");
4716  plist.erase ("windowstyle");
4717 
4720 }
4721 
4722 // ---------------------------------------------------------------------
4723 
4724 void
4725 axes::properties::init (void)
4726 {
4727  position.add_constraint (dim_vector (1, 4));
4728  outerposition.add_constraint (dim_vector (1, 4));
4729  tightinset.add_constraint (dim_vector (1, 4));
4730  looseinset.add_constraint (dim_vector (1, 4));
4731  colororder.add_constraint (dim_vector (-1, 3));
4732  dataaspectratio.add_constraint (3);
4733  dataaspectratio.add_constraint ("min", 0, false);
4734  dataaspectratio.add_constraint (FINITE);
4735  plotboxaspectratio.add_constraint (3);
4736  plotboxaspectratio.add_constraint ("min", 0, false);
4737  plotboxaspectratio.add_constraint (FINITE);
4738  // FIXME: Should these use dimension vectors? Currently can set 'xlim' to
4739  // any matrix size, but only first two elements are used.
4740  alim.add_constraint (2);
4741  alim.add_constraint (NOT_NAN);
4742  clim.add_constraint (2);
4743  clim.add_constraint (NOT_NAN);
4744  xlim.add_constraint (2);
4745  xlim.add_constraint (NOT_NAN);
4746  ylim.add_constraint (2);
4747  ylim.add_constraint (NOT_NAN);
4748  zlim.add_constraint (2);
4749  zlim.add_constraint (NOT_NAN);
4750  xtick.add_constraint (dim_vector (1, -1));
4751  xtick.add_constraint (FINITE);
4752  ytick.add_constraint (dim_vector (1, -1));
4753  ytick.add_constraint (FINITE);
4754  ztick.add_constraint (dim_vector (1, -1));
4755  ztick.add_constraint (FINITE);
4756  ticklength.add_constraint (dim_vector (1, 2));
4757  Matrix vw (1, 2, 0);
4758  vw(1) = 90;
4759  view = vw;
4760  view.add_constraint (dim_vector (1, 2));
4761  cameraposition.add_constraint (3);
4762  cameraposition.add_constraint (FINITE);
4763  cameratarget.add_constraint (3);
4764  cameratarget.add_constraint (FINITE);
4765  Matrix upv (1, 3, 0.0);
4766  upv(2) = 1.0;
4767  cameraupvector = upv;
4768  cameraupvector.add_constraint (3);
4769  cameraupvector.add_constraint (FINITE);
4770  cameraviewangle.add_constraint (FINITE);
4771  currentpoint.add_constraint (dim_vector (2, 3));
4772 
4773  // Range constraints for double properties
4774  fontsize.add_constraint ("min", 0.0, false);
4775  gridalpha.add_constraint ("min", 0.0, true);
4776  gridalpha.add_constraint ("max", 1.0, true);
4777  labelfontsizemultiplier.add_constraint ("min", 0.0, false);
4778  linewidth.add_constraint ("min", 0.0, false);
4779  minorgridalpha.add_constraint ("min", 0.0, true);
4780  minorgridalpha.add_constraint ("max", 1.0, true);
4781  titlefontsizemultiplier.add_constraint ("min", 0.0, false);
4782 
4783  // No constraints for hidden transform properties
4784  update_font ();
4785 
4786  x_zlim.resize (1, 2);
4787 
4788  sx = "linear";
4789  sy = "linear";
4790  sz = "linear";
4791 
4792  calc_ticklabels (xtick, xticklabel, xscale.is ("log"),
4793  xaxislocation_is ("origin"),
4794  yscale.is ("log") ? 2 : (yaxislocation_is ("origin") ? 0 :
4795  (yaxislocation_is ("left") ? -1 : 1)), xlim);
4796  calc_ticklabels (ytick, yticklabel, yscale.is ("log"),
4797  yaxislocation_is ("origin"),
4798  xscale.is ("log") ? 2 : (xaxislocation_is ("origin") ? 0 :
4799  (xaxislocation_is ("bottom") ? -1 : 1)), ylim);
4800  calc_ticklabels (ztick, zticklabel, zscale.is ("log"), false, 2, zlim);
4801 
4802  xset (xlabel.handle_value (), "handlevisibility", "off");
4803  xset (ylabel.handle_value (), "handlevisibility", "off");
4804  xset (zlabel.handle_value (), "handlevisibility", "off");
4805  xset (title.handle_value (), "handlevisibility", "off");
4806 
4807  xset (xlabel.handle_value (), "horizontalalignment", "center");
4808  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4809  xset (ylabel.handle_value (), "horizontalalignment", "center");
4810  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4811  xset (zlabel.handle_value (), "horizontalalignment", "right");
4812  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4813  xset (title.handle_value (), "horizontalalignment", "center");
4814  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4815 
4816  xset (xlabel.handle_value (), "verticalalignment", "top");
4817  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4818  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4819  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4820  xset (title.handle_value (), "verticalalignment", "bottom");
4821  xset (title.handle_value (), "verticalalignmentmode", "auto");
4822 
4823  xset (ylabel.handle_value (), "rotation", 90.0);
4824  xset (ylabel.handle_value (), "rotationmode", "auto");
4825 
4826  xset (zlabel.handle_value (), "visible", "off");
4827 
4828  xset (xlabel.handle_value (), "clipping", "off");
4829  xset (ylabel.handle_value (), "clipping", "off");
4830  xset (zlabel.handle_value (), "clipping", "off");
4831  xset (title.handle_value (), "clipping", "off");
4832 
4833  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
4834  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
4835  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
4836  xset (title.handle_value (), "__autopos_tag__", "title");
4837 
4838  double fs = labelfontsizemultiplier.double_value () *
4839  fontsize.double_value ();
4840  xset (xlabel.handle_value (), "fontsize", octave_value (fs));
4841  xset (ylabel.handle_value (), "fontsize", octave_value (fs));
4842  xset (zlabel.handle_value (), "fontsize", octave_value (fs));
4843  fs = titlefontsizemultiplier.double_value () * fontsize.double_value ();
4844  xset (title.handle_value (), "fontsize", octave_value (fs));
4845  xset (title.handle_value (), "fontweight", titlefontweight.get ());
4846 
4847  adopt (xlabel.handle_value ());
4848  adopt (ylabel.handle_value ());
4849  adopt (zlabel.handle_value ());
4850  adopt (title.handle_value ());
4851 
4852  Matrix tlooseinset = default_axes_position ();
4853  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4854  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4855  looseinset = tlooseinset;
4856 }
4857 
4858 /*
4859 ## Test validation of axes double properties range
4860 %!test
4861 %! hf = figure ("visible", "off");
4862 %! unwind_protect
4863 %! hax = axes ("parent", hf);
4864 %! try
4865 %! set (hax, "linewidth", -1);
4866 %! catch
4867 %! err = lasterr ();
4868 %! end_try_catch
4869 %! assert (err, 'set: "linewidth" must be greater than 0');
4870 %! try
4871 %! set (hax, "minorgridalpha", 1.5);
4872 %! catch
4873 %! err = lasterr ();
4874 %! end_try_catch
4875 %! assert (err, 'set: "minorgridalpha" must be less than or equal to 1');
4876 %! unwind_protect_cleanup
4877 %! delete (hf);
4878 %! end_unwind_protect
4879 */
4880 
4881 Matrix
4883 {
4884  Matrix pos = init_pos;
4886  Matrix parent_bb = go.get_properties ().get_boundingbox (true);
4887  Matrix ext = get_extent (true, true);
4888  ext(1) = parent_bb(3) - ext(1) - ext(3);
4889  ext(0)++;
4890  ext(1)++;
4891  ext = convert_position (ext, "pixels", get_units (),
4892  parent_bb.extract_n (0, 2, 1, 2));
4893  if (ext(0) < pos(0))
4894  {
4895  pos(2) += pos(0)-ext(0);
4896  pos(0) = ext(0);
4897  }
4898  if (ext(0)+ext(2) > pos(0)+pos(2))
4899  pos(2) = ext(0)+ext(2)-pos(0);
4900 
4901  if (ext(1) < pos(1))
4902  {
4903  pos(3) += pos(1)-ext(1);
4904  pos(1) = ext(1);
4905  }
4906  if (ext(1)+ext(3) > pos(1)+pos(3))
4907  pos(3) = ext(1)+ext(3)-pos(1);
4908 
4909  return pos;
4910 }
4911 
4912 void
4914 {
4915  // First part is equivalent to `update_tightinset ()'
4916  if (activepositionproperty.is ("position"))
4917  update_position ();
4918  else
4919  update_outerposition ();
4920  caseless_str old_units = get_units ();
4921  set_units ("normalized");
4922  Matrix pos = position.get ().matrix_value ();
4923  Matrix outpos = outerposition.get ().matrix_value ();
4924  Matrix tightpos = calc_tightbox (pos);
4925  Matrix tinset (1, 4, 1.0);
4926  tinset(0) = pos(0)-tightpos(0);
4927  tinset(1) = pos(1)-tightpos(1);
4928  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4929  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4930  tightinset = tinset;
4931  set_units (old_units);
4932  update_transform ();
4933  if (activepositionproperty.is ("position"))
4934  update_position ();
4935  else
4936  update_outerposition ();
4937 }
4938 
4939 /*
4940 %!testif HAVE_OPENGL, HAVE_FLTK; have_window_system
4941 %! hf = figure ("visible", "off");
4942 %! graphics_toolkit (hf, "fltk");
4943 %! unwind_protect
4944 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4945 %! hax = findall (gcf (), "type", "axes");
4946 %! positions = cell2mat (get (hax, "position"));
4947 %! outerpositions = cell2mat (get (hax, "outerposition"));
4948 %! looseinsets = cell2mat (get (hax, "looseinset"));
4949 %! tightinsets = cell2mat (get (hax, "tightinset"));
4950 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4951 %! hax = findall (gcf (), "type", "axes");
4952 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4953 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4954 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4955 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4956 %! unwind_protect_cleanup
4957 %! close (hf);
4958 %! end_unwind_protect
4959 
4960 %!testif HAVE_OPENGL, HAVE_FLTK; have_window_system
4961 %! hf = figure ("visible", "off");
4962 %! graphics_toolkit (hf, "fltk");
4963 %! fpos = get (hf, "position");
4964 %! unwind_protect
4965 %! plot (rand (3));
4966 %! position = get (gca, "position");
4967 %! outerposition = get (gca, "outerposition");
4968 %! looseinset = get (gca, "looseinset");
4969 %! tightinset = get (gca, "tightinset");
4970 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4971 %! set (hf, "position", fpos);
4972 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4973 %! assert (get (gca, "position"), position, 0.001);
4974 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4975 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4976 %! unwind_protect_cleanup
4977 %! close (hf);
4978 %! end_unwind_protect
4979 
4980 %!testif HAVE_OPENGL, HAVE_FLTK; have_window_system
4981 %! hf = figure ("visible", "off");
4982 %! graphics_toolkit (hf, "fltk");
4983 %! fpos = get (hf, "position");
4984 %! set (gca, "activepositionproperty", "position");
4985 %! unwind_protect
4986 %! plot (rand (3));
4987 %! position = get (gca, "position");
4988 %! outerposition = get (gca, "outerposition");
4989 %! looseinset = get (gca, "looseinset");
4990 %! tightinset = get (gca, "tightinset");
4991 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4992 %! set (hf, "position", fpos);
4993 %! assert (get (gca, "position"), position, 0.001);
4994 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4995 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4996 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4997 %! unwind_protect_cleanup
4998 %! close (hf);
4999 %! end_unwind_protect
5000 */
5001 
5002 void
5004  const std::string& who,
5005  const octave_value& v)
5006 {
5007  if (v.is_string ())
5008  {
5009  xset (hp.handle_value (), "string", v);
5010  return;
5011  }
5012 
5015 
5016  if (go.isa ("text"))
5017  val = ::reparent (v, "set", who, __myhandle__, false);
5018  else
5019  {
5020  std::string cname = v.class_name ();
5021 
5022  error ("set: expecting text graphics object or character string for %s property, found %s",
5023  who.c_str (), cname.c_str ());
5024  }
5025 
5026  xset (val, "handlevisibility", "off");
5027 
5029 
5031 
5032  hp = val;
5033 
5034  adopt (hp.handle_value ());
5035 }
5036 
5037 void
5038 axes::properties::set_xlabel (const octave_value& v)
5039 {
5040  set_text_child (xlabel, "xlabel", v);
5041  xset (xlabel.handle_value (), "positionmode", "auto");
5042  xset (xlabel.handle_value (), "rotationmode", "auto");
5043  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5044  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5045  xset (xlabel.handle_value (), "clipping", "off");
5046  xset (xlabel.handle_value (), "color", get_xcolor ());
5047  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5048  update_xlabel_position ();
5049 }
5050 
5051 void
5052 axes::properties::set_ylabel (const octave_value& v)
5053 {
5054  set_text_child (ylabel, "ylabel", v);
5055  xset (ylabel.handle_value (), "positionmode", "auto");
5056  xset (ylabel.handle_value (), "rotationmode", "auto");
5057  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5058  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5059  xset (ylabel.handle_value (), "clipping", "off");
5060  xset (ylabel.handle_value (), "color", get_ycolor ());
5061  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5062  update_ylabel_position ();
5063 }
5064 
5065 void
5066 axes::properties::set_zlabel (const octave_value& v)
5067 {
5068  set_text_child (zlabel, "zlabel", v);
5069  xset (zlabel.handle_value (), "positionmode", "auto");
5070  xset (zlabel.handle_value (), "rotationmode", "auto");
5071  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5072  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
5073  xset (zlabel.handle_value (), "clipping", "off");
5074  xset (zlabel.handle_value (), "color", get_zcolor ());
5075  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5076  update_zlabel_position ();
5077 }
5078 
5079 void
5080 axes::properties::set_title (const octave_value& v)
5081 {
5082  set_text_child (title, "title", v);
5083  xset (title.handle_value (), "positionmode", "auto");
5084  xset (title.handle_value (), "horizontalalignment", "center");
5085  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5086  xset (title.handle_value (), "verticalalignment", "bottom");
5087  xset (title.handle_value (), "verticalalignmentmode", "auto");
5088  xset (title.handle_value (), "clipping", "off");
5089  xset (title.handle_value (), "__autopos_tag__", "title");
5090  update_title_position ();
5091 }
5092 
5093 void
5095  const std::string& mode)
5096 {
5097  // FIXME: Should this have all properties in it?
5098  // Including ones we do don't implement?
5099 
5100  // FIXME: This function is probably never called without mode == "reset"
5101  // Check that this is the case with an assert statement (1/6/2017).
5102  // If there are reports of problems then figure out what code is
5103  // calling it with the mode set to something else.
5104  assert (mode == "reset");
5105 
5106  Matrix tlim (1, 2, 0.0);
5107  tlim(1) = 1;
5108  alim = tlim;
5109  xlim = tlim;
5110  ylim = tlim;
5111  zlim = tlim;
5112 
5113  alimmode = "auto";
5114  climmode = "auto";
5115  xlimmode = "auto";
5116  ylimmode = "auto";
5117  zlimmode = "auto";
5118 
5119  ambientlightcolor = Matrix (1, 3, 1.0);
5120 
5121  box = "off";
5122  boxstyle = "back";
5123 
5124  // Note: camera properties (not mode) will be set in update_transform
5125  camerapositionmode = "auto";
5126  cameratargetmode = "auto";
5127  cameraupvectormode = "auto";
5128  cameraviewanglemode = "auto";
5129 
5130  Matrix cl (1, 2, 0.0);
5131  cl(1) = 1;
5132  clim = cl;
5133 
5134  clippingstyle = "3dbox";
5135 
5136  color = color_values ("white");
5137  colororder = default_colororder ();
5138  colororderindex = 1.0;
5139 
5140  // Note: dataspectratio (not mode) will be set through update_aspectratios
5141  dataaspectratiomode = "auto";
5142 
5143  drawmode = "normal";
5144 
5145  fontangle = "normal";
5146  fontname = OCTAVE_DEFAULT_FONTNAME;
5147  fontsize = 10;
5148  fontunits = "points";
5149  fontsmoothing = "on";
5150  fontweight = "normal";
5151 
5152  gridalpha = 0.15;
5153  gridalphamode = "auto";
5154  gridcolor = color_values (0.15, 0.15, 0.15);
5155  gridcolormode = "auto";
5156  gridlinestyle = "-";
5157 
5158  labelfontsizemultiplier = 1.1;
5159 
5160  layer = "bottom";
5161 
5162  linestyleorder = "-";
5163  linestyleorderindex = 1.0;
5164 
5165  linewidth = 0.5;
5166 
5167  minorgridalpha = 0.25;
5168  minorgridalphamode = "auto";
5169  minorgridcolor = color_values (0.1, 0.1, 0.1);
5170  minorgridcolormode = "auto";
5171  minorgridlinestyle = ":";
5172 
5173  nextplot = "replace";
5174 
5175  // Note: plotboxaspectratio will be set through update_aspectratios
5176  plotboxaspectratiomode = "auto";
5177  projection = "orthographic";
5178 
5179  sortmethod = "depth";
5180 
5181  tickdir = "in";
5182  tickdirmode = "auto";
5183  ticklabelinterpreter = "tex";
5184  ticklength = default_axes_ticklength ();
5185 
5186  tightinset = Matrix (1, 4, 0.0);
5187 
5188  titlefontsizemultiplier = 1.1;
5189  titlefontweight = "bold";
5190 
5191  Matrix tview (1, 2, 0.0);
5192  tview(1) = 90;
5193  view = tview;
5194 
5195  xaxislocation = "bottom";
5196 
5197  xcolor = color_values (0.15, 0.15, 0.15);
5198  xcolormode = "auto";
5199  xdir = "normal";
5200  xgrid = "off";
5201  xminorgrid = "off";
5202  xminortick = "off";
5203  xscale = "linear";
5204  xtick = Matrix ();
5205  xticklabel = "";
5206  xticklabelmode = "auto";
5207  xticklabelrotation = 0.0;
5208  xtickmode = "auto";
5209 
5210  yaxislocation = "left";
5211 
5212  ycolor = color_values (0.15, 0.15, 0.15);
5213  ycolormode = "auto";
5214  ydir = "normal";
5215  ygrid = "off";
5216  yminorgrid = "off";
5217  yminortick = "off";
5218  yscale = "linear";
5219  ytick = Matrix ();
5220  yticklabel = "";
5221  yticklabelmode = "auto";
5222  yticklabelrotation = 0.0;
5223  ytickmode = "auto";
5224 
5225  zcolor = color_values (0.15, 0.15, 0.15);
5226  zcolormode = "auto";
5227  zdir = "normal";
5228  zgrid = "off";
5229  zminorgrid = "off";
5230  zminortick = "off";
5231  zscale = "linear";
5232  ztick = Matrix ();
5233  zticklabel = "";
5234  zticklabelmode = "auto";
5235  zticklabelrotation = 0.0;
5236  ztickmode = "auto";
5237 
5238  sx = "linear";
5239  sy = "linear";
5240  sz = "linear";
5241 
5242  visible = "on";
5243 
5244  graphics_object go = gh_manager::get_object (xlabel.handle_value ());
5246  go = gh_manager::get_object (ylabel.handle_value ());
5248  go = gh_manager::get_object (zlabel.handle_value ());
5250  go = gh_manager::get_object (title.handle_value ());
5252 
5253  xset (xlabel.handle_value (), "handlevisibility", "off");
5254  xset (ylabel.handle_value (), "handlevisibility", "off");
5255  xset (zlabel.handle_value (), "handlevisibility", "off");
5256  xset (title.handle_value (), "handlevisibility", "off");
5257 
5258  xset (xlabel.handle_value (), "horizontalalignment", "center");
5259  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5260  xset (ylabel.handle_value (), "horizontalalignment", "center");
5261  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5262  xset (zlabel.handle_value (), "horizontalalignment", "right");
5263  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5264  xset (title.handle_value (), "horizontalalignment", "center");
5265  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5266 
5267  xset (xlabel.handle_value (), "verticalalignment", "top");
5268  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5269  xset (ylabel.handle_value (), "verticalalignment", "bottom");
5270  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5271  xset (title.handle_value (), "verticalalignment", "bottom");
5272  xset (title.handle_value (), "verticalalignmentmode", "auto");
5273 
5274  xset (ylabel.handle_value (), "rotation", 90.0);
5275  xset (ylabel.handle_value (), "rotationmode", "auto");
5276 
5277  xset (zlabel.handle_value (), "visible", "off");
5278 
5279  xset (xlabel.handle_value (), "clipping", "off");
5280  xset (ylabel.handle_value (), "clipping", "off");
5281  xset (zlabel.handle_value (), "clipping", "off");
5282  xset (title.handle_value (), "clipping", "off");
5283 
5284  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5285  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5286  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5287  xset (title.handle_value (), "__autopos_tag__", "title");
5288 
5289  double fs;
5290  fs = labelfontsizemultiplier.double_value () * fontsize.double_value ();
5291  xset (xlabel.handle_value (), "fontsize", octave_value (fs));
5292  xset (ylabel.handle_value (), "fontsize", octave_value (fs));
5293  xset (zlabel.handle_value (), "fontsize", octave_value (fs));
5294  fs = titlefontsizemultiplier.double_value () * fontsize.double_value ();
5295  xset (title.handle_value (), "fontsize", octave_value (fs));
5296  xset (title.handle_value (), "fontweight", titlefontweight.get ());
5297 
5298  update_transform ();
5299  sync_positions ();
5300  override_defaults (bgo);
5301 }
5302 
5305 {
5306  if (__colormap__.get ().isempty ())
5307  {
5308  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
5309  graphics_object go_f (go.get_ancestor ("figure"));
5310  figure::properties& figure_props
5311  = reinterpret_cast<figure::properties&> (go_f.get_properties ());
5312  return figure_props.get_colormap ();
5313  }
5314 
5315  return get___colormap__ ();
5316 }
5317 
5318 void
5320 {
5321  graphics_handle h = hp.handle_value ();
5322 
5323  if (h.ok ())
5324  {
5326 
5327  if (go.valid_object ())
5328  gh_manager::free (h);
5329 
5331  }
5332 
5333  // FIXME: is it necessary to check whether the axes object is
5334  // being deleted now? I think this function is only called when an
5335  // individual child object is delete and not when the parent axes
5336  // object is deleted.
5337 
5338  if (! is_beingdeleted ())
5339  {
5340  hp = gh_manager::make_graphics_handle ("text", __myhandle__,
5341  false, false);
5342 
5343  xset (hp.handle_value (), "handlevisibility", "off");
5344 
5345  adopt (hp.handle_value ());
5346  }
5347 }
5348 
5349 void
5351 {
5352  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
5353  {
5354  delete_text_child (xlabel);
5355  update_xlabel_position ();
5356  }
5357  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
5358  {
5359  delete_text_child (ylabel);
5360  update_ylabel_position ();
5361  }
5362  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
5363  {
5364  delete_text_child (zlabel);
5365  update_zlabel_position ();
5366  }
5367  else if (title.handle_value ().ok () && h == title.handle_value ())
5368  {
5369  delete_text_child (title);
5370  update_title_position ();
5371  }
5372  else
5374 }
5375 
5376 inline Matrix
5378 {
5379  Matrix m (4, 4, 0.0);
5380 
5381  for (int i = 0; i < 4; i++)
5382  m(i,i) = 1;
5383 
5384  return m;
5385 }
5386 
5387 inline ColumnVector
5389 {
5390  ColumnVector v (4, 0.0);
5391 
5392  v(3) = 1;
5393 
5394  return v;
5395 }
5396 
5397 inline ColumnVector
5398 xform_vector (double x, double y, double z)
5399 {
5400  ColumnVector v (4, 1.0);
5401 
5402  v(0) = x;
5403  v(1) = y;
5404  v(2) = z;
5405 
5406  return v;
5407 }
5408 
5409 inline ColumnVector
5410 transform (const Matrix& m, double x, double y, double z)
5411 {
5412  return (m * xform_vector (x, y, z));
5413 }
5414 
5415 inline Matrix
5416 xform_scale (double x, double y, double z)
5417 {
5418  Matrix m (4, 4, 0.0);
5419 
5420  m(0,0) = x;
5421  m(1,1) = y;
5422  m(2,2) = z;
5423  m(3,3) = 1;
5424 
5425  return m;
5426 }
5427 
5428 inline Matrix
5429 xform_translate (double x, double y, double z)
5430 {
5431  Matrix m = xform_matrix ();
5432 
5433  m(0,3) = x;
5434  m(1,3) = y;
5435  m(2,3) = z;
5436  m(3,3) = 1;
5437 
5438  return m;
5439 }
5440 
5441 inline void
5442 scale (Matrix& m, double x, double y, double z)
5443 {
5444  m = m * xform_scale (x, y, z);
5445 }
5446 
5447 inline void
5448 translate (Matrix& m, double x, double y, double z)
5449 {
5450  m = m * xform_translate (x, y, z);
5451 }
5452 
5453 inline void
5454 xform (ColumnVector& v, const Matrix& m)
5455 {
5456  v = m * v;
5457 }
5458 
5459 inline void
5460 scale (ColumnVector& v, double x, double y, double z)
5461 {
5462  v(0) *= x;
5463  v(1) *= y;
5464  v(2) *= z;
5465 }
5466 
5467 inline void
5468 translate (ColumnVector& v, double x, double y, double z)
5469 {
5470  v(0) += x;
5471  v(1) += y;
5472  v(2) += z;
5473 }
5474 
5475 inline void
5477 {
5478  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5479  scale (v, fact, fact, fact);
5480 }
5481 
5482 inline double
5483 dot (const ColumnVector& v1, const ColumnVector& v2)
5484 {
5485  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5486 }
5487 
5488 inline double
5489 norm (const ColumnVector& v)
5490 {
5491  return sqrt (dot (v, v));
5492 }
5493 
5494 inline ColumnVector
5495 cross (const ColumnVector& v1, const ColumnVector& v2)
5496 {
5497  ColumnVector r = xform_vector ();
5498 
5499  r(0) = v1(1)*v2(2) - v1(2)*v2(1);
5500  r(1) = v1(2)*v2(0) - v1(0)*v2(2);
5501  r(2) = v1(0)*v2(1) - v1(1)*v2(0);
5502 
5503  return r;
5504 }
5505 
5506 inline Matrix
5508 {
5509  static double data[32] =
5510  {
5511  0,0,0,1,
5512  1,0,0,1,
5513  0,1,0,1,
5514  0,0,1,1,
5515  1,1,0,1,
5516  1,0,1,1,
5517  0,1,1,1,
5518  1,1,1,1
5519  };
5520  Matrix m (4, 8);
5521 
5522  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5523 
5524  return m;
5525 }
5526 
5527 inline ColumnVector
5529 {
5530  ColumnVector retval (4, 1.0);
5531 
5532  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
5533 
5534  return retval;
5535 }
5536 
5537 inline RowVector
5539 {
5540  return v.extract_n (0, 3).transpose ();
5541 }
5542 
5543 void
5545 {
5546  double xd = (xdir_is ("normal") ? 1 : -1);
5547  double yd = (ydir_is ("normal") ? 1 : -1);
5548  double zd = (zdir_is ("normal") ? 1 : -1);
5549 
5550  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
5551  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
5552  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
5553 
5554  double xo = xlimits(xd > 0 ? 0 : 1);
5555  double yo = ylimits(yd > 0 ? 0 : 1);
5556  double zo = zlimits(zd > 0 ? 0 : 1);
5557 
5558  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5559 
5560  bool autocam = (camerapositionmode_is ("auto")
5561  && cameratargetmode_is ("auto")
5562  && cameraupvectormode_is ("auto")
5563  && cameraviewanglemode_is ("auto"));
5564  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5565  && plotboxaspectratiomode_is ("auto"));
5566 
5567  ColumnVector c_eye (xform_vector ());
5568  ColumnVector c_center (xform_vector ());
5569  ColumnVector c_upv (xform_vector ());
5570 
5571  if (cameratargetmode_is ("auto"))
5572  {
5573  c_center(0) = (xlimits(0) + xlimits(1)) / 2;
5574  c_center(1) = (ylimits(0) + ylimits(1)) / 2;
5575  c_center(2) = (zlimits(0) + zlimits(1)) / 2;
5576 
5577  cameratarget = xform2cam (c_center);
5578  }
5579  else
5580  c_center = cam2xform (get_cameratarget ().matrix_value ());
5581 
5582  if (camerapositionmode_is ("auto"))
5583  {
5584  Matrix tview = get_view ().matrix_value ();
5585  double az = tview(0);
5586  double el = tview(1);
5587  double d = 5 * sqrt (pb(0)*pb(0) + pb(1)*pb(1) + pb(2)*pb(2));
5588 
5589  if (el == 90 || el == -90)
5590  c_eye(2) = d*octave::math::signum (el);
5591  else
5592  {
5593  az *= M_PI/180.0;
5594  el *= M_PI/180.0;
5595  c_eye(0) = d * cos (el) * sin (az);
5596  c_eye(1) = -d* cos (el) * cos (az);
5597  c_eye(2) = d * sin (el);
5598  }
5599  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
5600  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
5601  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
5602 
5603  cameraposition = xform2cam (c_eye);
5604  }
5605  else
5606  c_eye = cam2xform (get_cameraposition ().matrix_value ());
5607 
5608  if (cameraupvectormode_is ("auto"))
5609  {
5610  Matrix tview = get_view ().matrix_value ();
5611  double az = tview(0);
5612  double el = tview(1);
5613 
5614  if (el == 90 || el == -90)
5615  {
5616  c_upv(0) = -octave::math::signum (el)
5617  * sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
5618  c_upv(1) = octave::math::signum (el)
5619  * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
5620  }
5621  else
5622  c_upv(2) = 1;
5623 
5624  cameraupvector = xform2cam (c_upv);
5625  }
5626  else
5627  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
5628 
5629  Matrix x_view = xform_matrix ();
5630  Matrix x_projection = xform_matrix ();
5631  Matrix x_viewport = xform_matrix ();
5632  Matrix x_normrender = xform_matrix ();
5633  Matrix x_pre = xform_matrix ();
5634 
5635  x_render = xform_matrix ();
5636  x_render_inv = xform_matrix ();
5637 
5638  scale (x_pre, pb(0), pb(1), pb(2));
5639  translate (x_pre, -0.5, -0.5, -0.5);
5640  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5641  zd/(zlimits(1)-zlimits(0)));
5642  translate (x_pre, -xo, -yo, -zo);
5643 
5644  xform (c_eye, x_pre);
5645  xform (c_center, x_pre);
5646  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
5647  pb(2)/(zlimits(1)-zlimits(0)));
5648  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
5649 
5650  ColumnVector F (c_center), f (F), UP (c_upv);
5651  normalize (f);
5652  normalize (UP);
5653 
5654  if (std::abs (dot (f, UP)) > 1e-15)
5655  {
5656  double fa = 1 / sqrt (1 - f(2)*f(2));
5657  scale (UP, fa, fa, fa);
5658  }
5659 
5660  ColumnVector s = cross (f, UP);
5661  ColumnVector u = cross (s, f);
5662 
5663  scale (x_view, 1, 1, -1);
5664  Matrix l = xform_matrix ();
5665  l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
5666  l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
5667  l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
5668  x_view = x_view * l;
5669  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
5670  scale (x_view, pb(0), pb(1), pb(2));
5671  translate (x_view, -0.5, -0.5, -0.5);
5672 
5673  Matrix x_cube = x_view * unit_cube ();
5674  ColumnVector cmin = x_cube.row_min ();
5675  ColumnVector cmax = x_cube.row_max ();
5676  double xM = cmax(0) - cmin(0);
5677  double yM = cmax(1) - cmin(1);
5678 
5679  Matrix bb = get_boundingbox (true);
5680 
5681  double v_angle;
5682 
5683  if (cameraviewanglemode_is ("auto"))
5684  {
5685  double af;
5686 
5687  // FIXME: was this really needed? When compared to Matlab, it
5688  // does not seem to be required. Need investigation with concrete
5689  // graphics toolkit to see results visually.
5690  if (false && dowarp)
5691  af = (1.0 / (xM > yM ? xM : yM));
5692  else
5693  {
5694  if ((bb(2)/bb(3)) > (xM/yM))
5695  af = 1.0 / yM;
5696  else
5697  af = 1.0 / xM;
5698  }
5699  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
5700 
5701  cameraviewangle = v_angle;
5702  }
5703  else
5704  v_angle = get_cameraviewangle ();
5705 
5706  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
5707  scale (x_projection, pf, pf, 1);
5708 
5709  if (dowarp)
5710  {
5711  xM *= pf;
5712  yM *= pf;
5713  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5714  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
5715  }
5716  else
5717  {
5718  double pix = 1;
5719  if (autocam)
5720  {
5721  if ((bb(2)/bb(3)) > (xM/yM))
5722  pix = bb(3);
5723  else
5724  pix = bb(2);
5725  }
5726  else
5727  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
5728  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5729  scale (x_viewport, pix, -pix, 1);
5730  }
5731 
5732  x_normrender = x_viewport * x_projection * x_view;
5733 
5734  x_cube = x_normrender * unit_cube ();
5735  cmin = x_cube.row_min ();
5736  cmax = x_cube.row_max ();
5737  x_zlim.resize (1, 2);
5738  x_zlim(0) = cmin(2);
5739  x_zlim(1) = cmax(2);
5740 
5741  x_render = x_normrender;
5742  scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5743  zd/(zlimits(1)-zlimits(0)));
5744  translate (x_render, -xo, -yo, -zo);
5745 
5746  x_render_inv = x_render.inverse ();
5747 
5748  // Note: these matrices are a slight modified version of the regular matrices,
5749  // more suited for OpenGL rendering (x_gl_mat1 => light => x_gl_mat2)
5750  x_gl_mat1 = x_view;
5751  scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5752  zd/(zlimits(1)-zlimits(0)));
5753  translate (x_gl_mat1, -xo, -yo, -zo);
5754  x_gl_mat2 = x_viewport * x_projection;
5755 }
5756 
5757 static bool updating_axes_layout = false;
5758 
5759 void
5761 {
5763  return;
5764 
5765  graphics_xform xform = get_transform ();
5766 
5767  double xd = (xdir_is ("normal") ? 1 : -1);
5768  double yd = (ydir_is ("normal") ? 1 : -1);
5769  double zd = (zdir_is ("normal") ? 1 : -1);
5770 
5771  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
5772  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
5773  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
5774 
5775  double x_min, x_max, y_min, y_max, z_min, z_max;
5776  x_min = xlims(0), x_max = xlims(1);
5777  y_min = ylims(0), y_max = ylims(1);
5778  z_min = zlims(0), z_max = zlims(1);
5779 
5780  ColumnVector p1, p2, dir (3);
5781 
5782  xstate = ystate = zstate = AXE_ANY_DIR;
5783 
5784  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
5785  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
5786  dir(0) = octave::math::round (p2(0) - p1(0));
5787  dir(1) = octave::math::round (p2(1) - p1(1));
5788  dir(2) = (p2(2) - p1(2));
5789  if (dir(0) == 0 && dir(1) == 0)
5790  xstate = AXE_DEPTH_DIR;
5791  else if (dir(2) == 0)
5792  {
5793  if (dir(0) == 0)
5794  xstate = AXE_VERT_DIR;
5795  else if (dir(1) == 0)
5796  xstate = AXE_HORZ_DIR;
5797  }
5798 
5799  if (dir(2) == 0)
5800  {
5801  if (dir(1) == 0)
5802  xPlane = (dir(0) > 0 ? x_max : x_min);
5803  else
5804  xPlane = (dir(1) < 0 ? x_max : x_min);
5805  }
5806  else
5807  xPlane = (dir(2) < 0 ? x_min : x_max);
5808 
5809  xPlaneN = (xPlane == x_min ? x_max : x_min);
5810  fx = (x_max - x_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5811 
5812  p1 = xform.transform ((x_min + x_max)/2, y_min, (z_min + z_max)/2, false);
5813  p2 = xform.transform ((x_min + x_max)/2, y_max, (z_min + z_max)/2, false);
5814  dir(0) = octave::math::round (p2(0) - p1(0));
5815  dir(1) = octave::math::round (p2(1) - p1(1));
5816  dir(2) = (p2(2) - p1(2));
5817  if (dir(0) == 0 && dir(1) == 0)
5818  ystate = AXE_DEPTH_DIR;
5819  else if (dir(2) == 0)
5820  {
5821  if (dir(0) == 0)
5822  ystate = AXE_VERT_DIR;
5823  else if (dir(1) == 0)
5824  ystate = AXE_HORZ_DIR;
5825  }
5826 
5827  if (dir(2) == 0)
5828  {
5829  if (dir(1) == 0)
5830  yPlane = (dir(0) > 0 ? y_max : y_min);
5831  else
5832  yPlane = (dir(1) < 0 ? y_max : y_min);
5833  }
5834  else
5835  yPlane = (dir(2) < 0 ? y_min : y_max);
5836 
5837  yPlaneN = (yPlane == y_min ? y_max : y_min);
5838  fy = (y_max - y_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5839 
5840  p1 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_min, false);
5841  p2 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_max, false);
5842  dir(0) = octave::math::round (p2(0) - p1(0));
5843  dir(1) = octave::math::round (p2(1) - p1(1));
5844  dir(2) = (p2(2) - p1(2));
5845  if (dir(0) == 0 && dir(1) == 0)
5846  zstate = AXE_DEPTH_DIR;
5847  else if (dir(2) == 0)
5848  {
5849  if (dir(0) == 0)
5850  zstate = AXE_VERT_DIR;
5851  else if (dir(1) == 0)
5852  zstate = AXE_HORZ_DIR;
5853  }
5854 
5855  if (dir(2) == 0)
5856  {
5857  if (dir(1) == 0)
5858  zPlane = (dir(0) > 0 ? z_min : z_max);
5859  else
5860  zPlane = (dir(1) < 0 ? z_min : z_max);
5861  }
5862  else
5863  zPlane = (dir(2) < 0 ? z_min : z_max);
5864 
5865  zPlaneN = (zPlane == z_min ? z_max : z_min);
5866  fz = (z_max - z_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5867 
5870  updating_axes_layout = true;
5871 
5872  xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
5873  zSign = (zd*(zPlane-zPlaneN) <= 0);
5874  xyzSym = (zSign ? xySym : ! xySym);
5875  xpTick = (zSign ? xPlaneN : xPlane);
5876  ypTick = (zSign ? yPlaneN : yPlane);
5877  zpTick = (zSign ? zPlane : zPlaneN);
5878  xpTickN = (zSign ? xPlane : xPlaneN);
5879  ypTickN = (zSign ? yPlane : yPlaneN);
5880  zpTickN = (zSign ? zPlaneN : zPlane);
5881 
5882  // 2D mode
5883  x2Dtop = false;
5884  y2Dright = false;
5885  layer2Dtop = false;
5886  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
5887  {
5888  Matrix ylimits = get_ylim ().matrix_value ();
5889  if (xaxislocation_is ("top") ||
5890  (yscale_is ("log") && xaxislocation_is ("origin")
5891  && (ylimits(1) < 0.)))
5892  {
5893  std::swap (yPlane, yPlaneN);
5894  x2Dtop = true;
5895  }
5896  ypTick = yPlaneN;
5897  ypTickN = yPlane;
5898  Matrix xlimits = get_xlim ().matrix_value ();
5899  if (yaxislocation_is ("right") ||
5900  (xscale_is ("log") && yaxislocation_is ("origin")
5901  && (xlimits(1) < 0.)))
5902  {
5903  std::swap (xPlane, xPlaneN);
5904  y2Dright = true;
5905  }
5906  xpTick = xPlaneN;
5907  xpTickN = xPlane;
5908  if (layer_is ("top"))
5909  {
5910  zpTick = zPlaneN;
5911  layer2Dtop = true;
5912  }
5913  else
5914  zpTick = zPlane;
5915  }
5916 
5917  Matrix viewmat = get_view ().matrix_value ();
5918  nearhoriz = std::abs (viewmat(1)) <= 5;
5919  is2D = viewmat(1) == 90;
5920 
5921  update_ticklength ();
5922 }
5923 
5924 void
5926 {
5927  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
5928  (ystate > AXE_DEPTH_DIR ? 1 : 0) +
5929  (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5930 
5931  if (tickdirmode_is ("auto"))
5932  tickdir.set (mode2d ? "in" : "out", true);
5933 
5934  double ticksign = (tickdir_is ("in") ? -1 : 1);
5935 
5936  Matrix bbox = get_boundingbox (true);
5937  Matrix ticklen = get_ticklength ().matrix_value ();
5938  ticklen(0) *= std::max (bbox(2), bbox(3));
5939  ticklen(1) *= std::max (bbox(2), bbox(3));
5940 
5941  xticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5942  yticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5943  zticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5944 
5945  xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
5946  ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
5947  ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
5948 
5949  update_xlabel_position ();
5950  update_ylabel_position ();
5951  update_zlabel_position ();
5952  update_title_position ();
5953 }
5954 
5955 /*
5956 ## FIXME: A demo can't be called in a C++ file. This should be made a test
5957 ## or moved to a .m file where it can be called.
5958 %!demo
5959 %! clf;
5960 %! subplot (2,1,1);
5961 %! plot (rand (3));
5962 %! xlabel xlabel;
5963 %! ylabel ylabel;
5964 %! title title;
5965 %! subplot (2,1,2);
5966 %! plot (rand (3));
5967 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
5968 %! xlabel xlabel;
5969 %! ylabel ylabel;
5970 %! title title;
5971 */
5972 
5973 static ColumnVector
5975  const text::properties& props,
5976  const graphics_xform& xform,
5977  const Matrix& bbox)
5978 {
5980 
5981  std::string to_units = props.get_units ();
5982 
5983  if (to_units != "data")
5984  {
5985  ColumnVector v = xform.transform (p(0), p(1), p(2));
5986 
5987  retval.resize (3);
5988 
5989  retval(0) = v(0) - bbox(0) + 1;
5990  retval(1) = bbox(1) + bbox(3) - v(1) + 1;
5991  retval(2) = 0;
5992 
5993  retval = convert_position (retval, "pixels", to_units,
5994  bbox.extract_n (0, 2, 1, 2));
5995  }
5996  else
5997  retval = p;
5998 
5999  return retval;
6000 }
6001 
6002 static bool updating_xlabel_position = false;
6003 
6004 void
6006 {
6008  return;
6009 
6010  graphics_object go = gh_manager::get_object (get_xlabel ());
6011 
6012  if (! go.valid_object ())
6013  return;
6014 
6015  text::properties& xlabel_props
6016  = reinterpret_cast<text::properties&> (go.get_properties ());
6017 
6018  bool isempty = xlabel_props.get_string ().isempty ();
6019 
6022  updating_xlabel_position = true;
6023 
6024  if (! isempty)
6025  {
6026  if (xlabel_props.horizontalalignmentmode_is ("auto"))
6027  {
6028  xlabel_props.set_horizontalalignment
6029  (xstate > AXE_DEPTH_DIR ? "center" : (xyzSym ? "left" : "right"));
6030 
6031  xlabel_props.set_horizontalalignmentmode ("auto");
6032  }
6033 
6034  if (xlabel_props.verticalalignmentmode_is ("auto"))
6035  {
6036  xlabel_props.set_verticalalignment
6037  (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
6038 
6039  xlabel_props.set_verticalalignmentmode ("auto");
6040  }
6041  }
6042 
6043  if (xlabel_props.positionmode_is ("auto")
6044  || xlabel_props.rotationmode_is ("auto"))
6045  {
6046  graphics_xform xform = get_transform ();
6047 
6048  Matrix ext (1, 2, 0.0);
6049  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6050  get_xticklabel ().string_vector_value (),
6051  get_xlim ().matrix_value ());
6052 
6053  double wmax = ext(0);
6054  double hmax = ext(1);
6055  double angle = 0.0;
6056  ColumnVector p =
6057  graphics_xform::xform_vector ((xpTickN + xpTick)/2, ypTick, zpTick);
6058 
6059  bool tick_along_z = nearhoriz || octave::math::isinf (fy);
6060  if (tick_along_z)
6061  p(2) += (octave::math::signum (zpTick - zpTickN) * fz * xtickoffset);
6062  else
6063  p(1) += (octave::math::signum (ypTick - ypTickN) * fy * xtickoffset);
6064 
6065  p = xform.transform (p(0), p(1), p(2), false);
6066 
6067  switch (xstate)
6068  {
6069  case AXE_ANY_DIR:
6070  p(0) += (xyzSym ? wmax : -wmax);
6071  p(1) += hmax;
6072  break;
6073 
6074  case AXE_VERT_DIR:
6075  p(0) -= wmax;
6076  angle = 90;
6077  break;
6078 
6079  case AXE_HORZ_DIR:
6080  p(1) += (x2Dtop ? -hmax : hmax);
6081  break;
6082  }
6083 
6084  if (xlabel_props.positionmode_is ("auto"))
6085  {
6086  p = xform.untransform (p(0), p(1), p(2), true);
6087 
6088  p = convert_label_position (p, xlabel_props, xform,
6089  get_extent (false));
6090 
6091  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
6092  xlabel_props.set_positionmode ("auto");
6093  }
6094 
6095  if (! isempty && xlabel_props.rotationmode_is ("auto"))
6096  {
6097  xlabel_props.set_rotation (angle);
6098  xlabel_props.set_rotationmode ("auto");
6099  }
6100  }
6101 }
6102 
6103 static bool updating_ylabel_position = false;
6104 
6105 void
6107 {
6109  return;
6110 
6111  graphics_object go = gh_manager::get_object (get_ylabel ());
6112 
6113  if (! go.valid_object ())
6114  return;
6115 
6116  text::properties& ylabel_props
6117  = reinterpret_cast<text::properties&> (go.get_properties ());
6118 
6119  bool isempty = ylabel_props.get_string ().isempty ();
6120 
6123  updating_ylabel_position = true;
6124 
6125  if (! isempty)
6126  {
6127  if (ylabel_props.horizontalalignmentmode_is ("auto"))
6128  {
6129  ylabel_props.set_horizontalalignment
6130  (ystate > AXE_DEPTH_DIR ? "center" : (! xyzSym ? "left" : "right"));
6131 
6132  ylabel_props.set_horizontalalignmentmode ("auto");
6133  }
6134 
6135  if (ylabel_props.verticalalignmentmode_is ("auto"))
6136  {
6137  ylabel_props.set_verticalalignment
6138  (ystate == AXE_VERT_DIR && ! y2Dright ? "bottom" : "top");
6139 
6140  ylabel_props.set_verticalalignmentmode ("auto");
6141  }
6142  }
6143 
6144  if (ylabel_props.positionmode_is ("auto")
6145  || ylabel_props.rotationmode_is ("auto"))
6146  {
6147  graphics_xform xform = get_transform ();
6148 
6149  Matrix ext (1, 2, 0.0);
6150 
6151  // The underlying get_extents() from FreeType produces mismatched values.
6152  // x-extent accurately measures the width of the glyphs.
6153  // y-extent instead measures from baseline-to-baseline.
6154  // Pad x-extent (+4) so that it approximately matches y-extent.
6155  // This keeps ylabels about the same distance from y-axis as
6156  // xlabels are from x-axis.
6157  // ALWAYS use an even number for padding or horizontal alignment
6158  // will be off.
6159  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
6160  get_yticklabel ().string_vector_value (),
6161  get_ylim ().matrix_value ());
6162 
6163  double wmax = ext(0)+4;
6164  double hmax = ext(1);
6165  double angle = 0.0;
6166  ColumnVector p =
6167  graphics_xform::xform_vector (xpTick, (ypTickN + ypTick)/2, zpTick);
6168 
6169  bool tick_along_z = nearhoriz || octave::math::isinf (fx);
6170  if (tick_along_z)
6171  p(2) += (octave::math::signum (zpTick - zpTickN) * fz * ytickoffset);
6172  else
6173  p(0) += (octave::math::signum (xpTick - xpTickN) * fx * ytickoffset);
6174 
6175  p = xform.transform (p(0), p(1), p(2), false);
6176 
6177  switch (ystate)
6178  {
6179  case AXE_ANY_DIR:
6180  p(0) += (! xyzSym ? wmax : -wmax);
6181  p(1) += hmax;
6182  break;
6183 
6184  case AXE_VERT_DIR:
6185  p(0) += (y2Dright ? wmax : -wmax);
6186  angle = 90;
6187  break;
6188 
6189  case AXE_HORZ_DIR:
6190  p(1) += hmax;
6191  break;
6192  }
6193 
6194  if (ylabel_props.positionmode_is ("auto"))
6195  {
6196  p = xform.untransform (p(0), p(1), p(2), true);
6197 
6198  p = convert_label_position (p, ylabel_props, xform,
6199  get_extent (false));
6200 
6201  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
6202  ylabel_props.set_positionmode ("auto");
6203  }
6204 
6205  if (! isempty && ylabel_props.rotationmode_is ("auto"))
6206  {
6207  ylabel_props.set_rotation (angle);
6208  ylabel_props.set_rotationmode ("auto");
6209  }
6210  }
6211 }
6212 
6213 static bool updating_zlabel_position = false;
6214 
6215 void
6217 {
6219  return;
6220 
6221  graphics_object go = gh_manager::get_object (get_zlabel ());
6222 
6223  if (! go.valid_object ())
6224  return;
6225 
6226  text::properties& zlabel_props
6227  = reinterpret_cast<text::properties&> (go.get_properties ());
6228 
6229  bool camAuto = cameraupvectormode_is ("auto");
6230  bool isempty = zlabel_props.get_string ().isempty ();
6231 
6234  updating_zlabel_position = true;
6235 
6236  if (! isempty)
6237  {
6238  if (zlabel_props.horizontalalignmentmode_is ("auto"))
6239  {
6240  zlabel_props.set_horizontalalignment
6241  ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
6242 
6243  zlabel_props.set_horizontalalignmentmode ("auto");
6244  }
6245 
6246  if (zlabel_props.verticalalignmentmode_is ("auto"))
6247  {
6248  zlabel_props.set_verticalalignment
6249  (zstate == AXE_VERT_DIR
6250  ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
6251 
6252  zlabel_props.set_verticalalignmentmode ("auto");
6253  }
6254  }
6255 
6256  if (zlabel_props.positionmode_is ("auto")
6257  || zlabel_props.rotationmode_is ("auto"))
6258  {
6259  graphics_xform xform = get_transform ();
6260 
6261  Matrix ext (1, 2, 0.0);
6262  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
6263  get_zticklabel ().string_vector_value (),
6264  get_zlim ().matrix_value ());
6265 
6266  double wmax = ext(0);
6267  double hmax = ext(1);
6268  double angle = 0.0;
6269  ColumnVector p;
6270 
6271  if (xySym)
6272  {
6273  p = graphics_xform::xform_vector (xPlaneN, yPlane,
6274  (zpTickN + zpTick)/2);
6275  if (octave::math::isinf (fy))
6276  p(0) += octave::math::signum (xPlaneN - xPlane) * fx * ztickoffset;
6277  else
6278  p(1) += octave::math::signum (yPlane - yPlaneN) * fy * ztickoffset;
6279  }
6280  else
6281  {
6282  p = graphics_xform::xform_vector (xPlane, yPlaneN,
6283  (zpTickN + zpTick)/2);
6284  if (octave::math::isinf (fx))
6285  p(1) += octave::math::signum (yPlaneN - yPlane) * fy * ztickoffset;
6286  else
6287  p(0) += octave::math::signum (xPlane - xPlaneN) * fx * ztickoffset;
6288  }
6289 
6290  p = xform.transform (p(0), p(1), p(2), false);
6291 
6292  switch (zstate)
6293  {
6294  case AXE_ANY_DIR:
6295  if (camAuto)
6296  {
6297  p(0) -= wmax;
6298  angle = 90;
6299  }
6300 
6301  // FIXME: what's the correct offset?
6302  //
6303  // p[0] += (! xySym ? wmax : -wmax);
6304  // p[1] += (zSign ? hmax : -hmax);
6305 
6306  break;
6307 
6308  case AXE_VERT_DIR:
6309  p(0) -= wmax;
6310  angle = 90;
6311  break;
6312 
6313  case AXE_HORZ_DIR:
6314  p(1) += hmax;
6315  break;
6316  }
6317 
6318  if (zlabel_props.positionmode_is ("auto"))
6319  {
6320  p = xform.untransform (p(0), p(1), p(2), true);
6321 
6322  p = convert_label_position (p, zlabel_props, xform,
6323  get_extent (false));
6324 
6325  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
6326  zlabel_props.set_positionmode ("auto");
6327  }
6328 
6329  if (! isempty && zlabel_props.rotationmode_is ("auto"))
6330  {
6331  zlabel_props.set_rotation (angle);
6332  zlabel_props.set_rotationmode ("auto");
6333  }
6334  }
6335 }
6336 
6337 static bool updating_title_position = false;
6338 
6339 void
6341 {
6343  return;
6344 
6345  graphics_object go = gh_manager::get_object (get_title ());
6346 
6347  if (! go.valid_object ())
6348  return;
6349 
6350  text::properties& title_props
6351  = reinterpret_cast<text::properties&> (go.get_properties ());
6352 
6355  updating_title_position = true;
6356 
6357  if (title_props.positionmode_is ("auto"))
6358  {
6359  graphics_xform xform = get_transform ();
6360 
6361  // FIXME: bbox should be stored in axes::properties
6362  Matrix bbox = get_extent (false);
6363 
6364  ColumnVector p =
6365  graphics_xform::xform_vector (bbox(0) + bbox(2)/2,
6366  bbox(1) - 10,
6367  (x_zlim(0) + x_zlim(1))/2);
6368 
6369  if (x2Dtop)
6370  {
6371  Matrix ext (1, 2, 0.0);
6372  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6373  get_xticklabel ().string_vector_value (),
6374  get_xlim ().matrix_value ());
6375  p(1) -= ext(1);
6376  }
6377 
6378  p = xform.untransform (p(0), p(1), p(2), true);
6379 
6380  p = convert_label_position (p, title_props, xform, bbox);
6381 
6382  title_props.set_position (p.extract_n (0, 3).transpose ());
6383  title_props.set_positionmode ("auto");
6384  }
6385 }
6386 
6387 void
6389 {
6390  if (elem_type == "xlabel")
6391  update_xlabel_position ();
6392  else if (elem_type == "ylabel")
6393  update_ylabel_position ();
6394  else if (elem_type == "zlabel")
6395  update_zlabel_position ();
6396  else if (elem_type == "title")
6397  update_title_position ();
6398  else if (elem_type == "sync")
6399  sync_positions ();
6400 }
6401 
6402 static void
6403 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
6404  double xlength, double ylength, double zlength)
6405 {
6406  double xval = xlength / scalefactors(0);
6407  double yval = ylength / scalefactors(1);
6408  double zval = zlength / scalefactors(2);
6409 
6410  double minval = octave::math::min (octave::math::min (xval, yval), zval);
6411 
6412  aspectratios(0) = xval / minval;
6413  aspectratios(1) = yval / minval;
6414  aspectratios(2) = zval / minval;
6415 }
6416 
6417 static void
6418 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
6419  double pbfactor, double dafactor, char limit_type, bool tight)
6420 {
6421  if (tight)
6422  {
6423  double minval = octave::numeric_limits<double>::Inf ();
6424  double maxval = -octave::numeric_limits<double>::Inf ();
6425  double min_pos = octave::numeric_limits<double>::Inf ();
6426  double max_neg = -octave::numeric_limits<double>::Inf ();
6427  get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
6428  if (octave::math::isfinite (minval) && octave::math::isfinite (maxval))
6429  {
6430  limits(0) = minval;
6431  limits(1) = maxval;
6432  s = octave::math::max (s, (maxval - minval) / (pbfactor * dafactor));
6433  }
6434  }
6435  else
6436  s = octave::math::max (s, (limits(1) - limits(0)) / (pbfactor * dafactor));
6437 }
6438 
6439 static std::set<double> updating_aspectratios;
6440 
6441 void
6443 {
6444  if (updating_aspectratios.find (get___myhandle__ ().value ())
6445  != updating_aspectratios.end ())
6446  return;
6447 
6448  Matrix xlimits = get_xlim ().matrix_value ();
6449  Matrix ylimits = get_ylim ().matrix_value ();
6450  Matrix zlimits = get_zlim ().matrix_value ();
6451 
6452  double dx = (xlimits(1) - xlimits(0));
6453  double dy = (ylimits(1) - ylimits(0));
6454  double dz = (zlimits(1) - zlimits(0));
6455 
6456  Matrix da = get_dataaspectratio ().matrix_value ();
6457  Matrix pba = get_plotboxaspectratio ().matrix_value ();
6458 
6459  if (dataaspectratiomode_is ("auto"))
6460  {
6461  if (plotboxaspectratiomode_is ("auto"))
6462  {
6463  pba = Matrix (1, 3, 1.0);
6464  plotboxaspectratio.set (pba, false);
6465  }
6466 
6467  normalized_aspectratios (da, pba, dx, dy, dz);
6468  dataaspectratio.set (da, false);
6469  }
6470  else if (plotboxaspectratiomode_is ("auto"))
6471  {
6472  normalized_aspectratios (pba, da, dx, dy, dz);
6473  plotboxaspectratio.set (pba, false);
6474  }
6475  else
6476  {
6478  bool modified_limits = false;
6479  Matrix kids;
6480 
6481  if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
6482  {
6483  modified_limits = true;
6484  kids = get_children ();
6485  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
6486  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
6487  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
6488  }
6489  else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
6490  {
6491  modified_limits = true;
6492  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
6493  }
6494  else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
6495  {
6496  modified_limits = true;
6497  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
6498  }
6499  else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
6500  {
6501  modified_limits = true;
6502  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
6503  }
6504 
6505  if (modified_limits)
6506  {
6509 
6510  updating_aspectratios.insert (get___myhandle__ ().value ());
6511 
6512  dx = pba(0) * da(0);
6513  dy = pba(1) * da(1);
6514  dz = pba(2) * da(2);
6515  if (octave::math::isinf (s))
6516  s = 1 / octave::math::min (octave::math::min (dx, dy), dz);
6517 
6518  if (xlimmode_is ("auto"))
6519  {
6520  dx = s * dx;
6521  xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
6522  xlimits(1) = xlimits(0) + dx;
6523  set_xlim (xlimits);
6524  set_xlimmode ("auto");
6525  }
6526 
6527  if (ylimmode_is ("auto"))
6528  {
6529  dy = s * dy;
6530  ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
6531  ylimits(1) = ylimits(0) + dy;
6532  set_ylim (ylimits);
6533  set_ylimmode ("auto");
6534  }
6535 
6536  if (zlimmode_is ("auto"))
6537  {
6538  dz = s * dz;
6539  zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
6540  zlimits(1) = zlimits(0) + dz;
6541  set_zlim (zlimits);
6542  set_zlimmode ("auto");
6543  }
6544  }
6545  else
6546  {
6547  normalized_aspectratios (pba, da, dx, dy, dz);
6548  plotboxaspectratio.set (pba, false);
6549  }
6550  }
6551 }
6552 
6553 void
6554 axes::properties::update_label_color (handle_property label,
6555  color_property col)
6556 {
6557  gh_manager::get_object (label.handle_value ()).set ("color", col.get ());
6558 }
6559 
6560 void
6562 {
6563  if (! prop.empty ())
6564  {
6565  octave_value val = get (prop);
6566  octave_value tval = val;
6567  if (prop == "fontsize")
6568  {
6569  tval = octave_value (val.double_value () *
6570  get_titlefontsizemultiplier ());
6571  val = octave_value (val.double_value () *
6572  get_labelfontsizemultiplier ());
6573  }
6574  else if (prop == "fontweight")
6575  tval = get ("titlefontweight");
6576 
6577  gh_manager::get_object (get_xlabel ()).set (prop, val);
6578  gh_manager::get_object (get_ylabel ()).set (prop, val);
6579  gh_manager::get_object (get_zlabel ()).set (prop, val);
6580  gh_manager::get_object (get_title ()).set (prop, tval);
6581 
6582  }
6583 
6584  gh_manager::auto_lock guard;
6585  txt_renderer.set_font (get ("fontname").string_value (),
6586  get ("fontweight").string_value (),
6587  get ("fontangle").string_value (),
6588  get ("__fontsize_points__").double_value ());
6589 }
6590 
6591 // The INTERNAL flag defines whether position or outerposition is used.
6592 
6593 Matrix
6595  const Matrix& parent_pix_size) const
6596 {
6597  Matrix pos = (internal ? get_position ().matrix_value ()
6598  : get_outerposition ().matrix_value ());
6599  Matrix parent_size (parent_pix_size);
6600 
6601  if (parent_size.isempty ())
6602  {
6604 
6605  if (go.valid_object ())
6606  parent_size =
6607  go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
6608  else
6609  parent_size = default_figure_position ();
6610  }
6611 
6612  pos = convert_position (pos, get_units (), "pixels", parent_size);
6613 
6614  pos(0)--;
6615  pos(1)--;
6616  pos(1) = parent_size(1) - pos(1) - pos(3);
6617 
6618  return pos;
6619 }
6620 
6621 Matrix
6622 axes::properties::get_extent (bool with_text, bool only_text_height) const
6623 {
6624  graphics_xform xform = get_transform ();
6625 
6626  Matrix ext (1, 4, 0.0);
6627  ext(0) = ext(1) = octave::numeric_limits<double>::Inf ();
6628  ext(2) = ext(3) = -octave::numeric_limits<double>::Inf ();
6629  for (int i = 0; i <= 1; i++)
6630  for (int j = 0; j <= 1; j++)
6631  for (int k = 0; k <= 1; k++)
6632  {
6633  ColumnVector p = xform.transform (i ? xPlaneN : xPlane,
6634  j ? yPlaneN : yPlane,
6635  k ? zPlaneN : zPlane, false);
6636  ext(0) = std::min (ext(0), p(0));
6637  ext(1) = std::min (ext(1), p(1));
6638  ext(2) = std::max (ext(2), p(0));
6639  ext(3) = std::max (ext(3), p(1));
6640  }
6641 
6642  if (with_text)
6643  {
6644  for (int i = 0; i < 4; i++)
6645  {
6646  graphics_handle htext;
6647  if (i == 0)
6648  htext = get_title ();
6649  else if (i == 1)
6650  htext = get_xlabel ();
6651  else if (i == 2)
6652  htext = get_ylabel ();
6653  else if (i == 3)
6654  htext = get_zlabel ();
6655 
6656  text::properties& text_props
6657  = reinterpret_cast<text::properties&>
6659 
6660  Matrix text_pos = text_props.get_data_position ();
6661  text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
6662  if (text_props.get_string ().isempty ())
6663  {
6664  ext(0) = std::min (ext(0), text_pos(0));
6665  ext(1) = std::min (ext(1), text_pos(1));
6666  ext(2) = std::max (ext(2), text_pos(0));
6667  ext(3) = std::max (ext(3), text_pos(1));
6668  }
6669  else
6670  {
6671  Matrix text_ext = text_props.get_extent_matrix ();
6672 
6673  bool ignore_horizontal = false;
6674  bool ignore_vertical = false;
6675  if (only_text_height)
6676  {
6677  double text_rotation = text_props.get_rotation ();
6678  if (text_rotation == 0. || text_rotation == 180.)
6679  ignore_horizontal = true;
6680  else if (text_rotation == 90. || text_rotation == 270.)
6681  ignore_vertical = true;
6682  }
6683 
6684  if (! ignore_horizontal)
6685  {
6686  ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
6687  ext(2) = std::max (ext(2),
6688  text_pos(0)+text_ext(0)+text_ext(2));
6689  }
6690 
6691  if (! ignore_vertical)
6692  {
6693  ext(1) = std::min (ext(1),
6694  text_pos(1)-text_ext(1)-text_ext(3));
6695  ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
6696  }
6697  }
6698  }
6699  }
6700 
6701  ext(2) = ext(2) - ext(0);
6702  ext(3) = ext(3) - ext(1);
6703 
6704  return ext;
6705 }
6706 
6707 static octave_value
6709 {
6711 
6712  if (val.iscellstr ())
6713  {
6714  // Always return a column vector for Matlab compatibility
6715  if (val.columns () > 1)
6716  retval = val.reshape (dim_vector (val.numel (), 1));
6717  }
6718  else
6719  {
6720  string_vector sv;
6721  if (val.isnumeric ())
6722  {
6723  NDArray data = val.array_value ();
6724  std::ostringstream oss;
6725  oss.precision (5);
6726  for (octave_idx_type i = 0; i < val.numel (); i++)
6727  {
6728  oss.str ("");
6729  oss << data(i);
6730  sv.append (oss.str ());
6731  }
6732  }
6733  else if (val.is_string () && val.rows () == 1)
6734  {
6735  std::string valstr = val.string_value ();
6736  std::istringstream iss (valstr);
6737  std::string tmpstr;
6738 
6739  // Split string with delimiter '|'
6740  while (std::getline (iss, tmpstr, '|'))
6741  sv.append (tmpstr);
6742 
6743  // If string ends with '|' Matlab appends a null string
6744  if (*valstr.rbegin () == '|')
6745  sv.append (std::string (""));
6746  }
6747  else
6748  return retval;
6749 
6750  charMatrix chmat (sv, ' ');
6751 
6752  retval = octave_value (chmat);
6753  }
6754 
6755  return retval;
6756 }
6757 
6758 void
6759 axes::properties::set_xticklabel (const octave_value& val)
6760 {
6761  if (xticklabel.set (convert_ticklabel_string (val), false))
6762  {
6763  set_xticklabelmode ("manual");
6764  xticklabel.run_listeners (POSTSET);
6765  mark_modified ();
6766  }
6767  else
6768  set_xticklabelmode ("manual");
6769 
6770  sync_positions ();
6771 }
6772 
6773 void
6774 axes::properties::set_yticklabel (const octave_value& val)
6775 {
6776  if (yticklabel.set (convert_ticklabel_string (val), false))
6777  {
6778  set_yticklabelmode ("manual");
6779  yticklabel.run_listeners (POSTSET);
6780  mark_modified ();
6781  }
6782  else
6783  set_yticklabelmode ("manual");
6784 
6785  sync_positions ();
6786 }
6787 
6788 void
6789 axes::properties::set_zticklabel (const octave_value& val)
6790 {
6791  if (zticklabel.set (convert_ticklabel_string (val), false))
6792  {
6793  set_zticklabelmode ("manual");
6794  zticklabel.run_listeners (POSTSET);
6795  mark_modified ();
6796  }
6797  else
6798  set_zticklabelmode ("manual");
6799 
6800  sync_positions ();
6801 }
6802 
6803 // Almost identical to convert_ticklabel_string but it only accepts
6804 // cellstr or string, not numeric input.
6805 static octave_value
6807 {
6809 
6810  if (val.iscellstr ())
6811  {
6812  // Always return a column vector for Matlab Compatibility
6813  if (val.columns () > 1)
6814  retval = val.reshape (dim_vector (val.numel (), 1));
6815  }
6816  else
6817  {
6818  string_vector sv;
6819  if (val.is_string () && val.rows () == 1)
6820  {
6821  std::string valstr = val.string_value ();
6822  std::istringstream iss (valstr);
6823  std::string tmpstr;
6824 
6825  // Split string with delimiter '|'
6826  while (std::getline (iss, tmpstr, '|'))
6827  sv.append (tmpstr);
6828 
6829  // If string ends with '|' Matlab appends a null string
6830  if (*valstr.rbegin () == '|')
6831  sv.append (std::string (""));
6832  }
6833  else
6834  return retval;
6835 
6836  charMatrix chmat (sv, ' ');
6837 
6838  retval = octave_value (chmat);
6839  }
6840 
6841  return retval;
6842 }
6843 
6844 void
6845 axes::properties::set_linestyleorder (const octave_value& val)
6846 {
6847  linestyleorder.set (convert_linestyleorder_string (val), false);
6848 }
6849 
6850 void
6851 axes::properties::set_units (const octave_value& val)
6852 {
6853  caseless_str old_units = get_units ();
6854 
6855  if (units.set (val, true))
6856  {
6857  update_units (old_units);
6858  mark_modified ();
6859  }
6860 }
6861 
6862 void
6864 {
6866  Matrix parent_bb
6867  = parent_go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
6868  caseless_str new_units = get_units ();
6869  position.set (octave_value (convert_position (get_position ().matrix_value (),
6870  old_units, new_units,
6871  parent_bb)),
6872  false);
6873  outerposition.set (octave_value (convert_position (get_outerposition ().matrix_value (),
6874  old_units, new_units,
6875  parent_bb)),
6876  false);
6877  tightinset.set (octave_value (convert_position (get_tightinset ().matrix_value (),
6878  old_units, new_units,
6879  parent_bb)),
6880  false);
6881  looseinset.set (octave_value (convert_position (get_looseinset ().matrix_value (),
6882  old_units, new_units,
6883  parent_bb)),
6884  false);
6885 }
6886 
6887 void
6888 axes::properties::set_fontunits (const octave_value& val)
6889 {
6890  caseless_str old_fontunits = get_fontunits ();
6891 
6892  if (fontunits.set (val, true))
6893  {
6894  update_fontunits (old_fontunits);
6895  mark_modified ();
6896  }
6897 }
6898 
6899 void
6901 {
6902  caseless_str new_units = get_fontunits ();
6903  double parent_height = get_boundingbox (true).elem (3);
6904  double fontsz = get_fontsize ();
6905 
6906  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
6907 
6908  set_fontsize (octave_value (fontsz));
6909 }
6910 
6911 double
6912 axes::properties::get___fontsize_points__ (double box_pix_height) const
6913 {
6914  double fontsz = get_fontsize ();
6915  double parent_height = box_pix_height;
6916 
6917  if (fontunits_is ("normalized") && parent_height <= 0)
6918  parent_height = get_boundingbox (true).elem (3);
6919 
6920  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
6921 }
6922 
6924 graphics_xform::xform_vector (double x, double y, double z)
6925 {
6926  return ::xform_vector (x, y, z);
6927 }
6928 
6929 Matrix
6931 {
6933 }
6934 
6936 graphics_xform::transform (double x, double y, double z, bool use_scale) const
6937 {
6938  if (use_scale)
6939  {
6940  x = sx.scale (x);
6941  y = sy.scale (y);
6942  z = sz.scale (z);
6943  }
6944 
6945  return ::transform (xform, x, y, z);
6946 }
6947 
6949 graphics_xform::untransform (double x, double y, double z,
6950  bool use_scale) const
6951 {
6952  ColumnVector v = ::transform (xform_inv, x, y, z);
6953 
6954  if (use_scale)
6955  {
6956  v(0) = sx.unscale (v(0));
6957  v(1) = sy.unscale (v(1));
6958  v(2) = sz.unscale (v(2));
6959  }
6960 
6961  return v;
6962 }
6963 
6966 {
6968 
6969  if (retval.is_undefined ())
6970  {
6971  graphics_handle parent_h = get_parent ();
6972  graphics_object parent_go = gh_manager::get_object (parent_h);
6973 
6974  retval = parent_go.get_default (pname);
6975  }
6976 
6977  return retval;
6978 }
6979 
6980 // FIXME: remove.
6981 // FIXME: maybe this should go into array_property class?
6982 /*
6983 static void
6984 check_limit_vals (double& min_val, double& max_val,
6985  double& min_pos, double& max_neg,
6986  const array_property& data)
6987 {
6988  double val = data.min_val ();
6989  if (octave::math::isfinite (val) && val < min_val)
6990  min_val = val;
6991  val = data.max_val ();
6992  if (octave::math::isfinite (val) && val > max_val)
6993  max_val = val;
6994  val = data.min_pos ();
6995  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
6996  min_pos = val;
6997  val = data.max_neg ();
6998  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
6999  max_neg = val;
7000 }
7001 */
7002 
7003 static void
7004 check_limit_vals (double& min_val, double& max_val,
7005  double& min_pos, double& max_neg,
7006  const octave_value& data)
7007 {
7008  if (data.is_matrix_type ())
7009  {
7010  Matrix m = data.matrix_value ();
7011 
7012  if (m.numel () == 4)
7013  {
7014  double val;
7015 
7016  val = m(0);
7017  if (octave::math::isfinite (val) && val < min_val)
7018  min_val = val;
7019 
7020  val = m(1);
7021  if (octave::math::isfinite (val) && val > max_val)
7022  max_val = val;
7023 
7024  val = m(2);
7025  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7026  min_pos = val;
7027 
7028  val = m(3);
7029  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7030  max_neg = val;
7031  }
7032  }
7033 }
7034 
7035 // magform(x) Returns (a, b),
7036 // where x = a * 10^b, abs (a) >= 1., and b is integer.
7037 
7038 static void
7039 magform (double x, double& a, int& b)
7040 {
7041  if (x == 0)
7042  {
7043  a = 0;
7044  b = 0;
7045  }
7046  else
7047  {
7048  b = static_cast<int> (std::floor (std::log10 (std::abs (x))));
7049  a = x / std::pow (10.0, b);
7050  }
7051 }
7052 
7053 // A translation from Tom Holoryd's python code at
7054 // http://kurage.nimh.nih.gov/tomh/tics.py
7055 // FIXME: add log ticks
7056 
7057 double
7058 axes::properties::calc_tick_sep (double lo, double hi)
7059 {
7060  int ticint = 5;
7061 
7062  // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and SCALE3 for
7063  // Determination of Scales on Computer Generated Plots", Communications of
7064  // the ACM, 10 (1973), 639-640.
7065  // Also cited as ACM Algorithm 463.
7066 
7067  double a;
7068  int b, x;
7069 
7070  magform ((hi - lo) / ticint, a, b);
7071 
7072  static const double sqrt_2 = sqrt (2.0);
7073  static const double sqrt_10 = sqrt (10.0);
7074  static const double sqrt_50 = sqrt (50.0);
7075 
7076  if (a < sqrt_2)
7077  x = 1;
7078  else if (a < sqrt_10)
7079  x = 2;
7080  else if (a < sqrt_50)
7081  x = 5;
7082  else
7083  x = 10;
7084 
7085  return x * std::pow (10., b);
7086 }
7087 
7088 // Attempt to make "nice" limits from the actual max and min of the data.
7089 // For log plots, we will also use the smallest strictly positive value.
7090 
7091 Matrix
7093  double min_pos, double max_neg,
7094  const bool logscale)
7095 {
7096  Matrix retval;
7097 
7098  double min_val = xmin;
7099  double max_val = xmax;
7100 
7101  if (octave::math::isinf (min_val) && min_val > 0
7102  && octave::math::isinf (max_val) && max_val < 0)
7103  {
7104  retval = default_lim (logscale);
7105  return retval;
7106  }
7107  else if (! (octave::math::isinf (min_val) || octave::math::isinf (max_val)))
7108  {
7109  if (logscale)
7110  {
7111  if (octave::math::isinf (min_pos) && octave::math::isinf (max_neg))
7112  {
7113  // FIXME: max_neg is needed for "loglog ([0 -Inf])"
7114  // This is the *only* place where max_neg is needed.
7115  // Is there another way?
7116  retval = default_lim (logscale);
7117  return retval;
7118  }
7119  if (min_val <= 0)
7120  {
7121  if (max_val > 0)
7122  {
7123  warning_with_id ("Octave:negative-data-log-axis",
7124  "axis: omitting non-positive data in log plot");
7125  min_val = min_pos;
7126  }
7127  else if (max_val == 0)
7128  max_val = max_neg;
7129  }
7130  // FIXME: maybe this test should also be relative?
7131  if (std::abs (min_val - max_val)
7132  < sqrt (std::numeric_limits<double>::epsilon ()))
7133  {
7134  // Widen range when too small
7135  if (min_val >= 0)
7136  {
7137  min_val *= 0.9;
7138  max_val *= 1.1;
7139  }
7140  else
7141  {
7142  min_val *= 1.1;
7143  max_val *= 0.9;
7144  }
7145  }
7146  if (min_val > 0)
7147  {
7148  // Log plots with all positive data
7149  min_val = std::pow (10, std::floor (log10 (min_val)));
7150  max_val = std::pow (10, std::ceil (log10 (max_val)));
7151  }
7152  else
7153  {
7154  // Log plots with all negative data
7155  min_val = -std::pow (10, std::ceil (log10 (-min_val)));
7156  max_val = -std::pow (10, std::floor (log10 (-max_val)));
7157  }
7158  }
7159  else
7160  {
7161  if (min_val == 0 && max_val == 0)
7162  {
7163  min_val = -1;
7164  max_val = 1;
7165  }
7166  // FIXME: maybe this test should also be relative?
7167  else if (std::abs (min_val - max_val)
7168  < sqrt (std::numeric_limits<double>::epsilon ()))
7169  {
7170  min_val -= 0.1 * std::abs (min_val);
7171  max_val += 0.1 * std::abs (max_val);
7172  }
7173 
7174  double tick_sep = calc_tick_sep (min_val, max_val);
7175  double min_tick = std::floor (min_val / tick_sep);
7176  double max_tick = std::ceil (max_val / tick_sep);
7177  // Prevent round-off from cropping ticks
7178  min_val = std::min (min_val, tick_sep * min_tick);
7179  max_val = std::max (max_val, tick_sep * max_tick);
7180  }
7181  }
7182 
7183  retval.resize (1, 2);
7184 
7185  retval(0) = min_val;
7186  retval(1) = max_val;
7187 
7188  return retval;
7189 }
7190 
7191 void
7193  const bool logscale, char &update_type)
7194 {
7195  double min_val = octave::numeric_limits<double>::Inf ();
7196  double max_val = -octave::numeric_limits<double>::Inf ();
7197  double min_pos = octave::numeric_limits<double>::Inf ();
7198  double max_neg = -octave::numeric_limits<double>::Inf ();
7199  double eps = std::numeric_limits<double>::epsilon ();
7200  bool do_update = false;
7201  bool have_children_limits = false;
7202 
7203  // check whether we need to get children limits
7204  if (! octave::math::isfinite (limits(0)) ||
7205  ! octave::math::isfinite (limits(1)))
7206  {
7207  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7208  update_type);
7209  have_children_limits = true;
7210  }
7211  if (! octave::math::isfinite (limits(0)))
7212  {
7213  limits(0) = min_val;
7214  do_update = true;
7215  }
7216  if (! octave::math::isfinite (limits(1)))
7217  {
7218  limits(1) = max_val;
7219  do_update = true;
7220  }
7221  if (limits(0) == 0 && limits(1) == 0)
7222  {
7223  limits = default_lim (logscale);
7224  do_update = true;
7225  }
7226  // FIXME: maybe this test should also be relative?
7227  else if (std::abs (limits(0) - limits(1)) < sqrt (eps))
7228  {
7229  if (logscale)
7230  {
7231  limits(0) = (limits(0) < 0 ? 10.0 * limits(0) : 0.1 * limits(0));
7232  limits(1) = (limits(1) < 0 ? 0.1 * limits(1) : 10.0 * limits(1));
7233  }
7234  else
7235  {
7236  limits(0) -= 0.1 * std::abs (limits(0));
7237  limits(1) += 0.1 * std::abs (limits(1));
7238  }
7239  do_update = true;
7240  }
7241 
7242  if (logscale && limits(0)*limits(1) <= 0)
7243  {
7244  if (! have_children_limits)
7245  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7246  update_type);
7247 
7248  if (limits(1) > 0)
7249  {
7250  warning_with_id ("Octave:axis-non-positive-log-limits",
7251  "Non-positive limit for logarithmic axis ignored\n");
7252  if (octave::math::isfinite (min_pos))
7253  limits(0) = min_pos;
7254  else
7255  limits(0) = 0.1 * limits(1);
7256  }
7257  else
7258  {
7259  warning_with_id ("Octave:axis-non-negative-log-limits",
7260  "Non-negative limit for logarithmic axis ignored\n");
7261  if (octave::math::isfinite (max_neg))
7262  limits(1) = max_neg;
7263  else
7264  limits(1) = 0.1 * limits(0);
7265  }
7266  // FIXME: maybe this test should also be relative?
7267  if (std::abs (limits(0) - limits(1)) < sqrt (eps))
7268  {
7269  // Widen range when too small
7270  if (limits(0) > 0)
7271  {
7272  limits(0) *= 0.9;
7273  limits(1) *= 1.1;
7274  }
7275  else
7276  {
7277  limits(0) *= 1.1;
7278  limits(1) *= 0.9;
7279  }
7280  }
7281  do_update = true;
7282  }
7283 
7284  if (! do_update)
7285  update_type = 0;
7286 
7287 }
7288 
7289 /*
7290 ## Test validation of auto and manual axis limits
7291 %!test
7292 %! hf = figure ("visible", "off");
7293 %! unwind_protect
7294 %! hax = axes ("parent", hf);
7295 %! plot (0, pi);
7296 %! assert (get (hax, "xlim"), [-1, 1]);
7297 %! assert (get (hax, "xlimmode"), "auto");
7298 %! assert (get (hax, "ylim"), [2.8, 3.5], 2*eps);
7299 %! assert (get (hax, "ylimmode"), "auto");
7300 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7301 %! assert (get (hax, "xlim"), [0.9, 1.1]);
7302 %! assert (get (hax, "xlimmode"), "manual");
7303 %! assert (get (hax, "ylim"), [0, 1]);
7304 %! assert (get (hax, "ylimmode"), "manual");
7305 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7306 %! ## Matlab does not update the properties
7307 %! assert (get (hax, "xlim"), [0, 1]);
7308 %! assert (get (hax, "ylim"), [0.9, 1.1]*pi, 2*eps);
7309 %! unwind_protect_cleanup
7310 %! delete (hf);
7311 %! end_unwind_protect
7312 
7313 %!test
7314 %! hf = figure ("visible", "off");
7315 %! unwind_protect
7316 %! hax = axes ("parent", hf);
7317 %! plot ([0, exp(1)], [-pi, 3]);
7318 %! assert (get (hax, "xlim"), [0, 3]);
7319 %! assert (get (hax, "xlimmode"), "auto");
7320 %! assert (get (hax, "ylim"), [-4, 3]);
7321 %! assert (get (hax, "ylimmode"), "auto");
7322 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7323 %! ## Matlab does not update the properties but uses tight limits on screen
7324 %! assert (get (hax, "xlim"), [0, exp(1)]);
7325 %! assert (get (hax, "xlimmode"), "manual");
7326 %! assert (get (hax, "ylim"), [-pi, 3]);
7327 %! assert (get (hax, "ylimmode"), "manual");
7328 %! unwind_protect_cleanup
7329 %! delete (hf);
7330 %! end_unwind_protect
7331 
7332 %!test
7333 %! hf = figure ("visible", "off");
7334 %! unwind_protect
7335 %! hax = axes ("parent", hf);
7336 %! loglog (0, pi);
7337 %! assert (get (hax, "xlim"), [0.1, 1.0]);
7338 %! assert (get (hax, "xlimmode"), "auto");
7339 %! assert (get (hax, "ylim"), [1, 10]);
7340 %! assert (get (hax, "ylimmode"), "auto");
7341 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7342 %! assert (get (hax, "xlim"), [0.1, 10]);
7343 %! assert (get (hax, "xlimmode"), "manual");
7344 %! assert (get (hax, "ylim"), [0.1, 1.0]);
7345 %! assert (get (hax, "ylimmode"), "manual");
7346 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7347 %! ## Matlab does not update the properties
7348 %! assert (get (hax, "xlim"), [0.1, 1.0]);
7349 %! assert (get (hax, "ylim"), [0.1, 10]*pi);
7350 %! unwind_protect_cleanup
7351 %! delete (hf);
7352 %! end_unwind_protect
7353 
7354 %!test
7355 %! hf = figure ("visible", "off");
7356 %! unwind_protect
7357 %! hax = axes ("parent", hf);
7358 %! loglog ([0 -1], [0 1]);
7359 %! assert (get (hax, "xlim"), [-10, -0.1]);
7360 %! assert (get (hax, "xlimmode"), "auto");
7361 %! assert (get (hax, "ylim"), [0.1, 10]);
7362 %! assert (get (hax, "ylimmode"), "auto");
7363 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7364 %! ## Matlab does not update the properties
7365 %! assert (get (hax, "xlim"), [-1.1, -0.9]);
7366 %! assert (get (hax, "ylim"), [0.9, 1.1]);
7367 %! unwind_protect_cleanup
7368 %! delete (hf);
7369 %! end_unwind_protect
7370 
7371 %!test
7372 %! hf = figure ("visible", "off");
7373 %! unwind_protect
7374 %! hax = axes ("parent", hf);
7375 %! loglog ([1 -1], [1 pi]);
7376 %! assert (get (hax, "xlim"), [0.1, 10]);
7377 %! assert (get (hax, "xlimmode"), "auto");
7378 %! assert (get (hax, "ylim"), [1, 10]);
7379 %! assert (get (hax, "ylimmode"), "auto");
7380 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7381 %! ## Matlab does not update the properties but uses tight limits on screen
7382 %! assert (get (hax, "xlim"), [0.9, 1.1]);
7383 %! assert (get (hax, "ylim"), [1, pi]);
7384 %! unwind_protect_cleanup
7385 %! delete (hf);
7386 %! end_unwind_protect
7387 */
7388 
7389 void
7391  array_property& ticks,
7392  array_property& mticks,
7393  bool limmode_is_auto,
7394  bool tickmode_is_auto,
7395  bool is_logscale)
7396 {
7397  if (lims.get ().isempty ())
7398  return;
7399 
7400  double lo = (lims.get ().matrix_value ())(0);
7401  double hi = (lims.get ().matrix_value ())(1);
7402  double lo_lim = lo;
7403  double hi_lim = hi;
7404  bool is_negative = lo < 0 && hi < 0;
7405 
7406  // FIXME: should this be checked for somewhere else? (i.e., set{x,y,z}lim)
7407  if (hi < lo)
7408  std::swap (hi, lo);
7409 
7410  if (is_logscale)
7411  {
7412  if (is_negative)
7413  {
7414  double tmp = hi;
7415  hi = std::log10 (-lo);
7416  lo = std::log10 (-tmp);
7417  }
7418  else
7419  {
7420  hi = std::log10 (hi);
7421  lo = std::log10 (lo);
7422  }
7423  }
7424 
7425  Matrix tmp_ticks;
7426  if (tickmode_is_auto)
7427  {
7428  double tick_sep;
7429 
7430  if (is_logscale)
7431  {
7432  if (! (octave::math::isinf (hi) || octave::math::isinf (lo)))
7433  tick_sep = 1; // Tick is every order of magnitude (bug #39449)
7434  else
7435  tick_sep = 0;
7436  }
7437  else
7438  tick_sep = calc_tick_sep (lo, hi);
7439 
7440  double i1 = std::floor (lo / tick_sep);
7441  double i2 = std::ceil (hi / tick_sep);
7442 
7443  if (limmode_is_auto)
7444  {
7445  // Adjust limits to include min and max ticks
7446  Matrix tmp_lims (1,2);
7447  tmp_lims(0) = std::min (tick_sep * i1, lo);
7448  tmp_lims(1) = std::max (tick_sep * i2, hi);
7449 
7450  if (is_logscale)
7451  {
7452  tmp_lims(0) = std::pow (10., tmp_lims(0));
7453  tmp_lims(1) = std::pow (10., tmp_lims(1));
7454  if (tmp_lims(0) <= 0)
7455  tmp_lims(0) = std::pow (10., lo);
7456  if (is_negative)
7457  {
7458  double tmp = tmp_lims(0);
7459  tmp_lims(0) = -tmp_lims(1);
7460  tmp_lims(1) = -tmp;
7461  }
7462  }
7463  lims = tmp_lims;
7464  }
7465  else
7466  {
7467  // adjust min and max ticks to be within limits
7468  if (i1*tick_sep < lo)
7469  i1++;
7470  if (i2*tick_sep > hi && i2 > i1)
7471  i2--;
7472  }
7473 
7474  tmp_ticks = Matrix (1, i2-i1+1);
7475  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
7476  {
7477  tmp_ticks(i) = tick_sep * (i+i1);
7478  if (is_logscale)
7479  tmp_ticks(i) = std::pow (10., tmp_ticks(i));
7480  }
7481  if (is_logscale && is_negative)
7482  {
7483  Matrix rev_ticks (1, i2-i1+1);
7484  rev_ticks = -tmp_ticks;
7485  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
7486  tmp_ticks(i) = rev_ticks(i2-i1-i);
7487  }
7488 
7489  ticks = tmp_ticks;
7490  }
7491  else
7492  tmp_ticks = ticks.get ().matrix_value ();
7493 
7494  octave_idx_type n_ticks = tmp_ticks.numel ();
7495  if (n_ticks < 2)
7496  return;
7497 
7498  // minor ticks between, above, and below min and max ticks
7499  int n = (is_logscale ? 8 : 4);
7500  double mult_below = (is_logscale ? tmp_ticks(1) / tmp_ticks(0) : 1);
7501  double mult_above = (is_logscale ? tmp_ticks(n_ticks-1) / tmp_ticks(n_ticks-2)
7502  : 1);
7503 
7504  double d_below = (tmp_ticks(1) - tmp_ticks(0)) / mult_below / (n+1);
7505  int n_below = static_cast<int> (std::floor ((tmp_ticks(0)-lo_lim) / d_below));
7506  if (n_below < 0)
7507  n_below = 0;
7508  int n_between = n * (n_ticks - 1);
7509  double d_above = (tmp_ticks(n_ticks-1) - tmp_ticks(n_ticks-2)) * mult_above
7510  / (n+1);
7511  int n_above = static_cast<int> (std::floor ((hi_lim-tmp_ticks(n_ticks-1))
7512  / d_above));
7513  if (n_above < 0)
7514  n_above = 0;
7515 
7516  Matrix tmp_mticks (1, n_below + n_between + n_above);
7517  for (int i = 0; i < n_below; i++)
7518  tmp_mticks(i) = tmp_ticks(0) - (n_below-i) * d_below;
7519  for (int i = 0; i < n_ticks-1; i++)
7520  {
7521  double d = (tmp_ticks(i+1) - tmp_ticks(i)) / (n + 1);
7522  for (int j = 0; j < n; j++)
7523  tmp_mticks(n_below+n*i+j) = tmp_ticks(i) + d * (j+1);
7524  }
7525  for (int i = 0; i < n_above; i++)
7526  tmp_mticks(n_below+n_between+i) = tmp_ticks(n_ticks-1) + (i + 1) * d_above;
7527 
7528  mticks = tmp_mticks;
7529 }
7530 
7531 /*
7532 %!test <*45356>
7533 %! hf = figure ("visible", "off");
7534 %! unwind_protect
7535 %! plot (1:10);
7536 %! xlim ([4.75, 8.5]);
7537 %! tics = get (gca, "xtick");
7538 %! assert (tics, [5 6 7 8]);
7539 %! unwind_protect_cleanup
7540 %! close (hf);
7541 %! end_unwind_protect
7542 */
7543 
7544 void
7546  any_property& labels, bool logscale,
7547  const bool is_origin,
7548  const int other_axislocation,
7549  const array_property& axis_lims)
7550 {
7551  Matrix values = ticks.get ().matrix_value ();
7552  Matrix lims = axis_lims.get ().matrix_value ();
7553  Cell c (values.dims ());
7554  std::ostringstream os;
7555 
7556  // omit tick labels depending on location of other axis
7558  if (get_is2D () && is_origin)
7559  {
7560  if (other_axislocation == 0)
7561  {
7562  omit_ticks(0) = octave::math::max (octave::math::min (0., lims(1)),
7563  lims(0));
7564  }
7565  else if (other_axislocation == 1)
7566  omit_ticks(0) = lims(1);
7567  else if (other_axislocation == -1)
7568  omit_ticks(0) = lims(0);
7569  if (is_box ())
7570  {
7571  omit_ticks(1) = lims(0);
7572  omit_ticks(2) = lims(1);
7573  }
7574  }
7575 
7576  if (logscale)
7577  {
7578  double significand;
7579  double exponent;
7580  double exp_max = 0.0;
7581  double exp_min = 0.0;
7582 
7583  for (int i = 0; i < values.numel (); i++)
7584  {
7585  double exp = std::log10 (values(i));
7586  exp_min = std::min (exp_min, exp);
7587  exp_max = std::max (exp_max, exp);
7588  }
7589 
7590  for (int i = 0; i < values.numel (); i++)
7591  {
7592  bool omit_tick = false;
7593  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
7594  if (values(i) == omit_ticks(i_omit))
7595  omit_tick = true;
7596  if (omit_tick)
7597  {
7598  c(i) = "";
7599  continue;
7600  }
7601 
7602  if (values(i) < 0.0)
7603  exponent = std::floor (std::log10 (-values(i)));
7604  else
7605  exponent = std::floor (std::log10 (values(i)));
7606  significand = values(i) * std::pow (10., -exponent);
7607 
7608  os.str ("");
7609  if ((std::abs (significand) - 1) >
7610  10*std::numeric_limits<double>::epsilon())
7611  os << significand << 'x';
7612  else if (significand < 0)
7613  os << '-';
7614 
7615  os << "10^{";
7616 
7617  if (exponent < 0.0)
7618  {
7619  os << '-';
7620  exponent = -exponent;
7621  }
7622  if (exponent < 10. && (exp_max > 9 || exp_min < -9))
7623  os << '0';
7624  os << exponent << '}';
7625  c(i) = os.str ();
7626  }
7627  }
7628  else
7629  {
7630  for (int i = 0; i < values.numel (); i++)
7631  {
7632  bool omit_tick = false;
7633  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
7634  if (values(i) == omit_ticks(i_omit))
7635  omit_tick = true;
7636  if (omit_tick)
7637  c(i) = "";
7638  else
7639  {
7640  os.str ("");
7641  os << values(i);
7642  c(i) = os.str ();
7643  }
7644  }
7645  }
7646 
7647  labels = c;
7648 }
7649 
7650 Matrix
7652  const string_vector& ticklabels,
7653  const Matrix& limits)
7654 {
7655  Matrix ext (1, 2, 0.0);
7656  double wmax, hmax;
7657  wmax = hmax = 0.0;
7658  int n = std::min (ticklabels.numel (), ticks.numel ());
7659  for (int i = 0; i < n; i++)
7660  {
7661  double val = ticks(i);
7662  if (limits(0) <= val && val <= limits(1))
7663  {
7664  std::string label (ticklabels(i));
7665  label.erase (0, label.find_first_not_of (' '));
7666  label = label.substr (0, label.find_last_not_of (' ')+1);
7667 
7668  if (txt_renderer.ok ())
7669  {
7670  gh_manager::auto_lock guard;
7671  ext = txt_renderer.get_extent (label, 0.0,
7672  get_ticklabelinterpreter ());
7673 
7674  wmax = std::max (wmax, ext(0));
7675  hmax = std::max (hmax, ext(1));
7676  }
7677  else
7678  {
7679  // FIXME: find a better approximation
7680  double fsize = get ("fontsize").double_value ();
7681  int len = label.length ();
7682 
7683  wmax = std::max (wmax, 0.5*fsize*len);
7684  hmax = fsize;
7685  }
7686  }
7687  }
7688 
7689  ext(0) = wmax;
7690  ext(1) = hmax;
7691  return ext;
7692 }
7693 
7694 void
7695 get_children_limits (double& min_val, double& max_val,
7696  double& min_pos, double& max_neg,
7697  const Matrix& kids, char limit_type)
7698 {
7699  octave_idx_type n = kids.numel ();
7700 
7701  switch (limit_type)
7702  {
7703  case 'x':
7704  for (octave_idx_type i = 0; i < n; i++)
7705  {
7707 
7708  if (go.is_xliminclude ())
7709  {
7710  octave_value lim = go.get_xlim ();
7711 
7712  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
7713  }
7714  }
7715  break;
7716 
7717  case 'y':
7718  for (octave_idx_type i = 0; i < n; i++)
7719  {
7721 
7722  if (go.is_yliminclude ())
7723  {
7724  octave_value lim = go.get_ylim ();
7725 
7726  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
7727  }
7728  }
7729  break;
7730 
7731  case 'z':
7732  for (octave_idx_type i = 0; i < n; i++)
7733  {
7735 
7736  if (go.is_zliminclude ())
7737  {
7738  octave_value lim = go.get_zlim ();
7739 
7740  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
7741  }
7742  }
7743  break;
7744 
7745  case 'c':
7746  for (octave_idx_type i = 0; i < n; i++)
7747  {
7749 
7750  if (go.is_climinclude ())
7751  {
7752  octave_value lim = go.get_clim ();
7753 
7754  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
7755  }
7756  }
7757  break;
7758 
7759  case 'a':
7760  for (octave_idx_type i = 0; i < n; i++)
7761  {
7763 
7764  if (go.is_aliminclude ())
7765  {
7766  octave_value lim = go.get_alim ();
7767 
7768  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
7769  }
7770  }
7771  break;
7772 
7773  default:
7774  break;
7775  }
7776 }
7777 
7778 static std::set<double> updating_axis_limits;
7779 
7780 void
7782  const graphics_handle& h)
7783 {
7784  if (updating_axis_limits.find (get_handle ().value ())
7785  != updating_axis_limits.end ())
7786  return;
7787 
7788  Matrix kids = Matrix (1, 1, h.value ());
7789 
7790  double min_val = octave::numeric_limits<double>::Inf ();
7791  double max_val = -octave::numeric_limits<double>::Inf ();
7792  double min_pos = octave::numeric_limits<double>::Inf ();
7793  double max_neg = -octave::numeric_limits<double>::Inf ();
7794 
7795  char update_type = 0;
7796 
7797  Matrix limits;
7798  double val;
7799 
7800 #define FIX_LIMITS \
7801  if (limits.numel () == 4) \
7802  { \
7803  val = limits(0); \
7804  if (octave::math::isfinite (val)) \
7805  min_val = val; \
7806  val = limits(1); \
7807  if (octave::math::isfinite (val)) \
7808  max_val = val; \
7809  val = limits(2); \
7810  if (octave::math::isfinite (val)) \
7811  min_pos = val; \
7812  val = limits(3); \
7813  if (octave::math::isfinite (val)) \
7814  max_neg = val; \
7815  } \
7816  else \
7817  { \
7818  limits.resize (4, 1); \
7819  limits(0) = min_val; \
7820  limits(1) = max_val; \
7821  limits(2) = min_pos; \
7822  limits(3) = max_neg; \
7823  }
7824 
7825  if (axis_type == "xdata" || axis_type == "xscale"
7826  || axis_type == "xlimmode" || axis_type == "xliminclude"
7827  || axis_type == "xlim")
7828  {
7829  limits = xproperties.get_xlim ().matrix_value ();
7830  FIX_LIMITS;
7831 
7832  update_type = 'x';
7833  if (xproperties.xlimmode_is ("auto"))
7834  {
7835  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
7836 
7837  limits = xproperties.get_axis_limits (min_val, max_val,
7838  min_pos, max_neg,
7839  xproperties.xscale_is ("log"));
7840  }
7841  else
7842  xproperties.check_axis_limits (limits, kids,
7843  xproperties.xscale_is ("log"),
7844  update_type);
7845  }
7846  else if (axis_type == "ydata" || axis_type == "yscale"
7847  || axis_type == "ylimmode" || axis_type == "yliminclude"
7848  || axis_type == "ylim")
7849  {
7850  limits = xproperties.get_ylim ().matrix_value ();
7851  FIX_LIMITS;
7852 
7853  update_type = 'y';
7854  if (xproperties.ylimmode_is ("auto"))
7855  {
7856  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
7857 
7858  limits = xproperties.get_axis_limits (min_val, max_val,
7859  min_pos, max_neg,
7860  xproperties.yscale_is ("log"));
7861  }
7862  else
7863  xproperties.check_axis_limits (limits, kids,
7864  xproperties.yscale_is ("log"),
7865  update_type);
7866  }
7867  else if (axis_type == "zdata" || axis_type == "zscale"
7868  || axis_type == "zlimmode" || axis_type == "zliminclude"
7869  || axis_type == "zlim")
7870  {
7871  limits = xproperties.get_zlim ().matrix_value ();
7872  FIX_LIMITS;
7873 
7874  update_type = 'z';
7875  if (xproperties.zlimmode_is ("auto"))
7876  {
7877  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
7878 
7879  xproperties.set_has3Dkids ((max_val - min_val) >
7880  std::numeric_limits<double>::epsilon ());
7881 
7882  limits = xproperties.get_axis_limits (min_val, max_val,
7883  min_pos, max_neg,
7884  xproperties.zscale_is ("log"));
7885  }
7886  else
7887  {
7888  // FIXME: get_children_limits is only needed here in order to know
7889  // if there are 3D children. Is there a way to avoid this call?
7890  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
7891 
7892  xproperties.set_has3Dkids ((max_val - min_val) >
7893  std::numeric_limits<double>::epsilon ());
7894 
7895  xproperties.check_axis_limits (limits, kids,
7896  xproperties.zscale_is ("log"),
7897  update_type);
7898  }
7899  }
7900  else if (axis_type == "cdata" || axis_type == "climmode"
7901  || axis_type == "cdatamapping" || axis_type == "climinclude"
7902  || axis_type == "clim")
7903  {
7904  if (xproperties.climmode_is ("auto"))
7905  {
7906  limits = xproperties.get_clim ().matrix_value ();
7907  FIX_LIMITS;
7908 
7909  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
7910 
7911  if (min_val > max_val)
7912  {
7913  min_val = min_pos = 0;
7914  max_val = 1;
7915  }
7916  else if (min_val == max_val)
7917  {
7918  max_val = min_val + 1;
7919  min_val -= 1;
7920  }
7921 
7922  limits.resize (1, 2);
7923 
7924  limits(0) = min_val;
7925  limits(1) = max_val;
7926 
7927  update_type = 'c';
7928  }
7929 
7930  }
7931  else if (axis_type == "alphadata" || axis_type == "alimmode"
7932  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
7933  || axis_type == "alim")
7934  {
7935  if (xproperties.alimmode_is ("auto"))
7936  {
7937  limits = xproperties.get_alim ().matrix_value ();
7938  FIX_LIMITS;
7939 
7940  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
7941 
7942  if (min_val > max_val)
7943  {
7944  min_val = min_pos = 0;
7945  max_val = 1;
7946  }
7947  else if (min_val == max_val)
7948  max_val = min_val + 1;
7949 
7950  limits.resize (1, 2);
7951 
7952  limits(0) = min_val;
7953  limits(1) = max_val;
7954 
7955  update_type = 'a';
7956  }
7957 
7958  }
7959 
7960 #undef FIX_LIMITS
7961 
7964 
7965  updating_axis_limits.insert (get_handle ().value ());
7966  bool is_auto;
7967 
7968  switch (update_type)
7969  {
7970  case 'x':
7971  is_auto = xproperties.xlimmode_is ("auto");
7972  xproperties.set_xlim (limits);
7973  if (is_auto)
7974  xproperties.set_xlimmode ("auto");
7975  xproperties.update_xlim ();
7976  break;
7977 
7978  case 'y':
7979  is_auto = xproperties.ylimmode_is ("auto");
7980  xproperties.set_ylim (limits);
7981  if (is_auto)
7982  xproperties.set_ylimmode ("auto");
7983  xproperties.update_ylim ();
7984  break;
7985 
7986  case 'z':
7987  is_auto = xproperties.zlimmode_is ("auto");
7988  xproperties.set_zlim (limits);
7989  if (is_auto)
7990  xproperties.set_zlimmode ("auto");
7991  xproperties.update_zlim ();
7992  break;
7993 
7994  case 'c':
7995  xproperties.set_clim (limits);
7996  xproperties.set_climmode ("auto");
7997  break;
7998 
7999  case 'a':
8000  xproperties.set_alim (limits);
8001  xproperties.set_alimmode ("auto");
8002  break;
8003 
8004  default:
8005  break;
8006  }
8007 
8008  xproperties.update_transform ();
8009 }
8010 
8011 void
8013 {
8014  if ((updating_axis_limits.find (get_handle ().value ())
8015  != updating_axis_limits.end ())
8016  || (updating_aspectratios.find (get_handle ().value ())
8017  != updating_aspectratios.end ()))
8018  return;
8019 
8020  Matrix kids = xproperties.get_children ();
8021 
8022  double min_val = octave::numeric_limits<double>::Inf ();
8023  double max_val = -octave::numeric_limits<double>::Inf ();
8024  double min_pos = octave::numeric_limits<double>::Inf ();
8025  double max_neg = -octave::numeric_limits<double>::Inf ();
8026 
8027  char update_type = 0;
8028 
8029  Matrix limits;
8030 
8031  if (axis_type == "xdata" || axis_type == "xscale"
8032  || axis_type == "xlimmode" || axis_type == "xliminclude"
8033  || axis_type == "xlim")
8034  {
8035  update_type = 'x';
8036  if (xproperties.xlimmode_is ("auto"))
8037  {
8038  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8039 
8040  limits = xproperties.get_axis_limits (min_val, max_val,
8041  min_pos, max_neg,
8042  xproperties.xscale_is ("log"));
8043  }
8044  else
8045  {
8046  limits = xproperties.get_xlim ().matrix_value ();
8047  xproperties.check_axis_limits (limits, kids,
8048  xproperties.xscale_is ("log"),
8049  update_type);
8050  }
8051  }
8052  else if (axis_type == "ydata" || axis_type == "yscale"
8053  || axis_type == "ylimmode" || axis_type == "yliminclude"
8054  || axis_type == "ylim")
8055  {
8056  update_type = 'y';
8057  if (xproperties.ylimmode_is ("auto"))
8058  {
8059  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8060 
8061  limits = xproperties.get_axis_limits (min_val, max_val,
8062  min_pos, max_neg,
8063  xproperties.yscale_is ("log"));
8064  }
8065  else
8066  {
8067  limits = xproperties.get_ylim ().matrix_value ();
8068  xproperties.check_axis_limits (limits, kids,
8069  xproperties.yscale_is ("log"),
8070  update_type);
8071  }
8072  }
8073  else if (axis_type == "zdata" || axis_type == "zscale"
8074  || axis_type == "zlimmode" || axis_type == "zliminclude"
8075  || axis_type == "zlim")
8076  {
8077  update_type = 'z';
8078  if (xproperties.zlimmode_is ("auto"))
8079  {
8080  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8081 
8082  xproperties.set_has3Dkids ((max_val - min_val) >
8083  std::numeric_limits<double>::epsilon ());
8084 
8085 
8086  limits = xproperties.get_axis_limits (min_val, max_val,
8087  min_pos, max_neg,
8088  xproperties.zscale_is ("log"));
8089  }
8090  else
8091  {
8092  // FIXME: get_children_limits is only needed here in order to know
8093  // if there are 3D children. Is there a way to avoid this call?
8094  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8095 
8096  xproperties.set_has3Dkids ((max_val - min_val) >
8097  std::numeric_limits<double>::epsilon ());
8098 
8099  limits = xproperties.get_zlim ().matrix_value ();
8100  xproperties.check_axis_limits (limits, kids,
8101  xproperties.zscale_is ("log"),
8102  update_type);
8103  }
8104  }
8105  else if (axis_type == "cdata" || axis_type == "climmode"
8106  || axis_type == "cdatamapping" || axis_type == "climinclude"
8107  || axis_type == "clim")
8108  {
8109  if (xproperties.climmode_is ("auto"))
8110  {
8111  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8112 
8113  if (min_val > max_val)
8114  {
8115  min_val = min_pos = 0;
8116  max_val = 1;
8117  }
8118  else if (min_val == max_val)
8119  {
8120  max_val = min_val + 1;
8121  min_val -= 1;
8122  }
8123 
8124  limits.resize (1, 2);
8125 
8126  limits(0) = min_val;
8127  limits(1) = max_val;
8128 
8129  update_type = 'c';
8130  }
8131 
8132  }
8133  else if (axis_type == "alphadata" || axis_type == "alimmode"
8134  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8135  || axis_type == "alim")
8136  {
8137  if (xproperties.alimmode_is ("auto"))
8138  {
8139  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8140 
8141  if (min_val > max_val)
8142  {
8143  min_val = min_pos = 0;
8144  max_val = 1;
8145  }
8146  else if (min_val == max_val)
8147  max_val = min_val + 1;
8148 
8149  limits.resize (1, 2);
8150 
8151  limits(0) = min_val;
8152  limits(1) = max_val;
8153 
8154  update_type = 'a';
8155  }
8156 
8157  }
8158 
8161 
8162  updating_axis_limits.insert (get_handle ().value ());
8163  bool is_auto;
8164 
8165  switch (update_type)
8166  {
8167  case 'x':
8168  is_auto = xproperties.xlimmode_is ("auto");
8169  xproperties.set_xlim (limits);
8170  if (is_auto)
8171  xproperties.set_xlimmode ("auto");
8172  xproperties.update_xlim ();
8173  break;
8174 
8175  case 'y':
8176  is_auto = xproperties.ylimmode_is ("auto");
8177  xproperties.set_ylim (limits);
8178  if (is_auto)
8179  xproperties.set_ylimmode ("auto");
8180  xproperties.update_ylim ();
8181  break;
8182 
8183  case 'z':
8184  is_auto = xproperties.zlimmode_is ("auto");
8185  xproperties.set_zlim (limits);
8186  if (is_auto)
8187  xproperties.set_zlimmode ("auto");
8188  xproperties.update_zlim ();
8189  break;
8190 
8191  case 'c':
8192  xproperties.set_clim (limits);
8193  xproperties.set_climmode ("auto");
8194  break;
8195 
8196  case 'a':
8197  xproperties.set_alim (limits);
8198  xproperties.set_alimmode ("auto");
8199  break;
8200 
8201  default:
8202  break;
8203  }
8204 
8205  xproperties.update_transform ();
8206 }
8207 
8208 inline
8209 double force_in_range (double x, double lower, double upper)
8210 {
8211  if (x < lower)
8212  return lower;
8213  else if (x > upper)
8214  return upper;
8215  else
8216  return x;
8217 }
8218 
8219 static Matrix
8220 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
8221 {
8222  Matrix new_lims = lims;
8223 
8224  double lo = lims(0);
8225  double hi = lims(1);
8226 
8227  bool is_negative = lo < 0 && hi < 0;
8228 
8229  if (is_logscale)
8230  {
8231  if (is_negative)
8232  {
8233  double tmp = hi;
8234  hi = std::log10 (-lo);
8235  lo = std::log10 (-tmp);
8236  val = std::log10 (-val);
8237  }
8238  else
8239  {
8240  hi = std::log10 (hi);
8241  lo = std::log10 (lo);
8242  val = std::log10 (val);
8243  }
8244  }
8245 
8246  // Perform the zooming
8247  lo = val + (lo - val) / factor;
8248  hi = val + (hi - val) / factor;
8249 
8250  if (is_logscale)
8251  {
8252  if (is_negative)
8253  {
8254  double tmp = -std::pow (10.0, hi);
8255  hi = -std::pow (10.0, lo);
8256  lo = tmp;
8257  }
8258  else
8259  {
8260  lo = std::pow (10.0, lo);
8261  hi = std::pow (10.0, hi);
8262  }
8263  }
8264 
8265  new_lims(0) = lo;
8266  new_lims(1) = hi;
8267 
8268  return new_lims;
8269 }
8270 
8271 void
8273  double x, double y, double factor,
8274  bool push_to_zoom_stack)
8275 {
8276  // FIXME: Do we need error checking here?
8277  Matrix xlims = get_xlim ().matrix_value ();
8278  Matrix ylims = get_ylim ().matrix_value ();
8279 
8280  // Get children axes limits
8281  Matrix kids = get_children ();
8282  double minx = octave::numeric_limits<double>::Inf ();
8283  double maxx = -octave::numeric_limits<double>::Inf ();
8284  double min_pos_x = octave::numeric_limits<double>::Inf ();
8285  double max_neg_x = -octave::numeric_limits<double>::Inf ();
8286  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
8287 
8288  double miny = octave::numeric_limits<double>::Inf ();
8289  double maxy = -octave::numeric_limits<double>::Inf ();
8290  double min_pos_y = octave::numeric_limits<double>::Inf ();
8291  double max_neg_y = -octave::numeric_limits<double>::Inf ();
8292  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
8293 
8294  xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
8295  ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
8296 
8297  zoom (mode, xlims, ylims, push_to_zoom_stack);
8298 }
8299 
8300 void
8301 axes::properties::zoom (const std::string& mode, double factor,
8302  bool push_to_zoom_stack)
8303 {
8304  // FIXME: Do we need error checking here?
8305  Matrix xlims = get_xlim ().matrix_value ();
8306  Matrix ylims = get_ylim ().matrix_value ();
8307 
8308  double x = (xlims(0) + xlims(1)) / 2;
8309  double y = (ylims(0) + ylims(1)) / 2;
8310 
8311  zoom_about_point (mode, x, y, factor, push_to_zoom_stack);
8312 }
8313 
8314 void
8316 {
8317  if (zoom_stack.empty ())
8318  {
8319  zoom_stack.push_front (xlimmode.get ());
8320  zoom_stack.push_front (xlim.get ());
8321  zoom_stack.push_front (ylimmode.get ());
8322  zoom_stack.push_front (ylim.get ());
8323  zoom_stack.push_front (zlimmode.get ());
8324  zoom_stack.push_front (zlim.get ());
8325  zoom_stack.push_front (view.get ());
8326  }
8327 }
8328 
8329 void
8331  const Matrix& xl, const Matrix& yl,
8332  bool push_to_zoom_stack)
8333 {
8334  if (xl(0) == xl(1) || yl(0) == yl(1))
8335  {
8336  warning ("invalid zoom region");
8337  return;
8338  }
8339 
8340  if (push_to_zoom_stack)
8341  push_zoom_stack ();
8342 
8343  if (mode == "horizontal" || mode == "both")
8344  {
8345  xlim = xl;
8346  xlimmode = "manual";
8347  }
8348 
8349  if (mode == "vertical" || mode == "both")
8350  {
8351  ylim = yl;
8352  ylimmode = "manual";
8353  }
8354 
8355  update_transform ();
8356 
8357  if (mode == "horizontal" || mode == "both")
8358  update_xlim ();
8359 
8360  if (mode == "vertical" || mode == "both")
8361  update_ylim ();
8362 }
8363 
8364 static Matrix
8365 do_translate (double x0, double x1, const Matrix& lims, bool is_logscale)
8366 {
8367  Matrix new_lims = lims;
8368 
8369  double lo = lims(0);
8370  double hi = lims(1);
8371 
8372  bool is_negative = lo < 0 && hi < 0;
8373 
8374  double delta;
8375 
8376  if (is_logscale)
8377  {
8378  if (is_negative)
8379  {
8380  double tmp = hi;
8381  hi = std::log10 (-lo);
8382  lo = std::log10 (-tmp);
8383  x0 = -x0;
8384  x1 = -x1;
8385  }
8386  else
8387  {
8388  hi = std::log10 (hi);
8389  lo = std::log10 (lo);
8390  }
8391 
8392  delta = std::log10 (x0) - std::log10 (x1);
8393  }
8394  else
8395  {
8396  delta = x0 - x1;
8397  }
8398 
8399  // Perform the translation
8400  lo += delta;
8401  hi += delta;
8402 
8403  if (is_logscale)
8404  {
8405  if (is_negative)
8406  {
8407  double tmp = -std::pow (10.0, hi);
8408  hi = -std::pow (10.0, lo);
8409  lo = tmp;
8410  }
8411  else
8412  {
8413  lo = std::pow (10.0, lo);
8414  hi = std::pow (10.0, hi);
8415  }
8416  }
8417 
8418  new_lims(0) = lo;
8419  new_lims(1) = hi;
8420 
8421  return new_lims;
8422 }
8423 
8424 void
8426  double x0, double x1, double y0, double y1,
8427  bool push_to_zoom_stack)
8428 {
8429  // FIXME: Do we need error checking here?
8430  Matrix xlims = get_xlim ().matrix_value ();
8431  Matrix ylims = get_ylim ().matrix_value ();
8432 
8433  // Get children axes limits
8434  Matrix kids = get_children ();
8435  double minx = octave::numeric_limits<double>::Inf ();
8436  double maxx = -octave::numeric_limits<double>::Inf ();
8437  double min_pos_x = octave::numeric_limits<double>::Inf ();
8438  double max_neg_x = -octave::numeric_limits<double>::Inf ();
8439  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
8440 
8441  double miny = octave::numeric_limits<double>::Inf ();
8442  double maxy = -octave::numeric_limits<double>::Inf ();
8443  double min_pos_y = octave::numeric_limits<double>::Inf ();
8444  double max_neg_y = -octave::numeric_limits<double>::Inf ();
8445  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
8446 
8447  xlims = do_translate (x0, x1, xlims, xscale_is ("log"));
8448  ylims = do_translate (y0, y1, ylims, yscale_is ("log"));
8449 
8450  zoom (mode, xlims, ylims, push_to_zoom_stack);
8451 }
8452 
8453 void
8454 axes::properties::pan (const std::string& mode, double factor,
8455  bool push_to_zoom_stack)
8456 {
8457  // FIXME: Do we need error checking here?
8458  Matrix xlims = get_xlim ().matrix_value ();
8459  Matrix ylims = get_ylim ().matrix_value ();
8460 
8461  double x0 = (xlims(0) + xlims(1)) / 2;
8462  double y0 = (ylims(0) + ylims(1)) / 2;
8463 
8464  double x1 = x0 + (xlims(1) - xlims(0)) * factor;
8465  double y1 = y0 + (ylims(1) - ylims(0)) * factor;
8466 
8467  translate_view (mode, x0, x1, y0, y1, push_to_zoom_stack);
8468 }
8469 
8470 void
8471 axes::properties::rotate3d (double x0, double x1, double y0, double y1,
8472  bool push_to_zoom_stack)
8473 {
8474  if (push_to_zoom_stack)
8475  push_zoom_stack ();
8476 
8477  Matrix bb = get_boundingbox (true);
8478  Matrix new_view = get_view ().matrix_value ();
8479 
8480  // Compute new view angles
8481  new_view(0) += ((x0 - x1) * (180.0 / bb(2)));
8482  new_view(1) += ((y1 - y0) * (180.0 / bb(3)));
8483 
8484  // Clipping
8485  new_view(1) = std::min (new_view(1), 90.0);
8486  new_view(1) = std::max (new_view(1), -90.0);
8487  if (new_view(0) > 180.0)
8488  new_view(0) -= 360.0;
8489  else if (new_view(0) < -180.0)
8490  new_view(0) += 360.0;
8491 
8492  // Snapping
8493  double snapmargin = 1.0;
8494  for (int a = -90; a <= 90; a += 90)
8495  {
8496  if ((a - snapmargin) < new_view(1) && new_view(1) < (a + snapmargin))
8497  {
8498  new_view(1) = a;
8499  break;
8500  }
8501  }
8502 
8503  for (int a = -180; a <= 180; a += 180)
8504  if ((a - snapmargin) < new_view(0) && new_view(0) < (a + snapmargin))
8505  {
8506  if (a == 180)
8507  new_view(0) = -180;
8508  else
8509  new_view(0) = a;
8510  break;
8511  }
8512 
8513  // Update axes properties
8514  set_view (new_view);
8515 }
8516 
8517 void
8518 axes::properties::rotate_view (double delta_el, double delta_az,
8519  bool push_to_zoom_stack)
8520 {
8521  if (push_to_zoom_stack)
8522  push_zoom_stack ();
8523 
8524  Matrix v = get_view ().matrix_value ();
8525 
8526  v(1) += delta_el;
8527 
8528  if (v(1) > 90)
8529  v(1) = 90;
8530  if (v(1) < -90)
8531  v(1) = -90;
8532 
8533  v(0) = fmod (v(0) - delta_az + 720,360);
8534 
8535  set_view (v);
8536 
8537  update_transform ();
8538 }
8539 
8540 void
8542 {
8543  if (zoom_stack.size () >= 7)
8544  {
8545  view = zoom_stack.front ();
8546  zoom_stack.pop_front ();
8547 
8548  zlim = zoom_stack.front ();
8549  zoom_stack.pop_front ();
8550 
8551  zlimmode = zoom_stack.front ();
8552  zoom_stack.pop_front ();
8553 
8554  ylim = zoom_stack.front ();
8555  zoom_stack.pop_front ();
8556 
8557  ylimmode = zoom_stack.front ();
8558  zoom_stack.pop_front ();
8559 
8560  xlim = zoom_stack.front ();
8561  zoom_stack.pop_front ();
8562 
8563  xlimmode = zoom_stack.front ();
8564  zoom_stack.pop_front ();
8565 
8566  update_transform ();
8567 
8568  update_xlim ();
8569  update_ylim ();
8570  update_zlim ();
8571 
8572  update_view ();
8573  }
8574 }
8575 
8576 void
8578 {
8579  if (! is_handle_visible ())
8580  {
8581  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
8582  graphics_object fig (go.get_ancestor ("figure"));
8583  octave_value ca = fig.get ("currentaxes");
8584  if (! ca.isempty () && ca.double_value () == __myhandle__)
8585  {
8586  gh_manager::auto_lock guard;
8587  octave_value kids = fig.get ("children");
8588  if (kids.isempty ())
8589  fig.set ("currentaxes", Matrix ());
8590  else
8591  {
8592  NDArray kidsarray = kids.array_value ();
8593  fig.set ("currentaxes", kidsarray(0));
8594  }
8595  }
8596  }
8597 
8598  base_properties::update_handlevisibility ();
8599 }
8600 
8601 void
8603 {
8604  size_t items_to_leave_on_stack = (do_unzoom ? 7 : 0);
8605 
8606  while (zoom_stack.size () > items_to_leave_on_stack)
8607  zoom_stack.pop_front ();
8608 
8609  if (do_unzoom)
8610  unzoom ();
8611 }
8612 
8613 void
8615 {
8616  // empty list of local defaults
8618 
8619  // Save warning state of "Octave:deprecated-property"
8620  int state = toggle_warn ("Octave:deprecated-property", false);
8621 
8622  // reset factory defaults
8624  set_defaults ("reset");
8625 
8626  toggle_warn ("Octave:deprecated-property", true, state);
8627 }
8628 
8629 void
8631 {
8633 
8635  xinitialize (xproperties.get_xlabel ());
8636  xinitialize (xproperties.get_ylabel ());
8637  xinitialize (xproperties.get_zlabel ());
8638 
8639  xproperties.sync_positions ();
8640 }
8641 
8642 // ---------------------------------------------------------------------
8643 
8644 Matrix
8645 line::properties::compute_xlim (void) const
8646 {
8647  Matrix m (1, 4);
8648 
8649  m(0) = xdata.min_val ();
8650  m(1) = xdata.max_val ();
8651  m(2) = xdata.min_pos ();
8652  m(3) = xdata.max_neg ();
8653 
8654  return m;
8655 }
8656 
8657 Matrix
8658 line::properties::compute_ylim (void) const
8659 {
8660  Matrix m (1, 4);
8661 
8662  m(0) = ydata.min_val ();
8663  m(1) = ydata.max_val ();
8664  m(2) = ydata.min_pos ();
8665  m(3) = ydata.max_neg ();
8666 
8667  return m;
8668 }
8669 
8670 // ---------------------------------------------------------------------
8671 
8672 Matrix
8673 text::properties::get_data_position (void) const
8674 {
8675  Matrix pos = get_position ().matrix_value ();
8676 
8677  if (! units_is ("data"))
8678  pos = convert_text_position (pos, *this, get_units (), "data");
8679 
8680  return pos;
8681 }
8682 
8683 Matrix
8684 text::properties::get_extent_matrix (void) const
8685 {
8686  // FIXME: Should this function also add the (x,y) base position?
8687  return extent.get ().matrix_value ();
8688 }
8689 
8691 text::properties::get_extent (void) const
8692 {
8693  // FIXME: This doesn't work right for 3D plots.
8694  // (It doesn't in Matlab either, at least not in version 6.5.)
8695  Matrix m = extent.get ().matrix_value ();
8696  Matrix pos = get_position ().matrix_value ();
8697  Matrix p = convert_text_position (pos, *this, get_units (), "pixels");
8698 
8699  m(0) += p(0);
8700  m(1) += p(1);
8701 
8702  return convert_text_position (m, *this, "pixels", get_units ());
8703 }
8704 
8705 void
8706 text::properties::set_fontunits (const octave_value& val)
8707 {
8708  caseless_str old_fontunits = get_fontunits ();
8709 
8710  if (fontunits.set (val, true))
8711  {
8712  update_fontunits (old_fontunits);
8713  mark_modified ();
8714  }
8715 }
8716 
8717 void
8719 {
8720  caseless_str new_units = get_fontunits ();
8721  double parent_height = 0;
8722  double fontsz = get_fontsize ();
8723 
8724  if (new_units == "normalized")
8725  {
8726  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
8727  graphics_object ax (go.get_ancestor ("axes"));
8728 
8729  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
8730  }
8731 
8732  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
8733 
8734  set_fontsize (octave_value (fontsz));
8735 }
8736 
8737 void
8738 text::properties::update_font (void)
8739 {
8740  gh_manager::auto_lock guard;
8741  txt_renderer.set_font (get ("fontname").string_value (),
8742  get ("fontweight").string_value (),
8743  get ("fontangle").string_value (),
8744  get ("__fontsize_points__").double_value ());
8745 
8746  txt_renderer.set_color (get_color_rgb ());
8747 }
8748 
8749 void
8750 text::properties::update_text_extent (void)
8751 {
8752  int halign = 0;
8753  int valign = 0;
8754 
8755  if (horizontalalignment_is ("center"))
8756  halign = 1;
8757  else if (horizontalalignment_is ("right"))
8758  halign = 2;
8759 
8760  if (verticalalignment_is ("middle"))
8761  valign = 1;
8762  else if (verticalalignment_is ("top"))
8763  valign = 2;
8764  else if (verticalalignment_is ("baseline"))
8765  valign = 3;
8766  else if (verticalalignment_is ("cap"))
8767  valign = 4;
8768 
8769  Matrix bbox;
8770 
8771  // FIXME: string should be parsed only when modified, for efficiency
8772 
8773  octave_value string_prop = get_string ();
8774 
8775  string_vector sv = string_prop.string_vector_value ();
8776 
8777  gh_manager::auto_lock guard;
8778  txt_renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
8779  halign, valign, get_rotation (),
8780  get_interpreter ());
8781  // The bbox is relative to the text's position. We'll leave it that
8782  // way, because get_position does not return valid results when the
8783  // text is first constructed. Conversion to proper coordinates is
8784  // performed in get_extent.
8785  set_extent (bbox);
8786 
8787  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
8788  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
8789  update_autopos ("sync");
8790 }
8791 
8792 void
8793 text::properties::request_autopos (void)
8794 {
8795  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
8796  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
8797  update_autopos (get___autopos_tag__ ());
8798 }
8799 
8800 void
8802 {
8803  if (! units_is ("data"))
8804  {
8805  set_xliminclude ("off");
8806  set_yliminclude ("off");
8807  set_zliminclude ("off");
8808  }
8809 
8810  Matrix pos = get_position ().matrix_value ();
8811 
8812  pos = convert_text_position (pos, *this, cached_units, get_units ());
8813 
8814  // FIXME: if the current axes view is 2D, then one should probably drop
8815  // the z-component of "pos" and leave "zliminclude" to "off".
8816 
8817  bool autopos = positionmode_is ("auto");
8818 
8819  set_position (pos);
8820 
8821  if (autopos)
8822  set_positionmode ("auto");
8823 
8824  if (units_is ("data"))
8825  {
8826  set_xliminclude ("on");
8827  set_yliminclude ("on");
8828  // FIXME: see above
8829  set_zliminclude ("off");
8830  }
8831 
8832  cached_units = get_units ();
8833 }
8834 
8835 double
8836 text::properties::get___fontsize_points__ (double box_pix_height) const
8837 {
8838  double fontsz = get_fontsize ();
8839  double parent_height = box_pix_height;
8840 
8841  if (fontunits_is ("normalized") && parent_height <= 0)
8842  {
8843  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
8844  graphics_object ax (go.get_ancestor ("axes"));
8845 
8846  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
8847  }
8848 
8849  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
8850 }
8851 
8852 // ---------------------------------------------------------------------
8853 
8856 {
8857  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
8858 }
8859 
8860 // ---------------------------------------------------------------------
8861 
8864 {
8865  octave_value fvc = get_facevertexcdata ();
8866  if (fvc.is_undefined () || fvc.isempty ())
8867  return Matrix ();
8868  else
8869  return convert_cdata (*this, fvc, cdatamapping_is ("scaled"), 2);
8870 }
8871 
8872 static bool updating_patch_data = false;
8873 
8874 void
8875 patch::properties::update_fvc (void)
8876 {
8877  if (updating_patch_data)
8878  return;
8879 
8880  Matrix xd = get_xdata ().matrix_value ();
8881  Matrix yd = get_ydata ().matrix_value ();
8882  Matrix zd = get_zdata ().matrix_value ();
8883  NDArray cd = get_cdata ().array_value ();
8884 
8885  bad_data_msg = "";
8886  if (xd.dims () != yd.dims ()
8887  || (xd.dims () != zd.dims () && ! zd.isempty ()))
8888  {
8889  bad_data_msg = "x/y/zdata must have the same dimensions";
8890  return;
8891  }
8892 
8893  // Faces and Vertices
8894  dim_vector dv;
8895  bool is3D = false;
8896  octave_idx_type nr = xd.rows ();
8897  octave_idx_type nc = xd.columns ();
8898  if (nr == 1 && nc > 1)
8899  {
8900  nr = nc;
8901  nc = 1;
8902  xd = xd.as_column ();
8903  yd = yd.as_column ();
8904  zd = zd.as_column ();
8905  }
8906 
8907  dv(0) = nr * nc;
8908  if (zd.isempty ())
8909  dv(1) = 2;
8910  else
8911  {
8912  dv(1) = 3;
8913  is3D = true;
8914  }
8915 
8916  Matrix vert (dv);
8917  Matrix idx (nc, nr);
8918 
8919  octave_idx_type kk = 0;
8920  for (octave_idx_type jj = 0; jj < nc; jj++)
8921  {
8922  for (octave_idx_type ii = 0; ii < nr; ii++)
8923  {
8924  vert(kk,0) = xd(ii,jj);
8925  vert(kk,1) = yd(ii,jj);
8926  if (is3D)
8927  vert(kk,2) = zd(ii,jj);
8928 
8929  idx(jj,ii) = static_cast<double> (kk+1);
8930 
8931  kk++;
8932  }
8933  }
8934 
8935  // facevertexcdata
8936  Matrix fvc;
8937  if (cd.ndims () == 3)
8938  {
8939  dv(0) = cd.rows () * cd.columns ();
8940  dv(1) = cd.dims ()(2);
8941  fvc = cd.reshape (dv);
8942  }
8943  else
8944  fvc = cd.as_column ();
8945 
8946  // FIXME: shouldn't we update facevertexalphadata here ?
8947 
8950  updating_patch_data = true;
8951 
8952  faces.set (idx);
8953  vertices.set (vert);
8954  facevertexcdata.set (fvc);
8955 }
8956 
8957 void
8958 patch::properties::update_data (void)
8959 {
8960  if (updating_patch_data)
8961  return;
8962 
8963  Matrix idx = get_faces ().matrix_value ().transpose ();
8964  Matrix vert = get_vertices ().matrix_value ();
8965  NDArray fvc = get_facevertexcdata ().array_value ();
8966 
8967  octave_idx_type nfaces = idx.columns ();
8968  octave_idx_type nvert = vert.rows ();
8969 
8970  // Check all vertices in faces are defined
8971  bad_data_msg = "";
8972  if (static_cast<double> (nvert) < idx.row_max ().max ())
8973  {
8974  bad_data_msg = R"(some vertices in "faces" property are undefined)";
8975  return;
8976  }
8977 
8978  // Replace NaNs
8979  if (idx.any_element_is_inf_or_nan ())
8980  {
8981  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
8982  {
8983  double valid_vert = idx(0,jj);
8984  bool turn_valid = false;
8985  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
8986  {
8987  if (octave::math::isnan (idx(ii,jj)) || turn_valid)
8988  {
8989  idx(ii,jj) = valid_vert;
8990  turn_valid = true;
8991  }
8992  else
8993  valid_vert = idx(ii,jj);
8994  }
8995  }
8996  }
8997 
8998  // Build cdata
9000  NDArray cd;
9001  bool pervertex = false;
9002 
9003  if (fvc.rows () == nfaces || fvc.rows () == 1)
9004  {
9005  dv(0) = 1;
9006  dv(1) = fvc.rows ();
9007  dv(2) = fvc.columns ();
9008  cd = fvc.reshape (dv);
9009  }
9010  else
9011  {
9012  if (! fvc.isempty ())
9013  {
9014  dv(0) = idx.rows ();
9015  dv(1) = nfaces;
9016  dv(2) = fvc.columns ();
9017  cd.resize (dv);
9018  pervertex = true;
9019  }
9020  }
9021 
9022  // Build x,y,zdata and eventually per vertex cdata
9023  Matrix xd (idx.dims ());
9024  Matrix yd (idx.dims ());
9025  Matrix zd;
9026  bool has_zd = false;
9027  if (vert.columns () > 2)
9028  {
9029  zd = Matrix (idx.dims ());
9030  has_zd = true;
9031  }
9032 
9033  for (octave_idx_type jj = 0; jj < nfaces; jj++)
9034  {
9035  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
9036  {
9037  octave_idx_type row = static_cast<octave_idx_type> (idx(ii,jj)-1);
9038  xd(ii,jj) = vert(row,0);
9039  yd(ii,jj) = vert(row,1);
9040 
9041  if (has_zd)
9042  zd(ii,jj) = vert(row,2);
9043 
9044  if (pervertex)
9045  for (int kk = 0; kk < fvc.columns (); kk++)
9046  cd(ii,jj,kk) = fvc(row,kk);
9047  }
9048  }
9049 
9052  updating_patch_data = true;
9053 
9054  set_xdata (xd);
9055  set_ydata (yd);
9056  set_zdata (zd);
9057  set_cdata (cd);
9058 }
9059 
9060 // ---------------------------------------------------------------------
9061 
9064 {
9065  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
9066 }
9067 
9068 inline void
9069 cross_product (double x1, double y1, double z1,
9070  double x2, double y2, double z2,
9071  double& x, double& y, double& z)
9072 {
9073  x += (y1 * z2 - z1 * y2);
9074  y += (z1 * x2 - x1 * z2);
9075  z += (x1 * y2 - y1 * x2);
9076 }
9077 
9078 void
9079 surface::properties::update_vertex_normals (void)
9080 {
9081  if (vertexnormalsmode_is ("auto"))
9082  {
9083  Matrix x = get_xdata ().matrix_value ();
9084  Matrix y = get_ydata ().matrix_value ();
9085  Matrix z = get_zdata ().matrix_value ();
9086 
9087  int p = z.columns ();
9088  int q = z.rows ();
9089 
9090  // FIXME: There might be a cleaner way to do this. When data is changed
9091  // the update_xdata, update_ydata, update_zdata routines are called in a
9092  // serial fashion. Until the final call to update_zdata the matrices
9093  // will be of mismatched dimensions which can cause an out-of-bound
9094  // indexing in the code below. This one-liner prevents calculating
9095  // normals until dimensions match.
9096  if (x.columns () != p || y.rows () != q)
9097  return;
9098 
9099  NDArray n (dim_vector (q, p, 3), 0.0);
9100 
9101  bool x_mat = (x.rows () == q);
9102  bool y_mat = (y.columns () == p);
9103 
9104  int i1, i2, i3, j1, j2, j3;
9105  i1 = i2 = i3 = 0;
9106  j1 = j2 = j3 = 0;
9107 
9108  for (int i = 0; i < p; i++)
9109  {
9110  if (y_mat)
9111  {
9112  i1 = i - 1;
9113  i2 = i;
9114  i3 = i + 1;
9115  }
9116 
9117  for (int j = 0; j < q; j++)
9118  {
9119  if (x_mat)
9120  {
9121  j1 = j - 1;
9122  j2 = j;
9123  j3 = j + 1;
9124  }
9125 
9126  double& nx = n(j, i, 0);
9127  double& ny = n(j, i, 1);
9128  double& nz = n(j, i, 2);
9129 
9130  if ((j > 0) && (i > 0))
9131  // upper left quadrangle
9133  (x(j1,i-1)-x(j2,i), y(j-1,i1)-y(j,i2), z(j-1,i-1)-z(j,i),
9134  x(j2,i-1)-x(j1,i), y(j,i1)-y(j-1,i2), z(j,i-1)-z(j-1,i),
9135  nx, ny, nz);
9136 
9137  if ((j > 0) && (i < (p -1)))
9138  // upper right quadrangle
9140  (x(j1,i+1)-x(j2,i), y(j-1,i3)-y(j,i2), z(j-1,i+1)-z(j,i),
9141  x(j1,i)-x(j2,i+1), y(j-1,i2)-y(j,i3), z(j-1,i)-z(j,i+1),
9142  nx, ny, nz);
9143 
9144  if ((j < (q - 1)) && (i > 0))
9145  // lower left quadrangle
9147  (x(j2,i-1)-x(j3,i), y(j,i1)-y(j+1,i2), z(j,i-1)-z(j+1,i),
9148  x(j3,i-1)-x(j2,i), y(j+1,i1)-y(j,i2), z(j+1,i-1)-z(j,i),
9149  nx, ny, nz);
9150 
9151  if ((j < (q - 1)) && (i < (p -1)))
9152  // lower right quadrangle
9154  (x(j3,i)-x(j2,i+1), y(j+1,i2)-y(j,i3), z(j+1,i)-z(j,i+1),
9155  x(j3,i+1)-x(j2,i), y(j+1,i3)-y(j,i2), z(j+1,i+1)-z(j,i),
9156  nx, ny, nz);
9157 
9158  double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
9159 
9160  nx /= d;
9161  ny /= d;
9162  nz /= d;
9163  }
9164  }
9165  vertexnormals = n;
9166  }
9167 }
9168 
9169 // ---------------------------------------------------------------------
9170 
9171 void
9172 hggroup::properties::update_limits (void) const
9173 {
9174  graphics_object go = gh_manager::get_object (__myhandle__);
9175 
9176  if (go)
9177  {
9178  go.update_axis_limits ("xlim");
9179  go.update_axis_limits ("ylim");
9180  go.update_axis_limits ("zlim");
9181  go.update_axis_limits ("clim");
9182  go.update_axis_limits ("alim");
9183  }
9184 }
9185 
9186 void
9187 hggroup::properties::update_limits (const graphics_handle& h) const
9188 {
9189  graphics_object go = gh_manager::get_object (__myhandle__);
9190 
9191  if (go)
9192  {
9193  go.update_axis_limits ("xlim", h);
9194  go.update_axis_limits ("ylim", h);
9195  go.update_axis_limits ("zlim", h);
9196  go.update_axis_limits ("clim", h);
9197  go.update_axis_limits ("alim", h);
9198  }
9199 }
9200 
9201 static bool updating_hggroup_limits = false;
9202 
9203 void
9205  const graphics_handle& h)
9206 {
9208  return;
9209 
9210  Matrix kids = Matrix (1, 1, h.value ());
9211 
9212  double min_val = octave::numeric_limits<double>::Inf ();
9213  double max_val = -octave::numeric_limits<double>::Inf ();
9214  double min_pos = octave::numeric_limits<double>::Inf ();
9215  double max_neg = -octave::numeric_limits<double>::Inf ();
9216 
9217  Matrix limits;
9218  double val;
9219 
9220  char update_type = 0;
9221 
9222  if (axis_type == "xlim" || axis_type == "xliminclude")
9223  {
9224  limits = xproperties.get_xlim ().matrix_value ();
9225  update_type = 'x';
9226  }
9227  else if (axis_type == "ylim" || axis_type == "yliminclude")
9228  {
9229  limits = xproperties.get_ylim ().matrix_value ();
9230  update_type = 'y';
9231  }
9232  else if (axis_type == "zlim" || axis_type == "zliminclude")
9233  {
9234  limits = xproperties.get_zlim ().matrix_value ();
9235  update_type = 'z';
9236  }
9237  else if (axis_type == "clim" || axis_type == "climinclude")
9238  {
9239  limits = xproperties.get_clim ().matrix_value ();
9240  update_type = 'c';
9241  }
9242  else if (axis_type == "alim" || axis_type == "aliminclude")
9243  {
9244  limits = xproperties.get_alim ().matrix_value ();
9245  update_type = 'a';
9246  }
9247 
9248  if (limits.numel () == 4)
9249  {
9250  val = limits(0);
9252  min_val = val;
9253  val = limits(1);
9255  max_val = val;
9256  val = limits(2);
9258  min_pos = val;
9259  val = limits(3);
9261  max_neg = val;
9262  }
9263  else
9264  {
9265  limits.resize (4, 1);
9266  limits(0) = min_val;
9267  limits(1) = max_val;
9268  limits(2) = min_pos;
9269  limits(3) = max_neg;
9270  }
9271 
9272  get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
9273 
9276 
9277  updating_hggroup_limits = true;
9278 
9279  if (limits(0) != min_val || limits(1) != max_val
9280  || limits(2) != min_pos || limits(3) != max_neg)
9281  {
9282  limits(0) = min_val;
9283  limits(1) = max_val;
9284  limits(2) = min_pos;
9285  limits(3) = max_neg;
9286 
9287  switch (update_type)
9288  {
9289  case 'x':
9290  xproperties.set_xlim (limits);
9291  break;
9292 
9293  case 'y':
9294  xproperties.set_ylim (limits);
9295  break;
9296 
9297  case 'z':
9298  xproperties.set_zlim (limits);
9299  break;
9300 
9301  case 'c':
9302  xproperties.set_clim (limits);
9303  break;
9304 
9305  case 'a':
9306  xproperties.set_alim (limits);
9307  break;
9308 
9309  default:
9310  break;
9311  }
9312 
9314  }
9315 }
9316 
9317 void
9319 {
9321  return;
9322 
9323  Matrix kids = xproperties.get_children ();
9324 
9325  double min_val = octave::numeric_limits<double>::Inf ();
9326  double max_val = -octave::numeric_limits<double>::Inf ();
9327  double min_pos = octave::numeric_limits<double>::Inf ();
9328  double max_neg = -octave::numeric_limits<double>::Inf ();
9329 
9330  char update_type = 0;
9331 
9332  if (axis_type == "xlim" || axis_type == "xliminclude")
9333  {
9334  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
9335 
9336  update_type = 'x';
9337  }
9338  else if (axis_type == "ylim" || axis_type == "yliminclude")
9339  {
9340  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
9341 
9342  update_type = 'y';
9343  }
9344  else if (axis_type == "zlim" || axis_type == "zliminclude")
9345  {
9346  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
9347 
9348  update_type = 'z';
9349  }
9350  else if (axis_type == "clim" || axis_type == "climinclude")
9351  {
9352  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
9353 
9354  update_type = 'c';
9355  }
9356  else if (axis_type == "alim" || axis_type == "aliminclude")
9357  {
9358  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
9359 
9360  update_type = 'a';
9361  }
9362 
9365 
9366  updating_hggroup_limits = true;
9367 
9368  Matrix limits (1, 4, 0.0);
9369 
9370  limits(0) = min_val;
9371  limits(1) = max_val;
9372  limits(2) = min_pos;
9373  limits(3) = max_neg;
9374 
9375  switch (update_type)
9376  {
9377  case 'x':
9378  xproperties.set_xlim (limits);
9379  break;
9380 
9381  case 'y':
9382  xproperties.set_ylim (limits);
9383  break;
9384 
9385  case 'z':
9386  xproperties.set_zlim (limits);
9387  break;
9388 
9389  case 'c':
9390  xproperties.set_clim (limits);
9391  break;
9392 
9393  case 'a':
9394  xproperties.set_alim (limits);
9395  break;
9396 
9397  default:
9398  break;
9399  }
9400 
9402 }
9403 
9404 // ---------------------------------------------------------------------
9405 
9407 {
9408  std::list<graphics_handle> lst = xproperties.get_dependent_obj_list ();
9409  std::list<graphics_handle>::const_iterator it;
9410 
9411  for (it = lst.begin (); it != lst.end (); it++)
9412  {
9414 
9415  if (go.valid_object () &&
9416  go.get ("uicontextmenu") == xproperties.get___myhandle__ ())
9417  go.set ("uicontextmenu", Matrix ());
9418  }
9419 }
9420 
9421 /*
9422 ## Test deletion/reset of uicontextmenu
9423 %!test
9424 %! hf = figure ("visible", "off");
9425 %! hax = axes ("parent", hf);
9426 %! unwind_protect
9427 %! hctx1 = uicontextmenu ("parent", hf);
9428 %! hctx2 = uicontextmenu ("parent", hf);
9429 %! set (hf, "uicontextmenu", hctx2);
9430 %! set (hax, "uicontextmenu", hctx2);
9431 %! assert (get (hf, "uicontextmenu"), hctx2);
9432 %! assert (get (hax, "uicontextmenu"), hctx2);
9433 %! assert (get (hf, "children"), [hctx2; hctx1; hax]);
9434 %! delete (hctx2);
9435 %! pause (.005);
9436 %! assert (get (hf, "uicontextmenu"), []);
9437 %! assert (get (hax, "uicontextmenu"), []);
9438 %! assert (get (hf, "children"), [hctx1; hax]);
9439 %! set (hf, "uicontextmenu", hctx1);
9440 %! assert (get (hf, "uicontextmenu"), hctx1);
9441 %! set (hf, "uicontextmenu", []);
9442 %! assert (get (hf, "uicontextmenu"), []);
9443 %! assert (get (hf, "children"), [hctx1; hax]);
9444 %! unwind_protect_cleanup
9445 %! close (hf);
9446 %! end_unwind_protect;
9447 */
9448 
9449 // ---------------------------------------------------------------------
9450 
9452 uicontrol::properties::get_extent (void) const
9453 {
9454  Matrix m = extent.get ().matrix_value ();
9455 
9457  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
9458  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
9459 
9460  return convert_position (m, "pixels", get_units (), parent_size);
9461 }
9462 
9463 void
9464 uicontrol::properties::update_text_extent (void)
9465 {
9466  text_element *elt;
9467  octave::text_renderer txt_renderer;
9468  Matrix box;
9469 
9470  // FIXME: parsed content should be cached for efficiency
9471  // FIXME: support multiline text
9472 
9473  elt = text_parser::parse (get_string_string (), "none");
9474 
9475  gh_manager::auto_lock guard;
9476  txt_renderer.set_font (get_fontname (), get_fontweight (),
9477  get_fontangle (), get_fontsize ());
9478 
9479  box = txt_renderer.get_extent (elt, 0);
9480 
9481  delete elt;
9482 
9483  Matrix ext (1, 4);
9484 
9485  // FIXME: also handle left and bottom components
9486 
9487  ext(0) = ext(1) = 1;
9488  ext(2) = box(0);
9489  ext(3) = box(1);
9490 
9491  set_extent (ext);
9492 }
9493 
9494 void
9496 {
9497  Matrix pos = get_position ().matrix_value ();
9498 
9500  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
9501  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
9502 
9503  pos = convert_position (pos, cached_units, get_units (), parent_size);
9504  set_position (pos);
9505 
9506  cached_units = get_units ();
9507 }
9508 
9509 void
9510 uicontrol::properties::set_style (const octave_value& st)
9511 {
9512  if (! get___object__ ().isempty ())
9513  error ("set: cannot change the style of a uicontrol object after creation.");
9514 
9515  style = st;
9516 
9517  // if we know know what we are, can override value for listbox and popupmenu
9518  if (style_is ("listbox") || style_is ("popupmenu"))
9519  {
9520  Matrix v = value.get ().matrix_value ();
9521  if (v.numel () == 1 && v (0) == 0)
9522  value.set (octave_value (1));
9523  }
9524 }
9525 
9526 Matrix
9528  const Matrix& parent_pix_size) const
9529 {
9530  Matrix pos = get_position ().matrix_value ();
9531  Matrix parent_size (parent_pix_size);
9532 
9533  if (parent_size.isempty ())
9534  {
9536 
9537  if (go.valid_object ())
9538  parent_size =
9539  go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
9540  else
9541  parent_size = default_figure_position ();
9542  }
9543 
9544  pos = convert_position (pos, get_units (), "pixels", parent_size);
9545 
9546  pos(0)--;
9547  pos(1)--;
9548  pos(1) = parent_size(1) - pos(1) - pos(3);
9549 
9550  return pos;
9551 }
9552 
9553 void
9554 uicontrol::properties::set_fontunits (const octave_value& val)
9555 {
9556  caseless_str old_fontunits = get_fontunits ();
9557 
9558  if (fontunits.set (val, true))
9559  {
9560  update_fontunits (old_fontunits);
9561  mark_modified ();
9562  }
9563 }
9564 
9565 void
9567 {
9568  caseless_str new_units = get_fontunits ();
9569  double parent_height = get_boundingbox (false).elem (3);
9570  double fontsz = get_fontsize ();
9571 
9572  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
9573 
9574  fontsize.set (octave_value (fontsz), true);
9575 }
9576 
9577 double
9579 {
9580  double fontsz = get_fontsize ();
9581  double parent_height = box_pix_height;
9582 
9583  if (fontunits_is ("normalized") && parent_height <= 0)
9584  parent_height = get_boundingbox (false).elem (3);
9585 
9586  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
9587 }
9588 
9589 // ---------------------------------------------------------------------
9590 
9591 Matrix
9593  const Matrix& parent_pix_size) const
9594 {
9595  Matrix pos = get_position ().matrix_value ();
9596  Matrix parent_size (parent_pix_size);
9597 
9598  if (parent_size.isempty ())
9599  {
9601 
9602  parent_size =
9603  go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
9604  }
9605 
9606  pos = convert_position (pos, get_units (), "pixels", parent_size);
9607 
9608  pos(0)--;
9609  pos(1)--;
9610  pos(1) = parent_size(1) - pos(1) - pos(3);
9611 
9612  if (internal)
9613  {
9614  double outer_height = pos(3);
9615 
9616  pos(0) = pos(1) = 0;
9617 
9618  if (! bordertype_is ("none"))
9619  {
9620  double bw = get_borderwidth ();
9621  double mul = 1.0;
9622 
9623  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
9624  mul = 2.0;
9625 
9626  pos(0) += mul * bw;
9627  pos(1) += mul * bw;
9628  pos(2) -= 2 * mul * bw;
9629  pos(3) -= 2 * mul * bw;
9630  }
9631 
9632  if (! get_title ().empty ())
9633  {
9634  double fontsz = get_fontsize ();
9635 
9636  if (! fontunits_is ("pixels"))
9637  {
9638  double res = xget (0, "screenpixelsperinch").double_value ();
9639 
9640  if (fontunits_is ("points"))
9641  fontsz *= (res / 72.0);
9642  else if (fontunits_is ("inches"))
9643  fontsz *= res;
9644  else if (fontunits_is ("centimeters"))
9645  fontsz *= (res / 2.54);
9646  else if (fontunits_is ("normalized"))
9647  fontsz *= outer_height;
9648  }
9649 
9650  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
9651  || titleposition_is ("righttop"))
9652  pos(1) += (fontsz / 2);
9653  pos(3) -= (fontsz / 2);
9654  }
9655  }
9656 
9657  return pos;
9658 }
9659 
9660 void
9661 uibuttongroup::properties::set_units (const octave_value& val)
9662 {
9663  caseless_str old_units = get_units ();
9664 
9665  if (units.set (val, true))
9666  {
9667  update_units (old_units);
9668  mark_modified ();
9669  }
9670 }
9671 
9672 void
9674 {
9675  Matrix pos = get_position ().matrix_value ();
9676 
9678  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
9679  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
9680 
9681  pos = convert_position (pos, old_units, get_units (), parent_size);
9682  set_position (pos);
9683 }
9684 
9685 void
9686 uibuttongroup::properties::set_fontunits (const octave_value& val)
9687 {
9688  caseless_str old_fontunits = get_fontunits ();
9689 
9690  if (fontunits.set (val, true))
9691  {
9692  update_fontunits (old_fontunits);
9693  mark_modified ();
9694  }
9695 }
9696 
9697 void
9699 {
9700  caseless_str new_units = get_fontunits ();
9701  double parent_height = get_boundingbox (false).elem (3);
9702  double fontsz = get_fontsize ();
9703 
9704  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
9705 
9706  set_fontsize (octave_value (fontsz));
9707 }
9708 
9709 double
9711 {
9712  double fontsz = get_fontsize ();
9713  double parent_height = box_pix_height;
9714 
9715  if (fontunits_is ("normalized") && parent_height <= 0)
9716  parent_height = get_boundingbox (false).elem (3);
9717 
9718  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
9719 }
9720 
9721 void
9722 uibuttongroup::properties::set_selectedobject (const octave_value& v)
9723 {
9724  graphics_handle current_selectedobject = get_selectedobject();
9725  selectedobject = current_selectedobject;
9726  if (v.isempty ())
9727  {
9728  if (current_selectedobject.ok ())
9729  {
9730  selectedobject = graphics_handle ();
9731  mark_modified ();
9732  }
9733  return;
9734  }
9735 
9736  graphics_handle val (v);
9737  if (val.ok ())
9738  {
9740  base_properties& gop = go.get_properties ();
9741 
9742  if (go.valid_object ()
9743  && gop.get_parent () == get___myhandle__ ()
9744  && go.isa ("uicontrol"))
9745  {
9747  = dynamic_cast<uicontrol::properties&> (go.get_properties ());
9748  const caseless_str& style = cop.get_style ();
9749  if (style.compare ("radiobutton") || style.compare ("togglebutton"))
9750  {
9751  selectedobject = val;
9752  mark_modified ();
9753  return;
9754  }
9755  }
9756  }
9757  err_set_invalid ("selectedobject");
9758 }
9759 
9760 // ---------------------------------------------------------------------
9761 
9762 Matrix
9764  const Matrix& parent_pix_size) const
9765 {
9766  Matrix pos = get_position ().matrix_value ();
9767  Matrix parent_size (parent_pix_size);
9768 
9769  if (parent_size.isempty ())
9770  {
9772 
9773  parent_size =
9774  go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
9775  }
9776 
9777  pos = convert_position (pos, get_units (), "pixels", parent_size);
9778 
9779  pos(0)--;
9780  pos(1)--;
9781  pos(1) = parent_size(1) - pos(1) - pos(3);
9782 
9783  if (internal)
9784  {
9785  double outer_height = pos(3);
9786 
9787  pos(0) = pos(1) = 0;
9788 
9789  if (! bordertype_is ("none"))
9790  {
9791  double bw = get_borderwidth ();
9792  double mul = 1.0;
9793 
9794  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
9795  mul = 2.0;
9796 
9797  pos(0) += mul * bw;
9798  pos(1) += mul * bw;
9799  pos(2) -= 2 * mul * bw;
9800  pos(3) -= 2 * mul * bw;
9801  }
9802 
9803  if (! get_title ().empty ())
9804  {
9805  double fontsz = get_fontsize ();
9806 
9807  if (! fontunits_is ("pixels"))
9808  {
9809  double res = xget (0, "screenpixelsperinch").double_value ();
9810 
9811  if (fontunits_is ("points"))
9812  fontsz *= (res / 72.0);
9813  else if (fontunits_is ("inches"))
9814  fontsz *= res;
9815  else if (fontunits_is ("centimeters"))
9816  fontsz *= (res / 2.54);
9817  else if (fontunits_is ("normalized"))
9818  fontsz *= outer_height;
9819  }
9820 
9821  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
9822  || titleposition_is ("righttop"))
9823  pos(1) += (fontsz / 2);
9824  pos(3) -= (fontsz / 2);
9825  }
9826  }
9827 
9828  return pos;
9829 }
9830 
9831 void
9832 uipanel::properties::set_units (const octave_value& val)
9833 {
9834  caseless_str old_units = get_units ();
9835 
9836  if (units.set (val, true))
9837  {
9838  update_units (old_units);
9839  mark_modified ();
9840  }
9841 }
9842 
9843 void
9844 uipanel::properties::update_units (const caseless_str& old_units)
9845 {
9846  Matrix pos = get_position ().matrix_value ();
9847 
9849  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
9850  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
9851 
9852  pos = convert_position (pos, old_units, get_units (), parent_size);
9853  set_position (pos);
9854 }
9855 
9856 void
9857 uipanel::properties::set_fontunits (const octave_value& val)
9858 {
9859  caseless_str old_fontunits = get_fontunits ();
9860 
9861  if (fontunits.set (val, true))
9862  {
9863  update_fontunits (old_fontunits);
9864  mark_modified ();
9865  }
9866 }
9867 
9868 void
9869 uipanel::properties::update_fontunits (const caseless_str& old_units)
9870 {
9871  caseless_str new_units = get_fontunits ();
9872  double parent_height = get_boundingbox (false).elem (3);
9873  double fontsz = get_fontsize ();
9874 
9875  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
9876 
9877  set_fontsize (octave_value (fontsz));
9878 }
9879 
9880 double
9882 {
9883  double fontsz = get_fontsize ();
9884  double parent_height = box_pix_height;
9885 
9886  if (fontunits_is ("normalized") && parent_height <= 0)
9887  parent_height = get_boundingbox (false).elem (3);
9888 
9889  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
9890 }
9891 
9892 // ---------------------------------------------------------------------
9893 
9896 {
9898 
9899  if (retval.is_undefined ())
9900  {
9901  graphics_handle parent_h = get_parent ();
9902  graphics_object parent_go = gh_manager::get_object (parent_h);
9903 
9904  retval = parent_go.get_default (pname);
9905  }
9906 
9907  return retval;
9908 }
9909 
9910 void
9912 {
9913  // empty list of local defaults
9915 
9918 }
9919 
9920 // ---------------------------------------------------------------------
9921 
9924 {
9925  graphics_handle parent_h = get_parent ();
9926  graphics_object parent_go = gh_manager::get_object (parent_h);
9927 
9928  return parent_go.get_default (type () + pname);
9929 }
9930 
9933 {
9934  graphics_object parent_go = gh_manager::get_object (0);
9935 
9936  return parent_go.get_factory_default (type () + name);
9937 }
9938 
9939 // We use a random value for the handle to avoid issues with plots and
9940 // scalar values for the first argument.
9942  : handle_map (), handle_free_list (),
9943  next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
9944  figure_list (), graphics_lock (), event_queue (),
9945  callback_objects (), event_processing (0)
9946 {
9947  handle_map[0] = graphics_object (new root_figure ());
9948 
9949  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ("gh_manager");
9950 
9951  // Make sure the default graphics toolkit is registered.
9952  gtk_mgr.default_toolkit ();
9953 }
9954 
9955 void
9957 {
9958  instance = new gh_manager ();
9959 
9960  if (instance)
9962 }
9963 
9966  const graphics_handle& p,
9967  bool integer_figure_handle,
9968  bool do_createfcn,
9969  bool do_notify_toolkit)
9970 {
9971  graphics_handle h = get_handle (integer_figure_handle);
9972 
9973  base_graphics_object *bgo = nullptr;
9974 
9975  bgo = make_graphics_object_from_type (go_name, h, p);
9976 
9977  if (! bgo)
9978  error ("gh_manager::do_make_graphics_handle: invalid object type '%s'",
9979  go_name.c_str ());
9980 
9981  graphics_object go (bgo);
9982 
9983  handle_map[h] = go;
9984 
9985  // Overriding defaults will work now because the handle is valid
9986  // and we can find parent objects (not just handles).
9987  go.override_defaults ();
9988 
9989  if (go_name == "axes")
9990  {
9991  // Handle defaults for labels since overriding defaults for
9992  // them can't work before the axes object is fully
9993  // constructed.
9994 
9995  axes::properties& props =
9996  dynamic_cast<axes::properties&> (go.get_properties ());
9997 
9998  graphics_object tgo;
9999 
10000  tgo = gh_manager::get_object (props.get_xlabel ());
10001  tgo.override_defaults ();
10002 
10003  tgo = gh_manager::get_object (props.get_ylabel ());
10004  tgo.override_defaults ();
10005 
10006  tgo = gh_manager::get_object (props.get_zlabel ());
10007  tgo.override_defaults ();
10008 
10009  tgo = gh_manager::get_object (props.get_title ());
10010  tgo.override_defaults ();
10011  }
10012 
10013  if (do_createfcn)
10014  bgo->get_properties ().execute_createfcn ();
10015 
10016  // Notify graphics toolkit.
10017  if (do_notify_toolkit)
10018  go.initialize ();
10019 
10020  return h;
10021 }
10022 
10024 gh_manager::do_make_figure_handle (double val, bool do_notify_toolkit)
10025 {
10026  graphics_handle h = val;
10027 
10028  base_graphics_object *bgo = new figure (h, 0);
10029  graphics_object go (bgo);
10030 
10031  handle_map[h] = go;
10032 
10033  // Notify graphics toolkit.
10034  if (do_notify_toolkit)
10035  go.initialize ();
10036 
10037  go.override_defaults ();
10038 
10039  return h;
10040 }
10041 
10042 void
10044 {
10045  do_pop_figure (h);
10046 
10047  figure_list.push_front (h);
10048 }
10049 
10050 void
10052 {
10053  for (auto it = figure_list.begin (); it != figure_list.end (); it++)
10054  {
10055  if (*it == h)
10056  {
10057  figure_list.erase (it);
10058  break;
10059  }
10060  }
10061 }
10062 
10063 class
10065 {
10066 public:
10068  const octave_value& data = Matrix ())
10069  : base_graphics_event (), handle (h), callback_name (name),
10070  callback (), callback_data (data) { }
10071 
10073  const octave_value& data = Matrix ())
10074  : base_graphics_event (), handle (h), callback_name (),
10075  callback (cb), callback_data (data) { }
10076 
10077  void execute (void)
10078  {
10079  if (callback.is_defined ())
10080  gh_manager::execute_callback (handle, callback, callback_data);
10081  else
10082  gh_manager::execute_callback (handle, callback_name, callback_data);
10083  }
10084 
10085 private:
10087  : base_graphics_event (), handle (), callback_name (), callback_data ()
10088  { }
10089 
10090 private:
10095 };
10096 
10097 class
10099 {
10100 public:
10101 
10102  // function_event objects must be created with at least a function.
10103 
10104  function_event (void) = delete;
10105 
10107  : base_graphics_event (), function (fcn), function_data (data)
10108  { }
10109 
10110  // No copying!
10111 
10112  function_event (const function_event&) = delete;
10113 
10114  function_event & operator = (const function_event&) = delete;
10115 
10116  void execute (void)
10117  {
10118  function (function_data);
10119  }
10120 
10121 private:
10122 
10124 
10126 };
10127 
10128 class
10130 {
10131 public:
10133  const octave_value& value, bool do_notify_toolkit = true)
10134  : base_graphics_event (), handle (h), property_name (name),
10135  property_value (value), notify_toolkit (do_notify_toolkit) { }
10136 
10137  void execute (void)
10138  {
10139  gh_manager::auto_lock guard;
10140 
10142 
10143  if (go)
10144  {
10145  property p = go.get_properties ().get_property (property_name);
10146 
10147  if (p.ok ())
10148  p.set (property_value, true, notify_toolkit);
10149  }
10150  }
10151 
10152 private:
10153  set_event (void)
10154  : base_graphics_event (), handle (), property_name (), property_value ()
10155  { }
10156 
10157 private:
10162 };
10163 
10166  const std::string& name,
10167  const octave_value& data)
10168 {
10169  return graphics_event (new callback_event (h, name, data));
10170 }
10171 
10174  const octave_value& cb,
10175  const octave_value& data)
10176 {
10177  return graphics_event (new callback_event (h, cb, data));
10178 }
10179 
10182  void *data)
10183 {
10184  return graphics_event (new function_event (fcn, data));
10185 }
10186 
10189  const std::string& name,
10190  const octave_value& data,
10191  bool notify_toolkit)
10192 {
10193  return graphics_event (new set_event (h, name, data, notify_toolkit));
10194 }
10195 
10196 static void
10198 {
10200  root_figure::properties& props =
10201  dynamic_cast<root_figure::properties&> (go.get_properties ());
10202 
10203  props.set_callbackobject (h.as_octave_value ());
10204 }
10205 
10206 void
10208 {
10209  gh_manager::auto_lock guard;
10210 
10211  callback_objects.pop_front ();
10212 
10213  xset_gcbo (callback_objects.empty ()
10214  ? graphics_handle () : callback_objects.front ().get_handle ());
10215 }
10216 
10217 void
10219  const octave_value& l)
10220 {
10223  else
10224  {
10225  gh_manager::auto_lock guard;
10226 
10228  }
10229 }
10230 
10231 void
10233  const octave_value& cb_arg,
10234  const octave_value& data)
10235 {
10236  if (cb_arg.is_defined () && ! cb_arg.isempty ())
10237  {
10238  octave_value_list args;
10239  octave_function *fcn = nullptr;
10240 
10241  args(0) = h.as_octave_value ();
10242  if (data.is_defined ())
10243  args(1) = data;
10244  else
10245  args(1) = Matrix ();
10246 
10249 
10250  graphics_object go (get_object (h));
10251  if (go)
10252  {
10253  // FIXME: Is the lock necessary when we're only calling a
10254  // const "get" method?
10255  gh_manager::auto_lock guard;
10256  callback_objects.push_front (go);
10257  if (go.get ("handlevisibility").string_value () != "off")
10258  xset_gcbo (h);
10259  }
10260 
10261  // Copy CB because "function_value" method is non-const.
10262 
10263  octave_value cb = cb_arg;
10264 
10265  if (cb.is_function () || cb.is_function_handle ())
10266  fcn = cb.function_value ();
10267  else if (cb.is_string ())
10268  {
10269  int status;
10270  std::string s = cb.string_value ();
10271 
10272  try
10273  {
10274  octave::eval_string (s, false, status, 0);
10275  }
10276  catch (octave::execution_exception&)
10277  {
10278  std::cerr << "execution error in graphics callback function"
10279  << std::endl;
10280  Flasterr (ovl ("execution error in graphics callback function"));
10282  }
10283  }
10284  else if (cb.iscell () && cb.length () > 0
10285  && (cb.rows () == 1 || cb.columns () == 1)
10286  && (cb.cell_value ()(0).is_function ()
10287  || cb.cell_value ()(0).is_function_handle ()))
10288  {
10289  Cell c = cb.cell_value ();
10290 
10291  fcn = c(0).function_value ();
10292 
10293  for (int i = 1; i < c.numel () ; i++)
10294  args(1+i) = c(i);
10295  }
10296  else
10297  {
10298  std::string nm = cb.class_name ();
10299  error ("trying to execute non-executable object (class = %s)",
10300  nm.c_str ());
10301  }
10302 
10303  if (fcn)
10304  try
10305  {
10306  octave::feval (fcn, args);
10307  }
10308  catch (octave::execution_exception&)
10309  {
10310  std::cerr << "execution error in graphics callback function"
10311  << std::endl;
10312  Flasterr (ovl ("execution error in graphics callback function"));
10314  }
10315 
10316  // Redraw after interacting with a user-interface (ui*) object.
10317  if (Vdrawnow_requested)
10318  {
10319  if (go)
10320  {
10321  std::string go_name
10323 
10324  if (go_name.length () > 1
10325  && go_name[0] == 'u' && go_name[1] == 'i')
10326  {
10327  Fdrawnow ();
10328  Vdrawnow_requested = false;
10329  }
10330  }
10331  }
10332  }
10333 }
10334 
10335 void
10337 {
10338  event_queue.push_back (e);
10339 
10341 }
10342 
10343 void
10345  const octave_value& data)
10346 {
10347  gh_manager::auto_lock guard;
10348 
10349  graphics_object go = get_object (h);
10350 
10351  if (go.valid_object ())
10352  {
10353  if (callback_objects.empty ())
10355  else
10356  {
10357  const graphics_object& current = callback_objects.front ();
10358 
10359  if (current.get_properties ().is_interruptible ())
10361  data));
10362  else
10363  {
10364  std::string busy_action (go.get_properties ().get_busyaction ());
10365 
10366  if (busy_action == "queue")
10368  data));
10369  else
10370  {
10371  caseless_str cname (name);
10372 
10373  if (cname.compare ("deletefcn")
10374  || cname.compare ("createfcn")
10375  || (go.isa ("figure")
10376  && (cname.compare ("closerequestfcn")
10377  || cname.compare ("resizefcn"))))
10378  do_post_event (
10380  }
10381  }
10382  }
10383  }
10384 }
10385 
10386 void
10388 {
10389  gh_manager::auto_lock guard;
10390 
10392 }
10393 
10394 void
10396  const octave_value& value, bool notify_toolkit)
10397 {
10398  gh_manager::auto_lock guard;
10399 
10401  notify_toolkit));
10402 }
10403 
10404 int
10406 {
10407  graphics_event e;
10408  bool old_Vdrawnow_requested = Vdrawnow_requested;
10409  bool events_executed = false;
10410 
10411  do
10412  {
10413  e = graphics_event ();
10414 
10415  {
10416  gh_manager::auto_lock guard;
10417 
10418  if (! event_queue.empty ())
10419  {
10420  if (callback_objects.empty () || force)
10421  {
10422  e = event_queue.front ();
10423 
10424  event_queue.pop_front ();
10425  }
10426  else
10427  {
10428  const graphics_object& go = callback_objects.front ();
10429 
10430  if (go.get_properties ().is_interruptible ())
10431  {
10432  e = event_queue.front ();
10433 
10434  event_queue.pop_front ();
10435  }
10436  }
10437  }
10438  }
10439 
10440  if (e.ok ())
10441  {
10442  e.execute ();
10443  events_executed = true;
10444  }
10445  }
10446  while (e.ok ());
10447 
10448  {
10449  gh_manager::auto_lock guard;
10450 
10451  if (event_queue.empty () && event_processing == 0)
10453  }
10454 
10455  if (events_executed)
10457 
10458  if (Vdrawnow_requested && ! old_Vdrawnow_requested)
10459  {
10460  Fdrawnow ();
10461 
10462  Vdrawnow_requested = false;
10463  }
10464 
10465  return 0;
10466 }
10467 
10468 void
10470 {
10471  gh_manager::auto_lock guard;
10472 
10473  if (enable)
10474  {
10475  event_processing++;
10476 
10478  }
10479  else
10480  {
10481  event_processing--;
10482 
10483  if (event_queue.empty () && event_processing == 0)
10485  }
10486 }
10487 
10490 {
10492 
10493  plist_map["figure"] = figure::properties::factory_defaults ();
10494  plist_map["axes"] = axes::properties::factory_defaults ();
10495  plist_map["line"] = line::properties::factory_defaults ();
10496  plist_map["text"] = text::properties::factory_defaults ();
10497  plist_map["image"] = image::properties::factory_defaults ();
10498  plist_map["patch"] = patch::properties::factory_defaults ();
10499  plist_map["surface"] = surface::properties::factory_defaults ();
10500  plist_map["light"] = light::properties::factory_defaults ();
10501  plist_map["hggroup"] = hggroup::properties::factory_defaults ();
10502  plist_map["uimenu"] = uimenu::properties::factory_defaults ();
10503  plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
10504  plist_map["uibuttongroup"] = uibuttongroup::properties::factory_defaults ();
10505  plist_map["uipanel"] = uipanel::properties::factory_defaults ();
10506  plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
10507  plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
10508  plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
10509  plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
10510 
10511  return plist_map;
10512 }
10513 
10514 // ---------------------------------------------------------------------
10515 
10516 DEFUN (ishghandle, args, ,
10517  doc: /* -*- texinfo -*-
10518 @deftypefn {} {} ishghandle (@var{h})
10519 Return true if @var{h} is a graphics handle and false otherwise.
10520 
10521 @var{h} may also be a matrix of handles in which case a logical array is
10522 returned that is true where the elements of @var{h} are graphics handles and
10523 false where they are not.
10524 @seealso{isgraphics, isaxes, isfigure, ishandle}
10525 @end deftypefn */)
10526 {
10527  gh_manager::auto_lock guard;
10528 
10529  if (args.length () != 1)
10530  print_usage ();
10531 
10532  return ovl (is_hghandle (args(0)));
10533 }
10534 
10535 /*
10536 %!test
10537 %! hf = figure ("visible", "off");
10538 %! unwind_protect
10539 %! assert (ishghandle (hf));
10540 %! assert (! ishghandle (-hf));
10541 %! ax = gca;
10542 %! l = line;
10543 %! assert (ishghandle (ax));
10544 %! assert (! ishghandle (-ax));
10545 %! assert (ishghandle ([l, -1, ax, hf]), logical ([1, 0, 1, 1]));
10546 %! assert (ishghandle ([l, -1, ax, hf]'), logical ([1, 0, 1, 1]'));
10547 %! unwind_protect_cleanup
10548 %! close (hf);
10549 %! end_unwind_protect
10550 
10551 %!assert (ishghandle ([-1 0]), [false true])
10552 */
10553 
10554 static bool
10556 {
10557  return h.ok () && gh_manager::is_handle_visible (h);
10558 }
10559 
10560 static bool
10562 {
10564 }
10565 
10566 static octave_value
10568 {
10569  octave_value retval = false;
10570 
10571  if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
10572  retval = true;
10573  else if (val.isnumeric () && val.isreal ())
10574  {
10575  const NDArray handles = val.array_value ();
10576 
10577  boolNDArray result (handles.dims ());
10578 
10579  for (octave_idx_type i = 0; i < handles.numel (); i++)
10580  result.xelem (i) = is_handle_visible (handles(i));
10581 
10582  retval = result;
10583  }
10584 
10585  return retval;
10586 }
10587 
10588 DEFUN (__is_handle_visible__, args, ,
10589  doc: /* -*- texinfo -*-
10590 @deftypefn {} {} __is_handle_visible__ (@var{h})
10591 Undocumented internal function.
10592 @end deftypefn */)
10593 {
10594  if (args.length () != 1)
10595  print_usage ();
10596 
10597  return ovl (is_handle_visible (args(0)));
10598 }
10599 
10600 DEFUN (reset, args, ,
10601  doc: /* -*- texinfo -*-
10602 @deftypefn {} {} reset (@var{h})
10603 Reset the properties of the graphic object @var{h} to their default values.
10604 
10605 For figures, the properties @qcode{"position"}, @qcode{"units"},
10606 @qcode{"windowstyle"}, and @qcode{"paperunits"} are not affected.
10607 For axes, the properties @qcode{"position"} and @qcode{"units"} are
10608 not affected.
10609 
10610 The input @var{h} may also be a vector of graphic handles in which case
10611 each individual object will be reset.
10612 @seealso{cla, clf, newplot}
10613 @end deftypefn */)
10614 {
10615  if (args.length () != 1)
10616  print_usage ();
10617 
10618  // get vector of graphics handles
10619  ColumnVector hcv = args(0).xvector_value ("reset: H must be a graphics handle");
10620 
10621  // loop over graphics objects
10622  for (octave_idx_type n = 0; n < hcv.numel (); n++)
10623  gh_manager::get_object (hcv(n)).reset_default_properties ();
10624 
10625  Vdrawnow_requested = true;
10626 
10627  return ovl ();
10628 }
10629 
10630 /*
10631 
10632 %!test # line object
10633 %! hf = figure ("visible", "off");
10634 %! unwind_protect
10635 %! tol = 20 * eps;
10636 %! hax = axes ("defaultlinelinewidth", 3);
10637 %!
10638 %! hli = line (1:10, 1:10, 1:10, "marker", "o",
10639 %! "markerfacecolor", "b", "linestyle", ":");
10640 %!
10641 %! reset (hli);
10642 %! assert (get (hli, "marker"), get (0, "defaultlinemarker"));
10643 %! assert (get (hli, "markerfacecolor"),
10644 %! get (0, "defaultlinemarkerfacecolor"));
10645 %! assert (get (hli, "linestyle"), get (0, "defaultlinelinestyle"));
10646 %! assert (get (hli, "linewidth"), 3, tol); # parent axes defaults
10647 %!
10648 %! unwind_protect_cleanup
10649 %! close (hf);
10650 %! end_unwind_protect
10651 
10652 %!test # patch object
10653 %! hf = figure ("visible", "off");
10654 %! unwind_protect
10655 %! tol = 20 * eps;
10656 %! t1 = (1/16:1/8:1)' * 2*pi;
10657 %! t2 = ((1/16:1/16:1)' + 1/32) * 2*pi;
10658 %! x1 = sin (t1) - 0.8;
10659 %! y1 = cos (t1);
10660 %! x2 = sin (t2) + 0.8;
10661 %! y2 = cos (t2);
10662 %! vert = [x1, y1; x2, y2];
10663 %! fac = [1:8,NaN(1,8);9:24];
10664 %! hpa = patch ("Faces",fac, "Vertices",vert, "FaceColor","r");
10665 %!
10666 %! reset (hpa);
10667 %! assert (get (hpa, "faces"), get (0, "defaultpatchfaces"), tol);
10668 %! assert (get (hpa, "vertices"), get (0, "defaultpatchvertices"), tol);
10669 %! assert (get (hpa, "facevertexcdata"),
10670 %! get (0, "defaultpatchfacevertexcdata"), tol);
10671 %! unwind_protect_cleanup
10672 %! close (hf);
10673 %! end_unwind_protect
10674 
10675 %!test # surface object
10676 %! hf = figure ("visible", "off");
10677 %! unwind_protect
10678 %! tol = 20 * eps;
10679 %! hsu = surface (peaks, "edgecolor", "none");
10680 %!
10681 %! reset (hsu);
10682 %! assert (get (hsu, "xdata"), get (0, "defaultsurfacexdata"), tol);
10683 %! assert (get (hsu, "ydata"), get (0, "defaultsurfaceydata"), tol);
10684 %! assert (get (hsu, "zdata"), get (0, "defaultsurfacezdata"), tol);
10685 %! assert (get (hsu, "edgecolor"), get (0, "defaultsurfaceedgecolor"), tol);
10686 %! unwind_protect_cleanup
10687 %! close (hf);
10688 %! end_unwind_protect
10689 
10690 %!test # image object
10691 %! hf = figure ("visible", "off");
10692 %! unwind_protect
10693 %! tol = 20 * eps;
10694 %! him = image (rand (10,10), "cdatamapping", "scaled");
10695 %!
10696 %! reset (him);
10697 %! assert (get (him, "cdata"), get (0, "defaultimagecdata"), tol);
10698 %! assert (get (him, "cdatamapping"),
10699 %! get (0, "defaultimagecdatamapping"), tol);
10700 %! unwind_protect_cleanup
10701 %! close (hf);
10702 %! end_unwind_protect
10703 
10704 %!test # text object
10705 %! hf = figure ("visible", "off");
10706 %! unwind_protect
10707 %! tol = 20 * eps;
10708 %! hte = text (5, 5, "Hi!", "fontsize", 20 ,"color", "r");
10709 %!
10710 %! reset (hte);
10711 %! assert (get (hte, "position"), get (0, "defaulttextposition"), tol);
10712 %! assert (get (hte, "fontsize"), get (0, "defaulttextfontsize"), tol);
10713 %! assert (get (hte, "color"), get (0, "defaulttextcolor"), tol);
10714 %! unwind_protect_cleanup
10715 %! close (hf);
10716 %! end_unwind_protect
10717 
10718 %!test # axes object
10719 %! hf = figure ("visible", "off");
10720 %! unwind_protect
10721 %! tol = 20 * eps;
10722 %! pos = get (0, "defaultaxesposition") * .5;
10723 %! hax = axes ("linewidth", 2, "position", pos);
10724 %! title ("Reset me, please!");
10725 %!
10726 %! reset (hax);
10727 %! assert (get (hax, "linewidth"), get (0, "defaultaxeslinewidth"), tol);
10728 %! assert (get (hax, "position"), pos, tol); # axes position is unchanged
10729 %! assert (get (hax, "default"), struct ()); # no more axes' defaults
10730 %! assert (get (get (hax, "title"), "string"), "");
10731 %! unwind_protect_cleanup
10732 %! close (hf);
10733 %! end_unwind_protect
10734 
10735 %!test # root figure object
10736 %! set (0, "defaultfigurevisible", "off");
10737 %! hf = figure ("visible", "off", "paperunits", "centimeters",
10738 %! "papertype", "a4");
10739 %! unwind_protect
10740 %! reset (hf);
10741 %! assert (get (hf, "papertype"), get (0, "defaultfigurepapertype"));
10742 %! assert (get (hf, "paperunits"), "centimeters"); # paperunits is unchanged
10743 %! assert (get (hf, "visible"), get (0, "defaultfigurevisible"));
10744 %! unwind_protect_cleanup
10745 %! close (hf);
10746 %! set (0, "defaultfigurevisible", "remove");
10747 %! end_unwind_protect
10748 
10749 */
10750 
10751 DEFUN (set, args, nargout,
10752  doc: /* -*- texinfo -*-
10753 @deftypefn {} {} set (@var{h}, @var{property}, @var{value}, @dots{})
10754 @deftypefnx {} {} set (@var{h}, @var{properties}, @var{values})
10755 @deftypefnx {} {} set (@var{h}, @var{pv})
10756 @deftypefnx {} {@var{value_list} =} set (@var{h}, @var{property})
10757 @deftypefnx {} {@var{all_value_list} =} set (@var{h})
10758 Set named property values for the graphics handle (or vector of graphics
10759 handles) @var{h}.
10760 
10761 There are three ways to give the property names and values:
10762 
10763 @itemize
10764 @item as a comma separated list of @var{property}, @var{value} pairs
10765 
10766 Here, each @var{property} is a string containing the property name, each
10767 @var{value} is a value of the appropriate type for the property.
10768 
10769 @item as a cell array of strings @var{properties} containing property names
10770 and a cell array @var{values} containing property values.
10771 
10772 In this case, the number of columns of @var{values} must match the number of
10773 elements in @var{properties}. The first column of @var{values} contains
10774 values for the first entry in @var{properties}, etc. The number of rows of
10775 @var{values} must be 1 or match the number of elements of @var{h}. In the
10776 first case, each handle in @var{h} will be assigned the same values. In the
10777 latter case, the first handle in @var{h} will be assigned the values from
10778 the first row of @var{values} and so on.
10779 
10780 @item as a structure array @var{pv}
10781 
10782 Here, the field names of @var{pv} represent the property names, and the
10783 field values give the property values. In contrast to the previous case,
10784 all elements of @var{pv} will be set in all handles in @var{h} independent
10785 of the dimensions of @var{pv}.
10786 @end itemize
10787 
10788 @code{set} is also used to query the list of values a named property will
10789 take. @code{@var{clist} = set (@var{h}, "property")} will return the list
10790 of possible values for @qcode{"property"} in the cell list @var{clist}.
10791 If no output variable is used then the list is formatted and printed to the
10792 screen.
10793 
10794 If no property is specified (@code{@var{slist} = set (@var{h})}) then a
10795 structure @var{slist} is returned where the fieldnames are the properties of
10796 the object @var{h} and the fields are the list of possible values for each
10797 property. If no output variable is used then the list is formatted and
10798 printed to the screen.
10799 
10800 For example,
10801 
10802 @example
10803 @group
10804 hf = figure ();
10805 set (hf, "paperorientation")
10806 @result{} paperorientation: [ landscape | @{portrait@} | rotated ]
10807 @end group
10808 @end example
10809 
10810 @noindent
10811 shows the paperorientation property can take three values with the default
10812 being @qcode{"portrait"}.
10813 @seealso{get}
10814 @end deftypefn */)
10815 {
10817 
10818  int nargin = args.length ();
10819 
10820  if (nargin == 0)
10821  print_usage ();
10822 
10824 
10825  // get vector of graphics handles
10826  ColumnVector hcv = args(0).xvector_value ("set: H must be a graphics handle");
10827 
10828  bool request_drawnow = false;
10829 
10830  // loop over graphics objects
10831  for (octave_idx_type n = 0; n < hcv.numel (); n++)
10832  {
10834 
10835  if (! go)
10836  error ("set: invalid handle (= %g)", hcv(n));
10837 
10838  if (nargin == 3 && args(1).iscellstr () && args(2).iscell ())
10839  {
10840  if (args(2).cell_value ().rows () == 1)
10841  go.set (args(1).cellstr_value (), args(2).cell_value (), 0);
10842  else if (hcv.numel () == args(2).cell_value ().rows ())
10843  go.set (args(1).cellstr_value (), args(2).cell_value (), n);
10844  else
10845  error ("set: number of graphics handles must match number of value rows (%d != %d)",
10846  hcv.numel (), args(2).cell_value ().rows ());
10847  }
10848  else if (nargin == 2 && args(1).isstruct ())
10849  go.set (args(1).map_value ());
10850  else if (nargin == 2 && args(1).is_string ())
10851  {
10852  std::string property = args(1).string_value ();
10853  std::transform (property.begin (), property.end (),
10854  property.begin (), tolower);
10855 
10856  octave_map pmap = go.values_as_struct ();
10857 
10859  if (nargout != 0)
10860  retval = Matrix ();
10861  else
10862  octave_stdout << "set: " << property
10863  <<" is read-only" << std::endl;
10864  else if (pmap.isfield (property))
10865  {
10866  if (nargout != 0)
10867  retval = pmap.getfield (property)(0);
10868  else
10869  {
10871 
10872  octave_stdout << s;
10873  }
10874  }
10875  else
10876  error ("set: unknown property");
10877  }
10878  else if (nargin == 1)
10879  {
10880  if (nargout != 0)
10881  retval = go.values_as_struct ();
10882  else
10883  {
10884  std::string s = go.values_as_string ();
10885 
10886  octave_stdout << s;
10887  }
10888  }
10889  else
10890  {
10891  go.set (args.splice (0, 1));
10892  request_drawnow = true;
10893  }
10894 
10895  request_drawnow = true;
10896  }
10897 
10898  if (request_drawnow)
10899  Vdrawnow_requested = true;
10900 
10901  return retval;
10902 }
10903 
10904 static std::string
10906 {
10908 
10910 
10911  if (! go)
10912  error ("get: invalid handle (= %g)", val);
10913 
10914  return go.type ();
10915 }
10916 
10917 DEFUN (get, args, ,
10918  doc: /* -*- texinfo -*-
10919 @deftypefn {} {@var{val} =} get (@var{h})
10920 @deftypefnx {} {@var{val} =} get (@var{h}, @var{p})
10921 Return the value of the named property @var{p} from the graphics handle
10922 @var{h}.
10923 
10924 If @var{p} is omitted, return the complete property list for @var{h}.
10925 
10926 If @var{h} is a vector, return a cell array including the property values or
10927 lists respectively.
10928 @seealso{set}
10929 @end deftypefn */)
10930 {
10931  gh_manager::auto_lock guard;
10932 
10933  int nargin = args.length ();
10934 
10935  if (nargin < 1 || nargin > 2)
10936  print_usage ();
10937 
10938  if (args(0).isempty ())
10939  return ovl (Matrix ());
10940 
10941  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
10942 
10943  octave_idx_type hcv_len = hcv.numel ();
10944 
10945  if (nargin == 1 && hcv_len > 1)
10946  {
10948 
10949  for (octave_idx_type n = 1; n < hcv_len; n++)
10950  {
10952 
10953  if (typ != typ0)
10954  error ("get: vector of handles must all have the same type");
10955  }
10956  }
10957 
10959  Cell vals;
10960  bool use_cell_format = false;
10961 
10962  if (nargin > 1 && args(1).iscellstr ())
10963  {
10964  Array<std::string> plist = args(1).cellstr_value ();
10965 
10966  octave_idx_type plen = plist.numel ();
10967 
10968  use_cell_format = true;
10969 
10970  vals.resize (dim_vector (hcv_len, plen));
10971 
10972  for (octave_idx_type n = 0; n < hcv_len; n++)
10973  {
10975 
10976  if (! go)
10977  error ("get: invalid handle (= %g)", hcv(n));
10978 
10979  for (octave_idx_type m = 0; m < plen; m++)
10980  {
10981  caseless_str property = plist(m);
10982 
10983  vals(n, m) = go.get (property);
10984  }
10985  }
10986  }
10987  else
10988  {
10990 
10991  if (nargin > 1)
10992  property = args(1).xstring_value ("get: second argument must be property name or cell array of property names");
10993 
10994  vals.resize (dim_vector (hcv_len, 1));
10995 
10996  for (octave_idx_type n = 0; n < hcv_len; n++)
10997  {
10999 
11000  if (! go)
11001  error ("get: invalid handle (= %g)", hcv(n));
11002 
11003  if (nargin == 1)
11004  vals(n) = go.get ();
11005  else
11006  vals(n) = go.get (property);
11007  }
11008  }
11009 
11010  if (use_cell_format)
11011  retval = vals;
11012  else
11013  {
11014  octave_idx_type vals_len = vals.numel ();
11015 
11016  if (vals_len == 0)
11017  retval = Matrix ();
11018  else if (vals_len == 1)
11019  retval = vals(0);
11020  else if (vals_len > 1 && nargin == 1)
11021  {
11023 
11024  for (octave_idx_type n = 0; n < vals_len; n++)
11025  tmp[n] = vals(n).scalar_map_value ();
11026 
11027  retval = octave_map::cat (0, vals_len, tmp);
11028  }
11029  else
11030  retval = vals;
11031  }
11032 
11033  return retval;
11034 }
11035 
11036 /*
11037 %!assert (get (findobj (0, "Tag", "nonexistenttag"), "nonexistentproperty"), [])
11038 */
11039 
11040 // Return all properties from the graphics handle @var{h}.
11041 // If @var{h} is a vector, return a cell array including the
11042 // property values or lists respectively.
11043 
11044 DEFUN (__get__, args, ,
11045  doc: /* -*- texinfo -*-
11046 @deftypefn {} {} __get__ (@var{h})
11047 Undocumented internal function.
11048 @end deftypefn */)
11049 {
11050  gh_manager::auto_lock guard;
11051 
11052  if (args.length () != 1)
11053  print_usage ();
11054 
11055  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
11056 
11057  octave_idx_type hcv_len = hcv.numel ();
11058 
11059  Cell vals (dim_vector (hcv_len, 1));
11060 
11061 // vals.resize (dim_vector (hcv_len, 1));
11062 
11063  for (octave_idx_type n = 0; n < hcv_len; n++)
11064  {
11066 
11067  if (! go)
11068  error ("get: invalid handle (= %g)", hcv(n));
11069 
11070  // Disable "Octave:deprecated-property" warnings
11071  int state = toggle_warn ("Octave:deprecated-property", false);
11072 
11073  vals(n) = go.get (true);
11074 
11075  toggle_warn ("Octave:deprecated-property", true, state);
11076  }
11077 
11078  octave_idx_type vals_len = vals.numel ();
11079 
11080  if (vals_len > 1)
11081  return ovl (vals);
11082  else if (vals_len == 1)
11083  return ovl (vals(0));
11084  else
11085  return ovl ();
11086 }
11087 
11088 static octave_value
11089 make_graphics_object (const std::string& go_name,
11090  bool integer_figure_handle,
11091  const octave_value_list& args)
11092 {
11094 
11096 
11097  octave_value_list xargs = args.splice (0, 1);
11098 
11099  caseless_str p ("parent");
11100 
11101  for (int i = 0; i < xargs.length (); i++)
11102  {
11103  if (xargs(i).is_string () && p.compare (xargs(i).string_value ()))
11104  {
11105  if (i >= (xargs.length () - 1))
11106  error ("__go_%s__: missing value for parent property",
11107  go_name.c_str ());
11108 
11109  val = xargs(i+1).double_value ();
11110 
11111  xargs = xargs.splice (i, 2);
11112  break;
11113  }
11114  }
11115 
11116  if (octave::math::isnan (val))
11117  val = args(0).xdouble_value ("__go_%s__: invalid parent", go_name.c_str ());
11118 
11120 
11121  if (! parent.ok ())
11122  error ("__go_%s__: invalid parent", go_name.c_str ());
11123 
11125 
11126  try
11127  {
11128  h = gh_manager::make_graphics_handle (go_name, parent,
11129  integer_figure_handle,
11130  false, false);
11131  }
11132  catch (octave::execution_exception& e)
11133  {
11134  error (e, "__go_%s__: unable to create graphics handle",
11135  go_name.c_str ());
11136  }
11137 
11138  adopt (parent, h);
11139 
11140  try
11141  {
11142  xset (h, xargs);
11143  }
11144  catch (octave::execution_exception& e)
11145  {
11147  error (e, "__go_%s__: unable to create graphics handle",
11148  go_name.c_str ());
11149  }
11150 
11151  xcreatefcn (h);
11152  xinitialize (h);
11153 
11154  retval = h.value ();
11155 
11156  Vdrawnow_requested = true;
11157 
11158  return retval;
11159 }
11160 
11161 DEFUN (__go_figure__, args, ,
11162  doc: /* -*- texinfo -*-
11163 @deftypefn {} {} __go_figure__ (@var{fignum})
11164 Undocumented internal function.
11165 @end deftypefn */)
11166 {
11167  gh_manager::auto_lock guard;
11168 
11169  if (args.length () == 0)
11170  print_usage ();
11171 
11172  double val = args(0).xdouble_value ("__go_figure__: figure number must be a double value");
11173 
11175 
11176  if (is_figure (val))
11177  {
11179 
11180  xset (h, args.splice (0, 1));
11181 
11182  retval = h.value ();
11183  }
11184  else
11185  {
11186  bool int_fig_handle = true;
11187 
11188  octave_value_list xargs = args.splice (0, 1);
11189 
11191 
11192  if (octave::math::isnan (val))
11193  {
11194  caseless_str pname ("integerhandle");
11195 
11196  for (int i = 0; i < xargs.length (); i++)
11197  {
11198  if (xargs(i).is_string ()
11199  && pname.compare (xargs(i).string_value ()))
11200  {
11201  if (i < (xargs.length () - 1))
11202  {
11203  std::string pval = xargs(i+1).string_value ();
11204 
11205  caseless_str on ("on");
11206  int_fig_handle = on.compare (pval);
11207  xargs = xargs.splice (i, 2);
11208 
11209  break;
11210  }
11211  }
11212  }
11213 
11214  h = gh_manager::make_graphics_handle ("figure", 0,
11215  int_fig_handle,
11216  false, false);
11217 
11218  if (! int_fig_handle)
11219  {
11220  // We need to initialize the integerhandle property
11221  // without calling the set_integerhandle method,
11222  // because doing that will generate a new handle value...
11224  go.get_properties ().init_integerhandle ("off");
11225  }
11226  }
11227  else if (val > 0 && octave::math::x_nint (val) == val)
11229 
11230  if (! h.ok ())
11231  error ("__go_figure__: failed to create figure handle");
11232 
11233  adopt (0, h);
11234 
11236 
11237  xset (h, xargs);
11238  xcreatefcn (h);
11239  xinitialize (h);
11240 
11241  retval = h.value ();
11242  }
11243 
11244  return retval;
11245 }
11246 
11247 #define GO_BODY(TYPE) \
11248  gh_manager::auto_lock guard; \
11249  \
11250  if (args.length () == 0) \
11251  print_usage (); \
11252  \
11253  return octave_value (make_graphics_object (#TYPE, false, args)); \
11254 
11255 int
11256 calc_dimensions (const graphics_object& go)
11257 {
11258  int nd = 2;
11259 
11260  if (go.isa ("surface"))
11261  nd = 3;
11262  else if ((go.isa ("line") || go.isa ("patch"))
11263  && ! go.get ("zdata").isempty ())
11264  nd = 3;
11265  else
11266  {
11267  Matrix kids = go.get_properties ().get_children ();
11268 
11269  for (octave_idx_type i = 0; i < kids.numel (); i++)
11270  {
11271  graphics_handle hkid = gh_manager::lookup (kids(i));
11272 
11273  if (hkid.ok ())
11274  {
11275  const graphics_object& kid = gh_manager::get_object (hkid);
11276 
11277  if (kid.valid_object ())
11278  nd = calc_dimensions (kid);
11279 
11280  if (nd == 3)
11281  break;
11282  }
11283  }
11284  }
11285 
11286  return nd;
11287 }
11288 
11289 DEFUN (__calc_dimensions__, args, ,
11290  doc: /* -*- texinfo -*-
11291 @deftypefn {} {} __calc_dimensions__ (@var{axes})
11292 Internal function.
11293 
11294 Determine the number of dimensions in a graphics object, either 2 or 3.
11295 @end deftypefn */)
11296 {
11297  gh_manager::auto_lock guard;
11298 
11299  if (args.length () != 1)
11300  print_usage ();
11301 
11302  double h = args(0).xdouble_value ("__calc_dimensions__: first argument must be a graphics handle");
11303 
11305 }
11306 
11307 DEFUN (__go_axes__, args, ,
11308  doc: /* -*- texinfo -*-
11309 @deftypefn {} {} __go_axes__ (@var{parent})
11310 Undocumented internal function.
11311 @end deftypefn */)
11312 {
11313  GO_BODY (axes);
11314 }
11315 
11316 DEFUN (__go_line__, args, ,
11317  doc: /* -*- texinfo -*-
11318 @deftypefn {} {} __go_line__ (@var{parent})
11319 Undocumented internal function.
11320 @end deftypefn */)
11321 {
11322  GO_BODY (line);
11323 }
11324 
11325 DEFUN (__go_text__, args, ,
11326  doc: /* -*- texinfo -*-
11327 @deftypefn {} {} __go_text__ (@var{parent})
11328 Undocumented internal function.
11329 @end deftypefn */)
11330 {
11331  GO_BODY (text);
11332 }
11333 
11334 DEFUN (__go_image__, args, ,
11335  doc: /* -*- texinfo -*-
11336 @deftypefn {} {} __go_image__ (@var{parent})
11337 Undocumented internal function.
11338 @end deftypefn */)
11339 {
11340  GO_BODY (image);
11341 }
11342 
11343 DEFUN (__go_surface__, args, ,
11344  doc: /* -*- texinfo -*-
11345 @deftypefn {} {} __go_surface__ (@var{parent})
11346 Undocumented internal function.
11347 @end deftypefn */)
11348 {
11349  GO_BODY (surface);
11350 }
11351 
11352 DEFUN (__go_patch__, args, ,
11353  doc: /* -*- texinfo -*-
11354 @deftypefn {} {} __go_patch__ (@var{parent})
11355 Undocumented internal function.
11356 @end deftypefn */)
11357 {
11358  GO_BODY (patch);
11359 }
11360 
11361 DEFUN (__go_light__, args, ,
11362  doc: /* -*- texinfo -*-
11363 @deftypefn {} {} __go_light__ (@var{parent})
11364 Undocumented internal function.
11365 @end deftypefn */)
11366 {
11367  GO_BODY (light);
11368 }
11369 
11370 DEFUN (__go_hggroup__, args, ,
11371  doc: /* -*- texinfo -*-
11372 @deftypefn {} {} __go_hggroup__ (@var{parent})
11373 Undocumented internal function.
11374 @end deftypefn */)
11375 {
11376  GO_BODY (hggroup);
11377 }
11378 
11379 DEFUN (__go_uimenu__, args, ,
11380  doc: /* -*- texinfo -*-
11381 @deftypefn {} {} __go_uimenu__ (@var{parent})
11382 Undocumented internal function.
11383 @end deftypefn */)
11384 {
11385  GO_BODY (uimenu);
11386 }
11387 
11388 DEFUN (__go_uicontrol__, args, ,
11389  doc: /* -*- texinfo -*-
11390 @deftypefn {} {} __go_uicontrol__ (@var{parent})
11391 Undocumented internal function.
11392 @end deftypefn */)
11393 {
11394  GO_BODY (uicontrol);
11395 }
11396 
11397 DEFUN (__go_uibuttongroup__, args, ,
11398  doc: /* -*- texinfo -*-
11399 @deftypefn {} {} __go_uibuttongroup__ (@var{parent})
11400 Undocumented internal function.
11401 @end deftypefn */)
11402 {
11404 }
11405 
11406 DEFUN (__go_uipanel__, args, ,
11407  doc: /* -*- texinfo -*-
11408 @deftypefn {} {} __go_uipanel__ (@var{parent})
11409 Undocumented internal function.
11410 @end deftypefn */)
11411 {
11412  GO_BODY (uipanel);
11413 }
11414 
11415 DEFUN (__go_uicontextmenu__, args, ,
11416  doc: /* -*- texinfo -*-
11417 @deftypefn {} {} __go_uicontextmenu__ (@var{parent})
11418 Undocumented internal function.
11419 @end deftypefn */)
11420 {
11422 }
11423 
11424 DEFUN (__go_uitoolbar__, args, ,
11425  doc: /* -*- texinfo -*-
11426 @deftypefn {} {} __go_uitoolbar__ (@var{parent})
11427 Undocumented internal function.
11428 @end deftypefn */)
11429 {
11430  GO_BODY (uitoolbar);
11431 }
11432 
11433 DEFUN (__go_uipushtool__, args, ,
11434  doc: /* -*- texinfo -*-
11435 @deftypefn {} {} __go_uipushtool__ (@var{parent})
11436 Undocumented internal function.
11437 @end deftypefn */)
11438 {
11439  GO_BODY (uipushtool);
11440 }
11441 
11442 DEFUN (__go_uitoggletool__, args, ,
11443  doc: /* -*- texinfo -*-
11444 @deftypefn {} {} __go_uitoggletool__ (@var{parent})
11445 Undocumented internal function.
11446 @end deftypefn */)
11447 {
11449 }
11450 
11451 DEFUN (__go_delete__, args, ,
11452  doc: /* -*- texinfo -*-
11453 @deftypefn {} {} __go_delete__ (@var{h})
11454 Undocumented internal function.
11455 @end deftypefn */)
11456 {
11457  gh_manager::auto_lock guard;
11458 
11459  if (args.length () != 1)
11460  print_usage ();
11461 
11463 
11464  const NDArray vals = args(0).xarray_value ("delete: invalid graphics object");
11465 
11466  // Check all the handles to delete are valid first,
11467  // as callbacks might delete one of the handles we later want to delete.
11468  for (octave_idx_type i = 0; i < vals.numel (); i++)
11469  {
11470  h = gh_manager::lookup (vals(i));
11471 
11472  if (! h.ok ())
11473  error ("delete: invalid graphics object (= %g)", vals(i));
11474  }
11475 
11476  delete_graphics_objects (vals);
11477 
11478  return ovl ();
11479 }
11480 
11481 DEFUN (__go_handles__, args, ,
11482  doc: /* -*- texinfo -*-
11483 @deftypefn {} {} __go_handles__ (@var{show_hidden})
11484 Undocumented internal function.
11485 @end deftypefn */)
11486 {
11487  gh_manager::auto_lock guard;
11488 
11489  bool show_hidden = false;
11490 
11491  if (args.length () > 0)
11492  show_hidden = args(0).bool_value ();
11493 
11494  return ovl (gh_manager::handle_list (show_hidden));
11495 }
11496 
11497 DEFUN (__go_figure_handles__, args, ,
11498  doc: /* -*- texinfo -*-
11499 @deftypefn {} {} __go_figure_handles__ (@var{show_hidden})
11500 Undocumented internal function.
11501 @end deftypefn */)
11502 {
11503  gh_manager::auto_lock guard;
11504 
11505  bool show_hidden = false;
11506 
11507  if (args.length () > 0)
11508  show_hidden = args(0).bool_value ();
11509 
11510  return ovl (gh_manager::figure_handle_list (show_hidden));
11511 }
11512 
11513 DEFUN (__go_execute_callback__, args, ,
11514  doc: /* -*- texinfo -*-
11515 @deftypefn {} {} __go_execute_callback__ (@var{h}, @var{name})
11516 @deftypefnx {} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})
11517 Undocumented internal function.
11518 @end deftypefn */)
11519 {
11520  int nargin = args.length ();
11521 
11522  if (nargin < 2 || nargin > 3)
11523  print_usage ();
11524 
11525  const NDArray vals = args(0).xarray_value ("__go_execute_callback__: invalid graphics object");
11526 
11527  std::string name = args(1).xstring_value ("__go_execute_callback__: invalid callback name");
11528 
11529  for (octave_idx_type i = 0; i < vals.numel (); i++)
11530  {
11531  double val = vals(i);
11532 
11534 
11535  if (! h.ok ())
11536  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
11537 
11538  if (nargin == 2)
11540  else
11541  gh_manager::execute_callback (h, name, args(2));
11542  }
11543 
11544  return ovl ();
11545 }
11546 
11547 DEFUN (__image_pixel_size__, args, ,
11548  doc: /* -*- texinfo -*-
11549 @deftypefn {} {@var{sz} =} __image_pixel_size__ (@var{h})
11550 Internal function: returns the pixel size of the image in normalized units.
11551 @end deftypefn */)
11552 {
11553  if (args.length () != 1)
11554  print_usage ();
11555 
11556  double h = args(0).xdouble_value ("__image_pixel_size__: argument is not a handle");
11557 
11559  if (! go || ! go.isa ("image"))
11560  error ("__image_pixel_size__: object is not an image");
11561 
11562  image::properties& ip =
11563  dynamic_cast<image::properties&> (go.get_properties ());
11564 
11565  Matrix dp = Matrix (1, 2);
11566  dp(0) = ip.pixel_xsize ();
11567  dp(1) = ip.pixel_ysize ();
11568  return ovl (dp);
11569 }
11570 
11571 DEFMETHOD (available_graphics_toolkits, interp, , ,
11572  doc: /* -*- texinfo -*-
11573 @deftypefn {} {} available_graphics_toolkits ()
11574 Return a cell array of registered graphics toolkits.
11575 @seealso{graphics_toolkit, register_graphics_toolkit}
11576 @end deftypefn */)
11577 {
11578  gh_manager::auto_lock guard;
11579 
11580  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
11581 
11582  return ovl (gtk_mgr.available_toolkits_list ());
11583 }
11584 
11585 DEFMETHOD (register_graphics_toolkit, interp, args, ,
11586  doc: /* -*- texinfo -*-
11587 @deftypefn {} {} register_graphics_toolkit (@var{toolkit})
11588 List @var{toolkit} as an available graphics toolkit.
11589 @seealso{available_graphics_toolkits}
11590 @end deftypefn */)
11591 {
11592  gh_manager::auto_lock guard;
11593 
11594  if (args.length () != 1)
11595  print_usage ();
11596 
11597  std::string name = args(0).xstring_value ("register_graphics_toolkit: TOOLKIT must be a string");
11598 
11599  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
11600 
11601  gtk_mgr.register_toolkit (name);
11602 
11603  return ovl ();
11604 }
11605 
11606 DEFMETHOD (loaded_graphics_toolkits, interp, , ,
11607  doc: /* -*- texinfo -*-
11608 @deftypefn {} {} loaded_graphics_toolkits ()
11609 Return a cell array of the currently loaded graphics toolkits.
11610 @seealso{available_graphics_toolkits}
11611 @end deftypefn */)
11612 {
11613  gh_manager::auto_lock guard;
11614 
11615  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
11616 
11617  return ovl (gtk_mgr.loaded_toolkits_list ());
11618 }
11619 
11620 DEFUN (drawnow, args, ,
11621  doc: /* -*- texinfo -*-
11622 @deftypefn {} {} drawnow ()
11623 @deftypefnx {} {} drawnow ("expose")
11624 @deftypefnx {} {} drawnow (@var{term}, @var{file}, @var{debug_file})
11625 Update figure windows and their children.
11626 
11627 The event queue is flushed and any callbacks generated are executed.
11628 
11629 With the optional argument @qcode{"expose"}, only graphic objects are
11630 updated and no other events or callbacks are processed.
11631 
11632 The third calling form of @code{drawnow} is for debugging and is
11633 undocumented.
11634 @seealso{refresh}
11635 @end deftypefn */)
11636 {
11637  static int drawnow_executing = 0;
11638 
11639  if (args.length () > 3)
11640  print_usage ();
11641 
11643 
11645  frame.protect_var (drawnow_executing);
11646 
11647  // Redraw, unless we are in the middle of an existing redraw or deletion.
11648  if (++drawnow_executing <= 1 && ! delete_executing)
11649  {
11650  gh_manager::auto_lock guard;
11651 
11652  if (args.length () == 0 || args.length () == 1)
11653  {
11654  Matrix hlist = gh_manager::figure_handle_list (true);
11655 
11656  for (int i = 0; i < hlist.numel (); i++)
11657  {
11659 
11660  if (h.ok () && h != 0)
11661  {
11663  figure::properties& fprops
11664  = dynamic_cast<figure::properties&> (go.get_properties ());
11665 
11666  if (fprops.is_modified ())
11667  {
11668  if (fprops.is_visible ())
11669  {
11670  gh_manager::unlock ();
11671 
11672  fprops.get_toolkit ().redraw_figure (go);
11673 
11674  gh_manager::lock ();
11675  }
11676 
11677  fprops.set_modified (false);
11678  }
11679  }
11680  }
11681 
11682  bool do_events = true;
11683 
11684  if (args.length () == 1)
11685  {
11686  caseless_str val (args(0).xstring_value ("drawnow: first argument must be a string"));
11687 
11688  if (val.compare ("expose"))
11689  do_events = false;
11690  else
11691  error ("drawnow: invalid argument, 'expose' is only valid option");
11692  }
11693 
11694  if (do_events)
11695  {
11696  gh_manager::unlock ();
11697 
11699 
11700  gh_manager::lock ();
11701  }
11702  }
11703  else if (args.length () >= 2 && args.length () <= 3)
11704  {
11705  std::string term, file, debug_file;
11706 
11707  term = args(0).xstring_value ("drawnow: TERM must be a string");
11708 
11709  file = args(1).xstring_value ("drawnow: FILE must be a string");
11710 
11711  if (file.empty ())
11712  error ("drawnow: empty output ''");
11713  else if (file.length () == 1 && file[0] == '|')
11714  error ("drawnow: empty pipe '|'");
11715  else if (file[0] != '|')
11716  {
11717  size_t pos = file.find_last_of (octave::sys::file_ops::dir_sep_chars ());
11718 
11719  if (pos != std::string::npos)
11720  {
11721  std::string dirname = file.substr (0, pos+1);
11722 
11724 
11725  if (! fs || ! fs.is_dir ())
11726  error ("drawnow: nonexistent directory '%s'",
11727  dirname.c_str ());
11728 
11729  }
11730  }
11731 
11732  debug_file = (args.length () > 2 ? args(2).xstring_value ("drawnow: DEBUG_FILE must be a string") : "");
11733 
11734  graphics_handle h = gcf ();
11735 
11736  if (! h.ok ())
11737  error ("drawnow: nothing to draw");
11738 
11740 
11741  // FIXME: when using qt toolkit the print_figure method
11742  // returns immediately and Canvas::print doesn't have
11743  // enough time to lock the mutex before we lock it here
11744  // again. We thus wait 50 ms (which may not be enough) to
11745  // give it a chance: see http://octave.1599824.n4.nabble.com/Printing-issues-with-Qt-toolkit-tp4673270.html
11746 
11747  gh_manager::unlock ();
11748 
11749  go.get_toolkit ().print_figure (go, term, file, debug_file);
11750 
11751  // FIXME: In ObjectProxy.cc ObjectProxy::init
11752  // we now use connect (..., Qt::BlockingQueuedConnection)
11753  // which should make the sleep unnecessary.
11754  // See bug #44463 and #48519
11755  // Remove it and the FIXME block above after testing.
11756 
11757  // octave_sleep (0.05);
11758 
11759  gh_manager::lock ();
11760  }
11761  }
11762 
11763  return ovl ();
11764 }
11765 
11766 DEFUN (addlistener, args, ,
11767  doc: /* -*- texinfo -*-
11768 @deftypefn {} {} addlistener (@var{h}, @var{prop}, @var{fcn})
11769 Register @var{fcn} as listener for the property @var{prop} of the graphics
11770 object @var{h}.
11771 
11772 Property listeners are executed (in order of registration) when the property
11773 is set. The new value is already available when the listeners are executed.
11774 
11775 @var{prop} must be a string naming a valid property in @var{h}.
11776 
11777 @var{fcn} can be a function handle, a string or a cell array whose first
11778 element is a function handle. If @var{fcn} is a function handle, the
11779 corresponding function should accept at least 2 arguments, that will be
11780 set to the object handle and the empty matrix respectively. If @var{fcn}
11781 is a string, it must be any valid octave expression. If @var{fcn} is a cell
11782 array, the first element must be a function handle with the same signature
11783 as described above. The next elements of the cell array are passed
11784 as additional arguments to the function.
11785 
11786 Example:
11787 
11788 @example
11789 @group
11790 function my_listener (h, dummy, p1)
11791  fprintf ("my_listener called with p1=%s\n", p1);
11792 endfunction
11793 
11794 addlistener (gcf, "position", @{@@my_listener, "my string"@})
11795 @end group
11796 @end example
11797 
11798 @seealso{dellistener, addproperty, hggroup}
11799 @end deftypefn */)
11800 {
11801  gh_manager::auto_lock guard;
11802 
11803  int nargin = args.length ();
11804 
11805  if (nargin < 3 || nargin > 4)
11806  print_usage ();
11807 
11808  double h = args(0).xdouble_value ("addlistener: invalid handle H");
11809 
11810  std::string pname = args(1).xstring_value ("addlistener: PROP must be a string");
11811 
11813 
11814  if (! gh.ok ())
11815  error ("addlistener: invalid graphics object (= %g)", h);
11816 
11818 
11819  go.add_property_listener (pname, args(2), POSTSET);
11820 
11821  if (args.length () == 4)
11822  {
11823  caseless_str persistent = args(3).string_value ();
11824  if (persistent.compare ("persistent"))
11825  go.add_property_listener (pname, args(2), PERSISTENT);
11826  }
11827 
11828  return ovl ();
11829 }
11830 
11831 DEFUN (dellistener, args, ,
11832  doc: /* -*- texinfo -*-
11833 @deftypefn {} {} dellistener (@var{h}, @var{prop}, @var{fcn})
11834 Remove the registration of @var{fcn} as a listener for the property
11835 @var{prop} of the graphics object @var{h}.
11836 
11837 The function @var{fcn} must be the same variable (not just the same value),
11838 as was passed to the original call to @code{addlistener}.
11839 
11840 If @var{fcn} is not defined then all listener functions of @var{prop}
11841 are removed.
11842 
11843 Example:
11844 
11845 @example
11846 @group
11847 function my_listener (h, dummy, p1)
11848  fprintf ("my_listener called with p1=%s\n", p1);
11849 endfunction
11850 
11851 c = @{@@my_listener, "my string"@};
11852 addlistener (gcf, "position", c);
11853 dellistener (gcf, "position", c);
11854 @end group
11855 @end example
11856 
11857 @seealso{addlistener}
11858 @end deftypefn */)
11859 {
11860  gh_manager::auto_lock guard;
11861 
11862  if (args.length () < 2 || args.length () > 3)
11863  print_usage ();
11864 
11865  double h = args(0).xdouble_value ("dellistener: invalid handle");
11866 
11867  std::string pname = args(1).xstring_value ("dellistener: PROP must be a string");
11868 
11870 
11871  if (! gh.ok ())
11872  error ("dellistener: invalid graphics object (= %g)", h);
11873 
11875 
11876  if (args.length () == 2)
11878  else
11879  {
11880  if (args(2).is_string ()
11881  && args(2).string_value () == "persistent")
11882  {
11884  PERSISTENT);
11886  POSTSET);
11887  }
11888  else
11889  go.delete_property_listener (pname, args(2), POSTSET);
11890  }
11891 
11892  return ovl ();
11893 }
11894 
11895 DEFUN (addproperty, args, ,
11896  doc: /* -*- texinfo -*-
11897 @deftypefn {} {} addproperty (@var{name}, @var{h}, @var{type})
11898 @deftypefnx {} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})
11899 Create a new property named @var{name} in graphics object @var{h}.
11900 
11901 @var{type} determines the type of the property to create. @var{args}
11902 usually contains the default value of the property, but additional
11903 arguments might be given, depending on the type of the property.
11904 
11905 The supported property types are:
11906 
11907 @table @code
11908 @item string
11909 A string property. @var{arg} contains the default string value.
11910 
11911 @item any
11912 An @nospell{un-typed} property. This kind of property can hold any octave
11913 value. @var{args} contains the default value.
11914 
11915 @item radio
11916 A string property with a limited set of accepted values. The first
11917 argument must be a string with all accepted values separated by
11918 a vertical bar ('|'). The default value can be marked by enclosing
11919 it with a '@{' '@}' pair. The default value may also be given as
11920 an optional second string argument.
11921 
11922 @item boolean
11923 A boolean property. This property type is equivalent to a radio
11924 property with "on|off" as accepted values. @var{arg} contains
11925 the default property value.
11926 
11927 @item double
11928 A scalar double property. @var{arg} contains the default value.
11929 
11930 @item handle
11931 A handle property. This kind of property holds the handle of a
11932 graphics object. @var{arg} contains the default handle value.
11933 When no default value is given, the property is initialized to
11934 the empty matrix.
11935 
11936 @item data
11937 A data (matrix) property. @var{arg} contains the default data
11938 value. When no default value is given, the data is initialized to
11939 the empty matrix.
11940 
11941 @item color
11942 A color property. @var{arg} contains the default color value.
11943 When no default color is given, the property is set to black.
11944 An optional second string argument may be given to specify an
11945 additional set of accepted string values (like a radio property).
11946 @end table
11947 
11948 @var{type} may also be the concatenation of a core object type and
11949 a valid property name for that object type. The property created
11950 then has the same characteristics as the referenced property (type,
11951 possible values, hidden state@dots{}). This allows one to clone an
11952 existing property into the graphics object @var{h}.
11953 
11954 Examples:
11955 
11956 @example
11957 @group
11958 addproperty ("my_property", gcf, "string", "a string value");
11959 addproperty ("my_radio", gcf, "radio", "val_1|val_2|@{val_3@}");
11960 addproperty ("my_style", gcf, "linelinestyle", "--");
11961 @end group
11962 @end example
11963 
11964 @seealso{addlistener, hggroup}
11965 @end deftypefn */)
11966 {
11967  gh_manager::auto_lock guard;
11968 
11969  if (args.length () < 3)
11970  print_usage ();
11971 
11972  std::string name = args(0).xstring_value ("addproperty: NAME must be a string");
11973 
11974  double h = args(1).xdouble_value ("addproperty: invalid handle H");
11975 
11977 
11978  if (! gh.ok ())
11979  error ("addproperty: invalid graphics object (= %g)", h);
11980 
11982 
11983  std::string type = args(2).xstring_value ("addproperty: TYPE must be a string");
11984 
11985  if (go.get_properties ().has_property (name))
11986  error ("addproperty: a '%s' property already exists in the graphics object",
11987  name.c_str ());
11988 
11989  property p = property::create (name, gh, type, args.splice (0, 3));
11990 
11992 
11993  return ovl ();
11994 }
11995 
11998  const std::string& func)
11999 {
12000  gh_manager::auto_lock guard;
12001 
12003 
12004  if (! go)
12005  error ("%s: invalid handle (= %g)", func.c_str (), handle);
12006 
12007  return go.get (caseless_str (property));
12008 }
12009 
12010 bool
12012  const octave_value& arg, const std::string& func)
12013 {
12014  gh_manager::auto_lock guard;
12015 
12016  int ret = false;
12018 
12019  if (! go)
12020  error ("%s: invalid handle (= %g)", func.c_str (), handle);
12021 
12022  go.set (caseless_str (property), arg);
12023 
12024  ret = true;
12025 
12026  return ret;
12027 }
12028 
12029 static bool
12030 compare_property_values (const octave_value& ov1, const octave_value& ov2)
12031 {
12032  octave_value_list args(2);
12033 
12034  args(0) = ov1;
12035  args(1) = ov2;
12036 
12037  octave_value_list result = octave::feval ("isequal", args, 1);
12038 
12039  if (result.length () > 0)
12040  return result(0).bool_value ();
12041 
12042  return false;
12043 }
12044 
12045 static std::map<uint32_t, bool> waitfor_results;
12046 
12047 static void
12048 cleanup_waitfor_id (uint32_t id)
12049 {
12050  waitfor_results.erase (id);
12051 }
12052 
12053 static void
12056 {
12057  Cell c = listener.cell_value ();
12058 
12059  if (c.numel () >= 4)
12060  {
12061  double h = c(2).double_value ();
12062 
12063  caseless_str pname = c(3).string_value ();
12064 
12065  gh_manager::auto_lock guard;
12066 
12068 
12069  if (gh.ok ())
12070  {
12072 
12073  if (go.get_properties ().has_property (pname))
12074  {
12075  go.get_properties ().delete_listener (pname, listener, mode);
12076 
12077  if (mode == POSTSET)
12078  go.get_properties ().delete_listener (pname, listener,
12079  PERSISTENT);
12080  }
12081  }
12082  }
12083 }
12084 
12085 static void
12087 { do_cleanup_waitfor_listener (listener, POSTSET); }
12088 
12089 static void
12091 { do_cleanup_waitfor_listener (listener, PREDELETE); }
12092 
12094 {
12095  if (args.length () > 3)
12096  {
12097  uint32_t id = args(2).uint32_scalar_value ().value ();
12098 
12099  if (args.length () > 5)
12100  {
12101  double h = args(0).double_value ();
12102 
12103  caseless_str pname = args(4).string_value ();
12104 
12105  gh_manager::auto_lock guard;
12106 
12108 
12109  if (gh.ok ())
12110  {
12112  octave_value pvalue = go.get (pname);
12113 
12114  if (compare_property_values (pvalue, args(5)))
12115  waitfor_results[id] = true;
12116  }
12117  }
12118  else
12119  waitfor_results[id] = true;
12120  }
12121 
12122  return ovl ();
12123 }
12124 
12126 {
12127  if (args.length () > 2)
12128  {
12129  uint32_t id = args(2).uint32_scalar_value ().value ();
12130 
12131  waitfor_results[id] = true;
12132  }
12133 
12134  return ovl ();
12135 }
12136 
12137 DEFUN (waitfor, args, ,
12138  doc: /* -*- texinfo -*-
12139 @deftypefn {} {} waitfor (@var{h})
12140 @deftypefnx {} {} waitfor (@var{h}, @var{prop})
12141 @deftypefnx {} {} waitfor (@var{h}, @var{prop}, @var{value})
12142 @deftypefnx {} {} waitfor (@dots{}, "timeout", @var{timeout})
12143 Suspend the execution of the current program until a condition is
12144 satisfied on the graphics handle @var{h}.
12145 
12146 While the program is suspended graphics events are still processed normally,
12147 allowing callbacks to modify the state of graphics objects. This function
12148 is reentrant and can be called from a callback, while another @code{waitfor}
12149 call is pending at the top-level.
12150 
12151 In the first form, program execution is suspended until the graphics object
12152 @var{h} is destroyed. If the graphics handle is invalid, the function
12153 returns immediately.
12154 
12155 In the second form, execution is suspended until the graphics object is
12156 destroyed or the property named @var{prop} is modified. If the graphics
12157 handle is invalid or the property does not exist, the function returns
12158 immediately.
12159 
12160 In the third form, execution is suspended until the graphics object is
12161 destroyed or the property named @var{prop} is set to @var{value}. The
12162 function @code{isequal} is used to compare property values. If the graphics
12163 handle is invalid, the property does not exist or the property is already
12164 set to @var{value}, the function returns immediately.
12165 
12166 An optional timeout can be specified using the property @qcode{"timeout"}.
12167 This timeout value is the number of seconds to wait for the condition to be
12168 true. @var{timeout} must be at least 1. If a smaller value is specified, a
12169 warning is issued and a value of 1 is used instead. If the timeout value is
12170 not an integer, it is truncated towards 0.
12171 
12172 To define a condition on a property named @qcode{"timeout"}, use the string
12173 @qcode{'\timeout'} instead.
12174 
12175 In all cases, typing CTRL-C stops program execution immediately.
12176 @seealso{waitforbuttonpress, isequal}
12177 @end deftypefn */)
12178 {
12179  if (args.length () == 0)
12181 
12182  // return immediately if the graphics handle is invalid
12183  if (args(0).isempty ())
12184  return ovl ();
12185 
12186  double h = args(0).xdouble_value ("waitfor: invalid handle value");
12187 
12189 
12191 
12192  static uint32_t id_counter = 0;
12193  uint32_t id = 0;
12194 
12195  int max_arg_index = 0;
12196  int timeout_index = -1;
12197 
12198  double timeout = 0;
12199 
12200  if (args.length () > 1)
12201  {
12202  pname = args(1).xstring_value ("waitfor: PROP must be a string");
12203 
12204  if (pname.empty ())
12205  error ("waitfor: PROP must be a non-empty string");
12206 
12207  if (pname != "timeout")
12208  {
12209  if (pname.compare (R"(\timeout)"))
12210  pname = "timeout";
12211 
12212  static octave_value wf_listener;
12213 
12214  if (! wf_listener.is_defined ())
12215  wf_listener =
12217  "waitfor_listener"));
12218 
12219  max_arg_index++;
12220  if (args.length () > 2)
12221  {
12222  if (args(2).is_string ())
12223  {
12224  caseless_str s = args(2).string_value ();
12225 
12226  if (s.compare ("timeout"))
12227  timeout_index = 2;
12228  else
12229  max_arg_index++;
12230  }
12231  else
12232  max_arg_index++;
12233  }
12234 
12235  Cell listener (1, max_arg_index >= 2 ? 5 : 4);
12236 
12237  id = id_counter++;
12239  waitfor_results[id] = false;
12240 
12241  listener(0) = wf_listener;
12242  listener(1) = octave_uint32 (id);
12243  listener(2) = h;
12244  listener(3) = pname;
12245 
12246  if (max_arg_index >= 2)
12247  listener(4) = args(2);
12248 
12249  octave_value ov_listener (listener);
12250 
12251  gh_manager::auto_lock guard;
12252 
12254 
12255  if (gh.ok ())
12256  {
12258 
12259  if (max_arg_index >= 2
12260  && compare_property_values (go.get (pname), args(2)))
12261  waitfor_results[id] = true;
12262  else
12263  {
12264 
12266  go.add_property_listener (pname, ov_listener, POSTSET);
12267  go.add_property_listener (pname, ov_listener, PERSISTENT);
12268 
12270  {
12271  static octave_value wf_del_listener;
12272 
12273  if (! wf_del_listener.is_defined ())
12274  wf_del_listener =
12277  "waitfor_del_listener"));
12278 
12279  Cell del_listener (1, 4);
12280 
12281  del_listener(0) = wf_del_listener;
12282  del_listener(1) = octave_uint32 (id);
12283  del_listener(2) = h;
12284  del_listener(3) = pname;
12285 
12286  octave_value ov_del_listener (del_listener);
12287 
12289  ov_del_listener);
12290  go.add_property_listener (pname, ov_del_listener,
12291  PREDELETE);
12292  }
12293  }
12294  }
12295  }
12296  }
12297 
12298  if (timeout_index < 0 && args.length () > (max_arg_index + 1))
12299  {
12300  caseless_str s = args(max_arg_index + 1).xstring_value ("waitfor: invalid parameter, expected 'timeout'");
12301 
12302  if (! s.compare ("timeout"))
12303  error ("waitfor: invalid parameter '%s'", s.c_str ());
12304 
12306  }
12307 
12308  if (timeout_index >= 0)
12309  {
12310  if (args.length () <= (timeout_index + 1))
12311  error ("waitfor: missing TIMEOUT value");
12312 
12313  timeout = args(timeout_index + 1).xscalar_value ("waitfor: TIMEOUT must be a scalar >= 1");
12314 
12315  if (timeout < 1)
12316  {
12317  warning ("waitfor: TIMEOUT value must be >= 1, using 1 instead");
12318  timeout = 1;
12319  }
12320  }
12321 
12322  // FIXME: There is still a "hole" in the following loop. The code
12323  // assumes that an object handle is unique, which is a fair
12324  // assumption, except for figures. If a figure is destroyed
12325  // then recreated with the same figure ID, within the same
12326  // run of event hooks, then the figure destruction won't be
12327  // caught and the loop will not stop. This is an unlikely
12328  // possibility in practice, though.
12329  //
12330  // Using deletefcn callback is also unreliable as it could be
12331  // modified during a callback execution and the waitfor loop
12332  // would not stop.
12333  //
12334  // The only "good" implementation would require object
12335  // listeners, similar to property listeners.
12336 
12338 
12339  if (timeout > 0)
12340  start.stamp ();
12341 
12342  while (true)
12343  {
12344  if (true)
12345  {
12346  gh_manager::auto_lock guard;
12347 
12349 
12350  if (gh.ok ())
12351  {
12352  if (! pname.empty () && waitfor_results[id])
12353  break;
12354  }
12355  else
12356  break;
12357  }
12358 
12359  octave_sleep (0.1); // FIXME: really needed?
12360 
12361  octave_quit ();
12362 
12364 
12365  if (timeout > 0)
12366  {
12368 
12369  if (start + timeout < now)
12370  break;
12371  }
12372  }
12373 
12374  return ovl ();
12375 }
12376 
12377 DEFUN (__zoom__, args, ,
12378  doc: /* -*- texinfo -*-
12379 @deftypefn {} {} __zoom__ (@var{axes}, @var{mode}, @var{factor})
12380 @deftypefnx {} {} __zoom__ (@var{axes}, "out")
12381 @deftypefnx {} {} __zoom__ (@var{axes}, "reset")
12382 Undocumented internal function.
12383 @end deftypefn */)
12384 {
12385  int nargin = args.length ();
12386 
12387  if (nargin != 2 && nargin != 3)
12388  print_usage ();
12389 
12390  double h = args(0).double_value ();
12391 
12392  gh_manager::auto_lock guard;
12393 
12395 
12396  if (! handle.ok ())
12397  error ("__zoom__: invalid handle");
12398 
12400 
12401  axes::properties& ax_props =
12402  dynamic_cast<axes::properties&> (ax.get_properties ());
12403 
12404  if (nargin == 2)
12405  {
12406  std::string opt = args(1).string_value ();
12407 
12408  if (opt == "out" || opt == "reset")
12409  {
12410  if (opt == "out")
12411  {
12412  ax_props.clear_zoom_stack ();
12413  Vdrawnow_requested = true;
12414  }
12415  else
12416  ax_props.clear_zoom_stack (false);
12417  }
12418  }
12419  else
12420  {
12421  std::string mode = args(1).string_value ();
12422  double factor = args(2).scalar_value ();
12423 
12424  ax_props.zoom (mode, factor);
12425  Vdrawnow_requested = true;
12426  }
12427 
12428  return ovl ();
12429 }
12430 
12431 DEFUN (__get_frame__, args, ,
12432  doc: /* -*- texinfo -*-
12433 @deftypefn {} {@var{cdata} =} __get_frame__ (@var{hfig})
12434 Internal function.
12435 
12436 Return the pixel cdata of figure hfig in the form of a height-by-width-by-3
12437 uint8 array.
12438 @end deftypefn */)
12439 {
12440  if (args.length () != 1)
12441  print_usage ();
12442 
12443  double h = args(0).xdouble_value ("__get_frame__: HFIG is not a handle");
12444 
12446  if (! go || ! go.isa ("figure"))
12447  error ("__get_frame__: HFIG is not a figure");
12448 
12449  // For Matlab compatibility, getframe must flush the event queue.
12451 
12452  return ovl (go.get_toolkit ().get_pixels (go));
12453 }
static Matrix default_axes_ticklength(void)
Definition: graphics.cc:463
uint32_t id
Definition: graphics.cc:12193
bool Vdebug_on_error
Definition: error.cc:62
OCTINTERP_API octave_value_list feval(const std::string &name, const octave_value_list &args=octave_value_list(), int nargout=0)
int warning_enabled(const std::string &id)
Definition: error.cc:669
Matrix do_get_children(bool return_hidden) const
Definition: graphics.cc:1692
std::map< graphics_handle, graphics_object >::iterator iterator
Definition: graphics.in.h:6291
virtual std::string graphics_object_name(void) const
Definition: graphics.in.h:2189
octave_idx_type rows(void) const
Definition: Array.h:404
static void pop_figure(const graphics_handle &h)
Definition: graphics.in.h:6136
void calc_ticks_and_lims(array_property &lims, array_property &ticks, array_property &mticks, bool limmode_is_auto, bool tickmode_is_auto, bool is_logscale)
Definition: graphics.cc:7390
bool ok(void) const
Definition: oct-handle.h:109
static graphics_event create_set_event(const graphics_handle &h, const std::string &name, const octave_value &value, bool notify_toolkit=true)
Definition: graphics.cc:10188
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:816
scalar structure containing the one value The second produces an empty struct array with one field and no values since being passed an empty cell array of struct array values When the value is a cell array containing a single entry this becomes a scalar struct with that single entry as the value of the field That single entry happens to be an empty cell array Finally if the value is a non scalar cell array then ode isstruct
Matrix get_axis_limits(double xmin, double xmax, double min_pos, double max_neg, const bool logscale)
Definition: graphics.cc:7092
OCTINTERP_API bool validate(const octave_value &v) const
Definition: graphics.cc:1744
bool is_bool_scalar(void) const
Definition: ov.h:562
static void convert_cdata_2(bool is_scaled, bool is_real, double clim_0, double clim_1, const double *cmapv, double x, octave_idx_type lda, octave_idx_type nc, octave_idx_type i, double *av)
Definition: graphics.cc:873
plist_map_iterator find(const std::string &go_name)
Definition: graphics.in.h:2159
void build_user_defaults_map(property_list::pval_map_type &def, const std::string go_name) const
Definition: graphics.in.h:2686
radio_values radio_val
Definition: graphics.in.h:1351
void set_visible(const octave_value &val)
Definition: graphics.cc:3926
bool is_aliminclude(void) const
Definition: graphics.in.h:2832
OCTINTERP_API void octave_sleep(double seconds)
static Matrix default_screensize(void)
Definition: graphics.cc:226
octave_base_value * clone(void) const
static std::set< double > updating_aspectratios
Definition: graphics.cc:6439
bool is_handle_visible(void) const
Definition: graphics.cc:3267
static Matrix default_axes_tick(void)
Definition: graphics.cc:448
void rotate3d(double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:8471
virtual void reparent(const graphics_handle &np)
Definition: graphics.in.h:2552
OCTAVE_EXPORT octave_value_list column
Definition: sparse.cc:123
Definition: Cell.h:37
For example cd octave end example noindent changes the current working directory to file
Definition: dirfns.cc:124
void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:9318
bool compare(const std::string &s, size_t limit=std::string::npos) const
Definition: caseless-str.h:76
#define GO_BODY(TYPE)
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:9698
static double default_screenpixelsperinch(void)
Definition: graphics.cc:239
void execute(void)
Definition: graphics.cc:10116
static caseless_str validate_property_name(const std::string &who, const std::string &what, const std::set< std::string > &pnames, const caseless_str &pname)
Definition: graphics.cc:82
static octave_map cat(int dim, octave_idx_type n, const octave_scalar_map *map_list)
Definition: oct-map.cc:691
static Matrix default_axes_outerposition(void)
Definition: graphics.cc:424
static Matrix default_axes_view(void)
Definition: graphics.cc:437
virtual octave_value get_factory_default(const caseless_str &) const
Definition: graphics.cc:9932
bool is_uint32_type(void) const
Definition: ov.h:681
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:135
bool Vdebug_on_warning
Definition: error.cc:70
void * function_data
Definition: graphics.cc:10125
void build_user_defaults_map(property_list::pval_map_type &def, const std::string go_name) const
Definition: graphics.cc:3466
graphics_toolkit find_toolkit(const std::string &name) const
Definition: gtk-manager.h:64
virtual bool do_set(const octave_value &)
Definition: graphics.in.h:398
void override_defaults(base_graphics_object &obj)
Definition: graphics.in.h:3235
std::set< std::string > type_constraints
Definition: graphics.in.h:1482
graphics_handle do_get_handle(bool integer_figure_handle)
Definition: graphics.cc:2603
void reset_default_properties(void)
Definition: graphics.cc:8614
graphics_toolkit get_toolkit(void) const
Definition: graphics.cc:3857
void override_defaults(base_graphics_object &obj)
Definition: graphics.cc:3224
void convert_cdata_1(bool is_scaled, bool is_real, double clim_0, double clim_1, const double *cmapv, const T *cv, octave_idx_type lda, octave_idx_type nc, double *av)
Definition: graphics.cc:905
static Matrix default_control_sliderstep(void)
Definition: graphics.cc:533
std::string string_value(bool force=false) const
Definition: ov.h:955
static ColumnVector xform_vector(double x, double y, double z)
Definition: graphics.cc:6924
void do_renumber_figure(const graphics_handle &old_gh, const graphics_handle &new_gh)
Definition: graphics.cc:2687
bool isempty(void) const
Definition: ov.h:529
string_vector keys(void) const
Definition: oct-map.h:342
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:148
octave_int< T > xmax(const octave_int< T > &x, const octave_int< T > &y)
graphics_handle gca(void)
Definition: graphics.cc:2786
void erase(const std::string pname)
Definition: graphics.in.h:2118
static octave_value convert_cdata(const base_properties &props, const octave_value &cdata, bool is_scaled, int cdim)
Definition: graphics.cc:915
const T * data(void) const
Definition: Array.h:582
static Matrix default_figure_paperposition(void)
Definition: graphics.cc:498
double value(void) const
Definition: oct-handle.h:74
void update_xlabel_position(void)
Definition: graphics.cc:6005
static Matrix do_translate(double x0, double x1, const Matrix &lims, bool is_logscale)
Definition: graphics.cc:8365
static Matrix default_patch_xdata(void)
Definition: graphics.cc:391
static void recover_from_exception(void)
finite_type finite_constraint
Definition: graphics.in.h:1484
void insert_property(const std::string &name, property p)
Definition: graphics.in.h:2205
void do_execute_listener(const graphics_handle &h, const octave_value &l)
Definition: graphics.cc:10218
bool islogical(void) const
Definition: ov.h:696
void update_axes_layout(void)
Definition: graphics.cc:5760
std::string join(const std::string &sep="") const
Definition: str-vec.cc:134
static Matrix default_data(void)
Definition: graphics.cc:299
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:9881
double xdouble_value(const char *fmt,...) const
return ovl(is_hghandle(args(0)))
virtual void init_integerhandle(const octave_value &)
Definition: graphics.in.h:2195
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:9592
bool isempty(void) const
Definition: Array.h:565
static void run_event_hooks(void)
Definition: cmd-edit.cc:1574
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
std::string dir_sep_chars(void)
Definition: file-ops.cc:242
static Matrix default_patch_ydata(void)
Definition: graphics.cc:401
const_iterator find(const std::string pname) const
Definition: graphics.in.h:2071
graphics_handle get_handle(void) const
Definition: graphics.in.h:2774
octave_value get_factory_default(const caseless_str &name) const
Definition: graphics.in.h:2736
identity matrix If supplied two scalar respectively For allows like xample val
Definition: data.cc:4986
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE * f
octave_value get_color_data(void) const
Definition: graphics.cc:9063
OCTINTERP_API bool do_set(const octave_value &v)
Definition: graphics.cc:1376
std::string value_as_string(const std::string &prop)
Definition: graphics.in.h:2765
Return the CPU time used by your Octave session The first output is the total time spent executing your process and is equal to the sum of second and third which are the number of CPU seconds spent executing in user mode and the number of CPU seconds spent executing in system mode
Definition: data.cc:6348
octave::sys::time now
Definition: data.cc:6251
static OCTAVE_NORETURN void err_set_invalid(const std::string &pname)
Definition: graphics.cc:72
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:3759
#define CHECK_ARRAY_EQUAL(T, F, A)
static bool updating_zlabel_position
Definition: graphics.cc:6213
static bool updating_axes_layout
Definition: graphics.cc:5757
bool empty(void) const
Definition: ovl.h:98
RowVector xform2cam(const ColumnVector &v)
Definition: graphics.cc:5538
virtual void remove_all_listeners(void)
Definition: graphics.cc:3429
static void cleanup_waitfor_postset_listener(const octave_value &listener)
Definition: graphics.cc:12086
void update_font(std::string prop="")
Definition: graphics.cc:6561
Matrix xform_matrix(void)
Definition: graphics.cc:5377
static bool is_hghandle(const graphics_handle &h)
Definition: graphics.cc:2916
T max(T x, T y)
Definition: lo-mappers.h:383
void update_units(const caseless_str &old_units)
Definition: graphics.cc:9673
void do_post_event(const graphics_event &e)
Definition: graphics.cc:10336
callback_props & operator=(const callback_props &)=delete
property_list get_defaults_list(void) const
Definition: graphics.in.h:3279
for large enough k
Definition: lu.cc:617
void sync_positions(void)
Definition: graphics.cc:4913
void resize(int n, int fill_value=0)
Definition: dim-vector.h:310
const T * fortran_vec(void) const
Definition: Array.h:584
virtual void add_listener(const caseless_str &, const octave_value &, listener_mode=POSTSET)
Definition: graphics.cc:3380
int32NDArray int32_array_value(void) const
Definition: ov.h:937
static void push_figure(const graphics_handle &h)
Definition: graphics.in.h:6130
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:3937
OCTINTERP_API void run_listeners(listener_mode mode=POSTSET)
Definition: graphics.cc:1179
Matrix unit_cube(void)
Definition: graphics.cc:5507
void mark_modified(void)
Definition: graphics.cc:3213
void flush_stdout(void)
Definition: pager.cc:464
void add_fcn(void(*fcn)(void))
bool isnan(bool)
Definition: lo-mappers.h:187
graphics_handle handle_value(void) const
Definition: graphics.in.h:1634
void do_post_function(graphics_event::event_fcn fcn, void *fcn_data)
Definition: graphics.cc:10387
pval_map_type::const_iterator pval_map_const_iterator
Definition: graphics.in.h:2139
std::complex< T > ceil(const std::complex< T > &x)
Definition: lo-mappers.h:112
static void execute_listener(const graphics_handle &h, const octave_value &l)
Definition: graphics.in.h:6180
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:53
virtual void update_uicontextmenu(void) const
Definition: graphics.cc:3252
set_event(const graphics_handle &h, const std::string &name, const octave_value &value, bool do_notify_toolkit=true)
Definition: graphics.cc:10132
void error(const char *fmt,...)
Definition: error.cc:578
void set_dynamic(const caseless_str &pname, const octave_value &val)
Definition: graphics.cc:3134
octave_value_list splice(octave_idx_type offset, octave_idx_type len, const octave_value_list &lst=octave_value_list()) const
Definition: ovl.cc:124
void finalize(void)
Definition: graphics.in.h:2862
void reset_default_properties(void)
Definition: graphics.cc:3805
void do_free(const graphics_handle &h)
Definition: graphics.cc:2644
bool isinf(double x)
Definition: lo-mappers.h:225
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:139
void zoom(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8301
callback_event(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.cc:10067
OCTINTERP_API octave_value_list set_warning_state(const std::string &id, const std::string &state)
property get_property_dynamic(const caseless_str &pname)
Definition: graphics.cc:3151
std::set< std::string > type_constraints
Definition: graphics.in.h:1658
void set(const caseless_str &name, const octave_value &val)
Definition: graphics.cc:2085
std::ostream & list_in_columns(std::ostream &, int width=0, const std::string &prefix="") const
Definition: str-vec.cc:195
static Matrix default_axes_position(void)
Definition: graphics.cc:411
virtual void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:3404
pval_map_type::iterator pval_map_iterator
Definition: graphics.in.h:2138
virtual void initialize(const graphics_object &go)
Definition: graphics.in.h:2632
static T abs(T x)
Definition: pr-output.cc:1696
static Matrix figure_handle_list(bool show_hidden=false)
Definition: graphics.in.h:6174
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:6594
static graphics_event create_function_event(event_fcn fcn, void *data=nullptr)
Definition: graphics.cc:10181
void adopt(const graphics_handle &h)
Definition: graphics.in.h:2780
OCTINTERP_API bool str2rgb(const std::string &str)
Definition: graphics.cc:1263
bool contains(const std::string &val, std::string &match)
Definition: graphics.in.h:860
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:442
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:191
void update_handlevisibility(void)
Definition: graphics.cc:8577
static void xset_gcbo(const graphics_handle &h)
Definition: graphics.cc:10197
std::set< graphics_handle >::iterator free_list_iterator
Definition: graphics.in.h:6295
static void restore_gcbo(void)
Definition: graphics.in.h:6430
octave_value get_ylim(void) const
Definition: graphics.in.h:2826
void translate_view(const std::string &mode, double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:8425
static double make_handle_fraction(void)
Definition: graphics.cc:2595
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:8836
Cell available_toolkits_list(void) const
Definition: gtk-manager.h:74
static std::set< double > updating_axis_limits
Definition: graphics.cc:7778
std::string dirname(const std::string &path)
Definition: file-ops.cc:353
void do_restore_gcbo(void)
Definition: graphics.cc:10207
bool is_defined(void) const
Definition: ov.h:523
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:9763
double calc_tick_sep(double minval, double maxval)
Definition: graphics.cc:7058
void set_position(const octave_value &val)
Definition: graphics.in.h:4388
graphics_xform get_transform(void) const
Definition: graphics.in.h:3442
static graphics_handle make_graphics_handle(const std::string &go_name, const graphics_handle &parent, bool integer_figure_handle=false, bool do_createfcn=true, bool do_notify_toolkit=true)
Definition: graphics.in.h:6109
octave_idx_type columns(void) const
Definition: Array.h:413
T & elem(octave_idx_type n)
Definition: Array.h:488
void update_ticklength(void)
Definition: graphics.cc:5925
color_values color_val
Definition: graphics.in.h:1149
u
Definition: lu.cc:138
bool is_modified(void) const
Definition: graphics.in.h:2236
std::string default_toolkit(void) const
Definition: gtk-manager.h:112
static void force_close_figure(const graphics_handle &h)
Definition: graphics.cc:2853
Complex atan(const Complex &x)
Definition: lo-mappers.h:80
static double y_dpi(void)
Definition: display.h:90
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function t
Definition: ov-usr-fcn.cc:997
listener_map listeners
Definition: graphics.in.h:416
enum double_radio_property::current_enum current_type
static bool is_handle_visible(const graphics_handle &h)
Definition: graphics.in.h:6251
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2598
bool has_dynamic_property(const std::string &pname)
Definition: graphics.cc:3123
callback_props(void)
Definition: graphics.cc:1770
bool valid_object(void) const
Definition: graphics.in.h:3288
virtual void delete_children(bool clear=false)
Definition: graphics.in.h:2306
static void cleanup_waitfor_predelete_listener(const octave_value &listener)
Definition: graphics.cc:12090
std::pair< double, bool > minval
Definition: graphics.in.h:1485
static std::string get_graphics_object_type(double val)
Definition: graphics.cc:10905
bool is_yliminclude(void) const
Definition: graphics.in.h:2841
void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:8012
static void max_axes_scale(double &s, Matrix &limits, const Matrix &kids, double pbfactor, double dafactor, char limit_type, bool tight)
Definition: graphics.cc:6418
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
double fix(double x)
Definition: lo-mappers.h:127
static Matrix default_surface_xdata(void)
Definition: graphics.cc:326
graphics_handle gh
Definition: graphics.cc:11812
s
Definition: file-io.cc:2729
virtual Matrix get_boundingbox(bool=false, const Matrix &=Matrix()) const
Definition: graphics.in.h:2257
octave_value get_zlim(void) const
Definition: graphics.in.h:2829
static double default_screendepth(void)
Definition: graphics.cc:220
void delete_property_listener(const std::string &nm, const octave_value &v, listener_mode mode=POSTSET)
Definition: graphics.in.h:2856
static int toggle_warn(std::string id, bool on, int state=-1)
Definition: graphics.cc:2986
callback_event(void)
Definition: graphics.cc:10086
virtual octave_value get(bool all=false) const
Definition: graphics.in.h:2466
graphics_handle do_make_figure_handle(double val, bool do_notify_toolkit)
Definition: graphics.cc:10024
endfunction addlistener(gcf, "position", @my_listener, "my string") nd group nd example eealso
Definition: graphics.cc:11799
static void unlock(void)
Definition: graphics.in.h:6168
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:4689
i e
Definition: data.cc:2591
bool is_dir(void) const
Definition: file-stat.cc:57
void reset_default_properties(void)
Definition: graphics.cc:4705
Matrix get_children(void) const
Definition: graphics.in.h:2275
bool is_int16_type(void) const
Definition: ov.h:666
void delete_text_child(handle_property &h)
Definition: graphics.cc:5319
graphics_handle current_val
Definition: graphics.in.h:1661
static callback_props executing_callbacks
Definition: graphics.cc:1804
virtual void set(const caseless_str &pname, const octave_value &pval)
Definition: graphics.in.h:2453
void renumber_child(graphics_handle old_gh, graphics_handle new_gh)
Definition: graphics.in.h:2311
static Matrix default_lim(bool logscale=false)
Definition: graphics.cc:280
void update_axis_limits(const std::string &axis_type)
Definition: graphics.in.h:2795
octave_value arg
Definition: pr-output.cc:3244
octave_function * fcn
Definition: ov-class.cc:1754
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
OCTINTERP_API bool do_set(const octave_value &newval)
Definition: graphics.cc:1305
static gh_manager * instance
Definition: graphics.in.h:6289
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2786
octave_value get_clim(void) const
Definition: graphics.in.h:2820
static int height(void)
Definition: display.h:70
octave_value get(void) const
Definition: graphics.in.h:1098
ColumnVector untransform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:6949
std::set< caseless_str > possible_vals
Definition: graphics.in.h:908
virtual void update_boundingbox(void)
Definition: graphics.cc:3285
cell array If invoked with two or more scalar integer or a vector of integer values
Definition: ov-cell.cc:1241
virtual octave_value get_alim(void) const
Definition: graphics.in.h:2328
octave_value resize(const dim_vector &dv, bool fill=false) const
Definition: ov.h:511
std::list< dim_vector > size_constraints
Definition: graphics.in.h:1483
static void delete_graphics_object(const graphics_handle &h)
Definition: graphics.cc:2795
#define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL)
calling an anonymous function involves an overhead quite comparable to the overhead of an m file function Passing a handle to a built in function is because the interpreter is not involved in the internal loop For a
Definition: cellfun.cc:400
uint32NDArray uint32_array_value(void) const
Definition: ov.h:949
void do_post_callback(const graphics_handle &h, const std::string &name, const octave_value &data)
Definition: graphics.cc:10344
virtual void adopt(const graphics_handle &h)
Definition: graphics.in.h:2544
static void check_limit_vals(double &min_val, double &max_val, double &min_pos, double &max_neg, const octave_value &data)
Definition: graphics.cc:7004
bool swap
Definition: load-save.cc:738
graphics_toolkit get_toolkit(void) const
Definition: graphics.in.h:2850
graphics_handle gcf(void)
Definition: graphics.cc:2776
virtual void finalize(const graphics_object &go)
Definition: graphics.in.h:2638
std::string current_val
Definition: graphics.in.h:1151
void update_units(const caseless_str &old_units)
Definition: graphics.cc:4646
void pan(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8454
ColumnVector transform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:6936
void update_aspectratios(void)
Definition: graphics.cc:6442
int max_arg_index
Definition: graphics.cc:12195
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:6965
property_list default_properties
Definition: graphics.in.h:3301
static void cleanup_waitfor_id(uint32_t id)
Definition: graphics.cc:12048
static void initialize_r(const graphics_handle &h)
Definition: graphics.cc:1971
static void free(const graphics_handle &h)
Definition: graphics.in.h:6074
callback_event(const graphics_handle &h, const octave_value &cb, const octave_value &data=Matrix())
Definition: graphics.cc:10072
bool valid_object(void) const
Definition: graphics.in.h:2806
std::list< double > children_list
Definition: graphics.in.h:1781
static bool updating_xlabel_position
Definition: graphics.cc:6002
Matrix xform_translate(double x, double y, double z)
Definition: graphics.cc:5429
int16NDArray int16_array_value(void) const
Definition: ov.h:934
double h
Definition: graphics.cc:11808
void update_units(void)
Definition: graphics.cc:9495
void initialize(const graphics_object &go)
Definition: graphics.cc:8630
Matrix transpose(void) const
Definition: dMatrix.h:132
virtual octave_value get_default(const caseless_str &) const
Definition: graphics.cc:9923
done
Definition: syscalls.cc:251
create a structure array and initialize its values The dimensions of each cell array of values must match Singleton cells and non cell values are repeated so that they fill the entire array If the cells are empty
Definition: ov-struct.cc:1736
nd deftypefn *std::string name
Definition: sysdep.cc:647
octave_value & assign(assign_op op, const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
octave_value data
Definition: graphics.in.h:1477
OCTAVE_EXPORT octave_value_list isdir nd deftypefn *std::string nm
Definition: utils.cc:975
void do_pop_figure(const graphics_handle &h)
Definition: graphics.cc:10051
OCTINTERP_API void disable_warning(const std::string &id)
MArray< T > reshape(const dim_vector &new_dims) const
Definition: MArray.h:91
void update_paperunits(const caseless_str &old_paperunits)
Definition: graphics.cc:4348
static bool updating_ylabel_position
Definition: graphics.cc:6103
static bool updating_patch_data
Definition: graphics.cc:8872
octave_value get_default(const caseless_str &name) const
Definition: graphics.in.h:2731
void initialize(void)
Definition: graphics.in.h:2860
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function xample nargout(@histc)
Definition: ov-usr-fcn.cc:997
void do_enable_event_processing(bool enable=true)
Definition: graphics.cc:10469
static property_list::pval_map_type factory_defaults(void)
static Matrix default_patch_vertices(void)
Definition: graphics.cc:379
std::list< graphics_object > callback_objects
Definition: graphics.in.h:6321
Array< T > as_column(void) const
Return the array as a column vector.
Definition: Array.h:370
bool is_function_handle(void) const
Definition: ov.h:749
ColumnVector hcv
Definition: graphics.cc:10826
std::map< graphics_handle, graphics_object > handle_map
Definition: graphics.in.h:6302
void redraw_figure(const graphics_object &go) const
static octave_value box(JNIEnv *jni_env, void *jobj, void *jcls_arg=nullptr)
bool Vdrawnow_requested
Definition: input.cc:94
virtual bool has_property(const caseless_str &) const
Definition: graphics.in.h:2230
void update_title_position(void)
Definition: graphics.cc:6340
static bool is_figure(double val)
Definition: graphics.cc:2952
int event_processing
Definition: graphics.in.h:6324
OCTINTERP_API octave_value_list eval_string(const std::string &, bool silent, int &parse_status, int nargout)
OCTINTERP_API void execute(const octave_value &data=octave_value()) const
Definition: graphics.cc:1807
ColumnVector cam2xform(const Array< double > &m)
Definition: graphics.cc:5528
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:6912
void update_units(void)
Definition: graphics.cc:8801
void reset_default_properties(void)
Definition: graphics.cc:9911
plist_map_iterator end(void)
Definition: graphics.in.h:2156
double force_in_range(double x, double lower, double upper)
Definition: graphics.cc:8209
int timeout_index
Definition: graphics.cc:12196
virtual octave_value get_xlim(void) const
Definition: graphics.in.h:2330
std::list< graphics_handle > figure_list
Definition: graphics.in.h:6312
void mark_modified(void)
Definition: graphics.in.h:2674
static char default_im_data[]
virtual void delete_listener(const caseless_str &, const octave_value &, listener_mode=POSTSET)
Definition: graphics.cc:3391
graphics_event(void)=default
octave_idx_type columns(void) const
Definition: ov.h:474
ColumnVector xform_vector(void)
Definition: graphics.cc:5388
bool is_uint64_type(void) const
Definition: ov.h:684
static void add(fptr f)
octave_value get(bool all=false) const
Definition: graphics.in.h:2711
virtual void adopt(const graphics_handle &h)
Definition: graphics.in.h:2247
void set_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
static Matrix default_control_position(void)
Definition: graphics.cc:520
std::string get_name(void) const
Definition: graphics.in.h:291
bool is_single_type(void) const
Definition: ov.h:651
virtual graphics_toolkit get_toolkit(void) const
Definition: graphics.cc:3274
bool iscell(void) const
Definition: ov.h:536
void do_delete_children(bool clear)
Definition: graphics.cc:1729
~uicontextmenu(void)
Definition: graphics.cc:9406
octave_value callback_data
Definition: graphics.cc:10094
double signum(double x)
Definition: lo-mappers.h:244
OCTINTERP_API int calc_dimensions(const graphics_object &gh)
virtual void update_autopos(const std::string &elem_type)
Definition: graphics.cc:3299
octave_idx_type nelem(void) const
Definition: graphics.in.h:903
std::string str
Definition: hash.cc:118
static void xset(const graphics_handle &h, const caseless_str &pname, const octave_value &val)
Definition: graphics.cc:2718
static octave_value_list waitfor_listener(const octave_value_list &args, int)
Definition: graphics.cc:12093
nd group nd example bool waitfor_results
Definition: graphics.cc:11965
Matrix xform_scale(double x, double y, double z)
Definition: graphics.cc:5416
octave_value as_octave_value(void) const
Definition: oct-handle.h:76
void rotate_view(double delta_az, double delta_el, bool push_to_zoom_stack=true)
Definition: graphics.cc:8518
dim_vector dims(void) const
Definition: ov.h:469
static double x_dpi(void)
Definition: display.h:85
octave_value retval
Definition: graphics.cc:10823
static Matrix xform_eye(void)
Definition: graphics.cc:6930
static Matrix default_figure_position(void)
Definition: graphics.cc:474
double norm(const ColumnVector &v)
Definition: graphics.cc:5489
void unzoom(void)
Definition: graphics.cc:8541
static void remove_event_hook(event_hook_fcn f)
Definition: cmd-edit.cc:1562
double timeout
Definition: graphics.cc:12198
string_vector & append(const std::string &s)
Definition: str-vec.cc:107
void execute(void)
Definition: graphics.cc:10137
bool isinteger(void) const
Definition: ov.h:687
static uint32_t id_counter
Definition: graphics.cc:12192
static bool updating_hggroup_limits
Definition: graphics.cc:9201
function_event(graphics_event::event_fcn fcn, void *data=nullptr)
Definition: graphics.cc:10106
radio_values radio_val
Definition: graphics.in.h:1150
void resize(const dim_vector &dv, const T &rfv)
Resizing (with fill).
Definition: Array.cc:1010
void erase(const callback_property *ptr)
Definition: graphics.cc:1785
static base_graphics_object * make_graphics_object_from_type(const caseless_str &type, const graphics_handle &h=graphics_handle(), const graphics_handle &p=graphics_handle())
Definition: graphics.cc:1110
virtual octave_value get_ylim(void) const
Definition: graphics.in.h:2331
~callback_props(void)=default
octave_int< T > pow(const octave_int< T > &a, const octave_int< T > &b)
octave_idx_type rows(void) const
Definition: ov.h:472
double tmp
Definition: data.cc:6252
static Matrix default_panel_position(void)
Definition: graphics.cc:544
uint64NDArray uint64_array_value(void) const
Definition: ov.h:952
void set_defaults(base_graphics_object &obj, const std::string &mode)
Definition: graphics.cc:5094
ColumnVector row_max(void) const
Definition: dMatrix.cc:2660
void print_figure(const graphics_object &go, const std::string &term, const std::string &file, const std::string &debug_file="") const
virtual base_properties & get_properties(void)
Definition: graphics.in.h:2569
bool set_property_in_handle(double handle, const std::string &property, const octave_value &arg, const std::string &func)
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:9527
virtual graphics_handle get_parent(void) const
Definition: graphics.in.h:2520
bool request_drawnow
Definition: graphics.cc:10828
void set_toolkit(const graphics_toolkit &b)
Definition: graphics.cc:1987
static octave_value convert_linestyleorder_string(const octave_value &val)
Definition: graphics.cc:6806
octave_function * function_value(bool silent=false) const
const Cell & contents(const_iterator p) const
Definition: oct-map.h:317
static Matrix default_figure_papersize(void)
Definition: graphics.cc:487
base_properties & get_properties(void)
Definition: graphics.in.h:2788
virtual void remove_child(const graphics_handle &h)
Definition: graphics.in.h:2238
static void get_array_limits(const Array< T > &m, double &emin, double &emax, double &eminp, double &emaxp)
Definition: graphics.cc:1002
static bool lookup_object_name(const caseless_str &name, caseless_str &go_name, caseless_str &rest)
Definition: graphics.cc:1031
void register_toolkit(const std::string &name)
Definition: gtk-manager.cc:71
void reset_default_properties(void)
Definition: graphics.in.h:2866
virtual text_element * parse(const std::string &s)=0
static Matrix default_light_position(void)
Definition: graphics.cc:557
static Matrix papersize_from_type(const caseless_str punits, const caseless_str ptype)
Definition: graphics.cc:4077
virtual void update_axis_limits(const std::string &axis_type) const
Definition: graphics.cc:3233
static Matrix handle_list(bool show_hidden=false)
Definition: graphics.in.h:6148
void update_camera(void)
Definition: graphics.cc:5544
idx type
Definition: ov.cc:3114
std::string class_name(void) const
Definition: ov.h:1291
Definition: dMatrix.h:36
void zoom_about_point(const std::string &mode, double x, double y, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8272
graphics_handle get_handle(void) const
Definition: graphics.in.h:2528
sz
Definition: data.cc:5264
bool is_function(void) const
Definition: ov.h:758
void renumber_parent(graphics_handle new_gh)
Definition: graphics.in.h:2316
octave_value getfield(const std::string &key) const
Definition: oct-map.cc:184
Matrix map_to_boundingbox(double x, double y) const
Definition: graphics.cc:3989
octave_value get_dynamic(const caseless_str &pname) const
Definition: graphics.cc:3092
static Matrix default_surface_ydata(void)
Definition: graphics.cc:338
static Matrix convert_text_position(const Matrix &pos, const text::properties &props, const caseless_str &from_units, const caseless_str &to_units)
Definition: graphics.cc:772
graphics_handle get_parent(void) const
Definition: graphics.in.h:295
ColumnVector cross(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5495
octave_handle graphics_handle
static dim_vector alloc(int n)
Definition: dim-vector.h:264
bool is_int8_type(void) const
Definition: ov.h:663
static Matrix default_colororder(void)
Definition: graphics.cc:245
void calc_ticklabels(const array_property &ticks, any_property &labels, bool is_logscale, const bool is_origin, const int other_axislocation, const array_property &axis_lims)
Definition: graphics.cc:7545
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:9566
void override_defaults(base_graphics_object &obj)
Definition: graphics.in.h:2676
#define eps(C)
static void close_figure(const graphics_handle &h)
Definition: graphics.cc:2845
octave_value property_value
Definition: graphics.cc:10160
#define DECLARE_STATIC_FUNX(name, args_name, nargout_name)
Definition: defun-int.h:147
OCTINTERP_API radio_values(const std::string &opt_string="")
Definition: graphics.cc:1187
std::string pname
Definition: graphics.cc:11810
#define FIX_LIMITS
virtual octave_value get_zlim(void) const
Definition: graphics.in.h:2332
octave_idx_type numel(void) const
Definition: oct-map.h:375
void set(const caseless_str &name, const octave_value &val)
Definition: graphics.in.h:2694
static int width(void)
Definition: display.h:75
void update_zlabel_position(void)
Definition: graphics.cc:6216
static graphics_event create_callback_event(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.cc:10165
graphics_handle parent
Definition: graphics.in.h:414
virtual void defaults(void) const
Definition: graphics.in.h:2560
virtual void reset_default_properties(void)
Definition: graphics.cc:3489
With real return the complex result
Definition: data.cc:3260
set_event(void)
Definition: graphics.cc:10153
std::string get_title(void) const
Definition: graphics.cc:4671
static void execute_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.in.h:6187
std::list< graphics_event > event_queue
Definition: graphics.in.h:6318
octave_scalar_map as_struct(const std::string &prefix_arg) const
Definition: graphics.cc:2317
octave_value get_alim(void) const
Definition: graphics.in.h:2817
octave_int< uint32_t > octave_uint32
static void cleanup_instance(void)
Definition: graphics.in.h:6065
bool is_int32_type(void) const
Definition: ov.h:669
void check_axis_limits(Matrix &limits, const Matrix kids, const bool logscale, char &update_type)
Definition: graphics.cc:7192
graphics_handle get_parent(void) const
Definition: graphics.in.h:2772
Matrix map_from_boundingbox(double x, double y) const
Definition: graphics.cc:3972
void remove_child(const graphics_handle &h)
Definition: graphics.cc:5350
OCTAVE_EXPORT octave_value_list iscellstr
Definition: ov-cell.cc:1220
void set_position(const octave_value &val, bool do_notify_toolkit=true)
Definition: graphics.cc:4006
virtual std::string type(void) const
Definition: graphics.in.h:2592
Matrix get_all_children(void) const
Definition: graphics.in.h:2280
static uint32_t state[624]
Definition: randmtzig.cc:183
graphics_handle do_make_graphics_handle(const std::string &go_name, const graphics_handle &p, bool integer_figure_handle, bool do_createfcn, bool do_notify_toolkit)
Definition: graphics.cc:9965
void add_dependent_obj(graphics_handle gh)
Definition: graphics.in.h:5396
T min(T x, T y)
Definition: lo-mappers.h:376
static Matrix convert_position(const Matrix &pos, const caseless_str &from_units, const caseless_str &to_units, const Matrix &parent_dim)
Definition: graphics.cc:627
static octave_idx_type find(octave_idx_type i, octave_idx_type *pp)
Definition: colamd.cc:103
virtual void set_defaults(const std::string &)
Definition: graphics.in.h:2461
static Matrix default_surface_cdata(void)
Definition: graphics.cc:361
octave_int< T > xmin(const octave_int< T > &x, const octave_int< T > &y)
listener_mode
Definition: graphics.in.h:266
static void magform(double x, double &a, int &b)
Definition: graphics.cc:7039
octave_idx_type nfields(void) const
Definition: oct-map.h:330
void warning(const char *fmt,...)
Definition: error.cc:801
void do_post_set(const graphics_handle &h, const std::string &name, const octave_value &value, bool notify_toolkit=true)
Definition: graphics.cc:10395
static std::map< caseless_str, graphics_object > dprop_obj_map
Definition: graphics.cc:1827
virtual void finalize(const graphics_object &)
Matrix get_extent(bool with_text=false, bool only_text_height=false) const
Definition: graphics.cc:6622
void update_ylabel_position(void)
Definition: graphics.cc:6106
static std::string default_graphics_toolkit(void)
Definition: graphics.cc:511
octave::unwind_protect frame
Definition: graphics.cc:12190
octave_value get_property_from_handle(double handle, const std::string &property, const std::string &func)
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
std::string current_val
Definition: graphics.in.h:1352
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:125
void get_children_limits(double &min_val, double &max_val, double &min_pos, double &max_neg, const Matrix &kids, char limit_type)
Definition: graphics.cc:7695
std::string property_name
Definition: graphics.cc:10159
OCTINTERP_API bool validate(const octave_value &v)
Definition: graphics.cc:1418
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:9895
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:227
Matrix get_extent(text_element *elt, double rotation=0.0)
static graphics_handle make_figure_handle(double val, bool do_notify_toolkit=true)
Definition: graphics.in.h:6122
#define Inf
Definition: Faddeeva.cc:247
addproperty("my_style", gcf, "linelinestyle", "--")
octave_value get(void) const
Definition: graphics.in.h:1392
#define octave_stdout
Definition: pager.h:174
ColumnVector extract_n(octave_idx_type r1, octave_idx_type n) const
Definition: dColVector.cc:164
std::set< intptr_t > m_set
Definition: graphics.cc:1797
const octave_char_matrix & v2
bool any_element_is_inf_or_nan(void) const
Definition: dNDArray.cc:559
octave::sys::time start
Definition: graphics.cc:12337
std::pair< double, bool > maxval
Definition: graphics.in.h:1485
void update_autopos(const std::string &elem_type)
Definition: graphics.cc:6388
Cell values_as_cell(void) const
Definition: graphics.cc:1251
octave_value callback
Definition: graphics.cc:10093
bool is_undefined(void) const
Definition: ov.h:526
void do_close_all_figures(void)
Definition: graphics.cc:2865
void do_push_figure(const graphics_handle &h)
Definition: graphics.cc:10043
static void do_cleanup_waitfor_listener(const octave_value &listener, listener_mode mode=POSTSET)
Definition: graphics.cc:12054
static void adopt(const graphics_handle &parent_h, const graphics_handle &h)
Definition: graphics.cc:2909
std::string default_val
Definition: graphics.in.h:907
enum color_property::current_enum current_type
double dot(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5483
void insert(const callback_property *ptr)
Definition: graphics.cc:1780
octave_value get_colormap(void) const
Definition: graphics.cc:5304
void set_outerposition(const octave_value &val, bool do_notify_toolkit=true)
Definition: graphics.cc:4036
virtual std::string value_as_string(const std::string &prop)
Definition: graphics.cc:3535
static octave_value xget(const graphics_handle &h, const caseless_str &name)
Definition: graphics.cc:2736
std::map< std::string, pval_map_type > plist_map_type
Definition: graphics.in.h:2136
static octave_value convert_ticklabel_string(const octave_value &val)
Definition: graphics.cc:6708
properties xproperties
Definition: graphics.in.h:3226
void cross_product(double x1, double y1, double z1, double x2, double y2, double z2, double &x, double &y, double &z)
Definition: graphics.cc:9069
OCTAVE_EXPORT octave_value_list or N dimensional array whose elements are all equal to the IEEE symbol NaN(Not a Number). NaN is the result of operations which do not produce a well defined 0 result. Common operations which produce a NaN are arithmetic with infinity ex($\infty - \infty$)
static void xinitialize(const graphics_handle &h)
Definition: graphics.cc:2967
void remove_child(const graphics_handle &h)
Definition: graphics.in.h:2778
void push_zoom_stack(void)
Definition: graphics.cc:8315
uint16NDArray uint16_array_value(void) const
Definition: ov.h:946
std::string default_value(void) const
Definition: graphics.in.h:848
static Matrix default_image_cdata(void)
Definition: graphics.cc:310
p
Definition: lu.cc:138
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:227
OCTINTERP_API bool is_equal(const octave_value &v) const
Definition: graphics.cc:1521
static graphics_handle lookup(double val)
Definition: graphics.in.h:6087
static OCTINTERP_API property create(const std::string &name, const graphics_handle &parent, const caseless_str &type, const octave_value_list &args)
Definition: graphics.cc:1830
bool is_int64_type(void) const
Definition: ov.h:672
static int depth(void)
Definition: display.h:80
void add_property_listener(const std::string &nm, const octave_value &v, listener_mode mode=POSTSET)
Definition: graphics.in.h:2852
void normalize(ColumnVector &v)
Definition: graphics.cc:5476
OCTINTERP_API bool set(const octave_value &v, bool do_run=true, bool do_notify_toolkit=true)
Definition: graphics.cc:1156
double max(void) const
Definition: dColVector.cc:259
octave_map values_as_struct(void)
Definition: graphics.in.h:2770
int do_process_events(bool force=false)
Definition: graphics.cc:10405
void set_value_or_default(const caseless_str &name, const octave_value &val)
Definition: graphics.cc:2540
void remove_child(const graphics_handle &h)
Definition: graphics.cc:3829
octave_value lookup(const caseless_str &name) const
Definition: graphics.cc:2223
bool isfinite(double x)
Definition: lo-mappers.h:201
property(void)
Definition: graphics.in.h:1945
RowVector transpose(void) const
Definition: dColVector.cc:123
virtual void mark_modified(void)
Definition: graphics.in.h:2427
static graphics_handle get_handle(bool integer_figure_handle)
Definition: graphics.in.h:6067
void adopt(const graphics_handle &h)
Definition: graphics.cc:3894
std::string callback_name
Definition: graphics.cc:10092
double double_value(bool frc_str_conv=false) const
Definition: ov.h:822
octave_idx_type length(void) const
Definition: ovl.h:96
bool is_zliminclude(void) const
Definition: graphics.in.h:2844
octave_value get_color_data(void) const
Definition: graphics.cc:8863
int64NDArray int64_array_value(void) const
Definition: ov.h:940
void remove_child(const graphics_handle &h)
Definition: graphics.cc:3793
bool is_xliminclude(void) const
Definition: graphics.in.h:2838
static void normalized_aspectratios(Matrix &aspectratios, const Matrix &scalefactors, double xlength, double ylength, double zlength)
Definition: graphics.cc:6403
bool contains(const callback_property *ptr) const
Definition: graphics.cc:1790
int8NDArray int8_array_value(void) const
Definition: ov.h:931
the element is set to zero In other the statement xample y
Definition: data.cc:5264
static void renumber_figure(const graphics_handle &old_gh, const graphics_handle &new_gh)
Definition: graphics.in.h:6080
virtual octave_value get_clim(void) const
Definition: graphics.in.h:2329
array_property Matrix() callback_property closerequestfcn
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5442
b
Definition: cellfun.cc:400
void clear_zoom_stack(bool do_unzoom=true)
Definition: graphics.cc:8602
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:41
graphics_handle handle
Definition: graphics.cc:10158
static graphics_object get_object(double val)
Definition: graphics.in.h:6098
void set___graphics_toolkit__(const octave_value &val)
Definition: graphics.cc:3871
int nargin
Definition: graphics.cc:10818
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:9578
friend class graphics_object
Definition: graphics.in.h:2415
void update_fontunits(const caseless_str &old_fontunits)
Definition: graphics.cc:8718
void set_boundingbox(const Matrix &bb, bool internal=false, bool do_notify_toolkit=true)
Definition: graphics.cc:3954
octave::sys::file_stat fs(filename)
double round(double x)
Definition: lo-mappers.h:145
std::string values_as_string(void)
Definition: graphics.in.h:2763
void add_method(T *obj, void(T::*method)(void))
args.length() nargin
Definition: file-io.cc:589
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5410
void do_execute_callback(const graphics_handle &h, const octave_value &cb, const octave_value &data)
Definition: graphics.cc:10232
static void add_event_hook(event_hook_fcn f)
Definition: cmd-edit.cc:1554
bool iscomplex(void) const
Definition: ov.h:710
for i
Definition: data.cc:5264
void set_text_child(handle_property &h, const std::string &who, const octave_value &v)
Definition: graphics.cc:5003
static Matrix viridis_colormap(void)
Definition: graphics.cc:136
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:958
bool is_string(void) const
Definition: ov.h:577
void(* event_fcn)(void *)
Definition: graphics.in.h:6000
bool is_double_type(void) const
Definition: ov.h:648
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:9710
is a function function
Definition: bsxfun.cc:337
static Matrix do_zoom(double val, double factor, const Matrix &lims, bool is_logscale)
Definition: graphics.cc:8220
void execute(void)
Definition: graphics.cc:10077
bool is_climinclude(void) const
Definition: graphics.in.h:2835
gtk_manager & __get_gtk_manager__(const std::string &who)
void reparent(const graphics_handle &h)
Definition: graphics.in.h:2782
graphics_handle handle
Definition: graphics.cc:10091
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:295
graphics_toolkit get_toolkit(void) const
Definition: gtk-manager.cc:36
void update(int id)
Definition: graphics.in.h:2864
bool notify_toolkit
Definition: graphics.cc:10161
void set_modified(const octave_value &val)
Definition: graphics.in.h:2290
Matrix extract_n(octave_idx_type r1, octave_idx_type c1, octave_idx_type nr, octave_idx_type nc) const
Definition: dMatrix.cc:406
octave_value get_color_data(void) const
Definition: graphics.cc:8855
std::string type(void) const
Definition: graphics.in.h:2808
Matrix calc_tightbox(const Matrix &init_pos)
Definition: graphics.cc:4882
octave_value get_xlim(void) const
Definition: graphics.in.h:2823
static double convert_font_size(double font_size, const caseless_str &from_units, const caseless_str &to_units, double parent_height=0)
Definition: graphics.cc:569
is a function handle
Definition: bsxfun.cc:337
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
octave_idx_type length(void) const
static Matrix screen_size_pixels(void)
Definition: graphics.cc:864
graphics_object get_ancestor(const std::string &type) const
Definition: graphics.cc:3613
bool has_readonly_property(const caseless_str &pname) const
Definition: graphics.in.h:2758
static octave_value_list waitfor_del_listener(const octave_value_list &args, int)
Definition: graphics.cc:12125
gh_manager(void)
Definition: graphics.cc:9941
virtual octave_scalar_map values_as_struct(void)
Definition: graphics.cc:3562
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
static bool delete_executing
Definition: graphics.cc:2830
base_properties & get_properties(void)
Definition: graphics.in.h:3284
bool discard_error_messages
Definition: error.cc:119
static bool is_thread(void)
property_list get_factory_defaults_list(void) const
Definition: graphics.in.h:2753
bool is_uint8_type(void) const
Definition: ov.h:675
std::string values_as_string(void) const
Definition: graphics.cc:1222
Cell loaded_toolkits_list(void) const
Definition: gtk-manager.h:85
Matrix get_ticklabel_extents(const Matrix &ticks, const string_vector &ticklabels, const Matrix &limits)
Definition: graphics.cc:7651
void stamp(void)
Definition: oct-time.cc:106
octave_value callback
Definition: graphics.in.h:1937
void update_units(const caseless_str &old_units)
Definition: graphics.cc:6863
std::string type_name(void) const
Definition: ov.h:1289
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
nd group nd example For each display the value
Definition: sysdep.cc:866
static bool updating_title_position
Definition: graphics.cc:6337
virtual std::string values_as_string(void)
Definition: graphics.cc:3503
uint8NDArray get_pixels(const graphics_object &go) const
static Matrix default_surface_zdata(void)
Definition: graphics.cc:350
dim_vector dv
Definition: sub2ind.cc:263
static void lock(void)
Definition: graphics.in.h:6154
static void xcreatefcn(const graphics_handle &h)
Definition: graphics.cc:2960
static bool is_handle_visible(const graphics_handle &h)
Definition: graphics.cc:10555
octave::stream os
Definition: file-io.cc:627
Cell cell_value(void) const
bool empty(void) const
Definition: graphics.cc:1778
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:840
bool isnumeric(void) const
Definition: ov.h:723
plist_map_type::const_iterator plist_map_const_iterator
Definition: graphics.in.h:2142
bool is_matrix_type(void) const
Definition: ov.h:720
T x_nint(T x)
Definition: lo-mappers.h:284
void set_parent(const octave_value &val)
Definition: graphics.cc:3163
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:834
void xform(ColumnVector &v, const Matrix &m)
Definition: graphics.cc:5454
static graphics_handle current_figure(void)
Definition: graphics.in.h:6142
static void finalize_r(const graphics_handle &h)
Definition: graphics.cc:1955
void set_from_list(base_graphics_object &obj, property_list &defaults)
Definition: graphics.cc:3048
void translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5448
more on
Definition: toplev.cc:236
where the brackets indicate optional arguments and and character or cell array For character arrays the conversion is repeated for every row
Definition: str2double.cc:342
uint8NDArray uint8_array_value(void) const
Definition: ov.h:943
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x
static ColumnVector convert_label_position(const ColumnVector &p, const text::properties &props, const graphics_xform &xform, const Matrix &bbox)
Definition: graphics.cc:5974
static void delete_graphics_objects(const NDArray vals)
Definition: graphics.cc:2833
void update_fontunits(const caseless_str &old_fontunits)
Definition: graphics.cc:6900
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:204
ColumnVector row_min(void) const
Definition: dMatrix.cc:2605
virtual property get_property(const caseless_str &pname)
static Matrix default_patch_faces(void)
Definition: graphics.cc:367
static void create_instance(void)
Definition: graphics.cc:9956
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:107
OCTINTERP_API bool do_set(const octave_value &v)
Definition: graphics.cc:1620
OCTINTERP_API void get_data_limits(void)
Definition: graphics.cc:1580
static void xreset_default_properties(graphics_handle h, property_list::pval_map_type factory_pval)
Definition: graphics.cc:3004
int ndims(void) const
Definition: Array.h:590
void F(const TSRC *v, TRES *r, octave_idx_type m, octave_idx_type n)
Definition: mx-inlines.cc:756
std::set< std::string > dynamic_property_names(void) const
Definition: graphics.cc:3117
static int process_events(void)
Definition: graphics.in.h:6235
bool is_uint16_type(void) const
Definition: ov.h:678
#define OCTAVE_DEFAULT_FONTNAME
Definition: graphics.in.h:54
static property_list::plist_map_type init_factory_properties(void)
Definition: graphics.cc:10489