GNU Octave  8.1.0
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-2023 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cctype>
31 #include <cmath>
32 #include <cstdint>
33 #include <cstdlib>
34 
35 #include <algorithm>
36 #include <iostream>
37 #include <limits>
38 #include <list>
39 #include <map>
40 #include <set>
41 #include <string>
42 #include <sstream>
43 
44 #include "cmd-edit.h"
45 #include "file-ops.h"
46 #include "file-stat.h"
47 #include "oct-locbuf.h"
48 #include "oct-time.h"
49 
50 #include "builtin-defun-decls.h"
51 #include "defun.h"
52 #include "display.h"
53 #include "error.h"
54 #include "graphics.h"
55 #include "input.h"
56 #include "interpreter-private.h"
57 #include "interpreter.h"
58 #include "ov.h"
59 #include "ovl.h"
60 #include "oct-map.h"
61 #include "ov-fcn-handle.h"
62 #include "pager.h"
63 #include "parse.h"
64 #include "text-engine.h"
65 #include "text-renderer.h"
66 #include "unwind-prot.h"
67 #include "utils.h"
68 #include "octave-default-image.h"
69 
71 
72 // forward declarations
73 static octave_value xget (const graphics_handle& h, const caseless_str& name);
74 
75 OCTAVE_NORETURN static
76 void
77 err_set_invalid (const std::string& pname)
78 {
79  error ("set: invalid value for %s property", pname.c_str ());
80 }
81 
82 // Check to see that PNAME matches just one of PNAMES uniquely.
83 // Return the full name of the match, or an empty caseless_str object
84 // if there is no match, or the match is ambiguous.
85 
86 static caseless_str
87 validate_property_name (const std::string& who, const std::string& what,
88  const std::set<std::string>& pnames,
89  const caseless_str& pname)
90 {
91  std::size_t len = pname.length ();
92  std::set<std::string> matches;
93 
94  // Find exact or partial matches to property name
95  for (const auto& propnm : pnames)
96  {
97  if (pname.compare (propnm, len))
98  {
99  if (len == propnm.length ())
100  return pname; // Exact match.
101 
102  matches.insert (propnm);
103  }
104  }
105 
106  std::size_t num_matches = matches.size ();
107 
108  if (num_matches == 0)
109  error ("%s: unknown %s property %s",
110  who.c_str (), what.c_str (), pname.c_str ());
111  else if (num_matches > 1)
112  {
113  string_vector sv (matches);
114 
115  std::ostringstream os;
116 
117  sv.list_in_columns (os);
118 
119  std::string match_list = os.str ();
120 
121  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
122  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
123  }
124  else // num_matches == 1
125  {
126  // Exact match was handled above.
127  std::string possible_match = *(matches.begin ());
128 
129  warning_with_id ("Octave:abbreviated-property-match",
130  "%s: allowing %s to match %s property %s",
131  who.c_str (), pname.c_str (), what.c_str (),
132  possible_match.c_str ());
133 
134  return possible_match;
135  }
136 
137  return caseless_str ();
138 }
139 
140 static Matrix
142 {
143  // The values below have been produced by viridis (64)(:)
144  // It would be nice to be able to feval the
145  // viridis function but since there is a static property object that includes
146  // a colormap_property object, we need to initialize this before main is
147  // even called, so calling an interpreted function is not possible.
148 
149  const double cmapv[] =
150  {
151  2.67004010000000e-01, 2.72651720952381e-01, 2.77106307619048e-01,
152  2.80356151428571e-01, 2.82390045238095e-01, 2.83204606666667e-01,
153  2.82809341428571e-01, 2.81230763333333e-01, 2.78516153333333e-01,
154  2.74735528571429e-01, 2.69981791904762e-01, 2.64368580952381e-01,
155  2.58026184285714e-01, 2.51098684761905e-01, 2.43732853333333e-01,
156  2.36073294285714e-01, 2.28263191428571e-01, 2.20424955714286e-01,
157  2.12666598571429e-01, 2.05079113809524e-01, 1.97721880952381e-01,
158  1.90631350000000e-01, 1.83819438571429e-01, 1.77272360952381e-01,
159  1.70957518571429e-01, 1.64832915714286e-01, 1.58845368095238e-01,
160  1.52951235714286e-01, 1.47131626666667e-01, 1.41402210952381e-01,
161  1.35832975714286e-01, 1.30582113809524e-01, 1.25898377619048e-01,
162  1.22163105714286e-01, 1.19872409523810e-01, 1.19626570000000e-01,
163  1.22045948571429e-01, 1.27667691904762e-01, 1.36834947142857e-01,
164  1.49643331428571e-01, 1.65967274285714e-01, 1.85538397142857e-01,
165  2.08030450000000e-01, 2.33127309523809e-01, 2.60531475238095e-01,
166  2.90000730000000e-01, 3.21329971428571e-01, 3.54355250000000e-01,
167  3.88930322857143e-01, 4.24933143333333e-01, 4.62246770476190e-01,
168  5.00753620000000e-01, 5.40336957142857e-01, 5.80861172380952e-01,
169  6.22170772857143e-01, 6.64087320476191e-01, 7.06403823333333e-01,
170  7.48885251428571e-01, 7.91273132857143e-01, 8.33302102380952e-01,
171  8.74717527142857e-01, 9.15296319047619e-01, 9.54839555238095e-01,
172  9.93247890000000e-01, 4.87433000000000e-03, 2.58456800000000e-02,
173  5.09139004761905e-02, 7.42014957142857e-02, 9.59536042857143e-02,
174  1.16893314761905e-01, 1.37350195714286e-01, 1.57479940000000e-01,
175  1.77347967619048e-01, 1.96969168571429e-01, 2.16330337619048e-01,
176  2.35404660952381e-01, 2.54161735714286e-01, 2.72573219047619e-01,
177  2.90619516666667e-01, 3.08291041428571e-01, 3.25586450952381e-01,
178  3.42517215238095e-01, 3.59102207142857e-01, 3.75366067142857e-01,
179  3.91340913333333e-01, 4.07061480000000e-01, 4.22563764285714e-01,
180  4.37885543809524e-01, 4.53062984285714e-01, 4.68129543809524e-01,
181  4.83117059523810e-01, 4.98052961428571e-01, 5.12959473333333e-01,
182  5.27854311428571e-01, 5.42750087142857e-01, 5.57652481904762e-01,
183  5.72563073333333e-01, 5.87476284285714e-01, 6.02382410952381e-01,
184  6.17265840000000e-01, 6.32106955714286e-01, 6.46881817142857e-01,
185  6.61562926190476e-01, 6.76119717142857e-01, 6.90518987142857e-01,
186  7.04725181904762e-01, 7.18700950000000e-01, 7.32406441904762e-01,
187  7.45802021904762e-01, 7.58846480000000e-01, 7.71497934761905e-01,
188  7.83714033809524e-01, 7.95453081428571e-01, 8.06673890000000e-01,
189  8.17337565714286e-01, 8.27409135714286e-01, 8.36858167619048e-01,
190  8.45663399523809e-01, 8.53815582857143e-01, 8.61321019047619e-01,
191  8.68206316666667e-01, 8.74522215714286e-01, 8.80346158571429e-01,
192  8.85780083333333e-01, 8.90945338571429e-01, 8.95973498571429e-01,
193  9.01005800000000e-01, 9.06156570000000e-01, 3.29415190000000e-01,
194  3.53367293333333e-01, 3.76236064761905e-01, 3.97901482857143e-01,
195  4.18250757142857e-01, 4.37178920000000e-01, 4.54595888571429e-01,
196  4.70433883333333e-01, 4.84653865714286e-01, 4.97250492857143e-01,
197  5.08254501428571e-01, 5.17731949047619e-01, 5.25780221428571e-01,
198  5.32522206190476e-01, 5.38097133333333e-01, 5.42651800000000e-01,
199  5.46335411904762e-01, 5.49287148571429e-01, 5.51635008571429e-01,
200  5.53493173333333e-01, 5.54953478571429e-01, 5.56089070000000e-01,
201  5.56952166666667e-01, 5.57576145714286e-01, 5.57974025714286e-01,
202  5.58142745238095e-01, 5.58058673809524e-01, 5.57684744285714e-01,
203  5.56973310000000e-01, 5.55864478571429e-01, 5.54288677142857e-01,
204  5.52175699047619e-01, 5.49445382857143e-01, 5.46023368571429e-01,
205  5.41830633809524e-01, 5.36795616666667e-01, 5.30847985714286e-01,
206  5.23924198571429e-01, 5.15966779523810e-01, 5.06924262857143e-01,
207  4.96751861428571e-01, 4.85412122857143e-01, 4.72873300000000e-01,
208  4.59105875238095e-01, 4.44095883333333e-01, 4.27825852857143e-01,
209  4.10292713809524e-01, 3.91487632857143e-01, 3.71420688571429e-01,
210  3.50098750000000e-01, 3.27544678571429e-01, 3.03798967142857e-01,
211  2.78916748571429e-01, 2.53000856190476e-01, 2.26223670000000e-01,
212  1.98879439523810e-01, 1.71494930000000e-01, 1.45037631428572e-01,
213  1.21291048571429e-01, 1.03326155238095e-01, 9.53507900000000e-02,
214  1.00469958095238e-01, 1.17876387142857e-01, 1.43936200000000e-01
215  };
216 
217  // It would be nice if Matrix had a ctor allowing to do the
218  // following without a copy
219  Matrix cmap (64, 3, 0.0);
220  std::copy (cmapv, cmapv + (64*3), cmap.fortran_vec ());
221  return cmap;
222 }
223 
224 static double
226 {
227  octave::display_info& dpy_info = octave::__get_display_info__ ();
228 
229  return dpy_info.depth ();
230 }
231 
232 static Matrix
234 {
235  Matrix retval (1, 4);
236 
237  octave::display_info& dpy_info = octave::__get_display_info__ ();
238 
239  retval(0) = 1.0;
240  retval(1) = 1.0;
241  retval(2) = dpy_info.width ();
242  retval(3) = dpy_info.height ();
243 
244  return retval;
245 }
246 
247 static double
249 {
250  octave::display_info& dpy_info = octave::__get_display_info__ ();
251 
252  return (dpy_info.x_dpi () + dpy_info.y_dpi ()) / 2;
253 }
254 
255 static Matrix
257 {
258  Matrix retval (7, 3, 0.0);
259 
260  retval(0, 1) = 0.447;
261  retval(0, 2) = 0.741;
262 
263  retval(1, 0) = 0.850;
264  retval(1, 1) = 0.325;
265  retval(1, 2) = 0.098;
266 
267  retval(2, 0) = 0.929;
268  retval(2, 1) = 0.694;
269  retval(2, 2) = 0.125;
270 
271  retval(3, 0) = 0.494;
272  retval(3, 1) = 0.184;
273  retval(3, 2) = 0.556;
274 
275  retval(4, 0) = 0.466;
276  retval(4, 1) = 0.674;
277  retval(4, 2) = 0.188;
278 
279  retval(5, 0) = 0.301;
280  retval(5, 1) = 0.745;
281  retval(5, 2) = 0.933;
282 
283  retval(6, 0) = 0.635;
284  retval(6, 1) = 0.078;
285  retval(6, 2) = 0.184;
286 
287  return retval;
288 }
289 
290 static Matrix
291 default_lim (bool logscale = false)
292 {
293  Matrix m (1, 2);
294 
295  if (logscale)
296  {
297  m(0) = 0.1;
298  m(1) = 1.0;
299  }
300  else
301  {
302  m(0) = 0.0;
303  m(1) = 1.0;
304  }
305 
306  return m;
307 }
308 
309 static Matrix
311 {
312  Matrix retval (1, 2);
313 
314  retval(0) = 0;
315  retval(1) = 1;
316 
317  return retval;
318 }
319 
320 static Matrix
322 {
323  Matrix retval (1, 4);
324 
325  retval(0) = 0;
326  retval(1) = 1;
327  retval(2) = 1; // minimum positive
328  retval(3) = -octave::numeric_limits<double>::Inf (); // maximum negative
329 
330  return retval;
331 }
332 
333 static Matrix
335 {
336  Matrix m (64, 64);
337 
338  int i = 0;
339  for (int col = 0; col < 64; col++)
340  for (int row = 0; row < 64; row++)
341  {
342  m(col, row) = static_cast<double> (default_im_data[i]);
343  i++;
344  }
345 
346  return m;
347 }
348 
349 static Matrix
351 {
352  Matrix m (3, 3);
353 
354  for (int col = 0; col < 3; col++)
355  for (int row = 0; row < 3; row++)
356  m(row, col) = col+1;
357 
358  return m;
359 }
360 
361 static Matrix
363 {
364  Matrix m (3, 3);
365 
366  for (int row = 0; row < 3; row++)
367  for (int col = 0; col < 3; col++)
368  m(row, col) = row+1;
369 
370  return m;
371 }
372 
373 static Matrix
375 {
376  Matrix m (3, 3, 0.0);
377 
378  for (int row = 0; row < 3; row++)
379  m(row, row) = 1.0;
380 
381  return m;
382 }
383 
384 static Matrix
386 {
387  return default_surface_zdata ();
388 }
389 
390 static Matrix
392 {
393  Matrix m (1, 3);
394 
395  m(0) = 1.0;
396  m(1) = 2.0;
397  m(2) = 3.0;
398 
399  return m;
400 }
401 
402 static Matrix
404 {
405  Matrix m (3, 2, 0.0);
406 
407  m(1) = 1.0;
408  m(3) = 1.0;
409  m(4) = 1.0;
410 
411  return m;
412 }
413 
414 static Matrix
416 {
417  Matrix m (3, 1, 0.0);
418 
419  m(1) = 1.0;
420 
421  return m;
422 }
423 
424 static Matrix
426 {
427  Matrix m (3, 1, 1.0);
428 
429  m(2) = 0.0;
430 
431  return m;
432 }
433 
434 static Matrix
436 {
437  Matrix m (1, 4);
438 
439  m(0) = 0.13;
440  m(1) = 0.11;
441  m(2) = 0.775;
442  m(3) = 0.815;
443 
444  return m;
445 }
446 
447 static Matrix
449 {
450  Matrix m (1, 4);
451 
452  m(0) = 0.0;
453  m(1) = 0.0;
454  m(2) = 1.0;
455  m(3) = 1.0;
456 
457  return m;
458 }
459 
460 static Matrix
462 {
463  Matrix m (1, 2);
464 
465  m(0) = 0.0;
466  m(1) = 90.0;
467 
468  return m;
469 }
470 
471 static Matrix
473 {
474  Matrix m (1, 6);
475 
476  m(0) = 0.0;
477  m(1) = 0.2;
478  m(2) = 0.4;
479  m(3) = 0.6;
480  m(4) = 0.8;
481  m(5) = 1.0;
482 
483  return m;
484 }
485 
486 static Matrix
488 {
489  Matrix m (1, 2);
490 
491  m(0) = 0.01;
492  m(1) = 0.025;
493 
494  return m;
495 }
496 
497 static Matrix
499 {
500  Matrix m (1, 4);
501 
502  m(0) = 300;
503  m(1) = 200;
504  m(2) = 560;
505  m(3) = 420;
506 
507  return m;
508 }
509 
510 static Matrix
512 {
513  Matrix m (1, 2);
514 
515  m(0) = 8.5;
516  m(1) = 11.0;
517 
518  return m;
519 }
520 
521 static Matrix
523 {
524  Matrix m (1, 4);
525 
526  // Update if default_figure_position or default_figure_papersize change
527  m(0) = 1.3421852580027660;
528  m(1) = 3.3191389435020748;
529  m(2) = 5.8156294839944680;
530  m(3) = 4.3617221129958503;
531 
532  return m;
533 }
534 
535 static std::string
537 {
538  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
539 
540  return gtk_mgr.default_toolkit ();
541 }
542 
543 static Matrix
545 {
546  Matrix retval (1, 4);
547 
548  retval(0) = 0;
549  retval(1) = 0;
550  retval(2) = 80;
551  retval(3) = 30;
552 
553  return retval;
554 }
555 
556 static Matrix
558 {
559  Matrix retval (1, 2);
560 
561  retval(0) = 0.01;
562  retval(1) = 0.1;
563 
564  return retval;
565 }
566 
567 static Matrix
569 {
570  Matrix retval (1, 4);
571 
572  retval(0) = 0;
573  retval(1) = 0;
574  retval(2) = 1;
575  retval(3) = 1;
576 
577  return retval;
578 }
579 
580 static Matrix
582 {
583  Matrix m (1, 3);
584 
585  m(0) = 1.0;
586  m(1) = 0.0;
587  m(2) = 1.0;
588 
589  return m;
590 }
591 
592 static Matrix
594 {
595  Matrix retval (1, 4);
596 
597  retval(0) = 20;
598  retval(1) = 20;
599  retval(2) = 300;
600  retval(3) = 300;
601 
602  return retval;
603 }
604 
605 static Matrix
607 {
608  Matrix retval (2, 3);
609  retval(0, 0) = 1;
610  retval(0, 1) = 1;
611  retval(0, 2) = 1;
612  retval(1, 0) = 0.94;
613  retval(1, 1) = 0.94;
614  retval(1, 2) = 0.94;
615  return retval;
616 }
617 
618 static graphics_handle
619 make_graphics_handle (const std::string& go_name,
620  const graphics_handle& parent,
621  bool integer_figure_handle = false,
622  bool call_createfcn = true,
623  bool notify_toolkit = true)
624 {
625  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
626 
627  return gh_mgr.make_graphics_handle (go_name, parent, integer_figure_handle,
628  call_createfcn, notify_toolkit);
629 }
630 
631 static double
632 convert_font_size (double font_size, const caseless_str& from_units,
633  const caseless_str& to_units, double parent_height = 0)
634 {
635  // Simple case where from_units == to_units
636 
637  if (from_units.compare (to_units))
638  return font_size;
639 
640  // Converts the given fontsize using the following transformation:
641  // <old_font_size> => points => <new_font_size>
642 
643  double points_size = 0;
644  double res = 0;
645 
646  if (from_units.compare ("points"))
647  points_size = font_size;
648  else
649  {
650  res = xget (0, "screenpixelsperinch").double_value ();
651 
652  if (from_units.compare ("pixels"))
653  points_size = font_size * 72.0 / res;
654  else if (from_units.compare ("inches"))
655  points_size = font_size * 72.0;
656  else if (from_units.compare ("centimeters"))
657  points_size = font_size * 72.0 / 2.54;
658  else if (from_units.compare ("normalized"))
659  points_size = font_size * parent_height * 72.0 / res;
660  }
661 
662  double new_font_size = 0;
663 
664  if (to_units.compare ("points"))
665  new_font_size = points_size;
666  else
667  {
668  if (res <= 0)
669  res = xget (0, "screenpixelsperinch").double_value ();
670 
671  if (to_units.compare ("pixels"))
672  new_font_size = points_size * res / 72.0;
673  else if (to_units.compare ("inches"))
674  new_font_size = points_size / 72.0;
675  else if (to_units.compare ("centimeters"))
676  new_font_size = points_size * 2.54 / 72.0;
677  else if (to_units.compare ("normalized"))
678  {
679  // Avoid setting font size to (0/0) = NaN
680 
681  if (parent_height > 0)
682  new_font_size = points_size * res / (parent_height * 72.0);
683  }
684  }
685 
686  return new_font_size;
687 }
688 
689 static Matrix
690 convert_position (const Matrix& pos, const caseless_str& from_units,
691  const caseless_str& to_units, const Matrix& parent_dim)
692 {
693  Matrix retval (1, pos.numel (), 0.0);
694  double res = 0;
695  bool is_rectangle = (pos.numel () == 4);
696  bool is_2D = (pos.numel () == 2);
697 
698  if (from_units.compare ("pixels"))
699  retval = pos;
700  else if (from_units.compare ("normalized"))
701  {
702  retval(0) = pos(0) * parent_dim(0) + 1;
703  retval(1) = pos(1) * parent_dim(1) + 1;
704  if (is_rectangle)
705  {
706  retval(2) = pos(2) * parent_dim(0);
707  retval(3) = pos(3) * parent_dim(1);
708  }
709  else if (! is_2D)
710  retval(2) = 0;
711  }
712  else if (from_units.compare ("characters"))
713  {
714  if (res <= 0)
715  res = xget (0, "screenpixelsperinch").double_value ();
716 
717  double f = 0.0;
718 
719  // FIXME: this assumes the system font is Helvetica 10pt
720  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
721  f = 12.0 * res / 74.951;
722 
723  if (f > 0)
724  {
725  retval(0) = 0.5 * pos(0) * f;
726  retval(1) = pos(1) * f;
727  if (is_rectangle)
728  {
729  retval(2) = 0.5 * pos(2) * f;
730  retval(3) = pos(3) * f;
731  }
732  else if (! is_2D)
733  retval(2) = 0;
734  }
735  }
736  else
737  {
738  if (res <= 0)
739  res = xget (0, "screenpixelsperinch").double_value ();
740 
741  double f = 0.0;
742 
743  if (from_units.compare ("points"))
744  f = res / 72.0;
745  else if (from_units.compare ("inches"))
746  f = res;
747  else if (from_units.compare ("centimeters"))
748  f = res / 2.54;
749 
750  if (f > 0)
751  {
752  retval(0) = pos(0) * f + 1;
753  retval(1) = pos(1) * f + 1;
754  if (is_rectangle)
755  {
756  retval(2) = pos(2) * f;
757  retval(3) = pos(3) * f;
758  }
759  else if (! is_2D)
760  retval(2) = 0;
761  }
762  }
763 
764  if (! to_units.compare ("pixels"))
765  {
766  if (to_units.compare ("normalized"))
767  {
768  retval(0) = (retval(0) - 1) / parent_dim(0);
769  retval(1) = (retval(1) - 1) / parent_dim(1);
770  if (is_rectangle)
771  {
772  retval(2) /= parent_dim(0);
773  retval(3) /= parent_dim(1);
774  }
775  else if (! is_2D)
776  retval(2) = 0;
777  }
778  else if (to_units.compare ("characters"))
779  {
780  if (res <= 0)
781  res = xget (0, "screenpixelsperinch").double_value ();
782 
783  double f = 0.0;
784 
785  f = 12.0 * res / 74.951;
786 
787  if (f > 0)
788  {
789  retval(0) = 2 * retval(0) / f;
790  retval(1) = retval(1) / f;
791  if (is_rectangle)
792  {
793  retval(2) = 2 * retval(2) / f;
794  retval(3) = retval(3) / f;
795  }
796  else if (! is_2D)
797  retval(2) = 0;
798  }
799  }
800  else
801  {
802  if (res <= 0)
803  res = xget (0, "screenpixelsperinch").double_value ();
804 
805  double f = 0.0;
806 
807  if (to_units.compare ("points"))
808  f = res / 72.0;
809  else if (to_units.compare ("inches"))
810  f = res;
811  else if (to_units.compare ("centimeters"))
812  f = res / 2.54;
813 
814  if (f > 0)
815  {
816  retval(0) = (retval(0) - 1) / f;
817  retval(1) = (retval(1) - 1) / f;
818  if (is_rectangle)
819  {
820  retval(2) /= f;
821  retval(3) /= f;
822  }
823  else if (! is_2D)
824  retval(2) = 0;
825  }
826  }
827  }
828 
829  return retval;
830 }
831 
832 static Matrix
834  const caseless_str& from_units,
835  const caseless_str& to_units)
836 {
837  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
838 
839  graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
840 
841  graphics_object ax = go.get_ancestor ("axes");
842 
843  Matrix retval;
844 
845  if (ax.valid_object ())
846  {
847  const axes::properties& ax_props
848  = dynamic_cast<const axes::properties&> (ax.get_properties ());
849  graphics_xform ax_xform = ax_props.get_transform ();
850  bool is_rectangle = (pos.numel () == 4);
851  Matrix ax_bbox = ax_props.get_boundingbox (true),
852  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
853 
854  if (from_units.compare ("data"))
855  {
856  if (is_rectangle)
857  {
858  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
859  v2 = ax_xform.transform (pos(0) + pos(2),
860  pos(1) + pos(3), 0);
861 
862  retval.resize (1, 4);
863 
864  retval(0) = v1(0) - ax_bbox(0) + 1;
865  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
866  retval(2) = v2(0) - v1(0);
867  retval(3) = v1(1) - v2(1);
868  }
869  else
870  {
871  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
872 
873  retval.resize (1, 3);
874 
875  retval(0) = v(0) - ax_bbox(0) + 1;
876  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
877  retval(2) = 0;
878  }
879  }
880  else
881  retval = convert_position (pos, from_units, "pixels", ax_size);
882 
883  if (! to_units.compare ("pixels"))
884  {
885  if (to_units.compare ("data"))
886  {
887  if (is_rectangle)
888  {
889  ColumnVector v1, v2;
890  v1 = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
891  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
892  v2 = ax_xform.untransform (retval(0) + retval(2) + ax_bbox(0) - 1,
893  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
894 
895  retval.resize (1, 4);
896 
897  retval(0) = v1(0);
898  retval(1) = v1(1);
899  retval(2) = v2(0) - v1(0);
900  retval(3) = v2(1) - v1(1);
901  }
902  else
903  {
904  ColumnVector v;
905  v = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
906  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
907 
908  retval.resize (1, 3);
909 
910  retval(0) = v(0);
911  retval(1) = v(1);
912  retval(2) = v(2);
913  }
914  }
915  else
916  retval = convert_position (retval, "pixels", to_units, ax_size);
917  }
918  }
919 
920  return retval;
921 }
922 
923 // This function always returns the screensize in pixels
924 static Matrix
926 {
927  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
928 
929  graphics_object obj = gh_mgr.get_object (0);
930 
931  Matrix sz = obj.get ("screensize").matrix_value ();
932 
933  return convert_position (sz, obj.get ("units").string_value (), "pixels",
934  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
935 }
936 
937 static double
939 {
940  double retval = 1.0;
941 
942  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
943 
944  graphics_object fig = gh_mgr.get_object (h).get_ancestor ("figure");
945 
946  if (fig.valid_object ())
947  retval = fig.get ("__device_pixel_ratio__").double_value ();
948 
949  return retval;
950 }
951 
952 static void
953 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
954  const double *cmapv, double x, octave_idx_type lda,
955  octave_idx_type nc, octave_idx_type i, double *av)
956 {
957  if (is_scaled)
958  x = octave::math::fix (nc * (x - clim_0) / (clim_1 - clim_0));
959  else if (is_real)
960  x = octave::math::fix (x - 1);
961 
962  if (octave::math::isnan (x))
963  {
964  av[i] = x;
965  av[i+lda] = x;
966  av[i+2*lda] = x;
967  }
968  else
969  {
970  if (x < 0)
971  x = 0;
972  else if (x >= nc)
973  x = (nc - 1);
974 
975  octave_idx_type idx = static_cast<octave_idx_type> (x);
976 
977  av[i] = cmapv[idx];
978  av[i+lda] = cmapv[idx+nc];
979  av[i+2*lda] = cmapv[idx+2*nc];
980  }
981 }
982 
983 template <typename T>
984 void
985 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
986  const double *cmapv, const T *cv, octave_idx_type lda,
987  octave_idx_type nc, double *av)
988 {
989  for (octave_idx_type i = 0; i < lda; i++)
990  convert_cdata_2 (is_scaled, is_real,
991  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
992 }
993 
994 static octave_value
995 convert_cdata (const base_properties& props, const octave_value& cdata,
996  bool is_scaled, int cdim)
997 {
998  dim_vector dv (cdata.dims ());
999 
1000  // TrueColor data doesn't require conversion
1001  if (dv.ndims () == cdim && dv(cdim-1) == 3)
1002  return cdata;
1003 
1004  Matrix cmap (1, 3, 0.0);
1005  Matrix clim (1, 2, 0.0);
1006 
1007  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1008 
1009  graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
1010  graphics_object ax = go.get_ancestor ("axes");
1011 
1012  if (ax.valid_object ())
1013  {
1014  Matrix _cmap = ax.get (caseless_str ("colormap")).matrix_value ();
1015 
1016  cmap = _cmap;
1017 
1018  if (is_scaled)
1019  {
1020  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
1021 
1022  clim = _clim;
1023  }
1024  }
1025 
1026  dv.resize (cdim);
1027  dv(cdim-1) = 3;
1028 
1029  NDArray a (dv);
1030 
1031  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
1032  octave_idx_type nc = cmap.rows ();
1033 
1034  double *av = a.fortran_vec ();
1035  const double *cmapv = cmap.data ();
1036 
1037  double clim_0 = clim(0);
1038  double clim_1 = clim(1);
1039 
1040  // FIXME: There is a lot of processing time spent just on data conversion
1041  // both here in graphics.cc and again in gl-render.cc. There must
1042  // be room for improvement! Here a macro expands to a templated
1043  // function which in turn calls another function (covert_cdata_2).
1044  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
1045  // is supported anyways so there is another double for loop across
1046  // height and width to convert all of the input data to GLfloat.
1047 
1048 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
1049  do \
1050  { \
1051  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
1052  \
1053  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
1054  tmp.data (), lda, nc, av); \
1055  } \
1056  while (0)
1057 
1058  if (cdata.is_int8_type ())
1059  CONVERT_CDATA_1 (int8NDArray, int8_, false);
1060  else if (cdata.is_int16_type ())
1061  CONVERT_CDATA_1 (int16NDArray, int16_, false);
1062  else if (cdata.is_int32_type ())
1063  CONVERT_CDATA_1 (int32NDArray, int32_, false);
1064  else if (cdata.is_int64_type ())
1065  CONVERT_CDATA_1 (int64NDArray, int64_, false);
1066  else if (cdata.is_uint8_type ())
1067  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
1068  else if (cdata.is_uint16_type ())
1069  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
1070  else if (cdata.is_uint32_type ())
1071  CONVERT_CDATA_1 (uint32NDArray, uint32_, false);
1072  else if (cdata.is_uint64_type ())
1073  CONVERT_CDATA_1 (uint64NDArray, uint64_, false);
1074  else if (cdata.is_double_type ())
1075  CONVERT_CDATA_1 (NDArray,, true);
1076  else if (cdata.is_single_type ())
1077  CONVERT_CDATA_1 (FloatNDArray, float_, true);
1078  else if (cdata.islogical ())
1079  CONVERT_CDATA_1 (boolNDArray, bool_, false);
1080  else
1081  {
1082  // Don't throw an error; leads to an incomplete FLTK object (bug #46933).
1083  warning ("unsupported type for cdata (= %s). "
1084  "Valid types are int8, int16, int32, int64, uint8, uint16, "
1085  "uint32, uint64, double, single, and bool.",
1086  cdata.type_name ().c_str ());
1087  a = NDArray (dv, 0); // return 0 instead
1088  }
1089 
1090 #undef CONVERT_CDATA_1
1091 
1092  return octave_value (a);
1093 }
1094 
1095 template <typename T>
1096 static void
1097 get_array_limits (const Array<T>& m, double& emin, double& emax,
1098  double& eminp, double& emaxp)
1099 {
1100  const T *data = m.data ();
1101  octave_idx_type n = m.numel ();
1102 
1103  for (octave_idx_type i = 0; i < n; i++)
1104  {
1105  double e = double (data[i]);
1106 
1107  // Don't need to test for NaN here as NaN>x and NaN<x is always false
1108  if (! octave::math::isinf (e))
1109  {
1110  if (e < emin)
1111  emin = e;
1112 
1113  if (e > emax)
1114  emax = e;
1115 
1116  if (e > 0 && e < eminp)
1117  eminp = e;
1118 
1119  if (e < 0 && e > emaxp)
1120  emaxp = e;
1121  }
1122  }
1123 }
1124 
1125 static bool
1127  caseless_str& rest)
1128 {
1129  int len = name.length ();
1130  int offset = 0;
1131  bool result = false;
1132 
1133  if (len >= 4)
1134  {
1135  caseless_str pfx = name.substr (0, 4);
1136 
1137  if (pfx.compare ("axes") || pfx.compare ("line")
1138  || pfx.compare ("text"))
1139  offset = 4;
1140  else if (len >= 5)
1141  {
1142  pfx = name.substr (0, 5);
1143 
1144  if (pfx.compare ("image") || pfx.compare ("patch"))
1145  offset = 5;
1146  else if (len >= 6)
1147  {
1148  pfx = name.substr (0, 6);
1149 
1150  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1151  offset = 6;
1152  else if (len >= 7)
1153  {
1154  pfx = name.substr (0, 7);
1155 
1156  if (pfx.compare ("surface") || pfx.compare ("scatter")
1157  || pfx.compare ("hggroup") || pfx.compare ("uipanel")
1158  || pfx.compare ("uitable"))
1159  offset = 7;
1160  else if (len >= 9)
1161  {
1162  pfx = name.substr (0, 9);
1163 
1164  if (pfx.compare ("uicontrol")
1165  || pfx.compare ("uitoolbar"))
1166  offset = 9;
1167  else if (len >= 10)
1168  {
1169  pfx = name.substr (0, 10);
1170 
1171  if (pfx.compare ("uipushtool"))
1172  offset = 10;
1173  else if (len >= 12)
1174  {
1175  pfx = name.substr (0, 12);
1176 
1177  if (pfx.compare ("uitoggletool"))
1178  offset = 12;
1179  else if (len >= 13)
1180  {
1181  pfx = name.substr (0, 13);
1182 
1183  if (pfx.compare ("uicontextmenu")
1184  || pfx.compare ("uibuttongroup"))
1185  offset = 13;
1186  }
1187  }
1188  }
1189  }
1190  }
1191  }
1192  }
1193 
1194  if (offset > 0)
1195  {
1196  go_name = pfx;
1197  rest = name.substr (offset);
1198  result = true;
1199  }
1200  }
1201 
1202  return result;
1203 }
1204 
1205 static base_graphics_object *
1207  const graphics_handle& h = graphics_handle (),
1208  const graphics_handle& p = graphics_handle ())
1209 {
1210  base_graphics_object *go = nullptr;
1211 
1212  if (type.compare ("figure"))
1213  go = new figure (h, p);
1214  else if (type.compare ("axes"))
1215  go = new axes (h, p);
1216  else if (type.compare ("line"))
1217  go = new line (h, p);
1218  else if (type.compare ("text"))
1219  go = new text (h, p);
1220  else if (type.compare ("image"))
1221  go = new image (h, p);
1222  else if (type.compare ("light"))
1223  go = new light (h, p);
1224  else if (type.compare ("patch"))
1225  go = new patch (h, p);
1226  else if (type.compare ("scatter"))
1227  go = new scatter (h, p);
1228  else if (type.compare ("surface"))
1229  go = new surface (h, p);
1230  else if (type.compare ("hggroup"))
1231  go = new hggroup (h, p);
1232  else if (type.compare ("uimenu"))
1233  go = new uimenu (h, p);
1234  else if (type.compare ("uicontrol"))
1235  go = new uicontrol (h, p);
1236  else if (type.compare ("uipanel"))
1237  go = new uipanel (h, p);
1238  else if (type.compare ("uibuttongroup"))
1239  go = new uibuttongroup (h, p);
1240  else if (type.compare ("uicontextmenu"))
1241  go = new uicontextmenu (h, p);
1242  else if (type.compare ("uitable"))
1243  go = new uitable (h, p);
1244  else if (type.compare ("uitoolbar"))
1245  go = new uitoolbar (h, p);
1246  else if (type.compare ("uipushtool"))
1247  go = new uipushtool (h, p);
1248  else if (type.compare ("uitoggletool"))
1249  go = new uitoggletool (h, p);
1250  return go;
1251 }
1252 
1253 // ---------------------------------------------------------------------
1254 
1255 bool
1256 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1257 {
1258  if (do_set (v))
1259  {
1260  // Notify graphics toolkit.
1261  if (m_id >= 0 && do_notify_toolkit)
1262  {
1263  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1264 
1265  graphics_object go = gh_mgr.get_object (m_parent);
1266  if (go)
1267  go.update (m_id);
1268  }
1269 
1270  // run listeners
1271  if (do_run)
1272  run_listeners (GCB_POSTSET);
1273 
1274  return true;
1275  }
1276 
1277  return false;
1278 }
1279 
1280 void
1281 base_property::run_listeners (listener_mode mode)
1282 {
1283  const octave_value_list& l = m_listeners[mode];
1284 
1285  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1286 
1287  for (int i = 0; i < l.length (); i++)
1288  gh_mgr.execute_listener (m_parent, l(i));
1289 }
1290 
1291 radio_values::radio_values (const std::string& opt_string)
1292  : m_default_val (), m_possible_vals ()
1293 {
1294  std::size_t beg = 0;
1295  std::size_t len = opt_string.length ();
1296  bool done = len == 0;
1297 
1298  while (! done)
1299  {
1300  std::size_t end = opt_string.find ('|', beg);
1301 
1302  if (end == std::string::npos)
1303  {
1304  end = len;
1305  done = true;
1306  }
1307 
1308  std::string t = opt_string.substr (beg, end-beg);
1309 
1310  // Special case for '|' symbol itself
1311  if (t.empty () && opt_string[beg] == '|')
1312  {
1313  t = '|';
1314  end++;
1315  }
1316 
1317  // Might want more error checking on parsing default value...
1318  if (t[0] == '{')
1319  {
1320  t = t.substr (1, t.length () - 2);
1321  m_default_val = t;
1322  }
1323  else if (beg == 0) // ensure default value
1324  m_default_val = t;
1325 
1326  m_possible_vals.insert (t);
1327 
1328  beg = end + 1;
1329  }
1330 }
1331 
1332 std::string
1333 radio_values::values_as_string (void) const
1334 {
1335  std::string retval;
1336 
1337  for (const auto& val : m_possible_vals)
1338  {
1339  if (retval.empty ())
1340  {
1341  if (val == default_value ())
1342  retval = '{' + val + '}';
1343  else
1344  retval = val;
1345  }
1346  else
1347  {
1348  if (val == default_value ())
1349  retval += " | {" + val + '}';
1350  else
1351  retval += " | " + val;
1352  }
1353  }
1354 
1355  if (! retval.empty ())
1356  retval = "[ " + retval + " ]";
1357 
1358  return retval;
1359 }
1360 
1361 Cell
1362 radio_values::values_as_cell (void) const
1363 {
1364  octave_idx_type i = 0;
1365  Cell retval (nelem (), 1);
1366 
1367  for (const auto& val : m_possible_vals)
1368  retval(i++) = std::string (val);
1369 
1370  return retval;
1371 }
1372 
1373 bool
1374 color_values::str2rgb (const std::string& str_arg)
1375 {
1376  bool retval = true;
1377 
1378  double tmp_rgb[3] = {0, 0, 0};
1379 
1380  std::string str = str_arg;
1381  unsigned int len = str.length ();
1382 
1383  std::transform (str.begin (), str.end (), str.begin (), tolower);
1384 
1385  // "blue" must precede black for Matlab compatibility
1386  if (str.compare (0, len, "blue", 0, len) == 0)
1387  tmp_rgb[2] = 1;
1388  else if (str.compare (0, len, "black", 0, len) == 0
1389  || str.compare (0, len, "k", 0, len) == 0)
1390  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1391  else if (str.compare (0, len, "red", 0, len) == 0)
1392  tmp_rgb[0] = 1;
1393  else if (str.compare (0, len, "green", 0, len) == 0)
1394  tmp_rgb[1] = 1;
1395  else if (str.compare (0, len, "yellow", 0, len) == 0)
1396  tmp_rgb[0] = tmp_rgb[1] = 1;
1397  else if (str.compare (0, len, "magenta", 0, len) == 0)
1398  tmp_rgb[0] = tmp_rgb[2] = 1;
1399  else if (str.compare (0, len, "cyan", 0, len) == 0)
1400  tmp_rgb[1] = tmp_rgb[2] = 1;
1401  else if (str.compare (0, len, "white", 0, len) == 0
1402  || str.compare (0, len, "w", 0, len) == 0)
1403  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1404  else if (str[0] == '#' && len == 7)
1405  {
1406  try
1407  {
1408  tmp_rgb[0] = static_cast<double> (stoi (str.substr (1, 2), nullptr, 16))
1409  / 255.0;
1410  tmp_rgb[1] = static_cast<double> (stoi (str.substr (3, 2), nullptr, 16))
1411  / 255.0;
1412  tmp_rgb[2] = static_cast<double> (stoi (str.substr (5, 2), nullptr, 16))
1413  / 255.0;
1414  }
1415  catch (const octave::execution_exception&)
1416  {
1417  retval = false;
1418  }
1419  }
1420  else if (str[0] == '#' && len == 4)
1421  {
1422  try
1423  {
1424  tmp_rgb[0] = static_cast<double> (stoi (str.substr (1, 1), nullptr, 16))
1425  / 15.0;
1426  tmp_rgb[1] = static_cast<double> (stoi (str.substr (2, 1), nullptr, 16))
1427  / 15.0;
1428  tmp_rgb[2] = static_cast<double> (stoi (str.substr (3, 1), nullptr, 16))
1429  / 15.0;
1430  }
1431  catch (const octave::execution_exception&)
1432  {
1433  retval = false;
1434  }
1435  }
1436  else
1437  retval = false;
1438 
1439  if (retval)
1440  {
1441  for (int i = 0; i < 3; i++)
1442  m_rgb(i) = tmp_rgb[i];
1443  }
1444 
1445  return retval;
1446 }
1447 
1448 bool
1449 color_property::do_set (const octave_value& val)
1450 {
1451  if (val.is_string ())
1452  {
1453  std::string s = val.string_value ();
1454 
1455  if (s.empty ())
1456  error (R"(invalid value for color property "%s")",
1457  get_name ().c_str ());
1458 
1459  std::string match;
1460 
1461  if (m_radio_val.contains (s, match))
1462  {
1463  if (m_current_type != radio_t || match != m_current_val)
1464  {
1465  if (s.length () != match.length ())
1466  warning_with_id ("Octave:abbreviated-property-match",
1467  "%s: allowing %s to match %s value %s",
1468  "set", s.c_str (), get_name ().c_str (),
1469  match.c_str ());
1470  m_current_val = match;
1471  m_current_type = radio_t;
1472  return true;
1473  }
1474  }
1475  else
1476  {
1477  try
1478  {
1479  color_values col (s);
1480 
1481  if (m_current_type != color_t || col != m_color_val)
1482  {
1483  m_color_val = col;
1484  m_current_type = color_t;
1485  return true;
1486  }
1487  }
1488  catch (octave::execution_exception& ee)
1489  {
1490  error (ee, R"(invalid value for color property "%s" (value = %s))",
1491  get_name ().c_str (), s.c_str ());
1492  }
1493  }
1494  }
1495  else if (val.isnumeric ())
1496  {
1497  Matrix m = val.matrix_value ();
1498 
1499  if (m.numel () != 3)
1500  error (R"(invalid value for color property "%s")",
1501  get_name ().c_str ());
1502 
1503  color_values col (m(0), m(1), m(2));
1504 
1505  if (m_current_type != color_t || col != m_color_val)
1506  {
1507  m_color_val = col;
1508  m_current_type = color_t;
1509  return true;
1510  }
1511  }
1512  else
1513  error (R"(invalid value for color property "%s")",
1514  get_name ().c_str ());
1515 
1516  return false;
1517 }
1518 
1519 bool
1520 double_radio_property::do_set (const octave_value& val)
1521 {
1522  if (val.is_string ())
1523  {
1524  std::string s = val.string_value ();
1525  std::string match;
1526 
1527  if (s.empty () || ! m_radio_val.contains (s, match))
1528  error (R"(invalid value for double_radio property "%s")",
1529  get_name ().c_str ());
1530 
1531  if (m_current_type != radio_t || match != m_current_val)
1532  {
1533  if (s.length () != match.length ())
1534  warning_with_id ("Octave:abbreviated-property-match",
1535  "%s: allowing %s to match %s value %s",
1536  "set", s.c_str (), get_name ().c_str (),
1537  match.c_str ());
1538  m_current_val = match;
1539  m_current_type = radio_t;
1540  return true;
1541  }
1542  }
1543  else if (val.is_scalar_type () && val.isreal ())
1544  {
1545  double new_dval = val.double_value ();
1546 
1547  if (m_current_type != double_t || new_dval != m_dval)
1548  {
1549  m_dval = new_dval;
1550  m_current_type = double_t;
1551  return true;
1552  }
1553  }
1554  else
1555  error (R"(invalid value for double_radio property "%s")",
1556  get_name ().c_str ());
1557 
1558  return false;
1559 }
1560 
1561 bool
1562 array_property::validate (const octave_value& v)
1563 {
1564  bool xok = false;
1565 
1566  // check value type
1567  if (m_type_constraints.size () > 0)
1568  {
1569  if (m_type_constraints.find (v.class_name ()) != m_type_constraints.end ())
1570  xok = true;
1571 
1572  // check if complex is allowed (it's also of class "double", so
1573  // checking that alone is not enough to ensure real type)
1574  if (m_type_constraints.find ("real") != m_type_constraints.end ()
1575  && v.iscomplex ())
1576  xok = false;
1577  }
1578  else
1579  xok = v.isnumeric () || v.is_bool_scalar ();
1580 
1581  if (xok && m_size_constraints.size () > 0)
1582  {
1583  dim_vector vdims = v.dims ();
1584  int vlen = vdims.ndims ();
1585 
1586  xok = false;
1587 
1588  // check dimensional size constraints until a match is found
1589  for (auto it = m_size_constraints.cbegin ();
1590  ! xok && it != m_size_constraints.cend ();
1591  ++it)
1592  {
1593  dim_vector itdims = (*it);
1594 
1595  if (itdims.ndims () == vlen)
1596  {
1597  xok = true;
1598 
1599  for (int i = 0; xok && i < vlen; i++)
1600  {
1601  if (itdims(i) > 0)
1602  {
1603  if (itdims(i) != vdims(i))
1604  xok = false;
1605  }
1606  else if (itdims(i) == 0)
1607  {
1608  if (! v.isempty ())
1609  xok = false;
1610  break;
1611  }
1612  }
1613  }
1614  }
1615  }
1616 
1617  if (xok)
1618  {
1619  NDArray v_mat = v.array_value ();
1620  // Check min and max
1621  if (! octave::math::isnan (m_minval.first))
1622  {
1623  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1624  if (m_minval.second && m_minval.first > v_mat(i))
1625  error (R"(set: "%s" must be greater than or equal to %g)",
1626  get_name ().c_str (), m_minval.first);
1627  else if (! m_minval.second && m_minval.first >= v_mat(i))
1628  error (R"(set: "%s" must be greater than %g)",
1629  get_name ().c_str (), m_minval.first);
1630  }
1631 
1632  if (! octave::math::isnan (m_maxval.first))
1633  {
1634  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1635  if (m_maxval.second && m_maxval.first < v_mat(i))
1636  error (R"(set: "%s" must be less than or equal to %g)",
1637  get_name ().c_str (), m_maxval.first);
1638  else if (! m_maxval.second && m_maxval.first <= v_mat(i))
1639  error (R"(set: "%s" must be less than %g)",
1640  get_name ().c_str (), m_maxval.first);
1641  }
1642 
1643  if (m_finite_constraint == NO_CHECK) { /* do nothing */ }
1644  else if (m_finite_constraint == FINITE)
1645  {
1646  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1647  if (! octave::math::isfinite (v_mat(i)))
1648  error (R"(set: "%s" must be finite)", get_name ().c_str ());
1649  }
1650  else if (m_finite_constraint == NOT_NAN)
1651  {
1652  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1653  if (octave::math::isnan (v_mat(i)))
1654  error (R"(set: "%s" must not be nan)", get_name ().c_str ());
1655  }
1656  else if (m_finite_constraint == NOT_INF)
1657  {
1658  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1659  if (octave::math::isinf (v_mat(i)))
1660  error (R"(set: "%s" must not be infinite)", get_name ().c_str ());
1661  }
1662 
1663  }
1664 
1665  return xok;
1666 }
1667 
1668 bool
1669 array_property::is_equal (const octave_value& v) const
1670 {
1671  if (m_data.type_name () == v.type_name ())
1672  {
1673  if (m_data.dims () == v.dims ())
1674  {
1675 
1676 #define CHECK_ARRAY_EQUAL(T, F, A) \
1677  { \
1678  if (m_data.numel () == 1) \
1679  return m_data.F ## scalar_value () == \
1680  v.F ## scalar_value (); \
1681  else \
1682  { \
1683  /* Keep copy of array_value to allow */ \
1684  /* sparse/bool arrays that are converted, to */ \
1685  /* not be deallocated early */ \
1686  const A m1 = m_data.F ## array_value (); \
1687  const T *d1 = m1.data (); \
1688  const A m2 = v.F ## array_value (); \
1689  const T *d2 = m2.data (); \
1690  \
1691  bool flag = true; \
1692  \
1693  for (int i = 0; flag && i < m_data.numel (); i++) \
1694  if (d1[i] != d2[i]) \
1695  flag = false; \
1696  \
1697  return flag; \
1698  } \
1699  }
1700 
1701  if (m_data.is_double_type () || m_data.islogical ())
1702  CHECK_ARRAY_EQUAL (double,, NDArray)
1703  else if (m_data.is_single_type ())
1704  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1705  else if (m_data.is_int8_type ())
1707  else if (m_data.is_int16_type ())
1709  else if (m_data.is_int32_type ())
1711  else if (m_data.is_int64_type ())
1713  else if (m_data.is_uint8_type ())
1715  else if (m_data.is_uint16_type ())
1717  else if (m_data.is_uint32_type ())
1719  else if (m_data.is_uint64_type ())
1721  }
1722  }
1723 
1724  return false;
1725 }
1726 
1727 void
1728 array_property::get_data_limits (void)
1729 {
1730  m_min_val = m_min_pos = octave::numeric_limits<double>::Inf ();
1731  m_max_val = m_max_neg = -octave::numeric_limits<double>::Inf ();
1732 
1733  if (! m_data.isempty ())
1734  {
1735  if (m_data.isinteger ())
1736  {
1737  if (m_data.is_int8_type ())
1738  get_array_limits (m_data.int8_array_value (),
1739  m_min_val, m_max_val, m_min_pos, m_max_neg);
1740  else if (m_data.is_uint8_type ())
1741  get_array_limits (m_data.uint8_array_value (),
1742  m_min_val, m_max_val, m_min_pos, m_max_neg);
1743  else if (m_data.is_int16_type ())
1744  get_array_limits (m_data.int16_array_value (),
1745  m_min_val, m_max_val, m_min_pos, m_max_neg);
1746  else if (m_data.is_uint16_type ())
1747  get_array_limits (m_data.uint16_array_value (),
1748  m_min_val, m_max_val, m_min_pos, m_max_neg);
1749  else if (m_data.is_int32_type ())
1750  get_array_limits (m_data.int32_array_value (),
1751  m_min_val, m_max_val, m_min_pos, m_max_neg);
1752  else if (m_data.is_uint32_type ())
1753  get_array_limits (m_data.uint32_array_value (),
1754  m_min_val, m_max_val, m_min_pos, m_max_neg);
1755  else if (m_data.is_int64_type ())
1756  get_array_limits (m_data.int64_array_value (),
1757  m_min_val, m_max_val, m_min_pos, m_max_neg);
1758  else if (m_data.is_uint64_type ())
1759  get_array_limits (m_data.uint64_array_value (),
1760  m_min_val, m_max_val, m_min_pos, m_max_neg);
1761  }
1762  else
1763  get_array_limits (m_data.array_value (),
1764  m_min_val, m_max_val, m_min_pos, m_max_neg);
1765  }
1766 }
1767 
1768 bool
1769 handle_property::do_set (const octave_value& v)
1770 {
1771  // Users may want to use empty matrix to reset a handle property
1772  if (v.isempty ())
1773  {
1774  if (! get ().isempty ())
1775  {
1776  m_current_val = graphics_handle ();
1777  return true;
1778  }
1779  else
1780  return false;
1781  }
1782 
1783  double dv = v.xdouble_value (R"(set: invalid graphics handle for property "%s")",
1784  get_name ().c_str ());
1785 
1786  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1787 
1788  graphics_handle gh = gh_mgr.lookup (dv);
1789 
1790  // Check the object type if necessary
1791  bool type_ok = true;
1792  if (gh.ok () && ! m_type_constraints.empty ())
1793  {
1794  type_ok = false;
1795  graphics_object obj = gh_mgr.get_object (gh);
1796 
1797  for (const auto& type : m_type_constraints)
1798  if (obj.isa (type))
1799  {
1800  type_ok = true;
1801  break;
1802  }
1803  }
1804 
1805  if (! octave::math::isnan (gh.value ()) && ! (gh.ok () && type_ok))
1806  {
1807  if (type_ok)
1808  error (R"(set: invalid graphics handle (= %g) for property "%s")",
1809  dv, get_name ().c_str ());
1810  else
1811  error (R"(set: invalid graphics object type for property "%s")",
1812  get_name ().c_str ());
1813  }
1814 
1815  if (m_current_val != gh)
1816  {
1817  m_current_val = gh;
1818  return true;
1819  }
1820 
1821  return false;
1822 }
1823 
1824 /*
1825 ## Test validation of contextmenu property
1826 %!test
1827 %! hf = figure ("visible", "off");
1828 %! unwind_protect
1829 %! hax = axes ("parent", hf);
1830 %! hpa = patch ("parent", hax);
1831 %! try
1832 %! set (hax, "contextmenu", hpa);
1833 %! catch
1834 %! err = lasterr ();
1835 %! end_try_catch
1836 %! assert (err, 'set: invalid graphics object type for property "contextmenu"');
1837 %! unwind_protect_cleanup
1838 %! delete (hf);
1839 %! end_unwind_protect
1840 */
1841 
1842 Matrix
1843 children_property::do_get_children (bool return_hidden) const
1844 {
1845  Matrix retval (m_children_list.size (), 1);
1846  octave_idx_type k = 0;
1847 
1848  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1849 
1850  graphics_object go = gh_mgr.get_object (0);
1851 
1853  = dynamic_cast<root_figure::properties&> (go.get_properties ());
1854 
1855  if (! props.is_showhiddenhandles ())
1856  {
1857  for (const auto& hchild : m_children_list)
1858  {
1859  graphics_handle kid = hchild;
1860 
1861  if (gh_mgr.is_handle_visible (kid))
1862  {
1863  if (! return_hidden)
1864  retval(k++) = hchild;
1865  }
1866  else if (return_hidden)
1867  retval(k++) = hchild;
1868  }
1869 
1870  retval.resize (k, 1);
1871  }
1872  else
1873  {
1874  for (const auto& hchild : m_children_list)
1875  retval(k++) = hchild;
1876  }
1877 
1878  return retval;
1879 }
1880 
1881 void
1882 children_property::do_delete_children (bool clear, bool from_root)
1883 {
1884  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1885 
1886  if (from_root)
1887  {
1888  for (graphics_handle hchild : m_children_list)
1889  {
1890  graphics_object go = gh_mgr.get_object (hchild);
1891  if (go.valid_object ()
1892  && ! go.get_properties ().is_beingdeleted ())
1893  gh_mgr.free (hchild, from_root);
1894  }
1895  m_children_list.clear ();
1896  }
1897  else
1898  while (! m_children_list.empty ())
1899  {
1900  // gh_mgr.free removes hchild from children_list
1901  graphics_handle hchild = m_children_list.front ();
1902  graphics_object go = gh_mgr.get_object (hchild);
1903  if (go.valid_object ()
1904  && ! go.get_properties ().is_beingdeleted ())
1905  gh_mgr.free (hchild, from_root);
1906  }
1907 
1908  // FIXME: children_list should be clear anyway at this point.
1909  if (clear)
1910  m_children_list.clear ();
1911 }
1912 
1913 bool
1914 callback_property::validate (const octave_value& v) const
1915 {
1916  // case 1: empty matrix
1917  // case 2: function handle
1918  // case 3: string corresponding to known function name
1919  // case 4: string that can be eval()'ed
1920  // case 5: cell array with first element being a function handle
1921 
1922  if (v.isempty ())
1923  return true;
1924  else if (v.is_function_handle ())
1925  return true;
1926  else if (v.is_string ())
1927  // complete validation will be done at execution-time
1928  return true;
1929  else if (v.iscell () && (v.rows () == 1 || v.columns () == 1)
1930  && v.cell_value ()(0).is_function_handle ())
1931  return true;
1932 
1933  return false;
1934 }
1935 
1937 {
1938 public:
1939 
1940  callback_props (void) : m_set () { }
1941 
1942  callback_props (const callback_props&) = delete;
1943 
1945 
1946  ~callback_props (void) = default;
1947 
1948  bool empty (void) const { return m_set.empty (); }
1949 
1950  void insert (const callback_property *ptr)
1951  {
1952  m_set.insert (reinterpret_cast<intptr_t> (ptr));
1953  }
1954 
1955  void erase (const callback_property *ptr)
1956  {
1957  m_set.erase (reinterpret_cast<intptr_t> (ptr));
1958  }
1959 
1960  bool contains (const callback_property *ptr) const
1961  {
1962  return m_set.find (reinterpret_cast<intptr_t> (ptr)) != m_set.end ();
1963  }
1964 
1965 private:
1966 
1967  std::set<intptr_t> m_set;
1968 };
1969 
1970 // Elements of this set are pointers to currently executing
1971 // callback_property objects. Used to determine handle visibility
1972 // inside callback functions.
1973 
1975 
1976 void
1977 callback_property::execute (const octave_value& data) const
1978 {
1979  // We are executing a callback function, so allow handles that have
1980  // their handlevisibility property set to "callback" to be visible.
1981 
1982  octave::unwind_action executing_callbacks_cleanup
1983  ([=] () { executing_callbacks.erase (this); });
1984 
1985  if (! executing_callbacks.contains (this))
1986  {
1987  executing_callbacks.insert (this);
1988 
1989  if (m_callback.is_defined () && ! m_callback.isempty ())
1990  {
1991  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
1992 
1993  gh_mgr.execute_callback (get_parent (), m_callback, data);
1994  }
1995  }
1996 }
1997 
1998 // Used to cache dummy graphics objects from which dynamic properties can be
1999 // cloned.
2000 static std::map<caseless_str, graphics_object> dprop_obj_map;
2001 
2002 property
2003 property::create (const std::string& name, const graphics_handle& h,
2004  const caseless_str& type, const octave_value_list& args)
2005 {
2006  property retval;
2007 
2008  if (type.compare ("string"))
2009  {
2010  std::string sv = (args.length () > 0 ? args(0).string_value () : "");
2011 
2012  retval = property (new string_property (name, h, sv));
2013  }
2014  else if (type.compare ("any"))
2015  {
2016  octave_value ov = (args.length () > 0 ? args(0)
2017  : octave_value (Matrix ()));
2018 
2019  retval = property (new any_property (name, h, ov));
2020  }
2021  else if (type.compare ("radio"))
2022  {
2023  if (args.length () < 1)
2024  error ("addproperty: missing possible values for radio property");
2025 
2026  std::string sv = args(
2027  0).xstring_value ("addproperty: argument for radio property must be a string");
2028 
2029  retval = property (new radio_property (name, h, sv));
2030 
2031  if (args.length () > 1)
2032  retval.set (args(1));
2033  }
2034  else if (type.compare ("double"))
2035  {
2036  double dv = (args.length () > 0 ? args(0).double_value () : 0.0);
2037 
2038  retval = property (new double_property (name, h, dv));
2039  }
2040  else if (type.compare ("handle"))
2041  {
2042  double hv = (args.length () > 0 ? args(0).double_value ()
2044 
2045  graphics_handle gh (hv);
2046 
2047  retval = property (new handle_property (name, h, gh));
2048  }
2049  else if (type.compare ("boolean"))
2050  {
2051  retval = property (new bool_property (name, h, false));
2052 
2053  if (args.length () > 0)
2054  retval.set (args(0));
2055  }
2056  else if (type.compare ("data"))
2057  {
2058  retval = property (new array_property (name, h, Matrix ()));
2059 
2060  if (args.length () > 0)
2061  {
2062  retval.set (args(0));
2063  // FIXME: additional argument could define constraints,
2064  // but is this really useful?
2065  }
2066  }
2067  else if (type.compare ("color"))
2068  {
2069  color_values cv (0, 0, 0);
2070  radio_values rv;
2071 
2072  if (args.length () > 1)
2073  rv = radio_values (args(1).string_value ());
2074 
2075  retval = property (new color_property (name, h, cv, rv));
2076 
2077  if (args.length () > 0 && ! args(0).isempty ())
2078  retval.set (args(0));
2079  else
2080  retval.set (rv.default_value ());
2081  }
2082  else
2083  {
2084  caseless_str go_name, go_rest;
2085 
2086  if (! lookup_object_name (type, go_name, go_rest))
2087  error ("addproperty: unsupported type for dynamic property (= %s)",
2088  type.c_str ());
2089 
2090  graphics_object go;
2091 
2092  std::map<caseless_str, graphics_object>::const_iterator it
2093  = dprop_obj_map.find (go_name);
2094 
2095  if (it == dprop_obj_map.end ())
2096  {
2097  base_graphics_object *bgo = make_graphics_object_from_type (go_name);
2098 
2099  if (bgo)
2100  {
2101  go = graphics_object (bgo);
2102 
2103  dprop_obj_map[go_name] = go;
2104  }
2105  }
2106  else
2107  go = it->second;
2108 
2109  if (! go.valid_object ())
2110  error ("addproperty: invalid object type (= %s)",
2111  go_name.c_str ());
2112 
2113  property prop = go.get_properties ().get_property (go_rest);
2114 
2115  retval = prop.clone ();
2116 
2117  retval.set_parent (h);
2118  retval.set_name (name);
2119 
2120  if (args.length () > 0)
2121  retval.set (args(0));
2122  }
2123 
2124  return retval;
2125 }
2126 
2127 static void
2129 {
2130  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2131 
2132  graphics_object go = gh_mgr.get_object (h);
2133 
2134  if (go)
2135  {
2136  Matrix children = go.get_properties ().get_all_children ();
2137 
2138  for (int k = 0; k < children.numel (); k++)
2139  finalize_r (children(k));
2140 
2141  go.finalize ();
2142  }
2143 }
2144 
2145 static void
2147 {
2148  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2149 
2150  graphics_object go = gh_mgr.get_object (h);
2151 
2152  if (go)
2153  {
2154  Matrix children = go.get_properties ().get_all_children ();
2155 
2156  go.initialize ();
2157 
2158  for (int k = 0; k < children.numel (); k++)
2159  initialize_r (children(k));
2160  }
2161 }
2162 
2163 void
2164 figure::properties::set_toolkit (const octave::graphics_toolkit& b)
2165 {
2166  if (m_toolkit)
2167  finalize_r (get___myhandle__ ());
2168 
2169  m_toolkit = b;
2170  m___graphics_toolkit__ = b.get_name ();
2171  m___plot_stream__ = Matrix ();
2172 
2173  if (m_toolkit)
2174  initialize_r (get___myhandle__ ());
2175 
2176  mark_modified ();
2177 }
2178 
2179 void
2180 figure::properties::set___mouse_mode__ (const octave_value& val_arg)
2181 {
2182  std::string direction = "in";
2183 
2184  octave_value val = val_arg;
2185 
2186  if (val.is_string ())
2187  {
2188  std::string modestr = val.string_value ();
2189 
2190  if (modestr == "zoom in")
2191  {
2192  val = modestr = "zoom";
2193  direction = "in";
2194  }
2195  else if (modestr == "zoom out")
2196  {
2197  val = modestr = "zoom";
2198  direction = "out";
2199  }
2200 
2201  if (m___mouse_mode__.set (val, true))
2202  {
2203  std::string mode = m___mouse_mode__.current_value ();
2204 
2205  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
2206  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
2207  set___pan_mode__ (pm);
2208 
2209  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
2210  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
2211  set___rotate_mode__ (rm);
2212 
2213  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2214  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
2215  zm.setfield ("Direction", direction);
2216  set___zoom_mode__ (zm);
2217 
2218  mark_modified ();
2219  }
2220  else if (modestr == "zoom")
2221  {
2222  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2223  std::string curr_direction
2224  = zm.getfield ("Direction").string_value ();
2225 
2226  if (direction != curr_direction)
2227  {
2228  zm.setfield ("Direction", direction);
2229  set___zoom_mode__ (zm);
2230 
2231  mark_modified ();
2232  }
2233  }
2234  }
2235 }
2236 
2237 void
2238 figure::properties::update_handlevisibility (void)
2239 {
2240  if (! is_handle_visible ())
2241  {
2242  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2243 
2244  octave_value cf = gh_mgr.get_object (0).get ("currentfigure");
2245 
2246  if (! cf.isempty () && cf.double_value () == m___myhandle__)
2247  {
2248  octave::autolock guard (gh_mgr.graphics_lock ());
2249 
2250  octave_value kids = gh_mgr.get_object (0).get ("children");
2251 
2252  if (kids.isempty ())
2253  gh_mgr.get_object (0).set ("currentfigure", Matrix ());
2254  else
2255  {
2256  NDArray kidsarray = kids.array_value ();
2257  gh_mgr.get_object (0).set ("currentfigure", kidsarray(0));
2258  }
2259  }
2260  }
2261 
2262  base_properties::update_handlevisibility ();
2263 }
2264 
2265 static void
2267 {
2268  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2269 
2270  graphics_object go = gh_mgr.get_object (h);
2271 
2272  if (go.isa ("text"))
2273  {
2274  text::properties& tp
2275  = dynamic_cast<text::properties&> (go.get_properties ());
2276  tp.update_font ();
2277  tp.update_text_extent ();
2278  }
2279  else if (go.isa ("figure") || go.isa ("uipanel") || go.isa ("axes")
2280  || go.isa ("hggroup"))
2281  {
2282  Matrix ch = go.get_properties ().get_all_children ();
2283  for (octave_idx_type ii = 0; ii < ch.numel (); ii++)
2284  update_text_pos (graphics_handle (ch(ii)));
2285 
2286  if (go.isa ("axes"))
2287  {
2288  axes::properties& ap
2289  = dynamic_cast<axes::properties&> (go.get_properties ());
2290  ap.update_font ();
2291  ap.sync_positions ();
2292  }
2293  }
2294 }
2295 
2296 void
2297 figure::properties::update___device_pixel_ratio__ (void)
2298 {
2299  update_text_pos (get___myhandle__ ());
2300 }
2301 
2302 // ---------------------------------------------------------------------
2303 
2304 void
2305 property_list::set (const caseless_str& name, const octave_value& val)
2306 {
2307  std::size_t offset = 0;
2308 
2309  std::size_t len = name.length ();
2310 
2311  if (len > 4)
2312  {
2313  caseless_str pfx = name.substr (0, 4);
2314 
2315  if (pfx.compare ("axes") || pfx.compare ("line")
2316  || pfx.compare ("text"))
2317  offset = 4;
2318  else if (len > 5)
2319  {
2320  pfx = name.substr (0, 5);
2321 
2322  if (pfx.compare ("image") || pfx.compare ("patch"))
2323  offset = 5;
2324  else if (len > 6)
2325  {
2326  pfx = name.substr (0, 6);
2327 
2328  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2329  offset = 6;
2330  else if (len > 7)
2331  {
2332  pfx = name.substr (0, 7);
2333 
2334  if (pfx.compare ("surface") || pfx.compare ("scatter")
2335  || pfx.compare ("hggroup")|| pfx.compare ("uipanel")
2336  || pfx.compare ("uitable"))
2337  offset = 7;
2338  else if (len > 9)
2339  {
2340  pfx = name.substr (0, 9);
2341 
2342  if (pfx.compare ("uicontrol")
2343  || pfx.compare ("uitoolbar"))
2344  offset = 9;
2345  else if (len > 10)
2346  {
2347  pfx = name.substr (0, 10);
2348 
2349  if (pfx.compare ("uipushtool"))
2350  offset = 10;
2351  else if (len > 12)
2352  {
2353  pfx = name.substr (0, 12);
2354 
2355  if (pfx.compare ("uitoogletool"))
2356  offset = 12;
2357  else if (len > 13)
2358  {
2359  pfx = name.substr (0, 13);
2360 
2361  if (pfx.compare ("uicontextmenu")
2362  || pfx.compare ("uibuttongroup"))
2363  offset = 13;
2364  }
2365  }
2366  }
2367  }
2368  }
2369  }
2370  }
2371 
2372  if (offset > 0)
2373  {
2374  // FIXME: should we validate property names and values here?
2375 
2376  std::string pname = name.substr (offset);
2377 
2378  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2379  std::transform (pname.begin (), pname.end (), pname.begin (),
2380  tolower);
2381 
2382  bool has_property = false;
2383  if (pfx == "axes")
2384  has_property = axes::properties::has_core_property (pname);
2385  else if (pfx == "figure")
2386  has_property = figure::properties::has_core_property (pname);
2387  else if (pfx == "line")
2388  has_property = line::properties::has_core_property (pname);
2389  else if (pfx == "text")
2390  has_property = text::properties::has_core_property (pname);
2391  else if (pfx == "image")
2392  has_property = image::properties::has_core_property (pname);
2393  else if (pfx == "patch")
2394  has_property = patch::properties::has_core_property (pname);
2395  else if (pfx == "scatter")
2396  has_property = scatter::properties::has_core_property (pname);
2397  else if (pfx == "surface")
2398  has_property = surface::properties::has_core_property (pname);
2399  else if (pfx == "hggroup")
2400  has_property = hggroup::properties::has_core_property (pname);
2401  else if (pfx == "uimenu")
2402  has_property = uimenu::properties::has_core_property (pname);
2403  else if (pfx == "uicontrol")
2404  has_property = uicontrol::properties::has_core_property (pname);
2405  else if (pfx == "uibuttongroup")
2406  has_property = uibuttongroup::properties::has_core_property (pname);
2407  else if (pfx == "uipanel")
2408  has_property = uipanel::properties::has_core_property (pname);
2409  else if (pfx == "uicontextmenu")
2410  has_property = uicontextmenu::properties::has_core_property (pname);
2411  else if (pfx == "uitable")
2412  has_property = uitable::properties::has_core_property (pname);
2413  else if (pfx == "uitoolbar")
2414  has_property = uitoolbar::properties::has_core_property (pname);
2415  else if (pfx == "uipushtool")
2416  has_property = uipushtool::properties::has_core_property (pname);
2417 
2418  if (! has_property)
2419  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2420 
2421  bool remove = false;
2422  if (val.is_string ())
2423  {
2424  std::string sval = val.string_value ();
2425 
2426  remove = (sval == "remove");
2427  }
2428 
2429  pval_map_type& pval_map = m_plist_map[pfx];
2430 
2431  if (remove)
2432  {
2433  auto p = pval_map.find (pname);
2434 
2435  if (p != pval_map.end ())
2436  pval_map.erase (p);
2437  }
2438  else
2439  pval_map[pname] = val;
2440  }
2441  }
2442 
2443  if (offset == 0)
2444  error ("invalid default property specification");
2445 }
2446 
2448 property_list::lookup (const caseless_str& name) const
2449 {
2450  octave_value retval;
2451 
2452  std::size_t offset = 0;
2453 
2454  std::size_t len = name.length ();
2455 
2456  if (len > 4)
2457  {
2458  caseless_str pfx = name.substr (0, 4);
2459 
2460  if (pfx.compare ("axes") || pfx.compare ("line")
2461  || pfx.compare ("text"))
2462  offset = 4;
2463  else if (len > 5)
2464  {
2465  pfx = name.substr (0, 5);
2466 
2467  if (pfx.compare ("image") || pfx.compare ("patch"))
2468  offset = 5;
2469  else if (len > 6)
2470  {
2471  pfx = name.substr (0, 6);
2472 
2473  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2474  offset = 6;
2475  else if (len > 7)
2476  {
2477  pfx = name.substr (0, 7);
2478 
2479  if (pfx.compare ("surface") || pfx.compare ("scatter")
2480  || pfx.compare ("hggroup") || pfx.compare ("uipanel")
2481  || pfx.compare ("uitable"))
2482  offset = 7;
2483  else if (len > 9)
2484  {
2485  pfx = name.substr (0, 9);
2486 
2487  if (pfx.compare ("uicontrol")
2488  || pfx.compare ("uitoolbar"))
2489  offset = 9;
2490  else if (len > 10)
2491  {
2492  pfx = name.substr (0, 10);
2493 
2494  if (pfx.compare ("uipushtool"))
2495  offset = 10;
2496  else if (len > 12)
2497  {
2498  pfx = name.substr (0, 12);
2499 
2500  if (pfx.compare ("uitoggletool"))
2501  offset = 12;
2502  else if (len > 13)
2503  {
2504  pfx = name.substr (0, 13);
2505 
2506  if (pfx.compare ("uicontextmenu")
2507  || pfx.compare ("uibuttongroup"))
2508  offset = 13;
2509  }
2510  }
2511  }
2512  }
2513  }
2514  }
2515  }
2516 
2517  if (offset > 0)
2518  {
2519  std::string pname = name.substr (offset);
2520 
2521  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2522  std::transform (pname.begin (), pname.end (), pname.begin (),
2523  tolower);
2524 
2525  plist_map_const_iterator p = find (pfx);
2526 
2527  if (p != end ())
2528  {
2529  const pval_map_type& pval_map = p->second;
2530 
2531  pval_map_const_iterator q = pval_map.find (pname);
2532 
2533  if (q != pval_map.end ())
2534  retval = q->second;
2535  }
2536  }
2537  }
2538 
2539  return retval;
2540 }
2541 
2543 property_list::as_struct (const std::string& prefix_arg) const
2544 {
2546 
2547  for (auto p = begin (); p != end (); p++)
2548  {
2549  std::string prefix = prefix_arg + p->first;
2550 
2551  for (const auto& prop_val : p->second)
2552  m.assign (prefix + prop_val.first, prop_val.second);
2553  }
2554 
2555  return m;
2556 }
2557 
2558 // Set properties given as a cs-list of name, value pairs.
2559 
2560 void
2561 graphics_object::set (const octave_value_list& args)
2562 {
2563  int nargin = args.length ();
2564 
2565  if (nargin == 0)
2566  error ("graphics_object::set: Nothing to set");
2567 
2568  for (int i = 0; i < nargin; )
2569  {
2570  if (args(i).isstruct () )
2571  {
2572  set (args(i).map_value ());
2573  i++;
2574  }
2575  else if (i < nargin - 1)
2576  {
2577  caseless_str pname = args(i).xstring_value ("set: argument %d must be a property name", i);
2578  octave_value val = args(i+1);
2579  set_value_or_default (pname, val);
2580  i += 2;
2581  }
2582  else
2583  error ("set: invalid number of arguments");
2584  }
2585 }
2586 
2587 /*
2588 ## test set with name, value pairs
2589 %!test
2590 %! hf = figure ("visible", "off");
2591 %! h = plot (1:10, 10:-1:1);
2592 %! set (h, "linewidth", 10, "marker", "x");
2593 %! lw = get (h, "linewidth");
2594 %! mk = get (h, "marker");
2595 %! close (hf);
2596 %! assert (lw, 10);
2597 %! assert (mk, "x");
2598 */
2599 
2600 // Set properties given in two cell arrays containing names and values.
2601 void
2602 graphics_object::set (const Array<std::string>& pnames,
2603  const Cell& values, octave_idx_type row)
2604 {
2605  if (pnames.numel () != values.columns ())
2606  error ("set: number of names must match number of value columns "
2607  "(%" OCTAVE_IDX_TYPE_FORMAT " != %" OCTAVE_IDX_TYPE_FORMAT ")",
2608  pnames.numel (), values.columns ());
2609 
2610  octave_idx_type k = pnames.columns ();
2611 
2612  for (octave_idx_type column = 0; column < k; column++)
2613  {
2614  caseless_str pname = pnames(column);
2615  octave_value val = values(row, column);
2616 
2617  set_value_or_default (pname, val);
2618  }
2619 }
2620 
2621 /*
2622 ## test set with cell array arguments
2623 %!test
2624 %! hf = figure ("visible", "off");
2625 %! h = plot (1:10, 10:-1:1);
2626 %! set (h, {"linewidth", "marker"}, {10, "x"});
2627 %! lw = get (h, "linewidth");
2628 %! mk = get (h, "marker");
2629 %! close (hf);
2630 %! assert (lw, 10);
2631 %! assert (mk, "x");
2632 
2633 ## test set with multiple handles and cell array arguments
2634 %!test
2635 %! hf = figure ("visible", "off");
2636 %! unwind_protect
2637 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2638 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2639 %! assert (get (h, "linewidth"), {10; 5});
2640 %! assert (get (h, "marker"), {"x"; "o"});
2641 %! set (h, {"linewidth", "marker"}, {10, "x"});
2642 %! assert (get (h, "linewidth"), {10; 10});
2643 %! assert (get (h, "marker"), {"x"; "x"});
2644 %! unwind_protect_cleanup
2645 %! close (hf);
2646 %! end_unwind_protect
2647 
2648 %!error <set: number of graphics handles must match number of value rows>
2649 %! hf = figure ("visible", "off");
2650 %! unwind_protect
2651 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2652 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2653 %! unwind_protect_cleanup
2654 %! close (hf);
2655 %! end_unwind_protect
2656 
2657 %!error <set: number of names must match number of value columns>
2658 %! hf = figure ("visible", "off");
2659 %! unwind_protect
2660 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2661 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2662 %! unwind_protect_cleanup
2663 %! close (hf);
2664 %! end_unwind_protect
2665 */
2666 
2667 // Set properties given in a struct array
2668 void
2669 graphics_object::set (const octave_map& m)
2670 {
2671  for (octave_idx_type p = 0; p < m.nfields (); p++)
2672  {
2673  // FIXME: Would it be better to extract all the keys at once rather than
2674  // repeatedly call keys() inside a for loop?
2675  caseless_str pname = m.keys ()[p];
2676 
2677  octave_value val = octave_value (m.contents (pname).elem (m.numel () - 1));
2678 
2679  set_value_or_default (pname, val);
2680  }
2681 }
2682 
2683 /*
2684 ## test set ticklabels for compatibility
2685 %!test
2686 %! hf = figure ("visible", "off");
2687 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2688 %! xticklabel = get (gca (), "xticklabel");
2689 %! close (hf);
2690 %! assert (class (xticklabel), "char");
2691 %! assert (size (xticklabel), [6, 3]);
2692 
2693 %!test
2694 %! hf = figure ("visible", "off");
2695 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2696 %! xticklabel = get (gca (), "xticklabel");
2697 %! close (hf);
2698 %! assert (class (xticklabel), "char");
2699 %! assert (size (xticklabel), [6, 3]);
2700 
2701 %!test
2702 %! hf = figure ("visible", "off");
2703 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2704 %! xticklabel = get (gca (), "xticklabel");
2705 %! close (hf);
2706 %! assert (class (xticklabel), "char");
2707 %! assert (size (xticklabel), [6, 3]);
2708 
2709 %!test
2710 %! hf = figure ("visible", "off");
2711 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2712 %! xticklabel = get (gca (), "xticklabel");
2713 %! close (hf);
2714 %! assert (class (xticklabel), "cell");
2715 %! assert (size (xticklabel), [6, 1]);
2716 */
2717 
2718 /*
2719 ## test set with struct arguments
2720 %!test
2721 %! hf = figure ("visible", "off");
2722 %! unwind_protect
2723 %! h = plot (1:10, 10:-1:1);
2724 %! set (h, struct ("linewidth", 10, "marker", "x"));
2725 %! assert (get (h, "linewidth"), 10);
2726 %! assert (get (h, "marker"), "x");
2727 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2728 %! set (h, struct ("linewidth", {5, 10}));
2729 %! assert (get (h, "linewidth"), {10; 10});
2730 %! unwind_protect_cleanup
2731 %! close (hf);
2732 %! end_unwind_protect
2733 
2734 ## test ordering
2735 %!test
2736 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2737 %! hf = figure ("visible", "off");
2738 %! unwind_protect
2739 %! h = line ();
2740 %! set (h, "userdata", {});
2741 %! addlistener (h, "color", {markchanged, "color"});
2742 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2743 %! ## "linewidth" first
2744 %! props.linewidth = 2;
2745 %! props.color = "r";
2746 %! set (h, props);
2747 %! assert (get (h, "userdata"), fieldnames (props));
2748 %! clear props;
2749 %! clf ();
2750 %! h = line ();
2751 %! set (h, "userdata", {});
2752 %! addlistener (h, "color", {markchanged, "color"});
2753 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2754 %! ## "color" first
2755 %! props.color = "r";
2756 %! props.linewidth = 2;
2757 %! set (h, props);
2758 %! assert (get (h, "userdata"), fieldnames (props));
2759 %! unwind_protect_cleanup
2760 %! close (hf);
2761 %! end_unwind_protect
2762 */
2763 
2764 // Set a property to a value or to its (factory) default value.
2765 
2766 void
2767 graphics_object::set_value_or_default (const caseless_str& pname,
2768  const octave_value& val)
2769 {
2770  if (val.is_string () && val.rows () == 1)
2771  {
2772  std::string sval = val.string_value ();
2773 
2774  octave_value default_val;
2775 
2776  if (sval == "default")
2777  {
2778  default_val = get_default (pname);
2779 
2780  m_rep->set (pname, default_val);
2781  }
2782  else if (sval == "factory")
2783  {
2784  default_val = get_factory_default (pname);
2785 
2786  m_rep->set (pname, default_val);
2787  }
2788  else
2789  {
2790  // Matlab specifically uses "\default" to escape string setting
2791  if (sval == R"(\default)")
2792  m_rep->set (pname, "default");
2793  else if (sval == R"(\factory)")
2794  m_rep->set (pname, "factory");
2795  else
2796  m_rep->set (pname, val);
2797  }
2798  }
2799  else
2800  m_rep->set (pname, val);
2801 }
2802 
2803 /*
2804 ## test setting of default values
2805 %!test
2806 %! old_lw = get (0, "defaultlinelinewidth");
2807 %! unwind_protect
2808 %! hf = figure ("visible", "off");
2809 %! h = plot (1:10, 10:-1:1);
2810 %! set (0, "defaultlinelinewidth", 20);
2811 %! set (h, "linewidth", "default");
2812 %! assert (get (h, "linewidth"), 20);
2813 %! set (h, "linewidth", "factory");
2814 %! assert (get (h, "linewidth"), 0.5);
2815 %! unwind_protect_cleanup
2816 %! close (hf);
2817 %! set (0, "defaultlinelinewidth", old_lw);
2818 %! end_unwind_protect
2819 */
2820 
2821 static double
2823 {
2824  static double maxrand = RAND_MAX + 2.0;
2825 
2826  return (rand () + 1.0) / maxrand;
2827 }
2828 
2830 gh_manager::get_handle (bool integer_figure_handle)
2831 {
2832  graphics_handle retval;
2833 
2834  if (integer_figure_handle)
2835  {
2836  // Figure handles are positive integers corresponding
2837  // to the figure number.
2838 
2839  // We always want the lowest unused figure number.
2840 
2841  retval = 1;
2842 
2843  while (m_handle_map.find (retval) != m_handle_map.end ())
2844  retval++;
2845  }
2846  else
2847  {
2848  // Other graphics handles are negative integers plus some random
2849  // fractional part. To avoid running out of integers, we recycle the
2850  // integer part but tack on a new random part each time.
2851 
2852  auto p = m_handle_free_list.begin ();
2853 
2854  if (p != m_handle_free_list.end ())
2855  {
2856  retval = *p;
2857  m_handle_free_list.erase (p);
2858  }
2859  else
2860  {
2861  retval = graphics_handle (m_next_handle);
2862 
2863  m_next_handle = std::ceil (m_next_handle) - 1.0 - make_handle_fraction ();
2864  }
2865  }
2866 
2867  return retval;
2868 }
2869 
2870 static bool
2871 isfigure (double val)
2872 {
2873  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2874 
2875  graphics_object go = gh_mgr.get_object (val);
2876 
2877  return go && go.isa ("figure");
2878 }
2879 
2880 void
2881 gh_manager::free (const graphics_handle& h, bool from_root)
2882 {
2883  if (h.ok ())
2884  {
2885  if (h.value () == 0)
2886  error ("graphics_handle::free: can't delete root object");
2887 
2888  auto p = m_handle_map.find (h);
2889 
2890  if (p == m_handle_map.end ())
2891  error ("graphics_handle::free: invalid object %g", h.value ());
2892 
2893  base_properties& bp = p->second.get_properties ();
2894 
2895  if (! p->second.valid_object () || bp.is_beingdeleted ())
2896  return;
2897 
2898  graphics_handle parent_h = p->second.get_parent ();
2899  graphics_object parent_go = nullptr;
2900  if (! from_root || isfigure (h.value ()))
2901  parent_go = get_object (parent_h);
2902 
2903  bp.set_beingdeleted (true);
2904 
2905  // delete listeners before invalidating object
2906  p->second.remove_all_listeners ();
2907 
2908  bp.delete_children (true, from_root);
2909 
2910  // NOTE: Call the delete function while the object's state is still valid.
2911  octave_value val = bp.get_deletefcn ();
2912 
2913  bp.execute_deletefcn ();
2914 
2915  // Notify graphics toolkit.
2916  p->second.finalize ();
2917 
2918 
2919  // NOTE: Call remove_child before erasing the go from the map if not
2920  // removing from groot.
2921  // A callback function might have already deleted the parent
2922  if ((! from_root || isfigure (h.value ())) && parent_go.valid_object ()
2923  && h.ok ())
2924  parent_go.remove_child (h);
2925 
2926  // Note: this will be valid only for first explicitly deleted
2927  // object. All its children will then have an
2928  // unknown graphics toolkit.
2929 
2930  // Graphics handles for non-figure objects are negative
2931  // integers plus some random fractional part. To avoid
2932  // running out of integers, we recycle the integer part
2933  // but tack on a new random part each time.
2934 
2935  m_handle_map.erase (p);
2936 
2937  if (h.value () < 0)
2938  m_handle_free_list.insert
2939  (std::ceil (h.value ()) - make_handle_fraction ());
2940  }
2941 }
2942 
2943 void
2944 gh_manager::renumber_figure (const graphics_handle& old_gh,
2945  const graphics_handle& new_gh)
2946 {
2947  auto p = m_handle_map.find (old_gh);
2948 
2949  if (p == m_handle_map.end ())
2950  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2951 
2952  graphics_object go = p->second;
2953 
2954  m_handle_map.erase (p);
2955 
2956  m_handle_map[new_gh] = go;
2957 
2958  if (old_gh.value () < 0)
2959  m_handle_free_list.insert (std::ceil (old_gh.value ())
2960  - make_handle_fraction ());
2961 
2962  for (auto& hfig : m_figure_list)
2963  {
2964  if (hfig == old_gh)
2965  {
2966  hfig = new_gh;
2967  break;
2968  }
2969  }
2970 }
2971 
2972 static void
2973 xset (const graphics_handle& h, const caseless_str& pname,
2974  const octave_value& val)
2975 {
2976  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2977 
2978  graphics_object go = gh_mgr.get_object (h);
2979 
2980  go.set (pname, val);
2981 }
2982 
2983 static void
2984 xset (const graphics_handle& h, const octave_value_list& args)
2985 {
2986  if (args.length () > 0)
2987  {
2988  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
2989 
2990  graphics_object go = gh_mgr.get_object (h);
2991 
2992  go.set (args);
2993  }
2994 }
2995 
2996 static octave_value
2997 xget (const graphics_handle& h, const caseless_str& pname)
2998 {
2999  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3000 
3001  graphics_object go = gh_mgr.get_object (h);
3002 
3003  return go.get (pname);
3004 }
3005 
3006 static graphics_handle
3007 reparent (const octave_value& ov, const std::string& who,
3008  const std::string& pname, const graphics_handle& new_parent,
3009  bool adopt = true)
3010 {
3011  double hv = ov.xdouble_value ("%s: %s must be a graphics handle",
3012  who.c_str (), pname.c_str ());
3013 
3014  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3015 
3016  graphics_handle h = gh_mgr.lookup (hv);
3017 
3018  if (! h.ok ())
3019  error ("%s: invalid graphics handle (= %g) for %s",
3020  who.c_str (), hv, pname.c_str ());
3021 
3022  graphics_object go = gh_mgr.get_object (h);
3023 
3024  graphics_handle parent_h = go.get_parent ();
3025 
3026  graphics_object parent_go = gh_mgr.get_object (parent_h);
3027 
3028  parent_go.remove_child (h);
3029 
3030  if (adopt)
3031  go.set ("parent", new_parent.value ());
3032  else
3033  go.reparent (new_parent);
3034 
3035  return h;
3036 }
3037 
3038 // This function is NOT equivalent to the scripting language function gcf.
3040 gcf (void)
3041 {
3042  octave_value val = xget (0, "currentfigure");
3043 
3044  return val.isempty () ? octave::numeric_limits<double>::NaN ()
3045  : val.double_value ();
3046 }
3047 
3048 // This function is NOT equivalent to the scripting language function gca.
3050 gca (void)
3051 {
3052  octave_value val = xget (gcf (), "currentaxes");
3053 
3054  return val.isempty () ? octave::numeric_limits<double>::NaN ()
3055  : val.double_value ();
3056 }
3057 
3058 static void
3059 delete_graphics_object (const graphics_handle& h, bool from_root = false)
3060 {
3061  if (h.ok ())
3062  {
3063  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3064 
3065  graphics_object go = gh_mgr.get_object (h);
3066 
3067  // Don't do recursive deleting, due to callbacks
3068  if (! go.get_properties ().is_beingdeleted ())
3069  {
3070  // NOTE: Freeing the handle also calls any deletefcn. It also calls
3071  // the parent's delete_child function.
3072 
3073  gh_mgr.free (h, from_root || go.isa ("figure"));
3074 
3075  Vdrawnow_requested = true;
3076  }
3077  }
3078 }
3079 
3080 static void
3081 delete_graphics_object (double val, bool from_root = false)
3082 {
3083  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3084 
3085  delete_graphics_object (gh_mgr.lookup (val), from_root || isfigure (val));
3086 }
3087 
3088 // Flag to stop redraws due to callbacks while deletion is in progress.
3089 static bool delete_executing = false;
3090 
3091 static void
3092 delete_graphics_objects (const NDArray vals, bool from_root = false)
3093 {
3094  // Prevent redraw of partially deleted objects.
3095  octave::unwind_protect_var<bool> restore_var (delete_executing, true);
3096 
3097  for (octave_idx_type i = 0; i < vals.numel (); i++)
3098  delete_graphics_object (vals.elem (i), from_root);
3099 }
3100 
3101 static void
3103 {
3104  octave_value closerequestfcn = xget (h, "closerequestfcn");
3105 
3106  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3107 
3108  gh_mgr.execute_callback (h, closerequestfcn);
3109 }
3110 
3111 static void
3113 {
3114  // Remove the deletefcn and closerequestfcn callbacks
3115  // and delete the object directly.
3116 
3117  xset (h, "deletefcn", Matrix ());
3118  xset (h, "closerequestfcn", Matrix ());
3119 
3120  delete_graphics_object (h, true);
3121 }
3122 
3123 void
3124 gh_manager::close_all_figures (void)
3125 {
3126  // FIXME: should we process or discard pending events?
3127 
3128  m_event_queue.clear ();
3129 
3130  // Don't use m_figure_list_iterator because we'll be removing elements
3131  // from the list elsewhere.
3132 
3133  Matrix hlist = figure_handle_list (true);
3134 
3135  for (octave_idx_type i = 0; i < hlist.numel (); i++)
3136  {
3137  graphics_handle h = lookup (hlist(i));
3138 
3139  if (h.ok ())
3140  close_figure (h);
3141  }
3142 
3143  // They should all be closed now. If not, force them to close.
3144 
3145  hlist = figure_handle_list (true);
3146 
3147  for (octave_idx_type i = 0; i < hlist.numel (); i++)
3148  {
3149  graphics_handle h = lookup (hlist(i));
3150 
3151  if (h.ok ())
3152  force_close_figure (h);
3153  }
3154 
3155  // None left now, right?
3156 
3157  hlist = figure_handle_list (true);
3158 
3159  if (hlist.numel () != 0)
3160  warning ("gh_manager::close_all_figures: some graphics elements failed to close");
3161 
3162  // Clear all callback objects from our list.
3163 
3164  m_callback_objects.clear ();
3165 }
3166 
3167 static void
3168 adopt (const graphics_handle& parent_h, const graphics_handle& h)
3169 {
3170  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3171 
3172  graphics_object parent_go = gh_mgr.get_object (parent_h);
3173 
3174  parent_go.adopt (h);
3175 }
3176 
3177 static bool
3179 {
3180  return h.ok ();
3181 }
3182 
3183 static bool
3184 ishghandle (double val)
3185 {
3186  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3187 
3188  graphics_handle h = gh_mgr.lookup (val);
3189 
3190  return h.ok ();
3191 }
3192 
3193 static octave_value
3195 {
3196  octave_value retval = false;
3197 
3198  if (val.is_real_scalar () && ishghandle (val.double_value ()))
3199  retval = true;
3200  else if (val.isnumeric () && val.isreal ())
3201  {
3202  const NDArray handles = val.array_value ();
3203 
3204  boolNDArray result (handles.dims ());
3205 
3206  for (octave_idx_type i = 0; i < handles.numel (); i++)
3207  result.xelem (i) = ishghandle (handles(i));
3208 
3209  retval = result;
3210  }
3211 
3212  return retval;
3213 }
3214 
3215 static void
3217 {
3218  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3219 
3220  graphics_object go = gh_mgr.get_object (h);
3221 
3222  go.get_properties ().execute_createfcn ();
3223 }
3224 
3225 static void
3227 {
3228  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3229 
3230  graphics_object go = gh_mgr.get_object (h);
3231 
3232  if (go)
3233  go.initialize ();
3234 }
3235 
3236 // ---------------------------------------------------------------------
3237 
3238 static int
3239 toggle_warn (std::string id, bool on, int state = -1)
3240 {
3241  if (! on)
3242  {
3245  }
3246  else
3247  {
3248  if (state == 1)
3249  octave::set_warning_state (id, "on");
3250  else if (state == 2)
3251  octave::set_warning_state (id, "error");
3252  }
3253  return state;
3254 }
3255 
3256 static void
3258  property_list::pval_map_type factory_pval)
3259 {
3260  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3261 
3262  graphics_object go = gh_mgr.get_object (h);
3263 
3264  // Replace factory defaults by user defined ones
3265  std::string go_name = go.get_properties ().graphics_object_name ();
3266  property_list::pval_map_type pval;
3267  go.build_user_defaults_map (pval, go_name);
3268 
3269  for (const auto& p : pval)
3270  factory_pval[p.first] = p.second;
3271 
3272  // Save warning state of "Octave:deprecated-property"
3273  int state = toggle_warn ("Octave:deprecated-property", false);
3274 
3275  // Reset defaults
3276  for (const auto& p : factory_pval)
3277  {
3278  std::string pname = p.first;
3279 
3280  // Don't reset internal properties and handle_properties
3281  if (! go.has_readonly_property (pname)
3282  && pname.find ("__") != 0 && pname.find ("current") != 0
3283  && pname != "uicontextmenu" && pname != "parent")
3284  {
3285  // Store *mode prop/val in order to set them last
3286  if (pname.find ("mode") == (pname.length () - 4))
3287  pval[pname] = p.second;
3288  else
3289  go.set (pname, p.second);
3290  }
3291  }
3292 
3293  // set *mode properties
3294  for (const auto& p : pval)
3295  go.set (p.first, p.second);
3296 
3297  toggle_warn ("Octave:deprecated-property", true, state);
3298 }
3299 
3300 // ---------------------------------------------------------------------
3301 
3302 void
3303 base_properties::set_from_list (base_graphics_object& bgo,
3304  property_list& defaults)
3305 {
3306  std::string go_name = graphics_object_name ();
3307 
3308  property_list::plist_map_const_iterator plist = defaults.find (go_name);
3309 
3310  if (plist != defaults.end ())
3311  {
3312  const property_list::pval_map_type pval_map = plist->second;
3313 
3314  for (const auto& prop_val : pval_map)
3315  {
3316  std::string pname = prop_val.first;
3317 
3318  try
3319  {
3320  bgo.set (pname, prop_val.second);
3321  }
3322  catch (octave::execution_exception& ee)
3323  {
3324  error (ee, "error setting default property %s", pname.c_str ());
3325  }
3326  }
3327  }
3328 }
3329 
3330 /*
3331 ## test defaults are set in the order they were stored
3332 %!test
3333 %! set (0, "defaultfigureunits", "normalized");
3334 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
3335 %! hf = figure ("visible", "off");
3336 %! tol = 20 * eps;
3337 %! unwind_protect
3338 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
3339 %! unwind_protect_cleanup
3340 %! close (hf);
3341 %! set (0, "defaultfigureunits", "remove");
3342 %! set (0, "defaultfigureposition", "remove");
3343 %! end_unwind_protect
3344 */
3345 
3347 base_properties::get_dynamic (const caseless_str& pname) const
3348 {
3349  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it
3350  = m_all_props.find (pname);
3351 
3352  if (it == m_all_props.end ())
3353  error (R"(get: unknown property "%s")", pname.c_str ());
3354 
3355  return it->second.get ();
3356 }
3357 
3359 base_properties::get_dynamic (bool all) const
3360 {
3362 
3363  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
3364  it = m_all_props.begin (); it != m_all_props.end (); ++it)
3365  if (all || ! it->second.is_hidden ())
3366  m.assign (it->second.get_name (), it->second.get ());
3367 
3368  return m;
3369 }
3370 
3371 std::set<std::string>
3372 base_properties::dynamic_property_names (void) const
3373 {
3374  return m_dynamic_properties;
3375 }
3376 
3377 bool
3378 base_properties::has_dynamic_property (const std::string& pname) const
3379 {
3380  const std::set<std::string>& dynprops = dynamic_property_names ();
3381 
3382  if (dynprops.find (pname) != dynprops.end ())
3383  return true;
3384  else
3385  return m_all_props.find (pname) != m_all_props.end ();
3386 }
3387 
3388 void
3389 base_properties::set_dynamic (const caseless_str& pname,
3390  const octave_value& val)
3391 {
3392  auto it = m_all_props.find (pname);
3393 
3394  if (it == m_all_props.end ())
3395  error (R"(set: unknown property "%s")", pname.c_str ());
3396 
3397  it->second.set (val);
3398 
3399  m_dynamic_properties.insert (pname);
3400 
3401  mark_modified ();
3402 }
3403 
3404 property
3405 base_properties::get_property_dynamic (const caseless_str& pname) const
3406 {
3407  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it
3408  = m_all_props.find (pname);
3409 
3410  if (it == m_all_props.end ())
3411  error (R"(get_property: unknown property "%s")", pname.c_str ());
3412 
3413  return it->second;
3414 }
3415 
3416 void
3417 base_properties::set_parent (const octave_value& val)
3418 {
3419  double hp = val.xdouble_value ("set: parent must be a graphics handle");
3420  if (hp == m___myhandle__)
3421  error ("set: can not set object parent to be object itself");
3422 
3423  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3424 
3425  graphics_handle new_parent = gh_mgr.lookup (hp);
3426  if (! new_parent.ok ())
3427  error ("set: invalid graphics handle (= %g) for parent", hp);
3428 
3429  // Remove child from current parent
3430  graphics_object old_parent_go;
3431  old_parent_go = gh_mgr.get_object (get_parent ());
3432 
3433  if (old_parent_go.get_handle () != hp)
3434  old_parent_go.remove_child (m___myhandle__);
3435  else
3436  return; // Do nothing more
3437 
3438  // Check new parent's parent is not this child to avoid recursion
3439  graphics_object new_parent_go;
3440  new_parent_go = gh_mgr.get_object (new_parent);
3441  if (new_parent_go.get_parent () == m___myhandle__)
3442  {
3443  // new parent's parent gets child's original parent
3444  new_parent_go.get_properties ().set_parent (get_parent ().as_octave_value ());
3445  }
3446 
3447  // Set parent property to new_parent and do adoption
3448  m_parent = new_parent.as_octave_value ();
3449  octave::adopt (m_parent.handle_value (), m___myhandle__);
3450 }
3451 
3452 /*
3453 %!test
3454 %! hf = figure ("visible", "off");
3455 %! unwind_protect
3456 %! hax = gca ();
3457 %! set (hax, "parent", gcf ());
3458 %! assert (gca (), hax);
3459 %! unwind_protect_cleanup
3460 %! close (hf);
3461 %! end_unwind_protect
3462 */
3463 
3464 void
3465 base_properties::mark_modified (void)
3466 {
3467  // Mark existing object as modified
3468  m___modified__ = "on";
3469 
3470  // Attempt to mark parent object as modified if it exists
3471 
3472  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3473 
3474  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3475 
3476  if (parent_go)
3477  parent_go.mark_modified ();
3478 }
3479 
3480 void
3481 base_properties::override_defaults (base_graphics_object& obj)
3482 {
3483  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3484 
3485  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3486 
3487  if (parent_go)
3488  parent_go.override_defaults (obj);
3489 }
3490 
3491 void
3492 base_properties::update_axis_limits (const std::string& axis_type) const
3493 {
3494  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3495 
3496  graphics_object go = gh_mgr.get_object (m___myhandle__);
3497 
3498  if (go)
3499  go.update_axis_limits (axis_type);
3500 }
3501 
3502 void
3503 base_properties::update_axis_limits (const std::string& axis_type,
3504  const graphics_handle& h) const
3505 {
3506  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3507 
3508  graphics_object go = gh_mgr.get_object (m___myhandle__);
3509 
3510  if (go)
3511  go.update_axis_limits (axis_type, h);
3512 }
3513 
3514 void
3515 base_properties::update_contextmenu (void) const
3516 {
3517  if (m_contextmenu.get ().isempty ())
3518  return;
3519 
3520  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3521 
3522  graphics_object go = gh_mgr.get_object (m_contextmenu.get ());
3523 
3524  if (go && go.isa ("uicontextmenu"))
3525  {
3527  = reinterpret_cast<uicontextmenu::properties&> (go.get_properties ());
3528  props.add_dependent_obj (m___myhandle__);
3529  }
3530 }
3531 
3532 bool
3534 {
3535  return (m_handlevisibility.is ("on")
3536  || (! executing_callbacks.empty () && ! m_handlevisibility.is ("off")));
3537 }
3538 
3539 octave::graphics_toolkit
3540 base_properties::get_toolkit (void) const
3541 {
3542  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3543 
3544  graphics_object go = gh_mgr.get_object (get_parent ());
3545 
3546  if (go)
3547  return go.get_toolkit ();
3548  else
3549  return octave::graphics_toolkit ();
3550 }
3551 
3552 void
3553 base_properties::update_boundingbox (void)
3554 {
3555  Matrix kids = get_children ();
3556 
3557  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3558 
3559  for (int i = 0; i < kids.numel (); i++)
3560  {
3561  graphics_object go = gh_mgr.get_object (kids(i));
3562 
3563  if (go.valid_object ())
3564  go.get_properties ().update_boundingbox ();
3565  }
3566 }
3567 
3568 void
3569 base_properties::update_autopos (const std::string& elem_type)
3570 {
3571  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3572 
3573  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3574 
3575  if (parent_go.valid_object ())
3576  parent_go.get_properties ().update_autopos (elem_type);
3577 }
3578 
3579 void
3580 base_properties::update_handlevisibility (void)
3581 {
3582  if (is_handle_visible ())
3583  return;
3584 
3585  // This object should not be the figure "currentobject"
3586 
3587  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3588 
3589  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
3590 
3591  graphics_object fig (go.get_ancestor ("figure"));
3592 
3593  if (fig.valid_object ())
3594  {
3595  octave_value co = fig.get ("currentobject");
3596  if (! co.isempty () && co.double_value () == m___myhandle__)
3597  {
3598  octave::autolock guard (gh_mgr.graphics_lock ());
3599 
3600  auto& fig_props = dynamic_cast<figure::properties&> (fig.get_properties ());
3601  fig_props.set_currentobject (Matrix ());
3602  }
3603  }
3604 }
3605 
3606 /*
3607 ## test current figure and current axes have visible handles
3608 %!test
3609 %! hf1 = figure ("visible", "off");
3610 %! hf2 = figure ("visible", "off");
3611 %! hax1 = axes ();
3612 %! hax2 = axes ();
3613 %! unwind_protect
3614 %! assert (get (0, "currentfigure"), hf2);
3615 %! assert (get (hf2, "currentaxes"), hax2);
3616 %! set (hf2, "handlevisibility", "off");
3617 %! assert (get (0, "currentfigure"), hf1);
3618 %! set (hax2, "handlevisibility", "off");
3619 %! assert (get (hf2, "currentaxes"), hax1);
3620 %! assert (get (hf2, "currentobject"), []);
3621 %! unwind_protect_cleanup
3622 %! close ([hf1, hf2]);
3623 %! end_unwind_protect
3624 */
3625 
3626 /*
3627 ## test current callback object have visible handle
3628 %!test
3629 %! hf = figure ("handlevisibility", "off", "visible", "off");
3630 %! hax = axes ("parent", hf, "handlevisibility", "off");
3631 %! unwind_protect
3632 %! fcn = @(h, ~) setappdata (h, "testdata", gcbo ());
3633 %! addlistener (hf, "color", fcn);
3634 %! addlistener (hax, "color", fcn);
3635 %! set (hf, "color", "b");
3636 %! set (hax, "color", "b");
3637 %! assert (getappdata (hf, "testdata"), hf)
3638 %! assert (getappdata (hax, "testdata"), hax)
3639 %! unwind_protect_cleanup
3640 %! close (hf);
3641 %! end_unwind_protect
3642 */
3643 
3644 void
3645 base_properties::add_listener (const caseless_str& pname,
3646  const octave_value& val,
3647  listener_mode mode)
3648 {
3649  property p = get_property (pname);
3650 
3651  if (p.ok ())
3652  p.add_listener (val, mode);
3653 }
3654 
3655 void
3656 base_properties::delete_listener (const caseless_str& pname,
3657  const octave_value& val,
3658  listener_mode mode)
3659 {
3660  property p = get_property (pname);
3661 
3662  if (p.ok ())
3663  p.delete_listener (val, mode);
3664 }
3665 
3666 void
3667 base_properties::get_children_of_type (const caseless_str& chtype,
3668  bool get_invisible,
3669  bool traverse,
3670  std::list<graphics_object>& children_list) const
3671 {
3672  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3673 
3674  Matrix ch = get_children ();
3675 
3676  for (octave_idx_type i = 0; i < ch.numel (); i++)
3677  {
3678  graphics_handle hkid = gh_mgr.lookup (ch(i));
3679 
3680  if (hkid.ok ())
3681  {
3682  graphics_object go = gh_mgr.get_object (hkid);
3683  if ( get_invisible || go.get_properties ().is_visible () )
3684  {
3685  if (go.isa (chtype))
3686  children_list.push_back (go);
3687  else if (traverse && go.isa ("hggroup"))
3688  go.get_properties ().get_children_of_type (chtype,
3689  get_invisible,
3690  traverse,
3691  children_list);
3692  }
3693  }
3694  }
3695 }
3696 
3697 // ---------------------------------------------------------------------
3698 
3699 void
3700 base_graphics_object::update_axis_limits (const std::string& axis_type)
3701 {
3702  if (! valid_object ())
3703  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3704 
3705  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3706 
3707  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3708 
3709  if (parent_go)
3710  parent_go.update_axis_limits (axis_type);
3711 }
3712 
3713 void
3714 base_graphics_object::update_axis_limits (const std::string& axis_type,
3715  const graphics_handle& h)
3716 {
3717  if (! valid_object ())
3718  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3719 
3720  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3721 
3722  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3723 
3724  if (parent_go)
3725  parent_go.update_axis_limits (axis_type, h);
3726 }
3727 
3728 void
3729 base_graphics_object::remove_all_listeners (void)
3730 {
3731  int state = toggle_warn ("Octave:deprecated-property", false);
3732  octave_map m = get (true).map_value ();
3733  toggle_warn ("Octave:deprecated-property", true, state);
3734 
3735  for (const auto& pm : m)
3736  {
3737  // FIXME: there has to be a better way. I think we want to
3738  // ask whether it is OK to delete the listener for the given
3739  // property. How can we know in advance that it will be OK?
3740 
3741  octave::unwind_protect frame;
3742 
3743  octave::interpreter_try (frame);
3744 
3745  try
3746  {
3747  property p = get_properties ().get_property (pm.first);
3748 
3749  if (p.ok ())
3750  p.delete_listener ();
3751  }
3752  catch (const octave::execution_exception&)
3753  {
3754  octave::interpreter& interp = octave::__get_interpreter__ ();
3755 
3756  interp.recover_from_exception ();
3757  }
3758  }
3759 }
3760 
3761 void
3762 base_graphics_object::build_user_defaults_map (property_list::pval_map_type& def,
3763  const std::string go_name) const
3764 {
3765  property_list local_defaults = get_defaults_list ();
3766  const auto it = local_defaults.find (go_name);
3767 
3768  if (it != local_defaults.end ())
3769  {
3770  property_list::pval_map_type pval_lst = it->second;
3771  for (const auto& prop_val : pval_lst)
3772  {
3773  std::string pname = prop_val.first;
3774  if (def.find (pname) == def.end ())
3775  def[pname] = prop_val.second;
3776  }
3777  }
3778 
3779  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3780 
3781  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3782 
3783  if (parent_go)
3784  parent_go.build_user_defaults_map (def, go_name);
3785 }
3786 
3787 void
3788 base_graphics_object::reset_default_properties (void)
3789 {
3790  if (valid_object ())
3791  {
3792  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3793 
3794  property_list::pval_map_type factory_pval
3795  = gh_mgr.get_object (0).get_factory_defaults_list ().find (type ())->second;
3796 
3797  remove_all_listeners ();
3798  xreset_default_properties (get_handle (), factory_pval);
3799  }
3800 }
3801 
3802 std::string
3803 base_graphics_object::values_as_string (void)
3804 {
3805  if (! valid_object ())
3806  error ("base_graphics_object::values_as_string: invalid graphics object");
3807 
3808  std::string retval;
3809  octave_map m = get ().map_value ();
3810 
3811  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3812 
3813  graphics_object go = gh_mgr.get_object (get_handle ());
3814 
3815  for (const auto& pm : m)
3816  {
3817  const auto& pname = pm.first;
3818  if (pname != "children" && ! go.has_readonly_property (pname))
3819  {
3820  property p = get_properties ().get_property (pname);
3821 
3822  if (p.ok () && ! p.is_hidden ())
3823  {
3824  retval += "\n\t" + std::string (pname) + ": ";
3825  if (p.is_radio ())
3826  retval += p.values_as_string ();
3827  }
3828  }
3829  }
3830 
3831  if (! retval.empty ())
3832  retval += '\n';
3833 
3834  return retval;
3835 }
3836 
3837 std::string
3838 base_graphics_object::value_as_string (const std::string& prop)
3839 {
3840  std::string retval;
3841 
3842  if (! valid_object ())
3843  error ("base_graphics_object::value_as_string: invalid graphics object");
3844 
3845  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3846 
3847  graphics_object go = gh_mgr.get_object (get_handle ());
3848 
3849  if (prop != "children" && ! go.has_readonly_property (prop))
3850  {
3851  property p = get_properties ().get_property (prop);
3852 
3853  if (p.ok () && ! p.is_hidden ())
3854  {
3855  if (p.is_radio ())
3856  retval += p.values_as_string ();
3857  }
3858  }
3859 
3860  if (! retval.empty ())
3861  retval += '\n';
3862 
3863  return retval;
3864 }
3865 
3867 base_graphics_object::values_as_struct (void)
3868 {
3869  octave_scalar_map retval;
3870 
3871  if (! valid_object ())
3872  error ("base_graphics_object::values_as_struct: invalid graphics object");
3873 
3874  octave_scalar_map m = get ().scalar_map_value ();
3875 
3876  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3877 
3878  graphics_object go = gh_mgr.get_object (get_handle ());
3879 
3880  for (const auto& pm : m)
3881  {
3882  const auto& pname = pm.first;
3883  if (pname != "children" && ! go.has_readonly_property (pname))
3884  {
3885  property p = get_properties ().get_property (pname);
3886 
3887  if (p.ok () && ! p.is_hidden ())
3888  {
3889  if (p.is_radio ())
3890  retval.assign (p.get_name (), p.values_as_cell ());
3891  else
3892  retval.assign (p.get_name (), Cell ());
3893  }
3894  }
3895  }
3896 
3897  return retval;
3898 }
3899 
3900 /*
3901 %!test
3902 %! hfig = figure ("visible", "off");
3903 %! unwind_protect
3904 %! hax = axes ();
3905 %! ret = set (hax, "tightinset");
3906 %! assert (isempty (ret));
3907 %! ret = set (hax, "type");
3908 %! assert (isempty (ret));
3909 %! ret = set (hfig, "tag");
3910 %! assert (isempty (ret));
3911 %! ret = set (0, "commandwindowsize");
3912 %! assert (isempty (ret));
3913 %! ret = set (0);
3914 %! assert (! isfield (ret, "commandwindowsize"));
3915 %! unwind_protect_cleanup
3916 %! close (hfig);
3917 %! end_unwind_protect
3918 */
3919 
3920 graphics_object
3921 graphics_object::get_ancestor (const std::string& obj_type) const
3922 {
3923  if (valid_object ())
3924  {
3925  if (isa (obj_type))
3926  return *this;
3927  else
3928  {
3929  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3930 
3931  return gh_mgr.get_object (get_parent ()).get_ancestor (obj_type);
3932  }
3933  }
3934  else
3935  return graphics_object ();
3936 }
3937 
3938 // ---------------------------------------------------------------------
3939 
3940 #include "graphics-props.cc"
3941 
3942 // ---------------------------------------------------------------------
3943 
3944 void
3945 root_figure::properties::set_callbackobject (const octave_value& v)
3946 {
3947  graphics_handle val (v);
3948 
3949  if (octave::math::isnan (val.value ()))
3950  m_callbackobject = graphics_handle ();
3951  else if (ishghandle (val))
3952  m_callbackobject = val;
3953  else
3954  err_set_invalid ("callbackobject");
3955 }
3956 
3957 void
3958 root_figure::properties::set_currentfigure (const octave_value& v)
3959 {
3960  graphics_handle val (v);
3961 
3962  if (octave::math::isnan (val.value ()) || ishghandle (val))
3963  {
3964  m_currentfigure = val;
3965 
3966  if (val.ok ())
3967  {
3968  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3969 
3970  gh_mgr.push_figure (val);
3971  }
3972  }
3973  else
3974  err_set_invalid ("currentfigure");
3975 }
3976 
3977 void
3978 figure::properties::set_integerhandle (const octave_value& val)
3979 {
3980  if (m_integerhandle.set (val, true))
3981  {
3982  bool int_fig_handle = m_integerhandle.is_on ();
3983 
3984  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
3985 
3986  graphics_object this_go = gh_mgr.get_object (m___myhandle__);
3987 
3988  graphics_handle old_myhandle = m___myhandle__;
3989 
3990  m___myhandle__ = gh_mgr.get_handle (int_fig_handle);
3991 
3992  gh_mgr.renumber_figure (old_myhandle, m___myhandle__);
3993 
3994  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3995 
3996  base_properties& props = parent_go.get_properties ();
3997 
3998  props.renumber_child (old_myhandle, m___myhandle__);
3999 
4000  Matrix kids = get_children ();
4001 
4002  for (octave_idx_type i = 0; i < kids.numel (); i++)
4003  {
4004  graphics_object kid = gh_mgr.get_object (kids(i));
4005 
4006  kid.get_properties ().renumber_parent (m___myhandle__);
4007  }
4008 
4009  graphics_handle cf = gh_mgr.current_figure ();
4010 
4011  if (m___myhandle__ == cf)
4012  xset (0, "currentfigure", m___myhandle__.value ());
4013 
4014  this_go.update (m_integerhandle.get_id ());
4015 
4016  mark_modified ();
4017  }
4018 }
4019 
4020 // FIXME: This should update monitorpositions and pointerlocation, but as these
4021 // properties aren't yet used, it doesn't matter that they aren't set either.
4022 void
4023 root_figure::properties::update_units (void)
4024 {
4025  std::string xunits = get_units ();
4026 
4027  Matrix scrn_sz = default_screensize ();
4028 
4029  double dpi = get_screenpixelsperinch ();
4030 
4031  if (xunits == "pixels")
4032  {
4033  // Most common case (default).
4034  // Don't need to convert anything, but short-circuit if/else tree.
4035  }
4036  else if (xunits == "normalized")
4037  {
4038  scrn_sz = Matrix (1, 4, 1.0);
4039  scrn_sz(0) = 0;
4040  scrn_sz(1) = 0;
4041  }
4042  else if (xunits == "inches")
4043  {
4044  scrn_sz(0) = 0;
4045  scrn_sz(1) = 0;
4046  scrn_sz(2) /= dpi;
4047  scrn_sz(3) /= dpi;
4048  }
4049  else if (xunits == "centimeters")
4050  {
4051  scrn_sz(0) = 0;
4052  scrn_sz(1) = 0;
4053  scrn_sz(2) *= 2.54 / dpi;
4054  scrn_sz(3) *= 2.54 / dpi;
4055  }
4056  else if (xunits == "points")
4057  {
4058  scrn_sz(0) = 0;
4059  scrn_sz(1) = 0;
4060  scrn_sz(2) *= 72 / dpi;
4061  scrn_sz(3) *= 72 / dpi;
4062  }
4063  else if (xunits == "characters")
4064  {
4065  scrn_sz(0) = 0;
4066  scrn_sz(1) = 0;
4067  // FIXME: this assumes the system font is Helvetica 10pt
4068  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
4069  scrn_sz(2) *= 74.951 / 12.0 / dpi;
4070  scrn_sz(3) *= 74.951 / 12.0 / dpi;
4071  }
4072 
4073  set_screensize (scrn_sz);
4074 }
4075 
4076 Matrix
4077 root_figure::properties::get_boundingbox (bool, const Matrix&) const
4078 {
4079  Matrix screen_size = screen_size_pixels ();
4080  Matrix pos = Matrix (1, 4, 0.0);
4081 
4082  pos(2) = screen_size(0);
4083  pos(3) = screen_size(1);
4084 
4085  return pos;
4086 }
4087 
4088 /*
4089 %!test
4090 %! old_units = get (0, "units");
4091 %! unwind_protect
4092 %! set (0, "units", "pixels");
4093 %! sz = get (0, "screensize") - [1, 1, 0, 0];
4094 %! dpi = get (0, "screenpixelsperinch");
4095 %! set (0, "units", "inches");
4096 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
4097 %! set (0, "units", "centimeters");
4098 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
4099 %! set (0, "units", "points");
4100 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
4101 %! set (0, "units", "normalized");
4102 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
4103 %! set (0, "units", "pixels");
4104 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
4105 %! set (0, "units", "characters");
4106 %! assert (get (0, "screensize"),
4107 %! sz / dpi * (74.951 / 12.0), 0.5 / dpi * (74.951 / 12.0));
4108 %! unwind_protect_cleanup
4109 %! set (0, "units", old_units);
4110 %! end_unwind_protect
4111 */
4112 
4113 void
4114 root_figure::properties::remove_child (const graphics_handle& h, bool)
4115 {
4116  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
4117 
4118  gh_mgr.pop_figure (h);
4119 
4120  graphics_handle cf = gh_mgr.current_figure ();
4121 
4122  xset (0, "currentfigure", cf.value ());
4123 
4124  base_properties::remove_child (h, true);
4125 }
4126 
4127 void
4128 root_figure::reset_default_properties (void)
4129 {
4130  // empty list of local defaults
4131  m_default_properties = property_list ();
4132 
4133  remove_all_listeners ();
4134  xreset_default_properties (get_handle (),
4135  m_properties.factory_defaults ());
4136 }
4137 
4138 // ---------------------------------------------------------------------
4139 
4140 void
4141 figure::properties::set_currentaxes (const octave_value& val)
4142 {
4143  graphics_handle hax (val);
4144 
4145  if (octave::math::isnan (hax.value ()) || ishghandle (hax))
4146  m_currentaxes = hax;
4147  else
4148  err_set_invalid ("currentaxes");
4149 }
4150 
4151 void
4152 figure::properties::remove_child (const graphics_handle& h, bool from_root)
4153 {
4154  base_properties::remove_child (h, from_root);
4155 
4156  if (h == m_currentaxes.handle_value ())
4157  {
4158  graphics_handle new_currentaxes;
4159 
4160  Matrix kids = get_children ();
4161 
4162  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
4163 
4164  for (octave_idx_type i = 0; i < kids.numel (); i++)
4165  {
4166  graphics_handle kid = kids(i);
4167 
4168  graphics_object go = gh_mgr.get_object (kid);
4169 
4170  if (go.isa ("axes"))
4171  {
4172  new_currentaxes = kid;
4173  break;
4174  }
4175  }
4176 
4177  m_currentaxes = new_currentaxes;
4178  }
4179 }
4180 
4182 figure::properties::get_number (void) const
4183 {
4184  if (m_integerhandle.is_on ())
4185  return m___myhandle__.value ();
4186  else
4187  return Matrix ();
4188 }
4189 
4190 octave::graphics_toolkit
4191 figure::properties::get_toolkit (void) const
4192 {
4193  return m_toolkit;
4194 }
4195 
4196 void
4197 figure::properties::set___graphics_toolkit__ (const octave_value& val)
4198 {
4199  if (! val.is_string ())
4200  error ("set___graphics_toolkit__: toolkit must be a string");
4201 
4202  std::string nm = val.string_value ();
4203 
4204  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
4205 
4206  octave::graphics_toolkit b = gtk_mgr.find_toolkit (nm);
4207 
4208  if (b.get_name () != nm)
4209  error ("set___graphics_toolkit__: invalid graphics toolkit");
4210 
4211  if (nm != get___graphics_toolkit__ ())
4212  {
4213  set_toolkit (b);
4214  mark_modified ();
4215  }
4216 }
4217 
4218 void
4220 {
4222 
4223  if (! get_currentaxes ().ok ())
4224  {
4225  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
4226 
4227  graphics_object go = gh_mgr.get_object (h);
4228 
4229  if (go.type () == "axes")
4230  set_currentaxes (h.as_octave_value ());
4231  }
4232 }
4233 
4234 /*
4235 %!test
4236 %! hf1 = figure ("visible", "off");
4237 %! ax1 = subplot (1,2,1);
4238 %! ax2 = subplot (1,2,2);
4239 %! hf2 = figure ("visible", "off");
4240 %! unwind_protect
4241 %! set (ax2, "parent", hf2);
4242 %! assert (get (hf2, "currentaxes"), ax2);
4243 %! assert (get (hf1, "currentaxes"), ax1);
4244 %! set (ax1, "parent", hf2);
4245 %! assert (get (hf2, "currentaxes"), ax2);
4246 %! unwind_protect_cleanup
4247 %! close (hf1);
4248 %! close (hf2);
4249 %! end_unwind_protect
4250 */
4251 
4252 void
4253 figure::properties::set_visible (const octave_value& val)
4254 {
4255  std::string sval = val.string_value ();
4256 
4257  if (sval == "on")
4258  xset (0, "currentfigure", m___myhandle__.value ());
4259 
4260  m_visible = val;
4261 }
4262 
4263 Matrix
4264 figure::properties::get_boundingbox (bool internal, const Matrix&) const
4265 {
4266  Matrix screen_size = screen_size_pixels ();
4267  Matrix pos = (internal ?
4268  get_position ().matrix_value () :
4269  get_outerposition ().matrix_value ());
4270 
4271  pos = convert_position (pos, get_units (), "pixels", screen_size);
4272 
4273  pos(0)--;
4274  pos(1)--;
4275  pos(1) = screen_size(1) - pos(1) - pos(3);
4276 
4277  return pos;
4278 }
4279 
4280 Matrix
4281 figure::properties::bbox2position (const Matrix& bb) const
4282 {
4283  Matrix screen_size = screen_size_pixels ();
4284  Matrix pos = bb;
4285 
4286  pos(1) = screen_size(1) - pos(1) - pos(3);
4287  pos(1)++;
4288  pos(0)++;
4289  pos = convert_position (pos, "pixels", get_units (), screen_size);
4290  return pos;
4291 }
4292 
4293 void
4294 figure::properties::set_boundingbox (const Matrix& bb, bool internal,
4295  bool do_notify_toolkit)
4296 {
4297  Matrix screen_size = screen_size_pixels ();
4298  Matrix pos = bbox2position (bb);
4299 
4300  if (internal)
4301  set_position (pos, do_notify_toolkit);
4302  else
4303  set_outerposition (pos, do_notify_toolkit);
4304 }
4305 
4306 Matrix
4307 figure::properties::map_from_boundingbox (double x, double y) const
4308 {
4309  Matrix bb = get_boundingbox (true);
4310  Matrix pos (1, 2, 0.0);
4311 
4312  pos(0) = x;
4313  pos(1) = y;
4314 
4315  pos(1) = bb(3) - pos(1);
4316  pos(0)++;
4317  pos = convert_position (pos, "pixels", get_units (),
4318  bb.extract_n (0, 2, 1, 2));
4319 
4320  return pos;
4321 }
4322 
4323 Matrix
4324 figure::properties::map_to_boundingbox (double x, double y) const
4325 {
4326  Matrix bb = get_boundingbox (true);
4327  Matrix pos (1, 2, 0.0);
4328 
4329  pos(0) = x;
4330  pos(1) = y;
4331 
4332  pos = convert_position (pos, get_units (), "pixels",
4333  bb.extract_n (0, 2, 1, 2));
4334  pos(0)--;
4335  pos(1) = bb(3) - pos(1);
4336 
4337  return pos;
4338 }
4339 
4340 void
4341 figure::properties::set_position (const octave_value& v,
4342  bool do_notify_toolkit)
4343 {
4344  Matrix old_bb, new_bb;
4345  bool modified = false;
4346 
4347  old_bb = get_boundingbox (true);
4348  modified = m_position.set (v, false, do_notify_toolkit);
4349  new_bb = get_boundingbox (true);
4350 
4351  if (old_bb != new_bb)
4352  {
4353  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
4354  {
4355  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
4356 
4357  if (! get_resizefcn ().isempty ())
4358  gh_mgr.post_callback (m___myhandle__, "resizefcn");
4359 
4360  if (! get_sizechangedfcn ().isempty ())
4361  gh_mgr.post_callback (m___myhandle__, "sizechangedfcn");
4362 
4363  update_boundingbox ();
4364  }
4365  }
4366 
4367  if (modified)
4368  {
4369  m_position.run_listeners (GCB_POSTSET);
4370  mark_modified ();
4371  }
4372 
4373  if (m_paperpositionmode.is ("auto"))
4374  m_paperposition.set (get_auto_paperposition ());
4375 }
4376 
4377 void
4378 figure::properties::set_outerposition (const octave_value& v,
4379  bool do_notify_toolkit)
4380 {
4381  if (m_outerposition.set (v, true, do_notify_toolkit))
4382  mark_modified ();
4383 }
4384 
4385 void
4386 figure::properties::set_paperunits (const octave_value& val)
4387 {
4388  caseless_str punits = val.string_value ();
4389  caseless_str ptype = get_papertype ();
4390 
4391  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4392  error ("set: can't set paperunits to normalized when papertype is custom");
4393 
4394  caseless_str old_paperunits = get_paperunits ();
4395  if (m_paperunits.set (val, true))
4396  {
4397  update_paperunits (old_paperunits);
4398  mark_modified ();
4399  }
4400 }
4401 
4402 void
4403 figure::properties::set_papertype (const octave_value& val)
4404 {
4405  caseless_str ptype = val.string_value ();
4406  caseless_str punits = get_paperunits ();
4407 
4408  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4409  error ("set: can't set paperunits to normalized when papertype is custom");
4410 
4411  if (m_papertype.set (val, true))
4412  {
4413  update_papertype ();
4414  mark_modified ();
4415  }
4416 }
4417 
4418 static Matrix
4419 papersize_from_type (const caseless_str punits, const caseless_str ptype)
4420 {
4421  Matrix retval (1, 2, 1.0);
4422 
4423  if (! punits.compare ("normalized"))
4424  {
4425  double in2units;
4426  double mm2units;
4427 
4428  if (punits.compare ("inches"))
4429  {
4430  in2units = 1.0;
4431  mm2units = 1 / 25.4;
4432  }
4433  else if (punits.compare ("centimeters"))
4434  {
4435  in2units = 2.54;
4436  mm2units = 1 / 10.0;
4437  }
4438  else // points
4439  {
4440  in2units = 72.0;
4441  mm2units = 72.0 / 25.4;
4442  }
4443 
4444  if (ptype.compare ("usletter"))
4445  {
4446  retval(0) = 8.5 * in2units;
4447  retval(1) = 11.0 * in2units;
4448  }
4449  else if (ptype.compare ("uslegal"))
4450  {
4451  retval(0) = 8.5 * in2units;
4452  retval(1) = 14.0 * in2units;
4453  }
4454  else if (ptype.compare ("tabloid"))
4455  {
4456  retval(0) = 11.0 * in2units;
4457  retval(1) = 17.0 * in2units;
4458  }
4459  else if (ptype.compare ("a0"))
4460  {
4461  retval(0) = 841.0 * mm2units;
4462  retval(1) = 1189.0 * mm2units;
4463  }
4464  else if (ptype.compare ("a1"))
4465  {
4466  retval(0) = 594.0 * mm2units;
4467  retval(1) = 841.0 * mm2units;
4468  }
4469  else if (ptype.compare ("a2"))
4470  {
4471  retval(0) = 420.0 * mm2units;
4472  retval(1) = 594.0 * mm2units;
4473  }
4474  else if (ptype.compare ("a3"))
4475  {
4476  retval(0) = 297.0 * mm2units;
4477  retval(1) = 420.0 * mm2units;
4478  }
4479  else if (ptype.compare ("a4"))
4480  {
4481  retval(0) = 210.0 * mm2units;
4482  retval(1) = 297.0 * mm2units;
4483  }
4484  else if (ptype.compare ("a5"))
4485  {
4486  retval(0) = 148.0 * mm2units;
4487  retval(1) = 210.0 * mm2units;
4488  }
4489  else if (ptype.compare ("b0"))
4490  {
4491  retval(0) = 1029.0 * mm2units;
4492  retval(1) = 1456.0 * mm2units;
4493  }
4494  else if (ptype.compare ("b1"))
4495  {
4496  retval(0) = 728.0 * mm2units;
4497  retval(1) = 1028.0 * mm2units;
4498  }
4499  else if (ptype.compare ("b2"))
4500  {
4501  retval(0) = 514.0 * mm2units;
4502  retval(1) = 728.0 * mm2units;
4503  }
4504  else if (ptype.compare ("b3"))
4505  {
4506  retval(0) = 364.0 * mm2units;
4507  retval(1) = 514.0 * mm2units;
4508  }
4509  else if (ptype.compare ("b4"))
4510  {
4511  retval(0) = 257.0 * mm2units;
4512  retval(1) = 364.0 * mm2units;
4513  }
4514  else if (ptype.compare ("b5"))
4515  {
4516  retval(0) = 182.0 * mm2units;
4517  retval(1) = 257.0 * mm2units;
4518  }
4519  else if (ptype.compare ("arch-a"))
4520  {
4521  retval(0) = 9.0 * in2units;
4522  retval(1) = 12.0 * in2units;
4523  }
4524  else if (ptype.compare ("arch-b"))
4525  {
4526  retval(0) = 12.0 * in2units;
4527  retval(1) = 18.0 * in2units;
4528  }
4529  else if (ptype.compare ("arch-c"))
4530  {
4531  retval(0) = 18.0 * in2units;
4532  retval(1) = 24.0 * in2units;
4533  }
4534  else if (ptype.compare ("arch-d"))
4535  {
4536  retval(0) = 24.0 * in2units;
4537  retval(1) = 36.0 * in2units;
4538  }
4539  else if (ptype.compare ("arch-e"))
4540  {
4541  retval(0) = 36.0 * in2units;
4542  retval(1) = 48.0 * in2units;
4543  }
4544  else if (ptype.compare ("a"))
4545  {
4546  retval(0) = 8.5 * in2units;
4547  retval(1) = 11.0 * in2units;
4548  }
4549  else if (ptype.compare ("b"))
4550  {
4551  retval(0) = 11.0 * in2units;
4552  retval(1) = 17.0 * in2units;
4553  }
4554  else if (ptype.compare ("c"))
4555  {
4556  retval(0) = 17.0 * in2units;
4557  retval(1) = 22.0 * in2units;
4558  }
4559  else if (ptype.compare ("d"))
4560  {
4561  retval(0) = 22.0 * in2units;
4562  retval(1) = 34.0 * in2units;
4563  }
4564  else if (ptype.compare ("e"))
4565  {
4566  retval(0) = 34.0 * in2units;
4567  retval(1) = 43.0 * in2units;
4568  }
4569  }
4570 
4571  return retval;
4572 }
4573 
4574 Matrix
4575 figure::properties::get_auto_paperposition (void)
4576 {
4577  Matrix pos = get_position ().matrix_value ();
4578  Matrix sz;
4579 
4580  caseless_str funits = get_units ();
4581  caseless_str punits = get_paperunits ();
4582 
4583  // Convert position from figure units to paperunits
4584  if (funits == "normalized" || punits == "normalized")
4585  {
4586  sz = screen_size_pixels ();
4587  pos = convert_position (pos, funits, "inches", sz);
4588 
4589  if (punits == "normalized")
4590  sz = papersize_from_type ("points", get_papertype ());
4591 
4592  pos = convert_position (pos, "inches", punits, sz);
4593  }
4594  else
4595  pos = convert_position (pos, funits, punits, sz);
4596 
4597  // Center the figure on the page
4598  sz = get_papersize ().matrix_value ();
4599 
4600  pos(0) = sz(0)/2 - pos(2)/2;
4601  pos(1) = sz(1)/2 - pos(3)/2;
4602 
4603  return pos;
4604 }
4605 
4606 /*
4607 %!test
4608 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4609 %! in_pos = [0 0 4 5];
4610 %! tol = 20 * eps ();
4611 %! unwind_protect
4612 %! ## paperpositionmode "auto" converts figure size to paper units
4613 %! set (hf, "units", "inches");
4614 %! set (hf, "position", in_pos);
4615 %! set (hf, "paperunits", "centimeters");
4616 %! psz = get (hf, "papersize");
4617 %! fsz = in_pos(3:4) * 2.54;
4618 %! pos = [(psz/2 - fsz/2) fsz];
4619 %! set (hf, "paperpositionmode", "auto");
4620 %! assert (get (hf, "paperposition"), pos, tol);
4621 %! unwind_protect_cleanup
4622 %! close (hf);
4623 %! end_unwind_protect
4624 
4625 %!test
4626 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4627 %! in_pos = [0 0 4 5];
4628 %! tol = 20 * eps ();
4629 %! unwind_protect
4630 %! ## likewise with normalized units
4631 %! set (hf, "units", "inches");
4632 %! set (hf, "position", in_pos);
4633 %! psz = get (hf, "papersize");
4634 %! set (hf, "paperunits", "normalized");
4635 %! fsz = in_pos(3:4) ./ psz;
4636 %! pos = [([0.5 0.5] - fsz/2) fsz];
4637 %! assert (get (hf, "paperposition"), pos, tol);
4638 %! unwind_protect_cleanup
4639 %! close (hf);
4640 %! end_unwind_protect
4641 
4642 %!test
4643 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4644 %! in_pos = [0 0 4 5];
4645 %! tol = 20 * eps ();
4646 %! unwind_protect
4647 %! ## changing papertype updates paperposition
4648 %! set (hf, "units", "inches");
4649 %! set (hf, "position", in_pos);
4650 %! set (hf, "papertype", "a4");
4651 %! psz = get (hf, "papersize");
4652 %! fsz = in_pos(3:4);
4653 %! pos = [(psz/2 - fsz/2) fsz];
4654 %! assert (get (hf, "paperposition"), pos, tol);
4655 %! unwind_protect_cleanup
4656 %! close (hf);
4657 %! end_unwind_protect
4658 
4659 %!test
4660 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4661 %! in_pos = [0 0 4 5];
4662 %! tol = 20 * eps ();
4663 %! unwind_protect
4664 %! ## lanscape updates paperposition
4665 %! set (hf, "units", "inches");
4666 %! set (hf, "position", in_pos);
4667 %! set (hf, "paperorientation", "landscape");
4668 %! psz = get (hf, "papersize");
4669 %! fsz = in_pos(3:4);
4670 %! pos = [(psz/2 - fsz/2) fsz];
4671 %! assert (get (hf, "paperposition"), pos, tol);
4672 %! unwind_protect_cleanup
4673 %! close (hf);
4674 %! end_unwind_protect
4675 
4676 %!test
4677 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4678 %! in_pos = [0 0 4 5];
4679 %! unwind_protect
4680 %! ## back to manual mode
4681 %! set (hf, "paperposition", in_pos * 1.1);
4682 %! assert (get (hf, "paperpositionmode"), "manual");
4683 %! assert (get (hf, "paperposition"), in_pos * 1.1);
4684 %! unwind_protect_cleanup
4685 %! close (hf);
4686 %! end_unwind_protect
4687 */
4688 
4689 void
4690 figure::properties::update_paperunits (const caseless_str& old_paperunits)
4691 {
4692  Matrix pos = get_paperposition ().matrix_value ();
4693  Matrix sz = get_papersize ().matrix_value ();
4694 
4695  pos(0) /= sz(0);
4696  pos(1) /= sz(1);
4697  pos(2) /= sz(0);
4698  pos(3) /= sz(1);
4699 
4700  std::string porient = get_paperorientation ();
4701  caseless_str punits = get_paperunits ();
4702  caseless_str ptype = get_papertype ();
4703 
4704  if (ptype.compare ("<custom>"))
4705  {
4706  if (old_paperunits.compare ("centimeters"))
4707  {
4708  sz(0) /= 2.54;
4709  sz(1) /= 2.54;
4710  }
4711  else if (old_paperunits.compare ("points"))
4712  {
4713  sz(0) /= 72.0;
4714  sz(1) /= 72.0;
4715  }
4716 
4717  if (punits.compare ("centimeters"))
4718  {
4719  sz(0) *= 2.54;
4720  sz(1) *= 2.54;
4721  }
4722  else if (punits.compare ("points"))
4723  {
4724  sz(0) *= 72.0;
4725  sz(1) *= 72.0;
4726  }
4727  }
4728  else
4729  {
4730  sz = papersize_from_type (punits, ptype);
4731  if (porient == "landscape")
4732  std::swap (sz(0), sz(1));
4733  }
4734 
4735  pos(0) *= sz(0);
4736  pos(1) *= sz(1);
4737  pos(2) *= sz(0);
4738  pos(3) *= sz(1);
4739 
4740  m_papersize.set (octave_value (sz));
4741  m_paperposition.set (octave_value (pos));
4742 }
4743 
4744 void
4745 figure::properties::update_papertype (void)
4746 {
4747  std::string typ = get_papertype ();
4748  if (typ != "<custom>")
4749  {
4750  Matrix sz = papersize_from_type (get_paperunits (), typ);
4751  if (get_paperorientation () == "landscape")
4752  std::swap (sz(0), sz(1));
4753  // Call papersize.set rather than set_papersize to avoid loops
4754  // between update_papersize and update_papertype.
4755  m_papersize.set (octave_value (sz));
4756  }
4757 
4758  if (m_paperpositionmode.is ("auto"))
4759  m_paperposition.set (get_auto_paperposition ());
4760 }
4761 
4762 void
4763 figure::properties::update_papersize (void)
4764 {
4765  Matrix sz = get_papersize ().matrix_value ();
4766  if (sz(0) > sz(1))
4767  {
4768  std::swap (sz(0), sz(1));
4769  m_papersize.set (octave_value (sz));
4770  m_paperorientation.set (octave_value ("landscape"));
4771  }
4772  else
4773  {
4774  m_paperorientation.set ("portrait");
4775  }
4776 
4777  std::string punits = get_paperunits ();
4778  if (punits == "centimeters")
4779  {
4780  sz(0) /= 2.54;
4781  sz(1) /= 2.54;
4782  }
4783  else if (punits == "points")
4784  {
4785  sz(0) /= 72.0;
4786  sz(1) /= 72.0;
4787  }
4788  if (punits == "normalized")
4789  {
4790  if (get_papertype () == "<custom>")
4791  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4792  }
4793  else
4794  {
4795  // FIXME: The papersizes info is also in papersize_from_type().
4796  // Both should be rewritten to avoid the duplication.
4797  // Don't Repeat Yourself (DRY) principle.
4798  std::string ptype = "<custom>";
4799  const double mm2in = 1.0 / 25.4;
4800  const double tol = 0.01;
4801 
4802  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4803  ptype = "usletter";
4804  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4805  ptype = "uslegal";
4806  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4807  ptype = "tabloid";
4808  else if (std::abs (sz(0) - 841.0 * mm2in)
4809  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4810  ptype = "a0";
4811  else if (std::abs (sz(0) - 594.0 * mm2in)
4812  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4813  ptype = "a1";
4814  else if (std::abs (sz(0) - 420.0 * mm2in)
4815  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4816  ptype = "a2";
4817  else if (std::abs (sz(0) - 297.0 * mm2in)
4818  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4819  ptype = "a3";
4820  else if (std::abs (sz(0) - 210.0 * mm2in)
4821  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4822  ptype = "a4";
4823  else if (std::abs (sz(0) - 148.0 * mm2in)
4824  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4825  ptype = "a5";
4826  else if (std::abs (sz(0) - 1029.0 * mm2in)
4827  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4828  ptype = "b0";
4829  else if (std::abs (sz(0) - 728.0 * mm2in)
4830  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4831  ptype = "b1";
4832  else if (std::abs (sz(0) - 514.0 * mm2in)
4833  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4834  ptype = "b2";
4835  else if (std::abs (sz(0) - 364.0 * mm2in)
4836  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4837  ptype = "b3";
4838  else if (std::abs (sz(0) - 257.0 * mm2in)
4839  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4840  ptype = "b4";
4841  else if (std::abs (sz(0) - 182.0 * mm2in)
4842  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4843  ptype = "b5";
4844  else if (std::abs (sz(0) - 9.0)
4845  + std::abs (sz(1) - 12.0) < tol)
4846  ptype = "arch-a";
4847  else if (std::abs (sz(0) - 12.0)
4848  + std::abs (sz(1) - 18.0) < tol)
4849  ptype = "arch-b";
4850  else if (std::abs (sz(0) - 18.0)
4851  + std::abs (sz(1) - 24.0) < tol)
4852  ptype = "arch-c";
4853  else if (std::abs (sz(0) - 24.0)
4854  + std::abs (sz(1) - 36.0) < tol)
4855  ptype = "arch-d";
4856  else if (std::abs (sz(0) - 36.0)
4857  + std::abs (sz(1) - 48.0) < tol)
4858  ptype = "arch-e";
4859  else if (std::abs (sz(0) - 8.5)
4860  + std::abs (sz(1) - 11.0) < tol)
4861  ptype = "a";
4862  else if (std::abs (sz(0) - 11.0)
4863  + std::abs (sz(1) - 17.0) < tol)
4864  ptype = "b";
4865  else if (std::abs (sz(0) - 17.0)
4866  + std::abs (sz(1) - 22.0) < tol)
4867  ptype = "c";
4868  else if (std::abs (sz(0) - 22.0)
4869  + std::abs (sz(1) - 34.0) < tol)
4870  ptype = "d";
4871  else if (std::abs (sz(0) - 34.0)
4872  + std::abs (sz(1) - 43.0) < tol)
4873  ptype = "e";
4874  // Call papertype.set rather than set_papertype to avoid loops between
4875  // update_papersize and update_papertype
4876  m_papertype.set (ptype);
4877  }
4878  if (punits == "centimeters")
4879  {
4880  sz(0) *= 2.54;
4881  sz(1) *= 2.54;
4882  }
4883  else if (punits == "points")
4884  {
4885  sz(0) *= 72.0;
4886  sz(1) *= 72.0;
4887  }
4888  if (get_paperorientation () == "landscape")
4889  {
4890  std::swap (sz(0), sz(1));
4891  m_papersize.set (octave_value (sz));
4892  }
4893 
4894  if (m_paperpositionmode.is ("auto"))
4895  m_paperposition.set (get_auto_paperposition ());
4896 }
4897 
4898 /*
4899 %!test
4900 %! hf = figure ("visible", "off");
4901 %! unwind_protect
4902 %! set (hf, "paperunits", "inches");
4903 %! set (hf, "papersize", [5, 4]);
4904 %! set (hf, "paperunits", "points");
4905 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4906 %! papersize = get (hf, "papersize");
4907 %! set (hf, "papersize", papersize + 1);
4908 %! set (hf, "papersize", papersize);
4909 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4910 %! unwind_protect_cleanup
4911 %! close (hf);
4912 %! end_unwind_protect
4913 
4914 %!test
4915 %! hf = figure ("visible", "off");
4916 %! unwind_protect
4917 %! set (hf, "paperunits", "inches");
4918 %! set (hf, "papersize", [5, 4]);
4919 %! set (hf, "paperunits", "centimeters");
4920 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4921 %! papersize = get (hf, "papersize");
4922 %! set (hf, "papersize", papersize + 1);
4923 %! set (hf, "papersize", papersize);
4924 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4925 %! unwind_protect_cleanup
4926 %! close (hf);
4927 %! end_unwind_protect
4928 */
4929 
4930 void
4931 figure::properties::update_paperorientation (void)
4932 {
4933  std::string porient = get_paperorientation ();
4934  Matrix sz = get_papersize ().matrix_value ();
4935  if ((sz(0) > sz(1) && porient == "portrait")
4936  || (sz(0) < sz(1) && porient == "landscape"))
4937  {
4938  std::swap (sz(0), sz(1));
4939  // Call papertype.set rather than set_papertype to avoid loops
4940  // between update_papersize and update_papertype
4941  m_papersize.set (octave_value (sz));
4942  }
4943 
4944  if (m_paperpositionmode.is ("auto"))
4945  m_paperposition.set (get_auto_paperposition ());
4946 }
4947 
4948 /*
4949 %!test
4950 %! hf = figure ("visible", "off");
4951 %! unwind_protect
4952 %! tol = 100 * eps ();
4953 %! ## UPPER case and MiXed case is part of test and should not be changed.
4954 %! set (hf, "paperorientation", "PORTRAIT");
4955 %! set (hf, "paperunits", "inches");
4956 %! set (hf, "papertype", "USletter");
4957 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4958 %! set (hf, "paperorientation", "Landscape");
4959 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4960 %! set (hf, "paperunits", "centimeters");
4961 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4962 %! set (hf, "papertype", "a4");
4963 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4964 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4965 %! assert (get (hf, "papertype"), "usletter");
4966 %! assert (get (hf, "paperorientation"), "portrait");
4967 %! set (hf, "papersize", [11.0, 8.5]);
4968 %! assert (get (hf, "papertype"), "usletter");
4969 %! assert (get (hf, "paperorientation"), "landscape");
4970 %! unwind_protect_cleanup
4971 %! close (hf);
4972 %! end_unwind_protect
4973 */
4974 
4975 void
4976 figure::properties::set_units (const octave_value& val)
4977 {
4978  caseless_str old_units = get_units ();
4979 
4980  if (m_units.set (val, true))
4981  {
4982  update_units (old_units);
4983  mark_modified ();
4984  }
4985 }
4986 
4987 void
4988 figure::properties::update_units (const caseless_str& old_units)
4989 {
4990  m_position.set (convert_position (get_position ().matrix_value (),
4991  old_units, get_units (),
4992  screen_size_pixels ()), false);
4993 }
4994 
4995 /*
4996 %!test
4997 %! hf = figure ("visible", "off");
4998 %! old_units = get (0, "units");
4999 %! unwind_protect
5000 %! set (0, "units", "pixels");
5001 %! rsz = get (0, "screensize");
5002 %! set (gcf (), "units", "pixels");
5003 %! fsz = get (gcf (), "position");
5004 %! set (gcf (), "units", "normalized");
5005 %! pos = get (gcf (), "position");
5006 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
5007 %! unwind_protect_cleanup
5008 %! close (hf);
5009 %! set (0, "units", old_units);
5010 %! end_unwind_protect
5011 */
5012 
5013 std::string
5014 figure::properties::get_title (void) const
5015 {
5016  std::string title;
5017  if (! get_number ().isempty () && is_numbertitle ())
5018  {
5019  std::ostringstream os;
5020  std::string nm = get_name ();
5021 
5022  os << "Figure " << m___myhandle__.value ();
5023  if (! nm.empty ())
5024  os << ": " << get_name ();
5025 
5026  title = os.str ();
5027  }
5028  else
5029  title = get_name ();
5030 
5031  // Qt will use QCoreApplication name (set in main-window.cc)
5032  // if the name is empty, so force blank.
5033  if (title.empty ())
5034  title = " ";
5035 
5036  return title;
5037 }
5038 
5040 figure::get_default (const caseless_str& name) const
5041 {
5042  octave_value retval = m_default_properties.lookup (name);
5043 
5044  if (retval.is_undefined ())
5045  {
5046  graphics_handle parent_h = get_parent ();
5047 
5048  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5049 
5050  graphics_object parent_go = gh_mgr.get_object (parent_h);
5051 
5052  retval = parent_go.get_default (name);
5053  }
5054 
5055  return retval;
5056 }
5057 
5058 void
5059 figure::reset_default_properties (void)
5060 {
5061  // empty list of local defaults
5062  m_default_properties = property_list ();
5063  property_list::pval_map_type plist = m_properties.factory_defaults ();
5064 
5065  plist.erase ("units");
5066  plist.erase ("position");
5067  plist.erase ("outerposition");
5068  plist.erase ("paperunits");
5069  plist.erase ("paperposition");
5070  plist.erase ("windowstyle");
5071 
5072  remove_all_listeners ();
5073  xreset_default_properties (get_handle (), plist);
5074 }
5075 
5076 // ---------------------------------------------------------------------
5077 
5078 void
5079 axes::properties::init (void)
5080 {
5081  m_position.add_constraint (dim_vector (1, 4));
5082  m_outerposition.add_constraint (dim_vector (1, 4));
5083  m_tightinset.add_constraint (dim_vector (1, 4));
5084  m_looseinset.add_constraint (dim_vector (1, 4));
5085  m_colororder.add_constraint (dim_vector (-1, 3));
5086  m_dataaspectratio.add_constraint (3);
5087  m_dataaspectratio.add_constraint ("min", 0, false);
5088  m_dataaspectratio.add_constraint (FINITE);
5089  m_plotboxaspectratio.add_constraint (3);
5090  m_plotboxaspectratio.add_constraint ("min", 0, false);
5091  m_plotboxaspectratio.add_constraint (FINITE);
5092  // FIXME: Should these use dimension vectors? Currently can set 'xlim' to
5093  // any matrix size, but only first two elements are used.
5094  m_alim.add_constraint (2);
5095  m_alim.add_constraint (NOT_NAN);
5096  m_clim.add_constraint (2);
5097  m_clim.add_constraint (NOT_NAN);
5098  m_xlim.add_constraint (2);
5099  m_xlim.add_constraint (NOT_NAN);
5100  m_ylim.add_constraint (2);
5101  m_ylim.add_constraint (NOT_NAN);
5102  m_zlim.add_constraint (2);
5103  m_zlim.add_constraint (NOT_NAN);
5104  m_xtick.add_constraint (dim_vector (1, -1));
5105  m_xtick.add_constraint (FINITE);
5106  m_ytick.add_constraint (dim_vector (1, -1));
5107  m_ytick.add_constraint (FINITE);
5108  m_ztick.add_constraint (dim_vector (1, -1));
5109  m_ztick.add_constraint (FINITE);
5110  m_ticklength.add_constraint (dim_vector (1, 2));
5111  Matrix vw (1, 2, 0);
5112  vw(1) = 90;
5113  m_view = vw;
5114  m_view.add_constraint (dim_vector (1, 2));
5115  m_cameraposition.add_constraint (3);
5116  m_cameraposition.add_constraint (FINITE);
5117  m_cameratarget.add_constraint (3);
5118  m_cameratarget.add_constraint (FINITE);
5119  Matrix upv (1, 3, 0.0);
5120  upv(2) = 1.0;
5121  m_cameraupvector = upv;
5122  m_cameraupvector.add_constraint (3);
5123  m_cameraupvector.add_constraint (FINITE);
5124  m_cameraviewangle.add_constraint (FINITE);
5125  m_currentpoint.add_constraint (dim_vector (2, 3));
5126 
5127  // Range constraints for double properties
5128  m_fontsize.add_constraint ("min", 0.0, false);
5129  m_gridalpha.add_constraint ("min", 0.0, true);
5130  m_gridalpha.add_constraint ("max", 1.0, true);
5131  m_labelfontsizemultiplier.add_constraint ("min", 0.0, false);
5132  m_linewidth.add_constraint ("min", 0.0, false);
5133  m_minorgridalpha.add_constraint ("min", 0.0, true);
5134  m_minorgridalpha.add_constraint ("max", 1.0, true);
5135  m_titlefontsizemultiplier.add_constraint ("min", 0.0, false);
5136 
5137  // No constraints for hidden transform properties
5138  update_font ();
5139 
5140  m_x_zlim.resize (1, 2);
5141 
5142  m_sx = "linear";
5143  m_sy = "linear";
5144  m_sz = "linear";
5145 
5146  calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
5147  xaxislocation_is ("origin"),
5148  m_yscale.is ("log") ? 2 : (yaxislocation_is ("origin") ? 0 :
5149  (yaxislocation_is ("left") ? -1 : 1)), m_xlim);
5150  calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
5151  yaxislocation_is ("origin"),
5152  m_xscale.is ("log") ? 2 : (xaxislocation_is ("origin") ? 0 :
5153  (xaxislocation_is ("bottom") ? -1 : 1)), m_ylim);
5154  calc_ticklabels (m_ztick, m_zticklabel, m_zscale.is ("log"),
5155  false, 2, m_zlim);
5156 
5157  xset (m_xlabel.handle_value (), "handlevisibility", "off");
5158  xset (m_ylabel.handle_value (), "handlevisibility", "off");
5159  xset (m_zlabel.handle_value (), "handlevisibility", "off");
5160  xset (m_title.handle_value (), "handlevisibility", "off");
5161 
5162  xset (m_xlabel.handle_value (), "horizontalalignment", "center");
5163  xset (m_xlabel.handle_value (), "horizontalalignmentmode", "auto");
5164  xset (m_ylabel.handle_value (), "horizontalalignment", "center");
5165  xset (m_ylabel.handle_value (), "horizontalalignmentmode", "auto");
5166  xset (m_zlabel.handle_value (), "horizontalalignment", "right");
5167  xset (m_zlabel.handle_value (), "horizontalalignmentmode", "auto");
5168  xset (m_title.handle_value (), "horizontalalignment", "center");
5169  xset (m_title.handle_value (), "horizontalalignmentmode", "auto");
5170 
5171  xset (m_xlabel.handle_value (), "verticalalignment", "top");
5172  xset (m_xlabel.handle_value (), "verticalalignmentmode", "auto");
5173  xset (m_ylabel.handle_value (), "verticalalignment", "bottom");
5174  xset (m_ylabel.handle_value (), "verticalalignmentmode", "auto");
5175  xset (m_title.handle_value (), "verticalalignment", "bottom");
5176  xset (m_title.handle_value (), "verticalalignmentmode", "auto");
5177 
5178  xset (m_ylabel.handle_value (), "rotation", 90.0);
5179  xset (m_ylabel.handle_value (), "rotationmode", "auto");
5180 
5181  xset (m_zlabel.handle_value (), "visible", "off");
5182 
5183  xset (m_xlabel.handle_value (), "clipping", "off");
5184  xset (m_ylabel.handle_value (), "clipping", "off");
5185  xset (m_zlabel.handle_value (), "clipping", "off");
5186  xset (m_title.handle_value (), "clipping", "off");
5187 
5188  xset (m_xlabel.handle_value (), "__autopos_tag__", "xlabel");
5189  xset (m_ylabel.handle_value (), "__autopos_tag__", "ylabel");
5190  xset (m_zlabel.handle_value (), "__autopos_tag__", "zlabel");
5191  xset (m_title.handle_value (), "__autopos_tag__", "title");
5192 
5193  double fs = m_labelfontsizemultiplier.double_value () *
5194  m_fontsize.double_value ();
5195  xset (m_xlabel.handle_value (), "fontsize", octave_value (fs));
5196  xset (m_ylabel.handle_value (), "fontsize", octave_value (fs));
5197  xset (m_zlabel.handle_value (), "fontsize", octave_value (fs));
5198  fs = m_titlefontsizemultiplier.double_value () * m_fontsize.double_value ();
5199  xset (m_title.handle_value (), "fontsize", octave_value (fs));
5200  xset (m_title.handle_value (), "fontweight", m_titlefontweight.get ());
5201 
5202  adopt (m_xlabel.handle_value ());
5203  adopt (m_ylabel.handle_value ());
5204  adopt (m_zlabel.handle_value ());
5205  adopt (m_title.handle_value ());
5206 
5207  Matrix tlooseinset = default_axes_position ();
5208  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
5209  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
5210  m_looseinset = tlooseinset;
5211 }
5212 
5213 /*
5214 ## Test validation of axes double properties range
5215 %!test
5216 %! hf = figure ("visible", "off");
5217 %! unwind_protect
5218 %! hax = axes ("parent", hf);
5219 %! try
5220 %! set (hax, "linewidth", -1);
5221 %! catch
5222 %! err = lasterr ();
5223 %! end_try_catch
5224 %! assert (err, 'set: "linewidth" must be greater than 0');
5225 %! try
5226 %! set (hax, "minorgridalpha", 1.5);
5227 %! catch
5228 %! err = lasterr ();
5229 %! end_try_catch
5230 %! assert (err, 'set: "minorgridalpha" must be less than or equal to 1');
5231 %! unwind_protect_cleanup
5232 %! delete (hf);
5233 %! end_unwind_protect
5234 */
5235 
5236 Matrix
5237 axes::properties::calc_tightbox (const Matrix& init_pos)
5238 {
5239  Matrix pos = init_pos;
5240 
5241  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5242 
5243  graphics_object go = gh_mgr.get_object (get_parent ());
5244 
5245  Matrix parent_bb = go.get_properties ().get_boundingbox (true);
5246 
5247  // FIXME: The layout should be clean at this stage and we should not have to
5248  // update ticks and labels positions here again. See bug #48718.
5249  update_ticklength ();
5250 
5251  Matrix ext = get_extent (true, true);
5252  ext(1) = parent_bb(3) - ext(1) - ext(3);
5253  ext(0)++;
5254  ext(1)++;
5255  ext = convert_position (ext, "pixels", get_units (),
5256  parent_bb.extract_n (0, 2, 1, 2));
5257  if (ext(0) < pos(0))
5258  {
5259  pos(2) += pos(0)-ext(0);
5260  pos(0) = ext(0);
5261  }
5262  if (ext(0)+ext(2) > pos(0)+pos(2))
5263  pos(2) = ext(0)+ext(2)-pos(0);
5264 
5265  if (ext(1) < pos(1))
5266  {
5267  pos(3) += pos(1)-ext(1);
5268  pos(1) = ext(1);
5269  }
5270  if (ext(1)+ext(3) > pos(1)+pos(3))
5271  pos(3) = ext(1)+ext(3)-pos(1);
5272 
5273  return pos;
5274 }
5275 
5276 void
5277 axes::properties::sync_positions (void)
5278 {
5279  // First part is equivalent to 'update_tightinset ()'
5280  if (m_positionconstraint.is ("innerposition"))
5281  update_position ();
5282  else
5283  update_outerposition ();
5284  caseless_str old_units = get_units ();
5285  set_units ("normalized");
5286  Matrix pos = m_position.get ().matrix_value ();
5287  Matrix outpos = m_outerposition.get ().matrix_value ();
5288  Matrix tightpos = calc_tightbox (pos);
5289  Matrix tinset (1, 4, 1.0);
5290  tinset(0) = pos(0)-tightpos(0);
5291  tinset(1) = pos(1)-tightpos(1);
5292  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
5293  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
5294  m_tightinset = tinset;
5295  set_units (old_units);
5296  update_transform ();
5297  if (m_positionconstraint.is ("innerposition"))
5298  update_position ();
5299  else
5300  update_outerposition ();
5301 }
5302 
5303 /*
5304 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
5305 %! hf = figure ("visible", "off");
5306 %! graphics_toolkit (hf, "qt");
5307 %! unwind_protect
5308 %! subplot (2,1,1); plot (rand (10,1)); subplot (2,1,2); plot (rand (10,1));
5309 %! hax = findall (gcf (), "type", "axes");
5310 %! positions = cell2mat (get (hax, "position"));
5311 %! outerpositions = cell2mat (get (hax, "outerposition"));
5312 %! looseinsets = cell2mat (get (hax, "looseinset"));
5313 %! tightinsets = cell2mat (get (hax, "tightinset"));
5314 %! subplot (2,1,1); plot (rand (10,1)); subplot (2,1,2); plot (rand (10,1));
5315 %! hax = findall (gcf (), "type", "axes");
5316 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
5317 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
5318 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
5319 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
5320 %! unwind_protect_cleanup
5321 %! close (hf);
5322 %! end_unwind_protect
5323 
5324 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
5325 %! hf = figure ("visible", "off");
5326 %! graphics_toolkit (hf, "qt");
5327 %! fpos = get (hf, "position");
5328 %! unwind_protect
5329 %! plot (rand (3));
5330 %! position = get (gca, "position");
5331 %! outerposition = get (gca, "outerposition");
5332 %! looseinset = get (gca, "looseinset");
5333 %! tightinset = get (gca, "tightinset");
5334 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
5335 %! set (hf, "position", fpos);
5336 %! assert (get (gca, "outerposition"), outerposition, 0.001);
5337 %! assert (get (gca, "position"), position, 0.001);
5338 %! assert (get (gca, "looseinset"), looseinset, 0.001);
5339 %! assert (get (gca, "tightinset"), tightinset, 0.001);
5340 %! unwind_protect_cleanup
5341 %! close (hf);
5342 %! end_unwind_protect
5343 
5344 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
5345 %! hf = figure ("visible", "off");
5346 %! graphics_toolkit (hf, "qt");
5347 %! fpos = get (hf, "position");
5348 %! set (gca, "positionconstraint", "innerposition");
5349 %! unwind_protect
5350 %! plot (rand (3));
5351 %! position = get (gca, "position");
5352 %! outerposition = get (gca, "outerposition");
5353 %! looseinset = get (gca, "looseinset");
5354 %! tightinset = get (gca, "tightinset");
5355 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
5356 %! set (hf, "position", fpos);
5357 %! assert (get (gca, "position"), position, 0.001);
5358 %! assert (get (gca, "outerposition"), outerposition, 0.001);
5359 %! assert (get (gca, "looseinset"), looseinset, 0.001);
5360 %! assert (get (gca, "tightinset"), tightinset, 0.001);
5361 %! unwind_protect_cleanup
5362 %! close (hf);
5363 %! end_unwind_protect
5364 */
5365 
5366 void
5367 axes::properties::set_text_child (handle_property& hp,
5368  const std::string& who,
5369  const octave_value& v)
5370 {
5371  if (v.is_string ())
5372  {
5373  xset (hp.handle_value (), "string", v);
5374  return;
5375  }
5376 
5377  graphics_handle val;
5378 
5379  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5380 
5381  graphics_object go = gh_mgr.get_object (gh_mgr.lookup (v));
5382 
5383  if (go.isa ("text"))
5384  val = octave::reparent (v, "set", who, m___myhandle__, false);
5385  else
5386  {
5387  std::string cname = v.class_name ();
5388 
5389  error ("set: expecting text graphics object or character string for %s property, found %s",
5390  who.c_str (), cname.c_str ());
5391  }
5392 
5393  xset (val, "handlevisibility", "off");
5394 
5395  gh_mgr.free (hp.handle_value ());
5396 
5397  hp = val;
5398 
5399  adopt (hp.handle_value ());
5400 }
5401 
5402 void
5403 axes::properties::set_xlabel (const octave_value& v)
5404 {
5405  set_text_child (m_xlabel, "xlabel", v);
5406  xset (m_xlabel.handle_value (), "positionmode", "auto");
5407  xset (m_xlabel.handle_value (), "rotationmode", "auto");
5408  xset (m_xlabel.handle_value (), "horizontalalignmentmode", "auto");
5409  xset (m_xlabel.handle_value (), "verticalalignmentmode", "auto");
5410  xset (m_xlabel.handle_value (), "clipping", "off");
5411  xset (m_xlabel.handle_value (), "color", get_xcolor ());
5412  xset (m_xlabel.handle_value (), "__autopos_tag__", "xlabel");
5413  update_xlabel_position ();
5414 }
5415 
5416 void
5417 axes::properties::set_ylabel (const octave_value& v)
5418 {
5419  set_text_child (m_ylabel, "ylabel", v);
5420  xset (m_ylabel.handle_value (), "positionmode", "auto");
5421  xset (m_ylabel.handle_value (), "rotationmode", "auto");
5422  xset (m_ylabel.handle_value (), "horizontalalignmentmode", "auto");
5423  xset (m_ylabel.handle_value (), "verticalalignmentmode", "auto");
5424  xset (m_ylabel.handle_value (), "clipping", "off");
5425  xset (m_ylabel.handle_value (), "color", get_ycolor ());
5426  xset (m_ylabel.handle_value (), "__autopos_tag__", "ylabel");
5427  update_ylabel_position ();
5428 }
5429 
5430 void
5431 axes::properties::set_zlabel (const octave_value& v)
5432 {
5433  set_text_child (m_zlabel, "zlabel", v);
5434  xset (m_zlabel.handle_value (), "positionmode", "auto");
5435  xset (m_zlabel.handle_value (), "rotationmode", "auto");
5436  xset (m_zlabel.handle_value (), "horizontalalignmentmode", "auto");
5437  xset (m_zlabel.handle_value (), "verticalalignmentmode", "auto");
5438  xset (m_zlabel.handle_value (), "clipping", "off");
5439  xset (m_zlabel.handle_value (), "color", get_zcolor ());
5440  xset (m_zlabel.handle_value (), "__autopos_tag__", "zlabel");
5441  update_zlabel_position ();
5442 }
5443 
5444 void
5445 axes::properties::set_title (const octave_value& v)
5446 {
5447  set_text_child (m_title, "title", v);
5448  xset (m_title.handle_value (), "positionmode", "auto");
5449  xset (m_title.handle_value (), "horizontalalignment", "center");
5450  xset (m_title.handle_value (), "horizontalalignmentmode", "auto");
5451  xset (m_title.handle_value (), "verticalalignment", "bottom");
5452  xset (m_title.handle_value (), "verticalalignmentmode", "auto");
5453  xset (m_title.handle_value (), "clipping", "off");
5454  xset (m_title.handle_value (), "__autopos_tag__", "title");
5455  update_title_position ();
5456 }
5457 
5458 void
5459 axes::properties::set_defaults (base_graphics_object& bgo,
5460  const std::string& mode)
5461 {
5462  // FIXME: Should this have all properties in it?
5463  // Including ones we do don't implement?
5464 
5465  // FIXME: This function is probably never called without mode == "reset".
5466  // Verify this is the case with error_unless() (1/6/2017).
5467  // If there are reports of problems then figure out what code is
5468  // calling it with the mode set to something else.
5469  error_unless (mode == "reset");
5470 
5471  Matrix tlim (1, 2, 0.0);
5472  tlim(1) = 1;
5473  m_alim = tlim;
5474  m_xlim = tlim;
5475  m_ylim = tlim;
5476  m_zlim = tlim;
5477 
5478  m_alimmode = "auto";
5479  m_climmode = "auto";
5480  m_xlimmode = "auto";
5481  m_ylimmode = "auto";
5482  m_zlimmode = "auto";
5483 
5484  m_ambientlightcolor = Matrix (1, 3, 1.0);
5485 
5486  m_box = "off";
5487  m_boxstyle = "back";
5488 
5489  // Note: camera properties (not mode) will be set in update_transform
5490  m_camerapositionmode = "auto";
5491  m_cameratargetmode = "auto";
5492  m_cameraupvectormode = "auto";
5493  m_cameraviewanglemode = "auto";
5494 
5495  Matrix cl (1, 2, 0.0);
5496  cl(1) = 1;
5497  m_clim = cl;
5498 
5499  m_clippingstyle = "3dbox";
5500 
5501  m_color = color_values ("white");
5502  m_colororder = default_colororder ();
5503  m_colororderindex = 1.0;
5504 
5505  // Note: dataspectratio (not mode) will be set through update_aspectratios
5506  m_dataaspectratiomode = "auto";
5507 
5508  m_fontangle = "normal";
5509  m_fontname = OCTAVE_DEFAULT_FONTNAME;
5510  m_fontsize = 10;
5511  m_fontunits = "points";
5512  m_fontsmoothing = "on";
5513  m_fontweight = "normal";
5514 
5515  m_gridalpha = 0.15;
5516  m_gridalphamode = "auto";
5517  m_gridcolor = color_values (0.15, 0.15, 0.15);
5518  m_gridcolormode = "auto";
5519  m_gridlinestyle = "-";
5520 
5521  m_labelfontsizemultiplier = 1.1;
5522 
5523  m_layer = "bottom";
5524 
5525  m_linestyleorder = "-";
5526  m_linestyleorderindex = 1.0;
5527 
5528  m_linewidth = 0.5;
5529 
5530  m_minorgridalpha = 0.25;
5531  m_minorgridalphamode = "auto";
5532  m_minorgridcolor = color_values (0.1, 0.1, 0.1);
5533  m_minorgridcolormode = "auto";
5534  m_minorgridlinestyle = ":";
5535 
5536  m_nextplot = "replace";
5537 
5538  // Note: plotboxaspectratio will be set through update_aspectratios
5539  m_plotboxaspectratiomode = "auto";
5540  m_projection = "orthographic";
5541 
5542  m_sortmethod = "depth";
5543 
5544  m_tickdir = "in";
5545  m_tickdirmode = "auto";
5546  m_ticklabelinterpreter = "tex";
5547  m_ticklength = default_axes_ticklength ();
5548 
5549  m_tightinset = Matrix (1, 4, 0.0);
5550 
5551  m_titlefontsizemultiplier = 1.1;
5552  m_titlefontweight = "bold";
5553 
5554  Matrix tview (1, 2, 0.0);
5555  tview(1) = 90;
5556  m_view = tview;
5557 
5558  m_xaxislocation = "bottom";
5559 
5560  m_xcolor = color_values (0.15, 0.15, 0.15);
5561  m_xcolormode = "auto";
5562  m_xdir = "normal";
5563  m_xgrid = "off";
5564  m_xminorgrid = "off";
5565  m_xminortick = "off";
5566  m_xscale = "linear";
5567  m_xtick = Matrix ();
5568  m_xticklabel = "";
5569  m_xticklabelmode = "auto";
5570  m_xticklabelrotation = 0.0;
5571  m_xtickmode = "auto";
5572 
5573  m_yaxislocation = "left";
5574 
5575  m_ycolor = color_values (0.15, 0.15, 0.15);
5576  m_ycolormode = "auto";
5577  m_ydir = "normal";
5578  m_ygrid = "off";
5579  m_yminorgrid = "off";
5580  m_yminortick = "off";
5581  m_yscale = "linear";
5582  m_ytick = Matrix ();
5583  m_yticklabel = "";
5584  m_yticklabelmode = "auto";
5585  m_yticklabelrotation = 0.0;
5586  m_ytickmode = "auto";
5587 
5588  m_zcolor = color_values (0.15, 0.15, 0.15);
5589  m_zcolormode = "auto";
5590  m_zdir = "normal";
5591  m_zgrid = "off";
5592  m_zminorgrid = "off";
5593  m_zminortick = "off";
5594  m_zscale = "linear";
5595  m_ztick = Matrix ();
5596  m_zticklabel = "";
5597  m_zticklabelmode = "auto";
5598  m_zticklabelrotation = 0.0;
5599  m_ztickmode = "auto";
5600 
5601  m_sx = "linear";
5602  m_sy = "linear";
5603  m_sz = "linear";
5604 
5605  m_visible = "on";
5606 
5607  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5608 
5609  graphics_object go = gh_mgr.get_object (m_xlabel.handle_value ());
5610  go.reset_default_properties ();
5611  go = gh_mgr.get_object (m_ylabel.handle_value ());
5612  go.reset_default_properties ();
5613  go = gh_mgr.get_object (m_zlabel.handle_value ());
5614  go.reset_default_properties ();
5615  go = gh_mgr.get_object (m_title.handle_value ());
5616  go.reset_default_properties ();
5617 
5618  xset (m_xlabel.handle_value (), "handlevisibility", "off");
5619  xset (m_ylabel.handle_value (), "handlevisibility", "off");
5620  xset (m_zlabel.handle_value (), "handlevisibility", "off");
5621  xset (m_title.handle_value (), "handlevisibility", "off");
5622 
5623  xset (m_xlabel.handle_value (), "horizontalalignment", "center");
5624  xset (m_xlabel.handle_value (), "horizontalalignmentmode", "auto");
5625  xset (m_ylabel.handle_value (), "horizontalalignment", "center");
5626  xset (m_ylabel.handle_value (), "horizontalalignmentmode", "auto");
5627  xset (m_zlabel.handle_value (), "horizontalalignment", "right");
5628  xset (m_zlabel.handle_value (), "horizontalalignmentmode", "auto");
5629  xset (m_title.handle_value (), "horizontalalignment", "center");
5630  xset (m_title.handle_value (), "horizontalalignmentmode", "auto");
5631 
5632  xset (m_xlabel.handle_value (), "verticalalignment", "top");
5633  xset (m_xlabel.handle_value (), "verticalalignmentmode", "auto");
5634  xset (m_ylabel.handle_value (), "verticalalignment", "bottom");
5635  xset (m_ylabel.handle_value (), "verticalalignmentmode", "auto");
5636  xset (m_title.handle_value (), "verticalalignment", "bottom");
5637  xset (m_title.handle_value (), "verticalalignmentmode", "auto");
5638 
5639  xset (m_ylabel.handle_value (), "rotation", 90.0);
5640  xset (m_ylabel.handle_value (), "rotationmode", "auto");
5641 
5642  xset (m_zlabel.handle_value (), "visible", "off");
5643 
5644  xset (m_xlabel.handle_value (), "clipping", "off");
5645  xset (m_ylabel.handle_value (), "clipping", "off");
5646  xset (m_zlabel.handle_value (), "clipping", "off");
5647  xset (m_title.handle_value (), "clipping", "off");
5648 
5649  xset (m_xlabel.handle_value (), "__autopos_tag__", "xlabel");
5650  xset (m_ylabel.handle_value (), "__autopos_tag__", "ylabel");
5651  xset (m_zlabel.handle_value (), "__autopos_tag__", "zlabel");
5652  xset (m_title.handle_value (), "__autopos_tag__", "title");
5653 
5654  double fs;
5655  fs = m_labelfontsizemultiplier.double_value () * m_fontsize.double_value ();
5656  xset (m_xlabel.handle_value (), "fontsize", octave_value (fs));
5657  xset (m_ylabel.handle_value (), "fontsize", octave_value (fs));
5658  xset (m_zlabel.handle_value (), "fontsize", octave_value (fs));
5659  fs = m_titlefontsizemultiplier.double_value () * m_fontsize.double_value ();
5660  xset (m_title.handle_value (), "fontsize", octave_value (fs));
5661  xset (m_title.handle_value (), "fontweight", m_titlefontweight.get ());
5662 
5663  update_transform ();
5664  sync_positions ();
5665  override_defaults (bgo);
5666 }
5667 
5669 axes::properties::get_colormap (void) const
5670 {
5671  if (m___colormap__.get ().isempty ())
5672  {
5673  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5674 
5675  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
5676  graphics_object go_f (go.get_ancestor ("figure"));
5677  figure::properties& figure_props
5678  = reinterpret_cast<figure::properties&> (go_f.get_properties ());
5679  return figure_props.get_colormap ();
5680  }
5681 
5682  return get___colormap__ ();
5683 }
5684 
5685 void
5686 axes::properties::delete_text_child (handle_property& hp, bool from_root)
5687 {
5688  graphics_handle h = hp.handle_value ();
5689 
5690  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5691 
5692  if (h.ok ())
5693  {
5694  graphics_object go = gh_mgr.get_object (h);
5695 
5696  if (go.valid_object ())
5697  gh_mgr.free (h, from_root);
5698  }
5699 
5700  // FIXME: is it necessary to check whether the axes object is
5701  // being deleted now? I think this function is only called when an
5702  // individual child object is delete and not when the parent axes
5703  // object is deleted.
5704 
5705  if (! is_beingdeleted ())
5706  {
5707  hp = gh_mgr.make_graphics_handle ("text", m___myhandle__, false, false);
5708 
5709  xset (hp.handle_value (), "handlevisibility", "off");
5710 
5711  adopt (hp.handle_value ());
5712  }
5713 }
5714 
5715 void
5716 axes::properties::remove_child (const graphics_handle& h, bool from_root)
5717 {
5718  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5719 
5720  graphics_object go = gh_mgr.get_object (h);
5721 
5722  if (m_xlabel.handle_value ().ok () && h == m_xlabel.handle_value ())
5723  {
5724  delete_text_child (m_xlabel, from_root);
5725  update_xlabel_position ();
5726  }
5727  else if (m_ylabel.handle_value ().ok () && h == m_ylabel.handle_value ())
5728  {
5729  delete_text_child (m_ylabel, from_root);
5730  update_ylabel_position ();
5731  }
5732  else if (m_zlabel.handle_value ().ok () && h == m_zlabel.handle_value ())
5733  {
5734  delete_text_child (m_zlabel, from_root);
5735  update_zlabel_position ();
5736  }
5737  else if (m_title.handle_value ().ok () && h == m_title.handle_value ())
5738  {
5739  delete_text_child (m_title, from_root);
5740  update_title_position ();
5741  }
5742  else if (get_num_lights () > 0 && go.isa ("light")
5743  && go.get_properties ().is_visible ())
5744  decrease_num_lights ();
5745 
5746  if (go.valid_object ())
5747  base_properties::remove_child (h, from_root);
5748 
5749 }
5750 
5751 void
5753 {
5754  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
5755 
5756  graphics_object go (gh_mgr.get_object (h));
5757 
5758  if (go.isa ("light") && go.get_properties ().is_visible ())
5759  increase_num_lights ();
5760 
5762 
5763  // FIXME: For performance reasons, we would like to call
5764  // update_axis_limits ("xlim", h);
5765  // which updates the limits based ONLY on the new data from h.
5766  // But this isn't working properly at the moment, so we
5767  // call the other form which invokes a full tree traversal of all
5768  // of the axes children.
5769  if (xlimmode_is ("auto"))
5770  update_axis_limits ("xlim");
5771 
5772  if (ylimmode_is ("auto"))
5773  update_axis_limits ("ylim");
5774 
5775  if (zlimmode_is ("auto"))
5776  update_axis_limits ("zlim");
5777 
5778  if (climmode_is ("auto"))
5779  update_axis_limits ("clim");
5780 
5781  if (climmode_is ("auto"))
5782  update_axis_limits ("alim");
5783 }
5784 
5785 inline Matrix
5787 {
5788  Matrix m (4, 4, 0.0);
5789 
5790  for (int i = 0; i < 4; i++)
5791  m(i, i) = 1;
5792 
5793  return m;
5794 }
5795 
5796 inline ColumnVector
5798 {
5799  ColumnVector v (4, 0.0);
5800 
5801  v(3) = 1;
5802 
5803  return v;
5804 }
5805 
5806 inline ColumnVector
5807 xform_vector (double x, double y, double z)
5808 {
5809  ColumnVector v (4, 1.0);
5810 
5811  v(0) = x;
5812  v(1) = y;
5813  v(2) = z;
5814 
5815  return v;
5816 }
5817 
5818 inline ColumnVector
5819 transform (const Matrix& m, double x, double y, double z)
5820 {
5821  return (m * xform_vector (x, y, z));
5822 }
5823 
5824 inline Matrix
5825 xform_scale (double x, double y, double z)
5826 {
5827  Matrix m (4, 4, 0.0);
5828 
5829  m(0, 0) = x;
5830  m(1, 1) = y;
5831  m(2, 2) = z;
5832  m(3, 3) = 1;
5833 
5834  return m;
5835 }
5836 
5837 inline Matrix
5838 xform_translate (double x, double y, double z)
5839 {
5840  Matrix m = xform_matrix ();
5841 
5842  m(0, 3) = x;
5843  m(1, 3) = y;
5844  m(2, 3) = z;
5845  m(3, 3) = 1;
5846 
5847  return m;
5848 }
5849 
5850 inline void
5851 scale (Matrix& m, double x, double y, double z)
5852 {
5853  m = m * xform_scale (x, y, z);
5854 }
5855 
5856 inline void
5857 translate (Matrix& m, double x, double y, double z)
5858 {
5859  m = m * xform_translate (x, y, z);
5860 }
5861 
5862 inline void
5864 {
5865  v = m * v;
5866 }
5867 
5868 inline void
5869 scale (ColumnVector& v, double x, double y, double z)
5870 {
5871  v(0) *= x;
5872  v(1) *= y;
5873  v(2) *= z;
5874 }
5875 
5876 inline void
5877 translate (ColumnVector& v, double x, double y, double z)
5878 {
5879  v(0) += x;
5880  v(1) += y;
5881  v(2) += z;
5882 }
5883 
5884 inline void
5886 {
5887  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5888  scale (v, fact, fact, fact);
5889 }
5890 
5891 inline double
5892 dot (const ColumnVector& v1, const ColumnVector& v2)
5893 {
5894  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5895 }
5896 
5897 inline double
5898 norm (const ColumnVector& v)
5899 {
5900  return sqrt (dot (v, v));
5901 }
5902 
5903 inline ColumnVector
5904 cross (const ColumnVector& v1, const ColumnVector& v2)
5905 {
5907 
5908  r(0) = v1(1)*v2(2) - v1(2)*v2(1);
5909  r(1) = v1(2)*v2(0) - v1(0)*v2(2);
5910  r(2) = v1(0)*v2(1) - v1(1)*v2(0);
5911 
5912  return r;
5913 }
5914 
5915 inline Matrix
5917 {
5918  static double data[32] =
5919  {
5920  0, 0, 0, 1,
5921  1, 0, 0, 1,
5922  0, 1, 0, 1,
5923  0, 0, 1, 1,
5924  1, 1, 0, 1,
5925  1, 0, 1, 1,
5926  0, 1, 1, 1,
5927  1, 1, 1, 1
5928  };
5929  Matrix m (4, 8);
5930 
5931  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5932 
5933  return m;
5934 }
5935 
5936 inline ColumnVector
5938 {
5939  ColumnVector retval (4, 1.0);
5940 
5941  memcpy (retval.fortran_vec (), m.data (), sizeof (double)*3);
5942 
5943  return retval;
5944 }
5945 
5946 inline RowVector
5948 {
5949  return v.extract_n (0, 3).transpose ();
5950 }
5951 
5952 void
5953 axes::properties::update_camera (void)
5954 {
5955  double xd = (xdir_is ("normal") ? 1 : -1);
5956  double yd = (ydir_is ("normal") ? 1 : -1);
5957  double zd = (zdir_is ("normal") ? 1 : -1);
5958 
5959  Matrix xlimits = m_sx.scale (get_xlim ().matrix_value ());
5960  Matrix ylimits = m_sy.scale (get_ylim ().matrix_value ());
5961  Matrix zlimits = m_sz.scale (get_zlim ().matrix_value ());
5962 
5963  double xo = xlimits(xd > 0 ? 0 : 1);
5964  double yo = ylimits(yd > 0 ? 0 : 1);
5965  double zo = zlimits(zd > 0 ? 0 : 1);
5966 
5967  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5968 
5969  bool autocam = (camerapositionmode_is ("auto")
5970  && cameratargetmode_is ("auto")
5971  && cameraupvectormode_is ("auto")
5972  && cameraviewanglemode_is ("auto"));
5973  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5974  && plotboxaspectratiomode_is ("auto"));
5975 
5976  ColumnVector c_eye (xform_vector ());
5977  ColumnVector c_center (xform_vector ());
5978  ColumnVector c_upv (xform_vector ());
5979 
5980  if (cameratargetmode_is ("auto"))
5981  {
5982  c_center(0) = (xlimits(0) + xlimits(1)) / 2;
5983  c_center(1) = (ylimits(0) + ylimits(1)) / 2;
5984  c_center(2) = (zlimits(0) + zlimits(1)) / 2;
5985 
5986  m_cameratarget = xform2cam (c_center);
5987  }
5988  else
5989  c_center = cam2xform (get_cameratarget ().matrix_value ());
5990 
5991  if (camerapositionmode_is ("auto"))
5992  {
5993  Matrix tview = get_view ().matrix_value ();
5994  double az = tview(0);
5995  double el = tview(1);
5996  double d = 5 * sqrt (pb(0)*pb(0) + pb(1)*pb(1) + pb(2)*pb(2));
5997 
5998  if (el == 90 || el == -90)
5999  c_eye(2) = d*octave::math::signum (el);
6000  else
6001  {
6002  az *= M_PI/180.0;
6003  el *= M_PI/180.0;
6004  c_eye(0) = d * cos (el) * sin (az);
6005  c_eye(1) = -d* cos (el) * cos (az);
6006  c_eye(2) = d * sin (el);
6007  }
6008  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
6009  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
6010  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
6011 
6012  m_cameraposition = xform2cam (c_eye);
6013  }
6014  else
6015  c_eye = cam2xform (get_cameraposition ().matrix_value ());
6016 
6017  if (cameraupvectormode_is ("auto"))
6018  {
6019  Matrix tview = get_view ().matrix_value ();
6020  double az = tview(0);
6021  double el = tview(1);
6022 
6023  if (el == 90 || el == -90)
6024  {
6025  c_upv(0) = -octave::math::signum (el)
6026  * sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
6027  c_upv(1) = octave::math::signum (el)
6028  * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
6029  }
6030  else
6031  c_upv(2) = 1;
6032 
6033  m_cameraupvector = xform2cam (c_upv);
6034  }
6035  else
6036  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
6037 
6038  Matrix x_view = xform_matrix ();
6039  Matrix x_projection = xform_matrix ();
6040  Matrix x_viewport = xform_matrix ();
6041  Matrix x_normrender;
6042  Matrix x_pre = xform_matrix ();
6043 
6044  m_x_render = xform_matrix ();
6045  m_x_render_inv = xform_matrix ();
6046 
6047  scale (x_pre, pb(0), pb(1), pb(2));
6048  translate (x_pre, -0.5, -0.5, -0.5);
6049  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
6050  zd/(zlimits(1)-zlimits(0)));
6051  translate (x_pre, -xo, -yo, -zo);
6052 
6053  xform (c_eye, x_pre);
6054  xform (c_center, x_pre);
6055  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
6056  pb(2)/(zlimits(1)-zlimits(0)));
6057  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
6058 
6059  ColumnVector F (c_center), f (F), UP (c_upv);
6060  normalize (f);
6061  normalize (UP);
6062 
6063  if (std::abs (dot (f, UP)) > 1e-15)
6064  {
6065  double fa = 1 / sqrt (1 - f(2)*f(2));
6066  scale (UP, fa, fa, fa);
6067  }
6068 
6069  ColumnVector s = cross (f, UP);
6070  ColumnVector u = cross (s, f);
6071 
6072  scale (x_view, 1, 1, -1);
6073  Matrix l = xform_matrix ();
6074  l(0, 0) = s(0); l(0, 1) = s(1); l(0, 2) = s(2);
6075  l(1, 0) = u(0); l(1, 1) = u(1); l(1, 2) = u(2);
6076  l(2, 0) = -f(0); l(2, 1) = -f(1); l(2, 2) = -f(2);
6077  x_view = x_view * l;
6078  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
6079  scale (x_view, pb(0), pb(1), pb(2));
6080  translate (x_view, -0.5, -0.5, -0.5);
6081 
6082  Matrix x_cube = x_view * unit_cube ();
6083  ColumnVector cmin = x_cube.row_min ();
6084  ColumnVector cmax = x_cube.row_max ();
6085  double xM = cmax(0) - cmin(0);
6086  double yM = cmax(1) - cmin(1);
6087 
6088  Matrix bb = get_boundingbox (true);
6089 
6090  double v_angle;
6091 
6092  if (cameraviewanglemode_is ("auto"))
6093  {
6094  double af;
6095 
6096  // FIXME: was this really needed? When compared to Matlab, it
6097  // does not seem to be required. Need investigation with concrete
6098  // graphics toolkit to see results visually.
6099  if (false && dowarp)
6100  af = (1.0 / (xM > yM ? xM : yM));
6101  else
6102  {
6103  if ((bb(2)/bb(3)) > (xM/yM))
6104  af = 1.0 / yM;
6105  else
6106  af = 1.0 / xM;
6107  }
6108  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
6109 
6110  m_cameraviewangle = v_angle;
6111  }
6112  else
6113  v_angle = get_cameraviewangle ();
6114 
6115  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
6116  scale (x_projection, pf, pf, 1);
6117 
6118  if (dowarp)
6119  {
6120  xM *= pf;
6121  yM *= pf;
6122  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
6123  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
6124  }
6125  else
6126  {
6127  double pix = 1;
6128  if (autocam)
6129  {
6130  if ((bb(2)/bb(3)) > (xM/yM))
6131  pix = bb(3);
6132  else
6133  pix = bb(2);
6134  }
6135  else
6136  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
6137  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
6138  scale (x_viewport, pix, -pix, 1);
6139  }
6140 
6141  x_normrender = x_viewport * x_projection * x_view;
6142 
6143  x_cube = x_normrender * unit_cube ();
6144  cmin = x_cube.row_min ();
6145  cmax = x_cube.row_max ();
6146  m_x_zlim.resize (1, 2);
6147  m_x_zlim(0) = cmin(2);
6148  m_x_zlim(1) = cmax(2);
6149 
6150  m_x_render = x_normrender;
6151  scale (m_x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
6152  zd/(zlimits(1)-zlimits(0)));
6153  translate (m_x_render, -xo, -yo, -zo);
6154 
6155  m_x_render_inv = m_x_render.inverse ();
6156 
6157  // Note: these matrices are a slight modified version of the regular matrices,
6158  // more suited for OpenGL rendering (m_x_gl_mat1 => light => m_x_gl_mat2)
6159  m_x_gl_mat1 = x_view;
6160  scale (m_x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
6161  zd/(zlimits(1)-zlimits(0)));
6162  translate (m_x_gl_mat1, -xo, -yo, -zo);
6163  m_x_gl_mat2 = x_viewport * x_projection;
6164 }
6165 
6166 static bool updating_axes_layout = false;
6167 
6168 void
6169 axes::properties::update_axes_layout (void)
6170 {
6172  return;
6173 
6174  graphics_xform xform = get_transform ();
6175 
6176  double xd = (xdir_is ("normal") ? 1 : -1);
6177  double yd = (ydir_is ("normal") ? 1 : -1);
6178  double zd = (zdir_is ("normal") ? 1 : -1);
6179 
6180  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
6181  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
6182  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
6183 
6184  double x_min, x_max, y_min, y_max, z_min, z_max;
6185  x_min = xlims(0), x_max = xlims(1);
6186  y_min = ylims(0), y_max = ylims(1);
6187  z_min = zlims(0), z_max = zlims(1);
6188 
6189  ColumnVector p1, p2, dir (3);
6190 
6191  m_xstate = m_ystate = m_zstate = AXE_ANY_DIR;
6192 
6193  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
6194  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
6195  dir(0) = octave::math::round (p2(0) - p1(0));
6196  dir(1) = octave::math::round (p2(1) - p1(1));
6197  dir(2) = (p2(2) - p1(2));
6198  if (dir(0) == 0 && dir(1) == 0)
6199  m_xstate = AXE_DEPTH_DIR;
6200  else if (dir(2) == 0)
6201  {
6202  if (dir(0) == 0)
6203  m_xstate = AXE_VERT_DIR;
6204  else if (dir(1) == 0)
6205  m_xstate = AXE_HORZ_DIR;
6206  }
6207 
6208  if (dir(2) == 0)
6209  {
6210  if (dir(1) == 0)
6211  m_xPlane = (dir(0) > 0 ? x_max : x_min);
6212  else
6213  m_xPlane = (dir(1) < 0 ? x_max : x_min);
6214  }
6215  else
6216  m_xPlane = (dir(2) < 0 ? x_min : x_max);
6217 
6218  m_xPlaneN = (m_xPlane == x_min ? x_max : x_min);
6219  m_fx = (x_max - x_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
6220 
6221  p1 = xform.transform ((x_min + x_max)/2, y_min, (z_min + z_max)/2, false);
6222  p2 = xform.transform ((x_min + x_max)/2, y_max, (z_min + z_max)/2, false);
6223  dir(0) = octave::math::round (p2(0) - p1(0));
6224  dir(1) = octave::math::round (p2(1) - p1(1));
6225  dir(2) = (p2(2) - p1(2));
6226  if (dir(0) == 0 && dir(1) == 0)
6227  m_ystate = AXE_DEPTH_DIR;
6228  else if (dir(2) == 0)
6229  {
6230  if (dir(0) == 0)
6231  m_ystate = AXE_VERT_DIR;
6232  else if (dir(1) == 0)
6233  m_ystate = AXE_HORZ_DIR;
6234  }
6235 
6236  if (dir(2) == 0)
6237  {
6238  if (dir(1) == 0)
6239  m_yPlane = (dir(0) > 0 ? y_max : y_min);
6240  else
6241  m_yPlane = (dir(1) < 0 ? y_max : y_min);
6242  }
6243  else
6244  m_yPlane = (dir(2) < 0 ? y_min : y_max);
6245 
6246  m_yPlaneN = (m_yPlane == y_min ? y_max : y_min);
6247  m_fy = (y_max - y_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
6248 
6249  p1 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_min, false);
6250  p2 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_max, false);
6251  dir(0) = octave::math::round (p2(0) - p1(0));
6252  dir(1) = octave::math::round (p2(1) - p1(1));
6253  dir(2) = (p2(2) - p1(2));
6254  if (dir(0) == 0 && dir(1) == 0)
6255  m_zstate = AXE_DEPTH_DIR;
6256  else if (dir(2) == 0)
6257  {
6258  if (dir(0) == 0)
6259  m_zstate = AXE_VERT_DIR;
6260  else if (dir(1) == 0)
6261  m_zstate = AXE_HORZ_DIR;
6262  }
6263 
6264  if (dir(2) == 0)
6265  {
6266  if (dir(1) == 0)
6267  m_zPlane = (dir(0) > 0 ? z_min : z_max);
6268  else
6269  m_zPlane = (dir(1) < 0 ? z_min : z_max);
6270  }
6271  else
6272  m_zPlane = (dir(2) < 0 ? z_min : z_max);
6273 
6274  m_zPlaneN = (m_zPlane == z_min ? z_max : z_min);
6275  m_fz = (z_max - z_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
6276 
6277  octave::unwind_protect_var<bool> restore_var (updating_axes_layout, true);
6278 
6279  m_xySym = (xd*yd*(m_xPlane-m_xPlaneN)*(m_yPlane-m_yPlaneN) > 0);
6280  m_zSign = (zd*(m_zPlane-m_zPlaneN) <= 0);
6281  m_xyzSym = (m_zSign ? m_xySym : ! m_xySym);
6282  m_xpTick = (m_zSign ? m_xPlaneN : m_xPlane);
6283  m_ypTick = (m_zSign ? m_yPlaneN : m_yPlane);
6284  m_zpTick = (m_zSign ? m_zPlane : m_zPlaneN);
6285  m_xpTickN = (m_zSign ? m_xPlane : m_xPlaneN);
6286  m_ypTickN = (m_zSign ? m_yPlane : m_yPlaneN);
6287  m_zpTickN = (m_zSign ? m_zPlaneN : m_zPlane);
6288 
6289  // 2D mode
6290  m_x2Dtop = false;
6291  m_y2Dright = false;
6292  m_layer2Dtop = false;
6293  if (m_xstate == AXE_HORZ_DIR && m_ystate == AXE_VERT_DIR)
6294  {
6295  Matrix ylimits = get_ylim ().matrix_value ();
6296  if (xaxislocation_is ("top")
6297  || (yscale_is ("log") && xaxislocation_is ("origin")
6298  && (ylimits(1) < 0.)))
6299  {
6300  std::swap (m_yPlane, m_yPlaneN);
6301  m_x2Dtop = true;
6302  }
6303  m_ypTick = m_yPlaneN;
6304  m_ypTickN = m_yPlane;
6305  Matrix xlimits = get_xlim ().matrix_value ();
6306  if (yaxislocation_is ("right")
6307  || (xscale_is ("log") && yaxislocation_is ("origin")
6308  && (xlimits(1) < 0.)))
6309  {
6310  std::swap (m_xPlane, m_xPlaneN);
6311  m_y2Dright = true;
6312  }
6313  m_xpTick = m_xPlaneN;
6314  m_xpTickN = m_xPlane;
6315  if (layer_is ("top"))
6316  {
6317  m_zpTick = m_zPlaneN;
6318  m_layer2Dtop = true;
6319  }
6320  else
6321  m_zpTick = m_zPlane;
6322  }
6323 
6324  Matrix viewmat = get_view ().matrix_value ();
6325  m_nearhoriz = std::abs (viewmat(1)) <= 5;
6326  m_is2D = viewmat(1) == 90;
6327 
6328  update_ticklength ();
6329 }
6330 
6331 void
6332 axes::properties::update_ticklength (void)
6333 {
6334  bool mode2D = (((m_xstate > AXE_DEPTH_DIR ? 1 : 0) +
6335  (m_ystate > AXE_DEPTH_DIR ? 1 : 0) +
6336  (m_zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
6337 
6338  if (tickdirmode_is ("auto"))
6339  m_tickdir.set (mode2D ? "in" : "out", true);
6340 
6341  double ticksign = (tickdir_is ("in") ? -1 : 1);
6342 
6343  Matrix bbox = get_boundingbox (true);
6344  Matrix ticklen = get_ticklength ().matrix_value ();
6345  ticklen(0) *= std::max (bbox(2), bbox(3));
6346  // FIXME: This algorithm is not Matlab-compatible. See bug #55483.
6347  // Scale the results of Octave's algorithm for better visuals.
6348  ticklen(1) *= (0.76 * std::max (bbox(2), bbox(3)));
6349 
6350  m_xticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6351  m_yticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6352  m_zticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6353 
6354  double offset = get___fontsize_points__ () / 2;
6355 
6356  m_xtickoffset = (mode2D ? std::max (0., m_xticklen) : std::abs (m_xticklen)) +
6357  (m_xstate == AXE_HORZ_DIR ? offset*1.5 : offset);
6358  m_ytickoffset = (mode2D ? std::max (0., m_yticklen) : std::abs (m_yticklen)) +
6359  (m_ystate == AXE_HORZ_DIR ? offset*1.5 : offset);
6360  m_ztickoffset = (mode2D ? std::max (0., m_zticklen) : std::abs (m_zticklen)) +
6361  (m_zstate == AXE_HORZ_DIR ? offset*1.5 : offset);
6362 
6363  update_xlabel_position ();
6364  update_ylabel_position ();
6365  update_zlabel_position ();
6366  update_title_position ();
6367 }
6368 
6369 /*
6370 ## FIXME: A demo can't be called in a C++ file. This should be made a test
6371 ## or moved to a .m file where it can be called.
6372 %!demo
6373 %! clf;
6374 %! subplot (2,1,1);
6375 %! plot (rand (3));
6376 %! xlabel xlabel;
6377 %! ylabel ylabel;
6378 %! title title;
6379 %! subplot (2,1,2);
6380 %! plot (rand (3));
6381 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
6382 %! xlabel xlabel;
6383 %! ylabel ylabel;
6384 %! title title;
6385 */
6386 
6387 static ColumnVector
6389  const text::properties& props,
6390  const graphics_xform& xform,
6391  const Matrix& bbox)
6392 {
6393  ColumnVector retval;
6394 
6395  std::string to_units = props.get_units ();
6396 
6397  if (to_units != "data")
6398  {
6399  ColumnVector v = xform.transform (p(0), p(1), p(2));
6400 
6401  retval.resize (3);
6402 
6403  retval(0) = v(0) - bbox(0) + 1;
6404  retval(1) = bbox(1) + bbox(3) - v(1) + 1;
6405  retval(2) = 0;
6406 
6407  retval = convert_position (retval, "pixels", to_units,
6408  bbox.extract_n (0, 2, 1, 2));
6409  }
6410  else
6411  retval = p;
6412 
6413  return retval;
6414 }
6415 
6416 static bool updating_xlabel_position = false;
6417 
6418 void
6419 axes::properties::update_xlabel_position (void)
6420 {
6422  return;
6423 
6424  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
6425 
6426  graphics_object go = gh_mgr.get_object (get_xlabel ());
6427 
6428  if (! go.valid_object ())
6429  return;
6430 
6431  text::properties& xlabel_props
6432  = reinterpret_cast<text::properties&> (go.get_properties ());
6433 
6434  bool isempty = xlabel_props.get_string ().isempty ();
6435 
6436  octave::unwind_protect_var<bool>
6437  restore_var (updating_xlabel_position, true);
6438 
6439  if (! isempty)
6440  {
6441  if (xlabel_props.horizontalalignmentmode_is ("auto"))
6442  {
6443  xlabel_props.set_horizontalalignment
6444  (m_xstate > AXE_DEPTH_DIR ? "center"
6445  : (m_xyzSym ? "left" : "right"));
6446 
6447  xlabel_props.set_horizontalalignmentmode ("auto");
6448  }
6449 
6450  if (xlabel_props.verticalalignmentmode_is ("auto"))
6451  {
6452  xlabel_props.set_verticalalignment
6453  (m_xstate == AXE_VERT_DIR || m_x2Dtop ? "bottom" : "top");
6454 
6455  xlabel_props.set_verticalalignmentmode ("auto");
6456  }
6457  }
6458 
6459  if (xlabel_props.positionmode_is ("auto")
6460  || xlabel_props.rotationmode_is ("auto"))
6461  {
6462  graphics_xform xform = get_transform ();
6463 
6464  Matrix ext (1, 2, 0.0);
6465  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6466  get_xticklabel ().string_vector_value (),
6467  get_xlim ().matrix_value ());
6468 
6469  double margin = 5;
6470  double wmax = ext(0) + margin;
6471  double hmax = ext(1) + margin;
6472  double angle = 0.0;
6473  ColumnVector p
6474  = graphics_xform::xform_vector ((m_xpTickN + m_xpTick)/2, m_ypTick, m_zpTick);
6475 
6476  bool tick_along_z = m_nearhoriz || octave::math::isinf (m_fy);
6477  if (tick_along_z)
6478  p(2) += (octave::math::signum (m_zpTick - m_zpTickN) * m_fz * m_xtickoffset);
6479  else
6480  p(1) += (octave::math::signum (m_ypTick - m_ypTickN) * m_fy * m_xtickoffset);
6481 
6482  p = xform.transform (p(0), p(1), p(2), false);
6483 
6484  switch (m_xstate)
6485  {
6486  case AXE_ANY_DIR:
6487  p(0) += (m_xyzSym ? wmax : -wmax);
6488  p(1) += hmax;
6489  break;
6490 
6491  case AXE_VERT_DIR:
6492  p(0) -= wmax;
6493  angle = 90;
6494  break;
6495 
6496  case AXE_HORZ_DIR:
6497  p(1) += (m_x2Dtop ? -hmax : hmax);
6498  break;
6499  }
6500 
6501  if (xlabel_props.positionmode_is ("auto"))
6502  {
6503  p = xform.untransform (p(0), p(1), p(2), true);
6504 
6505  p = convert_label_position (p, xlabel_props, xform,
6506  get_extent (false));
6507 
6508  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
6509  xlabel_props.set_positionmode ("auto");
6510  }
6511 
6512  if (! isempty && xlabel_props.rotationmode_is ("auto"))
6513  {
6514  xlabel_props.set_rotation (angle);
6515  xlabel_props.set_rotationmode ("auto");
6516  }
6517  }
6518 }
6519 
6520 static bool updating_ylabel_position = false;
6521 
6522 void
6523 axes::properties::update_ylabel_position (void)
6524 {
6526  return;
6527 
6528  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
6529 
6530  graphics_object go = gh_mgr.get_object (get_ylabel ());
6531 
6532  if (! go.valid_object ())
6533  return;
6534 
6535  text::properties& ylabel_props
6536  = reinterpret_cast<text::properties&> (go.get_properties ());
6537 
6538  bool isempty = ylabel_props.get_string ().isempty ();
6539 
6540  octave::unwind_protect_var<bool>
6541  restore_var (updating_ylabel_position, true);
6542 
6543  if (! isempty)
6544  {
6545  if (ylabel_props.horizontalalignmentmode_is ("auto"))
6546  {
6547  ylabel_props.set_horizontalalignment
6548  (m_ystate > AXE_DEPTH_DIR ? "center"
6549  : (! m_xyzSym ? "left" : "right"));
6550 
6551  ylabel_props.set_horizontalalignmentmode ("auto");
6552  }
6553 
6554  if (ylabel_props.verticalalignmentmode_is ("auto"))
6555  {
6556  ylabel_props.set_verticalalignment
6557  (m_ystate == AXE_VERT_DIR && ! m_y2Dright ? "bottom" : "top");
6558 
6559  ylabel_props.set_verticalalignmentmode ("auto");
6560  }
6561  }
6562 
6563  if (ylabel_props.positionmode_is ("auto")
6564  || ylabel_props.rotationmode_is ("auto"))
6565  {
6566  graphics_xform xform = get_transform ();
6567 
6568  Matrix ext (1, 2, 0.0);
6569 
6570  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
6571  get_yticklabel ().string_vector_value (),
6572  get_ylim ().matrix_value ());
6573  double margin = 5;
6574  double wmax = ext(0) + margin;
6575  double hmax = ext(1) + margin;
6576  double angle = 0.0;
6577  ColumnVector p
6578  = graphics_xform::xform_vector (m_xpTick, (m_ypTickN + m_ypTick)/2, m_zpTick);
6579 
6580  bool tick_along_z = m_nearhoriz || octave::math::isinf (m_fx);
6581  if (tick_along_z)
6582  p(2) += (octave::math::signum (m_zpTick - m_zpTickN) * m_fz * m_ytickoffset);
6583  else
6584  p(0) += (octave::math::signum (m_xpTick - m_xpTickN) * m_fx * m_ytickoffset);
6585 
6586  p = xform.transform (p(0), p(1), p(2), false);
6587 
6588  switch (m_ystate)
6589  {
6590  case AXE_ANY_DIR:
6591  p(0) += (! m_xyzSym ? wmax : -wmax);
6592  p(1) += hmax;
6593  break;
6594 
6595  case AXE_VERT_DIR:
6596  p(0) += (m_y2Dright ? wmax : -wmax);
6597  angle = 90;
6598  break;
6599 
6600  case AXE_HORZ_DIR:
6601  p(1) += hmax;
6602  break;
6603  }
6604 
6605  if (ylabel_props.positionmode_is ("auto"))
6606  {
6607  p = xform.untransform (p(0), p(1), p(2), true);
6608 
6609  p = convert_label_position (p, ylabel_props, xform,
6610  get_extent (false));
6611 
6612  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
6613  ylabel_props.set_positionmode ("auto");
6614  }
6615 
6616  if (! isempty && ylabel_props.rotationmode_is ("auto"))
6617  {
6618  ylabel_props.set_rotation (angle);
6619  ylabel_props.set_rotationmode ("auto");
6620  }
6621  }
6622 }
6623 
6624 static bool updating_zlabel_position = false;
6625 
6626 void
6627 axes::properties::update_zlabel_position (void)
6628 {
6630  return;
6631 
6632  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
6633 
6634  graphics_object go = gh_mgr.get_object (get_zlabel ());
6635 
6636  if (! go.valid_object ())
6637  return;
6638 
6639  text::properties& zlabel_props
6640  = reinterpret_cast<text::properties&> (go.get_properties ());
6641 
6642  bool camAuto = cameraupvectormode_is ("auto");
6643  bool isempty = zlabel_props.get_string ().isempty ();
6644 
6645  octave::unwind_protect_var<bool>
6646  restore_updating_zlabel_position (updating_zlabel_position, true);
6647 
6648  if (! isempty)
6649  {
6650  if (zlabel_props.horizontalalignmentmode_is ("auto"))
6651  {
6652  zlabel_props.set_horizontalalignment
6653  ((m_zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
6654 
6655  zlabel_props.set_horizontalalignmentmode ("auto");
6656  }
6657 
6658  if (zlabel_props.verticalalignmentmode_is ("auto"))
6659  {
6660  zlabel_props.set_verticalalignment
6661  (m_zstate == AXE_VERT_DIR
6662  ? "bottom" : ((m_zSign || camAuto) ? "bottom" : "top"));
6663 
6664  zlabel_props.set_verticalalignmentmode ("auto");
6665  }
6666  }
6667 
6668  if (zlabel_props.positionmode_is ("auto")
6669  || zlabel_props.rotationmode_is ("auto"))
6670  {
6671  graphics_xform xform = get_transform ();
6672 
6673  Matrix ext (1, 2, 0.0);
6674  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
6675  get_zticklabel ().string_vector_value (),
6676  get_zlim ().matrix_value ());
6677 
6678  double margin = 5;
6679  double wmax = ext(0) + margin;
6680  double hmax = ext(1) + margin;
6681  double angle = 0.0;
6682  ColumnVector p;
6683 
6684  if (m_xySym)
6685  {
6686  p = graphics_xform::xform_vector (m_xPlaneN, m_yPlane,
6687  (m_zpTickN + m_zpTick)/2);
6688  if (octave::math::isinf (m_fy))
6689  p(0) += octave::math::signum (m_xPlaneN - m_xPlane) * m_fx * m_ztickoffset;
6690  else
6691  p(1) += octave::math::signum (m_yPlane - m_yPlaneN) * m_fy * m_ztickoffset;
6692  }
6693  else
6694  {
6695  p = graphics_xform::xform_vector (m_xPlane, m_yPlaneN,
6696  (m_zpTickN + m_zpTick)/2);
6697  if (octave::math::isinf (m_fx))
6698  p(1) += octave::math::signum (m_yPlaneN - m_yPlane) * m_fy * m_ztickoffset;
6699  else
6700  p(0) += octave::math::signum (m_xPlane - m_xPlaneN) * m_fx * m_ztickoffset;
6701  }
6702 
6703  p = xform.transform (p(0), p(1), p(2), false);
6704 
6705  switch (m_zstate)
6706  {
6707  case AXE_ANY_DIR:
6708  if (camAuto)
6709  {
6710  p(0) -= wmax;
6711  angle = 90;
6712  }
6713 
6714  // FIXME: what's the correct offset?
6715  //
6716  // p[0] += (! m_xySym ? wmax : -wmax);
6717  // p[1] += (m_zSign ? hmax : -hmax);
6718 
6719  break;
6720 
6721  case AXE_VERT_DIR:
6722  p(0) -= wmax;
6723  angle = 90;
6724  break;
6725 
6726  case AXE_HORZ_DIR:
6727  p(1) += hmax;
6728  break;
6729  }
6730 
6731  if (zlabel_props.positionmode_is ("auto"))
6732  {
6733  p = xform.untransform (p(0), p(1), p(2), true);
6734 
6735  p = convert_label_position (p, zlabel_props, xform,
6736  get_extent (false));
6737 
6738  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
6739  zlabel_props.set_positionmode ("auto");
6740  }
6741 
6742  if (! isempty && zlabel_props.rotationmode_is ("auto"))
6743  {
6744  zlabel_props.set_rotation (angle);
6745  zlabel_props.set_rotationmode ("auto");
6746  }
6747  }
6748 }
6749 
6750 static bool updating_title_position = false;
6751 
6752 void
6753 axes::properties::update_title_position (void)
6754 {
6756  return;
6757 
6758  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
6759 
6760  graphics_object go = gh_mgr.get_object (get_title ());
6761 
6762  if (! go.valid_object ())
6763  return;
6764 
6765  text::properties& title_props
6766  = reinterpret_cast<text::properties&> (go.get_properties ());
6767 
6768  octave::unwind_protect_var<bool> restore_var (updating_title_position, true);
6769 
6770  if (title_props.positionmode_is ("auto"))
6771  {
6772  graphics_xform xform = get_transform ();
6773 
6774  // FIXME: bbox should be stored in axes::properties
6775  Matrix bbox = get_extent (false);
6776 
6777  ColumnVector p
6778  = graphics_xform::xform_vector (bbox(0) + bbox(2)/2, bbox(1) - 10,
6779  (m_x_zlim(0) + m_x_zlim(1))/2);
6780 
6781  if (m_x2Dtop)
6782  {
6783  Matrix ext (1, 2, 0.0);
6784  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6785  get_xticklabel ().string_vector_value (),
6786  get_xlim ().matrix_value ());
6787  p(1) -= ext(1);
6788  }
6789 
6790  p = xform.untransform (p(0), p(1), p(2), true);
6791 
6792  p = convert_label_position (p, title_props, xform, bbox);
6793 
6794  title_props.set_position (p.extract_n (0, 3).transpose ());
6795  title_props.set_positionmode ("auto");
6796  }
6797 }
6798 
6799 void
6800 axes::properties::update_autopos (const std::string& elem_type)
6801 {
6802  if (elem_type == "xlabel")
6803  update_xlabel_position ();
6804  else if (elem_type == "ylabel")
6805  update_ylabel_position ();
6806  else if (elem_type == "zlabel")
6807  update_zlabel_position ();
6808  else if (elem_type == "title")
6809  update_title_position ();
6810  else if (elem_type == "sync")
6811  sync_positions ();
6812 }
6813 
6814 static void
6815 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
6816  double xlength, double ylength, double zlength)
6817 {
6818  double xval = xlength / scalefactors(0);
6819  double yval = ylength / scalefactors(1);
6820  double zval = zlength / scalefactors(2);
6821 
6822  double minval = octave::math::min (octave::math::min (xval, yval), zval);
6823 
6824  aspectratios(0) = xval / minval;
6825  aspectratios(1) = yval / minval;
6826  aspectratios(2) = zval / minval;
6827 }
6828 
6829 static void
6830 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
6831  double pbfactor, double dafactor, char limit_type, bool tight)
6832 {
6833  if (tight)
6834  {
6835  double minval = octave::numeric_limits<double>::Inf ();
6836  double maxval = -octave::numeric_limits<double>::Inf ();
6837  double min_pos = octave::numeric_limits<double>::Inf ();
6838  double max_neg = -octave::numeric_limits<double>::Inf ();
6839  get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
6840  if (octave::math::isfinite (minval) && octave::math::isfinite (maxval))
6841  {
6842  limits(0) = minval;
6843  limits(1) = maxval;
6844  s = octave::math::max (s, (maxval - minval) / (pbfactor * dafactor));
6845  }
6846  }
6847  else
6848  s = octave::math::max (s, (limits(1) - limits(0)) / (pbfactor * dafactor));
6849 }
6850 
6851 static std::set<double> updating_aspectratios;
6852 
6853 void
6854 axes::properties::update_aspectratios (void)
6855 {
6856  if (updating_aspectratios.find (get___myhandle__ ().value ())
6857  != updating_aspectratios.end ())
6858  return;
6859 
6860  Matrix xlimits = get_xlim ().matrix_value ();
6861  Matrix ylimits = get_ylim ().matrix_value ();
6862  Matrix zlimits = get_zlim ().matrix_value ();
6863 
6864  double dx = (xlimits(1) - xlimits(0));
6865  double dy = (ylimits(1) - ylimits(0));
6866  double dz = (zlimits(1) - zlimits(0));
6867 
6868  Matrix da = get_dataaspectratio ().matrix_value ();
6869  Matrix pba = get_plotboxaspectratio ().matrix_value ();
6870 
6871  if (dataaspectratiomode_is ("auto"))
6872  {
6873  if (plotboxaspectratiomode_is ("auto"))
6874  {
6875  pba = Matrix (1, 3, 1.0);
6876  m_plotboxaspectratio.set (pba, false);
6877  }
6878 
6879  normalized_aspectratios (da, pba, dx, dy, dz);
6880  m_dataaspectratio.set (da, false);
6881  }
6882  else if (plotboxaspectratiomode_is ("auto"))
6883  {
6884  normalized_aspectratios (pba, da, dx, dy, dz);
6885  m_plotboxaspectratio.set (pba, false);
6886  }
6887  else
6888  {
6889  double s = -octave::numeric_limits<double>::Inf ();
6890  bool modified_limits = false;
6891  Matrix kids;
6892 
6893  if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
6894  {
6895  modified_limits = true;
6896  kids = get_children ();
6897  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
6898  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
6899  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
6900  }
6901  else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
6902  {
6903  modified_limits = true;
6904  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
6905  }
6906  else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
6907  {
6908  modified_limits = true;
6909  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
6910  }
6911  else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
6912  {
6913  modified_limits = true;
6914  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
6915  }
6916 
6917  if (modified_limits)
6918  {
6919  octave::unwind_protect_var<std::set<double>>
6920  restore_var (updating_aspectratios);
6921 
6922  updating_aspectratios.insert (get___myhandle__ ().value ());
6923 
6924  dx = pba(0) * da(0);
6925  dy = pba(1) * da(1);
6926  dz = pba(2) * da(2);
6927  if (octave::math::isinf (s))
6928  s = 1 / octave::math::min (octave::math::min (dx, dy), dz);
6929 
6930  if (xlimmode_is ("auto"))
6931  {
6932  dx = s * dx;
6933  xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
6934  xlimits(1) = xlimits(0) + dx;
6935  set_xlim (xlimits);
6936  set_xlimmode ("auto");
6937  }
6938 
6939  if (ylimmode_is ("auto"))
6940  {
6941  dy = s * dy;
6942  ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
6943  ylimits(1) = ylimits(0) + dy;
6944  set_ylim (ylimits);
6945  set_ylimmode ("auto");
6946  }
6947 
6948  if (zlimmode_is ("auto"))
6949  {
6950  dz = s * dz;
6951  zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
6952  zlimits(1) = zlimits(0) + dz;
6953  set_zlim (zlimits);
6954  set_zlimmode ("auto");
6955  }
6956  }
6957  else
6958  {
6959  normalized_aspectratios (pba, da, dx, dy, dz);
6960  m_plotboxaspectratio.set (pba, false);
6961  }
6962  }
6963 }
6964 
6965 void
6966 axes::properties::update_label_color (handle_property label,
6967  color_property col)
6968 {
6969  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
6970 
6971  gh_mgr.get_object (label.handle_value ()).set ("color", col.get ());
6972 }
6973 
6974 void
6975 axes::properties::update_font (std::string prop)
6976 {
6977  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
6978 
6979  if (! prop.empty ())
6980  {
6981  octave_value val = get (prop);
6982  octave_value tval = val;
6983  if (prop == "fontsize")
6984  {
6985  tval = octave_value (val.double_value () *
6986  get_titlefontsizemultiplier ());
6987  val = octave_value (val.double_value () *
6988  get_labelfontsizemultiplier ());
6989  }
6990  else if (prop == "fontweight")
6991  tval = get ("titlefontweight");
6992 
6993  gh_mgr.get_object (get_xlabel ()).set (prop, val);
6994  gh_mgr.get_object (get_ylabel ()).set (prop, val);
6995  gh_mgr.get_object (get_zlabel ()).set (prop, val);
6996  gh_mgr.get_object (get_title ()).set (prop, tval);
6997  }
6998 
6999  double dpr = device_pixel_ratio (get___myhandle__ ());
7000 
7001  octave::autolock guard (gh_mgr.graphics_lock ());
7002 
7003  m_txt_renderer.set_font (get ("fontname").string_value (),
7004  get ("fontweight").string_value (),
7005  get ("fontangle").string_value (),
7006  get ("__fontsize_points__").double_value () * dpr);
7007 }
7008 
7009 // The INTERNAL flag defines whether position or outerposition is used.
7010 
7011 Matrix
7012 axes::properties::get_boundingbox (bool internal,
7013  const Matrix& parent_pix_size) const
7014 {
7015  Matrix pos = (internal ? get_position ().matrix_value ()
7016  : get_outerposition ().matrix_value ());
7017  Matrix parent_size (parent_pix_size);
7018 
7019  if (parent_size.isempty ())
7020  {
7021  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
7022 
7023  graphics_object go = gh_mgr.get_object (get_parent ());
7024 
7025  if (go.valid_object ())
7026  parent_size
7027  = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7028  else
7029  parent_size = default_figure_position ();
7030  }
7031 
7032  pos = convert_position (pos, get_units (), "pixels", parent_size);
7033 
7034  pos(0)--;
7035  pos(1)--;
7036  pos(1) = parent_size(1) - pos(1) - pos(3);
7037 
7038  return pos;
7039 }
7040 
7041 Matrix
7042 axes::properties::get_extent (bool with_text, bool only_text_height) const
7043 {
7044  graphics_xform xform = get_transform ();
7045 
7046  Matrix ext (1, 4, 0.0);
7047  ext(0) = ext(1) = octave::numeric_limits<double>::Inf ();
7048  ext(2) = ext(3) = -octave::numeric_limits<double>::Inf ();
7049  for (int i = 0; i <= 1; i++)
7050  for (int j = 0; j <= 1; j++)
7051  for (int k = 0; k <= 1; k++)
7052  {
7053  ColumnVector p = xform.transform (i ? m_xPlaneN : m_xPlane,
7054  j ? m_yPlaneN : m_yPlane,
7055  k ? m_zPlaneN : m_zPlane, false);
7056  ext(0) = std::min (ext(0), p(0));
7057  ext(1) = std::min (ext(1), p(1));
7058  ext(2) = std::max (ext(2), p(0));
7059  ext(3) = std::max (ext(3), p(1));
7060  }
7061 
7062  if (with_text)
7063  {
7064  for (int i = 0; i < 4; i++)
7065  {
7066  graphics_handle htext;
7067  if (i == 0)
7068  htext = get_title ();
7069  else if (i == 1)
7070  htext = get_xlabel ();
7071  else if (i == 2)
7072  htext = get_ylabel ();
7073  else if (i == 3)
7074  htext = get_zlabel ();
7075 
7076  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
7077 
7078  text::properties& text_props
7079  = reinterpret_cast<text::properties&>
7080  (gh_mgr.get_object (htext).get_properties ());
7081 
7082  Matrix text_pos = text_props.get_data_position ();
7083  text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
7084  if (text_props.get_string ().isempty ())
7085  {
7086  ext(0) = std::min (ext(0), text_pos(0));
7087  ext(1) = std::min (ext(1), text_pos(1));
7088  ext(2) = std::max (ext(2), text_pos(0));
7089  ext(3) = std::max (ext(3), text_pos(1));
7090  }
7091  else
7092  {
7093  Matrix text_ext = text_props.get_extent_matrix (true);
7094 
7095  // The text extent is returned in device pixels. Unscale and
7096  // work with logical pixels
7097  double dpr = device_pixel_ratio (get___myhandle__ ());
7098  if (dpr != 1.0)
7099  for (int j = 0; j < 4; j++)
7100  text_ext(j) /= dpr;
7101 
7102  bool ignore_horizontal = false;
7103  bool ignore_vertical = false;
7104  if (only_text_height)
7105  {
7106  double text_rotation = text_props.get_rotation ();
7107  if (text_rotation == 0. || text_rotation == 180.)
7108  ignore_horizontal = true;
7109  else if (text_rotation == 90. || text_rotation == 270.)
7110  ignore_vertical = true;
7111  }
7112 
7113  if (! ignore_horizontal)
7114  {
7115  ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
7116  ext(2) = std::max (ext(2),
7117  text_pos(0)+text_ext(0)+text_ext(2));
7118  }
7119 
7120  if (! ignore_vertical)
7121  {
7122  ext(1) = std::min (ext(1),
7123  text_pos(1)-text_ext(1)-text_ext(3));
7124  ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
7125  }
7126  }
7127  }
7128  }
7129 
7130  ext(2) = ext(2) - ext(0);
7131  ext(3) = ext(3) - ext(1);
7132 
7133  return ext;
7134 }
7135 
7136 static octave_value
7138 {
7139  octave_value retval = val;
7140 
7141  if (val.iscellstr ())
7142  {
7143  // Always return a column vector for Matlab compatibility
7144  if (val.columns () > 1)
7145  retval = val.reshape (dim_vector (val.numel (), 1));
7146  }
7147  else
7148  {
7149  string_vector sv;
7150  if (val.isnumeric ())
7151  {
7152  NDArray data = val.array_value ();
7153  std::ostringstream oss;
7154  oss.precision (5);
7155  for (octave_idx_type i = 0; i < val.numel (); i++)
7156  {
7157  oss.str ("");
7158  oss << data(i);
7159  sv.append (oss.str ());
7160  }
7161  }
7162  else if (val.is_string () && val.rows () == 1)
7163  {
7164  std::string valstr = val.string_value ();
7165  std::istringstream iss (valstr);
7166  std::string tmpstr;
7167 
7168  // Split string with delimiter '|'
7169  while (std::getline (iss, tmpstr, '|'))
7170  sv.append (tmpstr);
7171 
7172  // If string ends with '|' Matlab appends a null string
7173  if (*valstr.rbegin () == '|')
7174  sv.append (std::string (""));
7175  }
7176  else
7177  return retval;
7178 
7179  charMatrix chmat (sv, ' ');
7180 
7181  retval = octave_value (chmat);
7182  }
7183 
7184  return retval;
7185 }
7186 
7187 void
7188 axes::properties::set_xticklabel (const octave_value& val)
7189 {
7190  if (m_xticklabel.set (convert_ticklabel_string (val), false))
7191  {
7192  set_xticklabelmode ("manual");
7193  m_xticklabel.run_listeners (GCB_POSTSET);
7194  mark_modified ();
7195  }
7196  else
7197  set_xticklabelmode ("manual");
7198 
7199  sync_positions ();
7200 }
7201 
7202 void
7203 axes::properties::set_yticklabel (const octave_value& val)
7204 {
7205  if (m_yticklabel.set (convert_ticklabel_string (val), false))
7206  {
7207  set_yticklabelmode ("manual");
7208  m_yticklabel.run_listeners (GCB_POSTSET);
7209  mark_modified ();
7210  }
7211  else
7212  set_yticklabelmode ("manual");
7213 
7214  sync_positions ();
7215 }
7216 
7217 void
7218 axes::properties::set_zticklabel (const octave_value& val)
7219 {
7220  if (m_zticklabel.set (convert_ticklabel_string (val), false))
7221  {
7222  set_zticklabelmode ("manual");
7223  m_zticklabel.run_listeners (GCB_POSTSET);
7224  mark_modified ();
7225  }
7226  else
7227  set_zticklabelmode ("manual");
7228 
7229  sync_positions ();
7230 }
7231 
7232 // Almost identical to convert_ticklabel_string but it only accepts
7233 // cellstr or string, not numeric input.
7234 static octave_value
7236 {
7237  octave_value retval = val;
7238 
7239  if (val.iscellstr ())
7240  {
7241  // Always return a column vector for Matlab Compatibility
7242  if (val.columns () > 1)
7243  retval = val.reshape (dim_vector (val.numel (), 1));
7244  }
7245  else
7246  {
7247  string_vector sv;
7248  if (val.is_string () && val.rows () == 1)
7249  {
7250  std::string valstr = val.string_value ();
7251  std::istringstream iss (valstr);
7252  std::string tmpstr;
7253 
7254  // Split string with delimiter '|'
7255  while (std::getline (iss, tmpstr, '|'))
7256  sv.append (tmpstr);
7257 
7258  // If string ends with '|' Matlab appends a null string
7259  if (*valstr.rbegin () == '|')
7260  sv.append (std::string (""));
7261  }
7262  else
7263  return retval;
7264 
7265  charMatrix chmat (sv, ' ');
7266 
7267  retval = octave_value (chmat);
7268  }
7269 
7270  return retval;
7271 }
7272 
7273 void
7274 axes::properties::set_linestyleorder (const octave_value& val)
7275 {
7276  m_linestyleorder.set (convert_linestyleorder_string (val), false);
7277 }
7278 
7279 void
7280 axes::properties::set_units (const octave_value& val)
7281 {
7282  caseless_str old_units = get_units ();
7283 
7284  if (m_units.set (val, true))
7285  {
7286  update_units (old_units);
7287  mark_modified ();
7288  }
7289 }
7290 
7291 void
7292 axes::properties::update_units (const caseless_str& old_units)
7293 {
7294  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
7295 
7296  graphics_object parent_go = gh_mgr.get_object (get_parent ());
7297 
7298  Matrix parent_bb
7299  = parent_go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7300 
7301  caseless_str new_units = get_units ();
7302  m_position.set (octave_value
7303  (convert_position (get_position ().matrix_value (),
7304  old_units, new_units, parent_bb)),
7305  false);
7306  m_outerposition.set (octave_value
7307  (convert_position (get_outerposition ().matrix_value (),
7308  old_units, new_units, parent_bb)),
7309  false);
7310  m_tightinset.set (octave_value
7311  (convert_position (get_tightinset ().matrix_value (),
7312  old_units, new_units, parent_bb)),
7313  false);
7314  m_looseinset.set (octave_value
7315  (convert_position (get_looseinset ().matrix_value (),
7316  old_units, new_units, parent_bb)),
7317  false);
7318 }
7319 
7320 void
7321 axes::properties::set_fontunits (const octave_value& val)
7322 {
7323  caseless_str old_fontunits = get_fontunits ();
7324 
7325  if (m_fontunits.set (val, true))
7326  {
7327  update_fontunits (old_fontunits);
7328  mark_modified ();
7329  }
7330 }
7331 
7332 void
7333 axes::properties::update_fontunits (const caseless_str& old_units)
7334 {
7335  caseless_str new_units = get_fontunits ();
7336  double parent_height = get_boundingbox (true).elem (3);
7337  double fontsz = get_fontsize ();
7338 
7339  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
7340 
7341  set_fontsize (octave_value (fontsz));
7342 }
7343 
7344 double
7345 axes::properties::get___fontsize_points__ (double box_pix_height) const
7346 {
7347  double fontsz = get_fontsize ();
7348  double parent_height = box_pix_height;
7349 
7350  if (fontunits_is ("normalized") && parent_height <= 0)
7351  parent_height = get_boundingbox (true).elem (3);
7352 
7353  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
7354 }
7355 
7357 graphics_xform::xform_vector (double x, double y, double z)
7358 {
7359  return octave::xform_vector (x, y, z);
7360 }
7361 
7362 Matrix
7363 graphics_xform::xform_eye (void)
7364 {
7365  return octave::xform_matrix ();
7366 }
7367 
7369 graphics_xform::transform (double x, double y, double z, bool use_scale) const
7370 {
7371  if (use_scale)
7372  {
7373  x = m_sx.scale (x);
7374  y = m_sy.scale (y);
7375  z = m_sz.scale (z);
7376  }
7377 
7378  return octave::transform (m_xform, x, y, z);
7379 }
7380 
7382 graphics_xform::untransform (double x, double y, double z,
7383  bool use_scale) const
7384 {
7385  ColumnVector v = octave::transform (m_xform_inv, x, y, z);
7386 
7387  if (use_scale)
7388  {
7389  v(0) = m_sx.unscale (v(0));
7390  v(1) = m_sy.unscale (v(1));
7391  v(2) = m_sz.unscale (v(2));
7392  }
7393 
7394  return v;
7395 }
7396 
7398 axes::get_default (const caseless_str& pname) const
7399 {
7400  octave_value retval = m_default_properties.lookup (pname);
7401 
7402  if (retval.is_undefined ())
7403  {
7404  graphics_handle parent_h = get_parent ();
7405 
7406  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
7407 
7408  graphics_object parent_go = gh_mgr.get_object (parent_h);
7409 
7410  retval = parent_go.get_default (pname);
7411  }
7412 
7413  return retval;
7414 }
7415 
7416 // FIXME: remove.
7417 // FIXME: maybe this should go into array_property class?
7418 /*
7419 static void
7420 check_limit_vals (double& min_val, double& max_val,
7421  double& min_pos, double& max_neg,
7422  const array_property& data)
7423 {
7424  double val = data.min_val ();
7425  if (octave::math::isfinite (val) && val < min_val)
7426  min_val = val;
7427  val = data.max_val ();
7428  if (octave::math::isfinite (val) && val > max_val)
7429  max_val = val;
7430  val = data.min_pos ();
7431  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7432  min_pos = val;
7433  val = data.max_neg ();
7434  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7435  max_neg = val;
7436 }
7437 */
7438 
7439 static void
7440 check_limit_vals (double& min_val, double& max_val,
7441  double& min_pos, double& max_neg,
7442  const octave_value& data)
7443 {
7444  Matrix m;
7445 
7446  if (data.is_matrix_type ())
7447  m = data.matrix_value ();
7448 
7449  if (m.numel () != 4)
7450  {
7451  m = Matrix (1, 4, 0.0);
7454  }
7455 
7456  double val;
7457 
7458  val = m(0);
7459  if (octave::math::isfinite (val) && val < min_val)
7460  min_val = val;
7461 
7462  val = m(1);
7463  if (octave::math::isfinite (val) && val > max_val)
7464  max_val = val;
7465 
7466  val = m(2);
7467  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7468  min_pos = val;
7469 
7470  val = m(3);
7471  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7472  max_neg = val;
7473 }
7474 
7475 // magform(x) Returns (a, b),
7476 // where x = a * 10^b, abs (a) >= 1., and b is integer.
7477 
7478 static void
7479 magform (double x, double& a, int& b)
7480 {
7481  if (x == 0)
7482  {
7483  a = 0;
7484  b = 0;
7485  }
7486  else
7487  {
7488  b = static_cast<int> (std::floor (std::log10 (std::abs (x))));
7489  a = x / std::pow (10.0, b);
7490  }
7491 }
7492 
7493 void
7494 axes::properties::update_outerposition (void)
7495 {
7496  set_positionconstraint ("outerposition");
7497  caseless_str old_units = get_units ();
7498  set_units ("normalized");
7499 
7500  Matrix outerbox = m_outerposition.get ().matrix_value ();
7501 
7502  double outer_left = outerbox(0);
7503  double outer_bottom = outerbox(1);
7504  double outer_width = outerbox(2);
7505  double outer_height = outerbox(3);
7506 
7507  double outer_right = outer_width + outer_left;
7508  double outer_top = outer_height + outer_bottom;
7509 
7510  Matrix linset = m_looseinset.get ().matrix_value ();
7511  Matrix tinset = m_tightinset.get ().matrix_value ();
7512 
7513  double left_margin = std::max (linset(0), tinset(0));
7514  double bottom_margin = std::max (linset(1), tinset(1));
7515  double right_margin = std::max (linset(2), tinset(2));
7516  double top_margin = std::max (linset(3), tinset(3));
7517 
7518  double inner_left = outer_left;
7519  double inner_right = outer_right;
7520 
7521  if ((left_margin + right_margin) < outer_width)
7522  {
7523  inner_left += left_margin;
7524  inner_right -= right_margin;
7525  }
7526 
7527  double inner_bottom = outer_bottom;
7528  double inner_top = outer_top;
7529 
7530  if ((bottom_margin + top_margin) < outer_height)
7531  {
7532  inner_bottom += bottom_margin;
7533  inner_top -= top_margin;
7534  }
7535 
7536  double inner_width = inner_right - inner_left;
7537  double inner_height = inner_top - inner_bottom;
7538 
7539  Matrix innerbox (1, 4);
7540 
7541  innerbox(0) = inner_left;
7542  innerbox(1) = inner_bottom;
7543  innerbox(2) = inner_width;
7544  innerbox(3) = inner_height;
7545 
7546  m_position = innerbox;
7547 
7548  set_units (old_units);
7549  update_transform ();
7550 }
7551 
7552 void
7553 axes::properties::update_position (void)
7554 {
7555  set_positionconstraint ("innerposition");
7556  caseless_str old_units = get_units ();
7557  set_units ("normalized");
7558 
7559  Matrix innerbox = m_position.get ().matrix_value ();
7560 
7561  double inner_left = innerbox(0);
7562  double inner_bottom = innerbox(1);
7563  double inner_width = innerbox(2);
7564  double inner_height = innerbox(3);
7565 
7566  double inner_right = inner_width + inner_left;
7567  double inner_top = inner_height + inner_bottom;
7568 
7569  Matrix linset = m_looseinset.get ().matrix_value ();
7570  Matrix tinset = m_tightinset.get ().matrix_value ();
7571 
7572  double left_margin = std::max (linset(0), tinset(0));
7573  double bottom_margin = std::max (linset(1), tinset(1));
7574  double right_margin = std::max (linset(2), tinset(2));
7575  double top_margin = std::max (linset(3), tinset(3));
7576 
7577  // FIXME: do we need to place limits on any of these?
7578 
7579  double outer_left = inner_left - left_margin;
7580  double outer_bottom = inner_bottom - bottom_margin;
7581  double outer_right = inner_right + right_margin;
7582  double outer_top = inner_top + top_margin;
7583 
7584  double outer_width = outer_right - outer_left;
7585  double outer_height = outer_top - outer_bottom;
7586 
7587  Matrix outerbox (1, 4);
7588 
7589  outerbox(0) = outer_left;
7590  outerbox(1) = outer_bottom;
7591  outerbox(2) = outer_width;
7592  outerbox(3) = outer_height;
7593 
7594  m_outerposition = outerbox;
7595 
7596  set_units (old_units);
7597  update_transform ();
7598 }
7599 
7600 void
7601 axes::properties::update_looseinset (void)
7602 {
7603  caseless_str old_units = get_units ();
7604  set_units ("normalized");
7605 
7606  Matrix linset = m_looseinset.get ().matrix_value ();
7607  Matrix tinset = m_tightinset.get ().matrix_value ();
7608 
7609  double left_margin = std::max (linset(0), tinset(0));
7610  double bottom_margin = std::max (linset(1), tinset(1));
7611  double right_margin = std::max (linset(2), tinset(2));
7612  double top_margin = std::max (linset(3), tinset(3));
7613 
7614  if (m_positionconstraint.is ("innerposition"))
7615  {
7616  Matrix innerbox = m_position.get ().matrix_value ();
7617 
7618  double inner_left = innerbox(0);
7619  double inner_bottom = innerbox(1);
7620  double inner_width = innerbox(2);
7621  double inner_height = innerbox(3);
7622 
7623  double inner_right = inner_width + inner_left;
7624  double inner_top = inner_height + inner_bottom;
7625 
7626  // FIXME: do we need to place limits on any of these?
7627 
7628  double outer_left = inner_left - left_margin;
7629  double outer_bottom = inner_bottom - bottom_margin;
7630  double outer_right = inner_right + right_margin;
7631  double outer_top = inner_top + top_margin;
7632 
7633  double outer_width = outer_right - outer_left;
7634  double outer_height = outer_top - outer_bottom;
7635 
7636  Matrix outerbox (1, 4);
7637 
7638  outerbox(0) = outer_left;
7639  outerbox(1) = outer_bottom;
7640  outerbox(2) = outer_width;
7641  outerbox(3) = outer_height;
7642 
7643  m_outerposition = outerbox;
7644  }
7645  else
7646  {
7647  Matrix outerbox = m_outerposition.get ().matrix_value ();
7648 
7649  double outer_left = outerbox(0);
7650  double outer_bottom = outerbox(1);
7651  double outer_width = outerbox(2);
7652  double outer_height = outerbox(3);
7653 
7654  double outer_right = outer_width + outer_left;
7655  double outer_top = outer_height + outer_bottom;
7656 
7657  double inner_left = outer_left;
7658  double inner_right = outer_right;
7659 
7660  if ((left_margin + right_margin) < outer_width)
7661  {
7662  inner_left += left_margin;
7663  inner_right -= right_margin;
7664  }
7665 
7666  double inner_bottom = outer_bottom;
7667  double inner_top = outer_top;
7668 
7669  if ((bottom_margin + top_margin) < outer_height)
7670  {
7671  inner_bottom += bottom_margin;
7672  inner_top -= top_margin;
7673  }
7674 
7675  double inner_width = inner_right - inner_left;
7676  double inner_height = inner_top - inner_bottom;
7677 
7678  Matrix innerbox (1, 4);
7679 
7680  innerbox(0) = inner_left;
7681  innerbox(1) = inner_bottom;
7682  innerbox(2) = inner_width;
7683  innerbox(3) = inner_height;
7684 
7685  m_position = innerbox;
7686  }
7687 
7688  set_units (old_units);
7689  update_transform ();
7690 }
7691 
7692 // A translation from Tom Holoryd's python code at
7693 // http://kurage.nimh.nih.gov/tomh/tics.py
7694 // FIXME: add log ticks
7695 
7696 double
7697 axes::properties::calc_tick_sep (double lo, double hi)
7698 {
7699  int ticint = 5;
7700 
7701  // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and SCALE3 for
7702  // Determination of Scales on Computer Generated Plots", Communications of
7703  // the ACM, 10 (1973), 639-640.
7704  // Also cited as ACM Algorithm 463.
7705 
7706  double a;
7707  int b, x;
7708 
7709  magform ((hi - lo) / ticint, a, b);
7710 
7711  static const double sqrt_2 = sqrt (2.0);
7712  static const double sqrt_10 = sqrt (10.0);
7713  static const double sqrt_50 = sqrt (50.0);
7714 
7715  if (a < sqrt_2)
7716  x = 1;
7717  else if (a < sqrt_10)
7718  x = 2;
7719  else if (a < sqrt_50)
7720  x = 5;
7721  else
7722  x = 10;
7723 
7724  return x * std::pow (10., b);
7725 }
7726 
7727 // Attempt to make "nice" limits from the actual max and min of the data.
7728 // For log plots, we will also use the smallest strictly positive value.
7729 
7730 Matrix
7731 axes::properties::get_axis_limits (double xmin, double xmax,
7732  double min_pos, double max_neg,
7733  const bool logscale,
7734  const std::string& method)
7735 {
7736  Matrix retval;
7737 
7738  double min_val = xmin;
7739  double max_val = xmax;
7740 
7741  if (octave::math::isinf (min_val) && min_val > 0
7742  && octave::math::isinf (max_val) && max_val < 0)
7743  {
7744  retval = default_lim (logscale);
7745  return retval;
7746  }
7747  else if (! (octave::math::isinf (min_val) || octave::math::isinf (max_val)))
7748  {
7749  if (logscale)
7750  {
7751  if (octave::math::isinf (min_pos) && octave::math::isinf (max_neg))
7752  {
7753  // FIXME: max_neg is needed for "loglog ([0 -Inf])"
7754  // This is the *only* place where max_neg is needed.
7755  // Is there another way?
7756  retval = default_lim (logscale);
7757  return retval;
7758  }
7759  if (min_val <= 0)
7760  {
7761  if (max_val > 0)
7762  {
7763  warning_with_id ("Octave:negative-data-log-axis",
7764  "axis: omitting non-positive data in log plot");
7765  min_val = min_pos;
7766  }
7767  else if (max_val == 0)
7768  max_val = max_neg;
7769  }
7770  // FIXME: maybe this test should also be relative?
7771  if (std::abs (min_val - max_val)
7772  < sqrt (std::numeric_limits<double>::epsilon ()))
7773  {
7774  // Widen range when too small
7775  if (min_val >= 0)
7776  {
7777  min_val *= 0.9;
7778  max_val *= 1.1;
7779  }
7780  else
7781  {
7782  min_val *= 1.1;
7783  max_val *= 0.9;
7784  }
7785  }
7786 
7787  if (method == "tickaligned")
7788  {
7789  if (min_val > 0)
7790  {
7791  // Log plots with all positive data
7792  min_val = std::pow (10, std::floor (log10 (min_val)));
7793  max_val = std::pow (10, std::ceil (log10 (max_val)));
7794  }
7795  else
7796  {
7797  // Log plots with all negative data
7798  min_val = -std::pow (10, std::ceil (log10 (-min_val)));
7799  max_val = -std::pow (10, std::floor (log10 (-max_val)));
7800  }
7801  }
7802  else if (method == "padded")
7803  {
7804  if (min_val > 0)
7805  {
7806  // Log plots with all positive data
7807  double pad = (log10 (max_val) - log10 (min_val)) * 0.07;
7808  min_val = std::pow (10, log10 (min_val) - pad);
7809  max_val = std::pow (10, log10 (max_val) + pad);
7810  }
7811  else
7812  {
7813  // Log plots with all negative data
7814  double pad = (log10 (-min_val) - log10 (-max_val)) * 0.07;
7815  min_val = -std::pow (10, log10 (-min_val) + pad);
7816  max_val = -std::pow (10, log10 (-max_val) - pad);
7817  }
7818  }
7819  }
7820  else
7821  {
7822  if (min_val == 0 && max_val == 0)
7823  {
7824  min_val = -1;
7825  max_val = 1;
7826  }
7827  // FIXME: maybe this test should also be relative?
7828  else if (std::abs (min_val - max_val)
7829  < sqrt (std::numeric_limits<double>::epsilon ()))
7830  {
7831  min_val -= 0.1 * std::abs (min_val);
7832  max_val += 0.1 * std::abs (max_val);
7833  }
7834 
7835  if (method == "tickaligned")
7836  {
7837  double tick_sep = calc_tick_sep (min_val, max_val);
7838  double min_tick = std::floor (min_val / tick_sep);
7839  double max_tick = std::ceil (max_val / tick_sep);
7840  // Prevent round-off from cropping ticks
7841  min_val = std::min (min_val, tick_sep * min_tick);
7842  max_val = std::max (max_val, tick_sep * max_tick);
7843  }
7844  else if (method == "padded")
7845  {
7846  double pad = 0.07 * (max_val - min_val);
7847  min_val -= pad;
7848  max_val += pad;
7849  }
7850  }
7851  }
7852 
7853  retval.resize (1, 2);
7854 
7855  retval(0) = min_val;
7856  retval(1) = max_val;
7857 
7858  return retval;
7859 }
7860 
7861 void
7862 axes::properties::check_axis_limits (Matrix& limits, const Matrix kids,
7863  const bool logscale, char& update_type)
7864 {
7865  double min_val = octave::numeric_limits<double>::Inf ();
7866  double max_val = -octave::numeric_limits<double>::Inf ();
7867  double min_pos = octave::numeric_limits<double>::Inf ();
7868  double max_neg = -octave::numeric_limits<double>::Inf ();
7869  double eps = std::numeric_limits<double>::epsilon ();
7870  bool do_update = false;
7871  bool have_children_limits = false;
7872 
7873  // check whether we need to get children limits
7874  if (! octave::math::isfinite (limits(0))
7875  || ! octave::math::isfinite (limits(1)))
7876  {
7877  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7878  update_type);
7879  have_children_limits = true;
7880  }
7881  if (! octave::math::isfinite (limits(0)))
7882  {
7883  limits(0) = min_val;
7884  do_update = true;
7885  }
7886  if (! octave::math::isfinite (limits(1)))
7887  {
7888  limits(1) = max_val;
7889  do_update = true;
7890  }
7891  if (limits(0) == 0 && limits(1) == 0)
7892  {
7893  limits = default_lim (logscale);
7894  do_update = true;
7895  }
7896  // FIXME: maybe this test should also be relative?
7897  else if (! logscale && (std::abs (limits(0) - limits(1)) < sqrt (eps)))
7898  {
7899  limits(0) -= 0.1 * std::abs (limits(0));
7900  limits(1) += 0.1 * std::abs (limits(1));
7901  do_update = true;
7902  }
7903  else if (logscale
7904  && (std::abs (std::log10 (limits(0) / limits(1))) < sqrt (eps)))
7905  {
7906  limits(0) = (limits(0) < 0 ? 10.0 * limits(0) : 0.1 * limits(0));
7907  limits(1) = (limits(1) < 0 ? 0.1 * limits(1) : 10.0 * limits(1));
7908  do_update = true;
7909  }
7910 
7911  if (logscale && limits(0)*limits(1) <= 0)
7912  {
7913  if (! have_children_limits)
7914  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7915  update_type);
7916 
7917  if (limits(1) > 0)
7918  {
7919  warning_with_id ("Octave:axis-non-positive-log-limits",
7920  "Non-positive limit for logarithmic axis ignored\n");
7921  if (octave::math::isfinite (min_pos))
7922  limits(0) = min_pos;
7923  else
7924  limits(0) = 0.1 * limits(1);
7925  }
7926  else
7927  {
7928  warning_with_id ("Octave:axis-non-negative-log-limits",
7929  "Non-negative limit for logarithmic axis ignored\n");
7930  if (octave::math::isfinite (max_neg))
7931  limits(1) = max_neg;
7932  else
7933  limits(1) = 0.1 * limits(0);
7934  }
7935  // FIXME: maybe this test should also be relative?
7936  if (std::abs (limits(0) - limits(1)) < sqrt (eps))
7937  {
7938  // Widen range when too small
7939  if (limits(0) > 0)
7940  {
7941  limits(0) *= 0.9;
7942  limits(1) *= 1.1;
7943  }
7944  else
7945  {
7946  limits(0) *= 1.1;
7947  limits(1) *= 0.9;
7948  }
7949  }
7950  do_update = true;
7951  }
7952 
7953  if (! do_update)
7954  update_type = 0;
7955 
7956 }
7957 
7958 /*
7959 ## Test validation of auto and manual axis limits
7960 %!test
7961 %! hf = figure ("visible", "off");
7962 %! unwind_protect
7963 %! hax = axes ("parent", hf);
7964 %! plot (0, pi);
7965 %! assert (get (hax, "xlim"), [-1, 1]);
7966 %! assert (get (hax, "xlimmode"), "auto");
7967 %! assert (get (hax, "ylim"), [2.8, 3.5], 2*eps);
7968 %! assert (get (hax, "ylimmode"), "auto");
7969 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7970 %! assert (get (hax, "xlim"), [0.9, 1.1]);
7971 %! assert (get (hax, "xlimmode"), "manual");
7972 %! assert (get (hax, "ylim"), [0, 1]);
7973 %! assert (get (hax, "ylimmode"), "manual");
7974 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7975 %! ## Matlab does not update the properties
7976 %! assert (get (hax, "xlim"), [0, 1]);
7977 %! assert (get (hax, "ylim"), [0.9, 1.1]*pi, 2*eps);
7978 %! unwind_protect_cleanup
7979 %! delete (hf);
7980 %! end_unwind_protect
7981 
7982 %!test
7983 %! hf = figure ("visible", "off");
7984 %! unwind_protect
7985 %! hax = axes ("parent", hf);
7986 %! plot ([0, exp(1)], [-pi, 3]);
7987 %! assert (get (hax, "xlim"), [0, 3]);
7988 %! assert (get (hax, "xlimmode"), "auto");
7989 %! assert (get (hax, "ylim"), [-4, 3]);
7990 %! assert (get (hax, "ylimmode"), "auto");
7991 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7992 %! ## Matlab does not update the properties but uses tight limits on screen
7993 %! assert (get (hax, "xlim"), [0, exp(1)]);
7994 %! assert (get (hax, "xlimmode"), "manual");
7995 %! assert (get (hax, "ylim"), [-pi, 3]);
7996 %! assert (get (hax, "ylimmode"), "manual");
7997 %! unwind_protect_cleanup
7998 %! delete (hf);
7999 %! end_unwind_protect
8000 
8001 %!test
8002 %! hf = figure ("visible", "off");
8003 %! unwind_protect
8004 %! hax = axes ("parent", hf);
8005 %! loglog (0, pi);
8006 %! assert (get (hax, "xlim"), [0.1, 1.0]);
8007 %! assert (get (hax, "xlimmode"), "auto");
8008 %! assert (get (hax, "ylim"), [1, 10]);
8009 %! assert (get (hax, "ylimmode"), "auto");
8010 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
8011 %! assert (get (hax, "xlim"), [0.1, 10]);
8012 %! assert (get (hax, "xlimmode"), "manual");
8013 %! assert (get (hax, "ylim"), [0.1, 1.0]);
8014 %! assert (get (hax, "ylimmode"), "manual");
8015 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
8016 %! ## Matlab does not update the properties
8017 %! assert (get (hax, "xlim"), [0.1, 1.0]);
8018 %! assert (get (hax, "ylim"), [0.1, 10]*pi);
8019 %! unwind_protect_cleanup
8020 %! delete (hf);
8021 %! end_unwind_protect
8022 
8023 %!test
8024 %! hf = figure ("visible", "off");
8025 %! unwind_protect
8026 %! hax = axes ("parent", hf);
8027 %! loglog ([0 -1], [0 1]);
8028 %! assert (get (hax, "xlim"), [-10, -0.1]);
8029 %! assert (get (hax, "xlimmode"), "auto");
8030 %! assert (get (hax, "ylim"), [0.1, 10]);
8031 %! assert (get (hax, "ylimmode"), "auto");
8032 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
8033 %! ## Matlab does not update the properties
8034 %! assert (get (hax, "xlim"), [-1.1, -0.9]);
8035 %! assert (get (hax, "ylim"), [0.9, 1.1]);
8036 %! unwind_protect_cleanup
8037 %! delete (hf);
8038 %! end_unwind_protect
8039 
8040 %!test
8041 %! hf = figure ("visible", "off");
8042 %! unwind_protect
8043 %! hax = axes ("parent", hf);
8044 %! loglog ([1 -1], [1 pi]);
8045 %! assert (get (hax, "xlim"), [0.1, 10]);
8046 %! assert (get (hax, "xlimmode"), "auto");
8047 %! assert (get (hax, "ylim"), [1, 10]);
8048 %! assert (get (hax, "ylimmode"), "auto");
8049 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
8050 %! ## Matlab does not update the properties but uses tight limits on screen
8051 %! assert (get (hax, "xlim"), [0.9, 1.1]);
8052 %! assert (get (hax, "ylim"), [1, pi]);
8053 %! unwind_protect_cleanup
8054 %! delete (hf);
8055 %! end_unwind_protect
8056 
8057 ## Check that graphics objects with hidden handle visibility are included in
8058 ## axis limit calculation.
8059 %!test <*63095>
8060 %! hf = figure ("visible", "off");
8061 %! unwind_protect
8062 %! hax = axes ("parent", hf);
8063 %! plot (hax, [0, 1]);
8064 %! assert (get (hax, "ylim"), [0, 1]);
8065 %! hold (hax, "on");
8066 %! plot (hax, [2, 0], "handlevisibility", "off");
8067 %! assert (get (hax, "ylim"), [0, 2]);
8068 %! unwind_protect_cleanup
8069 %! delete (hf);
8070 %! end_unwind_protect
8071 
8072 ## Check automatically determined axes ticks with "tickaligned" (default) and
8073 ## "tight" xlimitmethod.
8074 %!test <*63624>
8075 %! hf = figure ("visible", "off");
8076 %! unwind_protect
8077 %! hax = axes ("parent", hf);
8078 %! plot (hax, [1, 201], [0, 1]);
8079 %! assert (get (hax, "xtick"), 0:50:250);
8080 %! axis (hax, "tight");
8081 %! assert (get (hax, "xtick"), 50:50:200);
8082 %! unwind_protect_cleanup
8083 %! delete (hf);
8084 %! end_unwind_protect
8085 */
8086 
8087 void
8088 axes::properties::calc_ticks_and_lims (array_property& lims,
8089  array_property& ticks,
8090  array_property& mticks,
8091  bool limmode_is_auto,
8092  bool tickmode_is_auto,
8093  bool is_logscale,
8094  bool method_is_padded,
8095  bool method_is_tight)
8096 {
8097  if (lims.get ().isempty ())
8098  return;
8099 
8100  double lo = (lims.get ().matrix_value ())(0);
8101  double hi = (lims.get ().matrix_value ())(1);
8102 
8103  double lo_lim = lo;
8104  double hi_lim = hi;
8105  bool is_negative = lo < 0 && hi < 0;
8106 
8107  // FIXME: should this be checked for somewhere else? (i.e., set{x,y,z}lim)
8108  if (hi < lo)
8109  std::swap (hi, lo);
8110 
8111  if (is_logscale)
8112  {
8113  if (is_negative)
8114  {
8115  double tmp = hi;
8116  hi = std::log10 (-lo);
8117  lo = std::log10 (-tmp);
8118  }
8119  else
8120  {
8121  hi = std::log10 (hi);
8122  lo = std::log10 (lo);
8123  }
8124  }
8125 
8126  Matrix tmp_ticks;
8127  if (tickmode_is_auto)
8128  {
8129  double tick_sep;
8130 
8131  if (is_logscale)
8132  {
8133  if (! (octave::math::isinf (hi) || octave::math::isinf (lo)))
8134  tick_sep = 1; // Tick is every order of magnitude (bug #39449)
8135  else
8136  tick_sep = 0;
8137  }
8138  else
8139  tick_sep = calc_tick_sep (lo, hi);
8140 
8141  double i1 = std::floor (lo / tick_sep);
8142  double i2 = std::ceil (hi / tick_sep);
8143 
8144  if (limmode_is_auto)
8145  {
8146  Matrix tmp_lims (1, 2);
8147 
8148  if (! method_is_padded && ! method_is_tight)
8149  {
8150  // Adjust limits to include min and max ticks
8151  tmp_lims(0) = std::min (tick_sep * i1, lo);
8152  tmp_lims(1) = std::max (tick_sep * i2, hi);
8153  }
8154  else
8155  {
8156  tmp_lims(0) = lo;
8157  tmp_lims(1) = hi;
8158 
8159  // adjust min and max ticks to be within limits
8160  if (i1*tick_sep < lo)
8161  i1++;
8162  if (i2*tick_sep > hi && i2 > i1)
8163  i2--;
8164  }
8165 
8166  if (is_logscale)
8167  {
8168  tmp_lims(0) = std::pow (10., tmp_lims(0));
8169  tmp_lims(1) = std::pow (10., tmp_lims(1));
8170 
8171  if (tmp_lims(0) <= 0)
8172  tmp_lims(0) = std::pow (10., lo);
8173 
8174  if (is_negative)
8175  {
8176  double tmp = tmp_lims(0);
8177  tmp_lims(0) = -tmp_lims(1);
8178  tmp_lims(1) = -tmp;
8179  }
8180  }
8181 
8182  lims = tmp_lims;
8183  }
8184  else
8185  {
8186  // adjust min and max ticks to be within limits
8187  if (i1*tick_sep < lo)
8188  i1++;
8189  if (i2*tick_sep > hi && i2 > i1)
8190  i2--;
8191  }
8192 
8193  tmp_ticks = Matrix (1, i2-i1+1);
8194  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
8195  {
8196  tmp_ticks(i) = tick_sep * (i+i1);
8197  if (is_logscale)
8198  tmp_ticks(i) = std::pow (10., tmp_ticks(i));
8199  }
8200  if (is_logscale && is_negative)
8201  {
8202  Matrix rev_ticks (1, i2-i1+1);
8203  rev_ticks = -tmp_ticks;
8204  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
8205  tmp_ticks(i) = rev_ticks(i2-i1-i);
8206  }
8207 
8208  ticks = tmp_ticks;
8209  }
8210  else
8211  tmp_ticks = ticks.get ().matrix_value ();
8212 
8213  octave_idx_type n_ticks = tmp_ticks.numel ();
8214  if (n_ticks < 2)
8215  return;
8216 
8217  // minor ticks between, above, and below min and max ticks
8218  int n = (is_logscale ? 8 : 4);
8219  double mult_below = (is_logscale ? tmp_ticks(1) / tmp_ticks(0) : 1);
8220  double mult_above = (is_logscale ? tmp_ticks(n_ticks-1) / tmp_ticks(n_ticks-2)
8221  : 1);
8222 
8223  double d_below = (tmp_ticks(1) - tmp_ticks(0)) / mult_below / (n+1);
8224  int n_below = static_cast<int> (std::floor ((tmp_ticks(0)-lo_lim) / d_below));
8225  if (n_below < 0)
8226  n_below = 0;
8227  int n_between = n * (n_ticks - 1);
8228  double d_above = (tmp_ticks(n_ticks-1) - tmp_ticks(n_ticks-2)) * mult_above
8229  / (n+1);
8230  int n_above = static_cast<int> (std::floor ((hi_lim-tmp_ticks(n_ticks-1))
8231  / d_above));
8232  if (n_above < 0)
8233  n_above = 0;
8234 
8235  Matrix tmp_mticks (1, n_below + n_between + n_above);
8236  for (int i = 0; i < n_below; i++)
8237  tmp_mticks(i) = tmp_ticks(0) - (n_below-i) * d_below;
8238  for (int i = 0; i < n_ticks-1; i++)
8239  {
8240  double d = (tmp_ticks(i+1) - tmp_ticks(i)) / (n + 1);
8241  for (int j = 0; j < n; j++)
8242  tmp_mticks(n_below+n*i+j) = tmp_ticks(i) + d * (j+1);
8243  }
8244  for (int i = 0; i < n_above; i++)
8245  tmp_mticks(n_below+n_between+i) = tmp_ticks(n_ticks-1) + (i + 1) * d_above;
8246 
8247  mticks = tmp_mticks;
8248 }
8249 
8250 /*
8251 %!test <*45356>
8252 %! hf = figure ("visible", "off");
8253 %! unwind_protect
8254 %! plot (1:10);
8255 %! xlim ([4.75, 8.5]);
8256 %! tics = get (gca, "xtick");
8257 %! assert (tics, [5 6 7 8]);
8258 %! unwind_protect_cleanup
8259 %! close (hf);
8260 %! end_unwind_protect
8261 */
8262 
8263 void
8264 axes::properties::calc_ticklabels (const array_property& ticks,
8265  any_property& labels, bool logscale,
8266  const bool is_origin,
8267  const int other_axislocation,
8268  const array_property& axis_lims)
8269 {
8270  Matrix values = ticks.get ().matrix_value ();
8271  Matrix lims = axis_lims.get ().matrix_value ();
8272  Cell c (values.dims ());
8273  std::ostringstream os;
8274 
8275  // omit tick labels depending on location of other axis
8277  if (get_is2D () && is_origin)
8278  {
8279  if (other_axislocation == 0)
8280  {
8281  omit_ticks(0) = octave::math::max (octave::math::min (0., lims(1)),
8282  lims(0));
8283  }
8284  else if (other_axislocation == 1)
8285  omit_ticks(0) = lims(1);
8286  else if (other_axislocation == -1)
8287  omit_ticks(0) = lims(0);
8288  if (is_box ())
8289  {
8290  omit_ticks(1) = lims(0);
8291  omit_ticks(2) = lims(1);
8292  }
8293  }
8294 
8295  if (logscale)
8296  {
8297  double significand;
8298  double exponent;
8299  double exp_max = 0.0;
8300  double exp_min = 0.0;
8301 
8302  for (int i = 0; i < values.numel (); i++)
8303  {
8304  double exp = std::log10 (values(i));
8305  exp_min = std::min (exp_min, exp);
8306  exp_max = std::max (exp_max, exp);
8307  }
8308 
8309  for (int i = 0; i < values.numel (); i++)
8310  {
8311  bool omit_tick = false;
8312  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
8313  if (values(i) == omit_ticks(i_omit))
8314  omit_tick = true;
8315  if (omit_tick)
8316  {
8317  c(i) = "";
8318  continue;
8319  }
8320 
8321  if (values(i) < 0.0)
8322  exponent = std::floor (std::log10 (-values(i)));
8323  else
8324  exponent = std::floor (std::log10 (values(i)));
8325  significand = values(i) * std::pow (10., -exponent);
8326 
8327  os.str ("");
8328  if ((std::abs (significand) - 1) >
8329  10*std::numeric_limits<double>::epsilon())
8330  os << significand << 'x';
8331  else if (significand < 0)
8332  os << '-';
8333 
8334  os << "10^{";
8335 
8336  if (exponent < 0.0)
8337  {
8338  os << '-';
8339  exponent = -exponent;
8340  }
8341  if (exponent < 10. && (exp_max > 9 || exp_min < -9))
8342  os << '0';
8343  os << exponent << '}';
8344 
8345  if (m_ticklabelinterpreter.is ("latex"))
8346  c(i) = "$" + os.str () + "$";
8347  else
8348  c(i) = os.str ();
8349  }
8350  }
8351  else
8352  {
8353  for (int i = 0; i < values.numel (); i++)
8354  {
8355  bool omit_tick = false;
8356  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
8357  if (values(i) == omit_ticks(i_omit))
8358  omit_tick = true;
8359  if (omit_tick)
8360  c(i) = "";
8361  else
8362  {
8363  os.str ("");
8364  os << values(i);
8365  c(i) = os.str ();
8366  }
8367  }
8368  }
8369 
8370  labels = c;
8371 }
8372 
8373 Matrix
8374 axes::properties::get_ticklabel_extents (const Matrix& ticks,
8375  const string_vector& ticklabels,
8376  const Matrix& limits)
8377 {
8378  Matrix ext (1, 2, 0.0);
8379  double wmax, hmax;
8380  double dpr = device_pixel_ratio (get___myhandle__ ());
8381  wmax = hmax = 0.0;
8382  int n = std::min (ticklabels.numel (), ticks.numel ());
8383  for (int i = 0; i < n; i++)
8384  {
8385  double val = ticks(i);
8386  if (limits(0) <= val && val <= limits(1))
8387  {
8388  std::string label (ticklabels(i));
8389  label.erase (0, label.find_first_not_of (' '));
8390  label = label.substr (0, label.find_last_not_of (' ')+1);
8391 
8392  if (m_txt_renderer.ok ())
8393  {
8394  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
8395 
8396  octave::autolock guard (gh_mgr.graphics_lock ());
8397 
8398  ext = m_txt_renderer.get_extent (label, 0.0,
8399  get_ticklabelinterpreter ());
8400 
8401  wmax = std::max (wmax, ext(0) / dpr);
8402  hmax = std::max (hmax, ext(1) / dpr);
8403  }
8404  else
8405  {
8406  // FIXME: find a better approximation
8407  double fsize = get ("fontsize").double_value ();
8408  int len = label.length ();
8409 
8410  wmax = std::max (wmax, 0.5*fsize*len);
8411  hmax = fsize;
8412  }
8413  }
8414  }
8415 
8416  ext(0) = wmax;
8417  ext(1) = hmax;
8418  return ext;
8419 }
8420 
8421 void
8422 get_children_limits (double& min_val, double& max_val,
8423  double& min_pos, double& max_neg,
8424  const Matrix& kids, char limit_type)
8425 {
8426  octave_idx_type n = kids.numel ();
8427 
8428  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
8429 
8430  switch (limit_type)
8431  {
8432  case 'x':
8433  for (octave_idx_type i = 0; i < n; i++)
8434  {
8435  graphics_object go = gh_mgr.get_object (kids(i));
8436 
8437  if (go.is_xliminclude ())
8438  {
8439  octave_value lim = go.get_xlim ();
8440 
8441  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8442  }
8443  }
8444  break;
8445 
8446  case 'y':
8447  for (octave_idx_type i = 0; i < n; i++)
8448  {
8449  graphics_object go = gh_mgr.get_object (kids(i));
8450 
8451  if (go.is_yliminclude ())
8452  {
8453  octave_value lim = go.get_ylim ();
8454 
8455  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8456  }
8457  }
8458  break;
8459 
8460  case 'z':
8461  for (octave_idx_type i = 0; i < n; i++)
8462  {
8463  graphics_object go = gh_mgr.get_object (kids(i));
8464 
8465  if (go.is_zliminclude ())
8466  {
8467  octave_value lim = go.get_zlim ();
8468 
8469  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8470  }
8471  }
8472  break;
8473 
8474  case 'c':
8475  for (octave_idx_type i = 0; i < n; i++)
8476  {
8477  graphics_object go = gh_mgr.get_object (kids(i));
8478 
8479  if (go.is_climinclude ())
8480  {
8481  octave_value lim = go.get_clim ();
8482 
8483  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8484  }
8485  }
8486  break;
8487 
8488  case 'a':
8489  for (octave_idx_type i = 0; i < n; i++)
8490  {
8491  graphics_object go = gh_mgr.get_object (kids(i));
8492 
8493  if (go.is_aliminclude ())
8494  {
8495  octave_value lim = go.get_alim ();
8496 
8497  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8498  }
8499  }
8500  break;
8501 
8502  default:
8503  break;
8504  }
8505 }
8506 
8507 static std::set<double> updating_axis_limits;
8508 
8509 void
8510 axes::update_axis_limits (const std::string& axis_type,
8511  const graphics_handle& h)
8512 {
8513  if (updating_axis_limits.find (get_handle ().value ())
8514  != updating_axis_limits.end ())
8515  return;
8516 
8517  Matrix kids = Matrix (1, 1, h.value ());
8518 
8519  double min_val = octave::numeric_limits<double>::Inf ();
8520  double max_val = -octave::numeric_limits<double>::Inf ();
8521  double min_pos = octave::numeric_limits<double>::Inf ();
8522  double max_neg = -octave::numeric_limits<double>::Inf ();
8523 
8524  char update_type = 0;
8525 
8526  Matrix limits (1, 2);
8527  double val;
8528 
8529 #define FIX_LIMITS \
8530  val = limits(0); \
8531  if (octave::math::isfinite (val)) \
8532  min_val = val; \
8533  val = limits(1); \
8534  if (octave::math::isfinite (val)) \
8535  max_val = val;
8536 
8537  if (axis_type == "xdata" || axis_type == "xscale"
8538  || axis_type == "xlimmode" || axis_type == "xliminclude"
8539  || axis_type == "xlim")
8540  {
8541  limits = m_properties.get_xlim ().matrix_value ();
8542  FIX_LIMITS;
8543 
8544  update_type = 'x';
8545  if (m_properties.xlimmode_is ("auto"))
8546  {
8547  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8548 
8549  std::string method = m_properties.get_xlimitmethod ();
8550  limits = m_properties.get_axis_limits (min_val, max_val,
8551  min_pos, max_neg,
8552  m_properties.xscale_is ("log"),
8553  method);
8554  }
8555  else
8556  m_properties.check_axis_limits (limits, kids,
8557  m_properties.xscale_is ("log"),
8558  update_type);
8559  }
8560  else if (axis_type == "ydata" || axis_type == "yscale"
8561  || axis_type == "ylimmode" || axis_type == "yliminclude"
8562  || axis_type == "ylim")
8563  {
8564  limits = m_properties.get_ylim ().matrix_value ();
8565  FIX_LIMITS;
8566 
8567  update_type = 'y';
8568  if (m_properties.ylimmode_is ("auto"))
8569  {
8570  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8571 
8572  std::string method = m_properties.get_ylimitmethod ();
8573  limits = m_properties.get_axis_limits (min_val, max_val,
8574  min_pos, max_neg,
8575  m_properties.yscale_is ("log"),
8576  method);
8577  }
8578  else
8579  m_properties.check_axis_limits (limits, kids,
8580  m_properties.yscale_is ("log"),
8581  update_type);
8582  }
8583  else if (axis_type == "zdata" || axis_type == "zscale"
8584  || axis_type == "zlimmode" || axis_type == "zliminclude"
8585  || axis_type == "zlim")
8586  {
8587  limits = m_properties.get_zlim ().matrix_value ();
8588  FIX_LIMITS;
8589 
8590  update_type = 'z';
8591  if (m_properties.zlimmode_is ("auto"))
8592  {
8593  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8594 
8595  m_properties.set_has3Dkids ((max_val - min_val) >
8596  std::numeric_limits<double>::epsilon ());
8597 
8598  std::string method = m_properties.get_zlimitmethod ();
8599  limits = m_properties.get_axis_limits (min_val, max_val,
8600  min_pos, max_neg,
8601  m_properties.zscale_is ("log"),
8602  method);
8603  }
8604  else
8605  {
8606  // FIXME: get_children_limits is only needed here in order to know
8607  // if there are 3D children. Is there a way to avoid this call?
8608  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8609 
8610  m_properties.set_has3Dkids ((max_val - min_val) >
8611  std::numeric_limits<double>::epsilon ());
8612 
8613  m_properties.check_axis_limits (limits, kids,
8614  m_properties.zscale_is ("log"),
8615  update_type);
8616  }
8617  }
8618  else if (axis_type == "cdata" || axis_type == "climmode"
8619  || axis_type == "cdatamapping" || axis_type == "climinclude"
8620  || axis_type == "clim")
8621  {
8622  if (m_properties.climmode_is ("auto"))
8623  {
8624  limits = m_properties.get_clim ().matrix_value ();
8625  FIX_LIMITS;
8626 
8627  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8628 
8629  if (min_val > max_val)
8630  {
8631  min_val = min_pos = 0;
8632  max_val = 1;
8633  }
8634  else if (min_val == max_val)
8635  {
8636  max_val = min_val + 1;
8637  min_val -= 1;
8638  }
8639 
8640  limits(0) = min_val;
8641  limits(1) = max_val;
8642 
8643  update_type = 'c';
8644  }
8645  }
8646  else if (axis_type == "alphadata" || axis_type == "alimmode"
8647  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8648  || axis_type == "alim")
8649  {
8650  if (m_properties.alimmode_is ("auto"))
8651  {
8652  limits = m_properties.get_alim ().matrix_value ();
8653  FIX_LIMITS;
8654 
8655  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8656 
8657  if (min_val > max_val)
8658  {
8659  min_val = min_pos = 0;
8660  max_val = 1;
8661  }
8662  else if (min_val == max_val)
8663  max_val = min_val + 1;
8664 
8665  limits(0) = min_val;
8666  limits(1) = max_val;
8667 
8668  update_type = 'a';
8669  }
8670  }
8671 
8672 #undef FIX_LIMITS
8673 
8674  octave::unwind_protect_var<std::set<double>>
8675  restore_var (updating_axis_limits);
8676 
8677  updating_axis_limits.insert (get_handle ().value ());
8678  bool is_auto;
8679 
8680  switch (update_type)
8681  {
8682  case 'x':
8683  is_auto = m_properties.xlimmode_is ("auto");
8684  m_properties.set_xlim (limits);
8685  if (is_auto)
8686  m_properties.set_xlimmode ("auto");
8687  m_properties.update_xlim ();
8688  break;
8689 
8690  case 'y':
8691  is_auto = m_properties.ylimmode_is ("auto");
8692  m_properties.set_ylim (limits);
8693  if (is_auto)
8694  m_properties.set_ylimmode ("auto");
8695  m_properties.update_ylim ();
8696  break;
8697 
8698  case 'z':
8699  is_auto = m_properties.zlimmode_is ("auto");
8700  m_properties.set_zlim (limits);
8701  if (is_auto)
8702  m_properties.set_zlimmode ("auto");
8703  m_properties.update_zlim ();
8704  break;
8705 
8706  case 'c':
8707  m_properties.set_clim (limits);
8708  m_properties.set_climmode ("auto");
8709  break;
8710 
8711  case 'a':
8712  m_properties.set_alim (limits);
8713  m_properties.set_alimmode ("auto");
8714  break;
8715 
8716  default:
8717  break;
8718  }
8719 
8720  m_properties.update_transform ();
8721 }
8722 
8723 // FIXME: This function is called repeatedly while the axes are being set up.
8724 // There is probably some way to make this more efficient.
8725 
8726 void
8727 axes::update_axis_limits (const std::string& axis_type)
8728 {
8729  if ((updating_axis_limits.find (get_handle ().value ())
8730  != updating_axis_limits.end ())
8731  || (updating_aspectratios.find (get_handle ().value ())
8732  != updating_aspectratios.end ()))
8733  return;
8734 
8735  Matrix kids = m_properties.get_all_children ();
8736 
8737  double min_val = octave::numeric_limits<double>::Inf ();
8738  double max_val = -octave::numeric_limits<double>::Inf ();
8739  double min_pos = octave::numeric_limits<double>::Inf ();
8740  double max_neg = -octave::numeric_limits<double>::Inf ();
8741 
8742  char update_type = 0;
8743 
8744  Matrix limits;
8745 
8746  if (axis_type == "xdata" || axis_type == "xscale"
8747  || axis_type == "xlimmode" || axis_type == "xliminclude"
8748  || axis_type == "xlim")
8749  {
8750  update_type = 'x';
8751  if (m_properties.xlimmode_is ("auto"))
8752  {
8753  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8754 
8755  std::string method = m_properties.get_xlimitmethod ();
8756  limits = m_properties.get_axis_limits (min_val, max_val,
8757  min_pos, max_neg,
8758  m_properties.xscale_is ("log"),
8759  method);
8760  }
8761  else
8762  {
8763  limits = m_properties.get_xlim ().matrix_value ();
8764  m_properties.check_axis_limits (limits, kids,
8765  m_properties.xscale_is ("log"),
8766  update_type);
8767  if (axis_type == "xscale")
8768  update_type = 'x';
8769  }
8770  }
8771  else if (axis_type == "ydata" || axis_type == "yscale"
8772  || axis_type == "ylimmode" || axis_type == "yliminclude"
8773  || axis_type == "ylim")
8774  {
8775  update_type = 'y';
8776  if (m_properties.ylimmode_is ("auto"))
8777  {
8778  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8779 
8780  std::string method = m_properties.get_ylimitmethod ();
8781  limits = m_properties.get_axis_limits (min_val, max_val,
8782  min_pos, max_neg,
8783  m_properties.yscale_is ("log"),
8784  method);
8785  }
8786  else
8787  {
8788  limits = m_properties.get_ylim ().matrix_value ();
8789  m_properties.check_axis_limits (limits, kids,
8790  m_properties.yscale_is ("log"),
8791  update_type);
8792  if (axis_type == "yscale")
8793  update_type = 'y';
8794  }
8795  }
8796  else if (axis_type == "zdata" || axis_type == "zscale"
8797  || axis_type == "zlimmode" || axis_type == "zliminclude"
8798  || axis_type == "zlim")
8799  {
8800  update_type = 'z';
8801  if (m_properties.zlimmode_is ("auto"))
8802  {
8803  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8804 
8805  m_properties.set_has3Dkids ((max_val - min_val) >
8806  std::numeric_limits<double>::epsilon ());
8807 
8808  // FIXME: How to correctly handle (positive or negative) log scale?
8809  if ((! octave::math::isfinite (min_val)
8810  || ! octave::math::isfinite (max_val))
8811  && ! m_properties.zscale_is ("log"))
8812  min_val = max_val = 0.;
8813 
8814  std::string method = m_properties.get_zlimitmethod ();
8815  limits = m_properties.get_axis_limits (min_val, max_val,
8816  min_pos, max_neg,
8817  m_properties.zscale_is ("log"),
8818  method);
8819  }
8820  else
8821  {
8822  // FIXME: get_children_limits is only needed here in order to know
8823  // if there are 3D children. Is there a way to avoid this call?
8824  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8825 
8826  m_properties.set_has3Dkids ((max_val - min_val) >
8827  std::numeric_limits<double>::epsilon ());
8828 
8829  limits = m_properties.get_zlim ().matrix_value ();
8830  m_properties.check_axis_limits (limits, kids,
8831  m_properties.zscale_is ("log"),
8832  update_type);
8833  if (axis_type == "zscale")
8834  update_type = 'z';
8835  }
8836  }
8837  else if (axis_type == "cdata" || axis_type == "climmode"
8838  || axis_type == "cdatamapping" || axis_type == "climinclude"
8839  || axis_type == "clim")
8840  {
8841  if (m_properties.climmode_is ("auto"))
8842  {
8843  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8844 
8845  if (min_val > max_val)
8846  {
8847  min_val = min_pos = 0;
8848  max_val = 1;
8849  }
8850  else if (min_val == max_val)
8851  {
8852  max_val = min_val + 1;
8853  min_val -= 1;
8854  }
8855 
8856  limits.resize (1, 2);
8857 
8858  limits(0) = min_val;
8859  limits(1) = max_val;
8860 
8861  update_type = 'c';
8862  }
8863 
8864  }
8865  else if (axis_type == "alphadata" || axis_type == "alimmode"
8866  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8867  || axis_type == "alim")
8868  {
8869  if (m_properties.alimmode_is ("auto"))
8870  {
8871  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8872 
8873  if (min_val > max_val)
8874  {
8875  min_val = min_pos = 0;
8876  max_val = 1;
8877  }
8878  else if (min_val == max_val)
8879  max_val = min_val + 1;
8880 
8881  limits.resize (1, 2);
8882 
8883  limits(0) = min_val;
8884  limits(1) = max_val;
8885 
8886  update_type = 'a';
8887  }
8888 
8889  }
8890 
8891  octave::unwind_protect_var<std::set<double>>
8892  restore_var (updating_axis_limits);
8893 
8894  updating_axis_limits.insert (get_handle ().value ());
8895  bool is_auto;
8896 
8897  switch (update_type)
8898  {
8899  case 'x':
8900  is_auto = m_properties.xlimmode_is ("auto");
8901  m_properties.set_xlim (limits);
8902  if (is_auto)
8903  m_properties.set_xlimmode ("auto");
8904  m_properties.update_xlim ();
8905  break;
8906 
8907  case 'y':
8908  is_auto = m_properties.ylimmode_is ("auto");
8909  m_properties.set_ylim (limits);
8910  if (is_auto)
8911  m_properties.set_ylimmode ("auto");
8912  m_properties.update_ylim ();
8913  break;
8914 
8915  case 'z':
8916  is_auto = m_properties.zlimmode_is ("auto");
8917  m_properties.set_zlim (limits);
8918  if (is_auto)
8919  m_properties.set_zlimmode ("auto");
8920  m_properties.update_zlim ();
8921  break;
8922 
8923  case 'c':
8924  m_properties.set_clim (limits);
8925  m_properties.set_climmode ("auto");
8926  break;
8927 
8928  case 'a':
8929  m_properties.set_alim (limits);
8930  m_properties.set_alimmode ("auto");
8931  break;
8932 
8933  default:
8934  break;
8935  }
8936 
8937  m_properties.update_transform ();
8938 }
8939 
8940 inline
8941 double force_in_range (double x, double lower, double upper)
8942 {
8943  if (x < lower)
8944  return lower;
8945  else if (x > upper)
8946  return upper;
8947  else
8948  return x;
8949 }
8950 
8951 static Matrix
8952 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
8953 {
8954  Matrix new_lims = lims;
8955 
8956  double lo = lims(0);
8957  double hi = lims(1);
8958 
8959  bool is_negative = lo < 0 && hi < 0;
8960 
8961  if (is_logscale)
8962  {
8963  if (is_negative)
8964  {
8965  double tmp = hi;
8966  hi = std::log10 (-lo);
8967  lo = std::log10 (-tmp);
8968  val = std::log10 (-val);
8969  }
8970  else
8971  {
8972  hi = std::log10 (hi);
8973  lo = std::log10 (lo);
8974  val = std::log10 (val);
8975  }
8976  }
8977 
8978  // Perform the zooming
8979  lo = val + (lo - val) / factor;
8980  hi = val + (hi - val) / factor;
8981 
8982  if (is_logscale)
8983  {
8984  if (is_negative)
8985  {
8986  double tmp = -std::pow (10.0, hi);
8987  hi = -std::pow (10.0, lo);
8988  lo = tmp;
8989  }
8990  else
8991  {
8992  lo = std::pow (10.0, lo);
8993  hi = std::pow (10.0, hi);
8994  }
8995  }
8996 
8997  new_lims(0) = lo;
8998  new_lims(1) = hi;
8999 
9000  return new_lims;
9001 }
9002 
9003 void
9004 axes::properties::zoom_about_point (const std::string& mode,
9005  double x, double y, double factor,
9006  bool push_to_zoom_stack)
9007 {
9008  // FIXME: Do we need error checking here?
9009  Matrix xlims = get_xlim ().matrix_value ();
9010  Matrix ylims = get_ylim ().matrix_value ();
9011 
9012  // Get children axes limits
9013  Matrix kids = get_children ();
9014  double minx = octave::numeric_limits<double>::Inf ();
9015  double maxx = -octave::numeric_limits<double>::Inf ();
9016  double min_pos_x = octave::numeric_limits<double>::Inf ();
9017  double max_neg_x = -octave::numeric_limits<double>::Inf ();
9018  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
9019 
9020  double miny = octave::numeric_limits<double>::Inf ();
9021  double maxy = -octave::numeric_limits<double>::Inf ();
9022  double min_pos_y = octave::numeric_limits<double>::Inf ();
9023  double max_neg_y = -octave::numeric_limits<double>::Inf ();
9024  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
9025 
9026  xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
9027  ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
9028 
9029  zoom (mode, xlims, ylims, push_to_zoom_stack);
9030 }
9031 
9032 void
9033 axes::properties::zoom (const std::string& mode, double factor,
9034  bool push_to_zoom_stack)
9035 {
9036  // FIXME: Do we need error checking here?
9037  Matrix xlims = get_xlim ().matrix_value ();
9038  Matrix ylims = get_ylim ().matrix_value ();
9039 
9040  double x = (xlims(0) + xlims(1)) / 2;
9041  double y = (ylims(0) + ylims(1)) / 2;
9042 
9043  zoom_about_point (mode, x, y, factor, push_to_zoom_stack);
9044 }
9045 
9046 void
9047 axes::properties::push_zoom_stack (void)
9048 {
9049  if (m_zoom_stack.empty ())
9050  {
9051  m_zoom_stack.push_front (m_xlimmode.get ());
9052  m_zoom_stack.push_front (m_xlim.get ());
9053  m_zoom_stack.push_front (m_ylimmode.get ());
9054  m_zoom_stack.push_front (m_ylim.get ());
9055  m_zoom_stack.push_front (m_zlimmode.get ());
9056  m_zoom_stack.push_front (m_zlim.get ());
9057  m_zoom_stack.push_front (m_view.get ());
9058  }
9059 }
9060 
9061 void
9062 axes::properties::zoom (const std::string& mode,
9063  const Matrix& xl, const Matrix& yl,
9064  bool push_to_zoom_stack)
9065 {
9066  if (xl(0) == xl(1) || yl(0) == yl(1))
9067  {
9068  warning ("invalid zoom region");
9069  return;
9070  }
9071 
9072  if (push_to_zoom_stack)
9073  push_zoom_stack ();
9074 
9075  if (mode == "horizontal" || mode == "both")
9076  {
9077  m_xlim = xl;
9078  m_xlimmode = "manual";
9079  }
9080 
9081  if (mode == "vertical" || mode == "both")
9082  {
9083  m_ylim = yl;
9084  m_ylimmode = "manual";
9085  }
9086 
9087  update_transform ();
9088 
9089  if (mode == "horizontal" || mode == "both")
9090  update_xlim ();
9091 
9092  if (mode == "vertical" || mode == "both")
9093  update_ylim ();
9094 }
9095 
9096 static Matrix
9097 do_translate (double x0, double x1, const Matrix& lims, bool is_logscale)
9098 {
9099  Matrix new_lims = lims;
9100 
9101  double lo = lims(0);
9102  double hi = lims(1);
9103 
9104  bool is_negative = lo < 0 && hi < 0;
9105 
9106  double delta;
9107 
9108  if (is_logscale)
9109  {
9110  if (is_negative)
9111  {
9112  double tmp = hi;
9113  hi = std::log10 (-lo);
9114  lo = std::log10 (-tmp);
9115  x0 = -x0;
9116  x1 = -x1;
9117  }
9118  else
9119  {
9120  hi = std::log10 (hi);
9121  lo = std::log10 (lo);
9122  }
9123 
9124  delta = std::log10 (x0) - std::log10 (x1);
9125  }
9126  else
9127  {
9128  delta = x0 - x1;
9129  }
9130 
9131  // Perform the translation
9132  lo += delta;
9133  hi += delta;
9134 
9135  if (is_logscale)
9136  {
9137  if (is_negative)
9138  {
9139  double tmp = -std::pow (10.0, hi);
9140  hi = -std::pow (10.0, lo);
9141  lo = tmp;
9142  }
9143  else
9144  {
9145  lo = std::pow (10.0, lo);
9146  hi = std::pow (10.0, hi);
9147  }
9148  }
9149 
9150  new_lims(0) = lo;
9151  new_lims(1) = hi;
9152 
9153  return new_lims;
9154 }
9155 
9156 void
9157 axes::properties::translate_view (const std::string& mode,
9158  double x0, double x1, double y0, double y1,
9159  bool push_to_zoom_stack)
9160 {
9161  // FIXME: Do we need error checking here?
9162  Matrix xlims = get_xlim ().matrix_value ();
9163  Matrix ylims = get_ylim ().matrix_value ();
9164 
9165  // Get children axes limits
9166  Matrix kids = get_children ();
9167  double minx = octave::numeric_limits<double>::Inf ();
9168  double maxx = -octave::numeric_limits<double>::Inf ();
9169  double min_pos_x = octave::numeric_limits<double>::Inf ();
9170  double max_neg_x = -octave::numeric_limits<double>::Inf ();
9171  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
9172 
9173  double miny = octave::numeric_limits<double>::Inf ();
9174  double maxy = -octave::numeric_limits<double>::Inf ();
9175  double min_pos_y = octave::numeric_limits<double>::Inf ();
9176  double max_neg_y = -octave::numeric_limits<double>::Inf ();
9177  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
9178 
9179  xlims = do_translate (x0, x1, xlims, xscale_is ("log"));
9180  ylims = do_translate (y0, y1, ylims, yscale_is ("log"));
9181 
9182  zoom (mode, xlims, ylims, push_to_zoom_stack);
9183 }
9184 
9185 void
9186 axes::properties::pan (const std::string& mode, double factor,
9187  bool push_to_zoom_stack)
9188 {
9189  // FIXME: Do we need error checking here?
9190  Matrix xlims = get_xlim ().matrix_value ();
9191  Matrix ylims = get_ylim ().matrix_value ();
9192 
9193  double x0 = (xlims(0) + xlims(1)) / 2;
9194  double y0 = (ylims(0) + ylims(1)) / 2;
9195 
9196  double x1 = x0 + (xlims(1) - xlims(0)) * factor;
9197  double y1 = y0 + (ylims(1) - ylims(0)) * factor;
9198 
9199  translate_view (mode, x0, x1, y0, y1, push_to_zoom_stack);
9200 }
9201 
9202 void
9203 axes::properties::rotate3d (double x0, double x1, double y0, double y1,
9204  bool push_to_zoom_stack)
9205 {
9206  if (push_to_zoom_stack)
9207  push_zoom_stack ();
9208 
9209  Matrix bb = get_boundingbox (true);
9210  Matrix new_view = get_view ().matrix_value ();
9211 
9212  // Compute new view angles
9213  new_view(0) += ((x0 - x1) * (180.0 / bb(2)));
9214  new_view(1) += ((y1 - y0) * (180.0 / bb(3)));
9215 
9216  // Clipping
9217  new_view(1) = std::min (new_view(1), 90.0);
9218  new_view(1) = std::max (new_view(1), -90.0);
9219  if (new_view(0) > 180.0)
9220  new_view(0) -= 360.0;
9221  else if (new_view(0) < -180.0)
9222  new_view(0) += 360.0;
9223 
9224  // Snapping
9225  double snapmargin = 1.0;
9226  for (int a = -90; a <= 90; a += 90)
9227  {
9228  if ((a - snapmargin) < new_view(1) && new_view(1) < (a + snapmargin))
9229  {
9230  new_view(1) = a;
9231  break;
9232  }
9233  }
9234 
9235  for (int a = -180; a <= 180; a += 180)
9236  if ((a - snapmargin) < new_view(0) && new_view(0) < (a + snapmargin))
9237  {
9238  if (a == 180)
9239  new_view(0) = -180;
9240  else
9241  new_view(0) = a;
9242  break;
9243  }
9244 
9245  // Update axes properties
9246  set_view (new_view);
9247 }
9248 
9249 void
9250 axes::properties::rotate_view (double delta_el, double delta_az,
9251  bool push_to_zoom_stack)
9252 {
9253  if (push_to_zoom_stack)
9254  push_zoom_stack ();
9255 
9256  Matrix v = get_view ().matrix_value ();
9257 
9258  v(1) += delta_el;
9259 
9260  if (v(1) > 90)
9261  v(1) = 90;
9262  if (v(1) < -90)
9263  v(1) = -90;
9264 
9265  v(0) = fmod (v(0) - delta_az + 720, 360);
9266 
9267  set_view (v);
9268 
9269  update_transform ();
9270 }
9271 
9272 void
9273 axes::properties::unzoom (void)
9274 {
9275  if (m_zoom_stack.size () >= 7)
9276  {
9277  m_view = m_zoom_stack.front ();
9278  m_zoom_stack.pop_front ();
9279 
9280  m_zlim = m_zoom_stack.front ();
9281  m_zoom_stack.pop_front ();
9282 
9283  m_zlimmode = m_zoom_stack.front ();
9284  m_zoom_stack.pop_front ();
9285 
9286  m_ylim = m_zoom_stack.front ();
9287  m_zoom_stack.pop_front ();
9288 
9289  m_ylimmode = m_zoom_stack.front ();
9290  m_zoom_stack.pop_front ();
9291 
9292  m_xlim = m_zoom_stack.front ();
9293  m_zoom_stack.pop_front ();
9294 
9295  m_xlimmode = m_zoom_stack.front ();
9296  m_zoom_stack.pop_front ();
9297 
9298  update_transform ();
9299 
9300  update_xlim ();
9301  update_ylim ();
9302  update_zlim ();
9303 
9304  update_view ();
9305  }
9306 }
9307 
9308 void
9309 axes::properties::update_handlevisibility (void)
9310 {
9311  if (! is_handle_visible ())
9312  {
9313  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9314 
9315  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9316 
9317  graphics_object fig (go.get_ancestor ("figure"));
9318  octave_value ca = fig.get ("currentaxes");
9319  if (! ca.isempty () && ca.double_value () == m___myhandle__)
9320  {
9321  octave::autolock guard (gh_mgr.graphics_lock ());
9322 
9323  octave_value kids = fig.get ("children");
9324  if (kids.isempty ())
9325  fig.set ("currentaxes", Matrix ());
9326  else
9327  {
9328  NDArray kidsarray = kids.array_value ();
9329  fig.set ("currentaxes", kidsarray(0));
9330  }
9331  }
9332  }
9333 
9334  base_properties::update_handlevisibility ();
9335 }
9336 
9337 void
9338 figure::properties::init_toolkit (void)
9339 {
9340  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
9341 
9342  m_toolkit = gtk_mgr.get_toolkit ();
9343 }
9344 
9345 void
9346 axes::properties::clear_zoom_stack (bool do_unzoom)
9347 {
9348  std::size_t items_to_leave_on_stack = (do_unzoom ? 7 : 0);
9349 
9350  while (m_zoom_stack.size () > items_to_leave_on_stack)
9351  m_zoom_stack.pop_front ();
9352 
9353  if (do_unzoom)
9354  unzoom ();
9355 }
9356 
9357 void
9358 axes::properties::trigger_normals_calc (void)
9359 {
9360  // Find all patch (and surface) objects within axes
9361  std::list<graphics_object> children_list;
9362  std::list<graphics_object>::iterator children_list_iter;
9363  get_children_of_type ("patch", false, true, children_list);
9364  get_children_of_type ("surface", false, true, children_list);
9365 
9366  // trigger normals calculation for these objects
9367  for (children_list_iter = children_list.begin ();
9368  children_list_iter != children_list.end (); children_list_iter++)
9369  {
9370  graphics_object kid = *children_list_iter;
9371  if (kid.isa ("patch"))
9372  {
9373  patch::properties& patch_props
9374  = dynamic_cast<patch::properties&> (kid.get_properties ());
9375  patch_props.update_normals (false);
9376  }
9377  else
9378  {
9379  surface::properties& surface_props
9380  = dynamic_cast<surface::properties&> (kid.get_properties ());
9381  surface_props.update_normals (false);
9382  }
9383  }
9384 }
9385 
9386 void
9387 axes::reset_default_properties (void)
9388 {
9389  // empty list of local defaults
9390  m_default_properties = property_list ();
9391 
9392  // Save warning state of "Octave:deprecated-property"
9393  int state = toggle_warn ("Octave:deprecated-property", false);
9394 
9395  // reset factory defaults
9396  remove_all_listeners ();
9397  set_defaults ("reset");
9398 
9399  toggle_warn ("Octave:deprecated-property", true, state);
9400 }
9401 
9402 void
9403 axes::initialize (const graphics_object& go)
9404 {
9406 
9407  xinitialize (m_properties.get_title ());
9408  xinitialize (m_properties.get_xlabel ());
9409  xinitialize (m_properties.get_ylabel ());
9410  xinitialize (m_properties.get_zlabel ());
9411 
9412  m_properties.sync_positions ();
9413 }
9414 
9415 // ---------------------------------------------------------------------
9416 
9417 Matrix
9418 line::properties::compute_xlim (void) const
9419 {
9420  Matrix m (1, 4);
9421 
9422  m(0) = m_xdata.min_val ();
9423  m(1) = m_xdata.max_val ();
9424  m(2) = m_xdata.min_pos ();
9425  m(3) = m_xdata.max_neg ();
9426 
9427  return m;
9428 }
9429 
9430 Matrix
9431 line::properties::compute_ylim (void) const
9432 {
9433  Matrix m (1, 4);
9434 
9435  m(0) = m_ydata.min_val ();
9436  m(1) = m_ydata.max_val ();
9437  m(2) = m_ydata.min_pos ();
9438  m(3) = m_ydata.max_neg ();
9439 
9440  return m;
9441 }
9442 
9443 // ---------------------------------------------------------------------
9444 
9445 Matrix
9446 text::properties::get_data_position (void) const
9447 {
9448  Matrix pos = get_position ().matrix_value ();
9449 
9450  if (! units_is ("data"))
9451  pos = convert_text_position (pos, *this, get_units (), "data");
9452 
9453  return pos;
9454 }
9455 
9456 Matrix
9457 text::properties::get_extent_matrix (bool rotated) const
9458 {
9459  // FIXME: Should this function also add the (x,y) base position?
9460  Matrix ext = m_extent.get ().matrix_value ();
9461 
9462  if (rotated && get_rotation () != 0)
9463  {
9464  double rot = get_rotation () * 4.0 * atan (1.0) / 180;
9465  double x0 = ext(0) * cos (rot) - ext(1) * sin (rot);
9466  double x1 = x0;
9467  double y0 = ext(0) * sin (rot) + ext(1) * cos (rot);
9468  double y1 = y0;
9469 
9470  double tmp = (ext(0)+ext(2)) * cos (rot) - ext(1) * sin (rot);
9471  x0 = std::min (x0, tmp);
9472  x1 = std::max (x1, tmp);
9473  tmp = (ext(0)+ext(2)) * sin (rot) + ext(1) * cos (rot);
9474  y0 = std::min (y0, tmp);
9475  y1 = std::max (y1, tmp);
9476 
9477  tmp = (ext(0)+ext(2)) * cos (rot) - (ext(1)+ext(3)) * sin (rot);
9478  x0 = std::min (x0, tmp);
9479  x1 = std::max (x1, tmp);
9480  tmp = (ext(0)+ext(2)) * sin (rot) + (ext(1)+ext(3)) * cos (rot);
9481  y0 = std::min (y0, tmp);
9482  y1 = std::max (y1, tmp);
9483 
9484  tmp = ext(0) * cos (rot) - (ext(1)+ext(3)) * sin (rot);
9485  x0 = std::min (x0, tmp);
9486  x1 = std::max (x1, tmp);
9487  tmp = ext(0) * sin (rot) + (ext(1)+ext(3)) * cos (rot);
9488  y0 = std::min (y0, tmp);
9489  y1 = std::max (y1, tmp);
9490 
9491  ext(0) = x0;
9492  ext(1) = y0;
9493  ext(2) = x1 - x0;
9494  ext(3) = y1 - y0;
9495  }
9496 
9497  return ext;
9498 }
9499 
9501 text::properties::get_extent (void) const
9502 {
9503  // FIXME: This doesn't work right for 3D plots.
9504  // (It doesn't in Matlab either, at least not in version 6.5.)
9505  Matrix m = get_extent_matrix (true);
9506  Matrix pos = get_position ().matrix_value ();
9507  Matrix p = convert_text_position (pos, *this, get_units (), "pixels");
9508 
9509  m(0) += p(0);
9510  m(1) += p(1);
9511 
9512  Matrix bbox = convert_text_position (m, *this, "pixels", get_units ());
9513 
9514  double dpr = device_pixel_ratio (get___myhandle__ ());
9515 
9516  for (octave_idx_type ii = 0; ii < bbox.numel (); ii++)
9517  bbox(ii) = bbox(ii) / dpr;
9518 
9519  return bbox;
9520 }
9521 
9522 void
9523 text::properties::set_fontunits (const octave_value& val)
9524 {
9525  caseless_str old_fontunits = get_fontunits ();
9526 
9527  if (m_fontunits.set (val, true))
9528  {
9529  update_fontunits (old_fontunits);
9530  mark_modified ();
9531  }
9532 }
9533 
9534 void
9535 text::properties::update_fontunits (const caseless_str& old_units)
9536 {
9537  caseless_str new_units = get_fontunits ();
9538  double parent_height = 0;
9539  double fontsz = get_fontsize ();
9540 
9541  if (new_units == "normalized" || old_units == "normalized")
9542  {
9543  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9544 
9545  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9546 
9547  graphics_object ax (go.get_ancestor ("axes"));
9548 
9549  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
9550  }
9551 
9552  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
9553 
9554  set_fontsize (octave_value (fontsz));
9555 }
9556 
9557 void
9558 text::properties::update_font (void)
9559 {
9560  double dpr = device_pixel_ratio (get___myhandle__ ());
9561 
9562  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9563 
9564  octave::autolock guard (gh_mgr.graphics_lock ());
9565 
9566  m_txt_renderer.set_font (get ("fontname").string_value (),
9567  get ("fontweight").string_value (),
9568  get ("fontangle").string_value (),
9569  get ("__fontsize_points__").double_value () * dpr);
9570 
9571  m_txt_renderer.set_anti_aliasing (is_fontsmoothing ());
9572 
9573  Matrix c = get_color_rgb ();
9574  if (! c.isempty ())
9575  m_txt_renderer.set_color (c);
9576 
9577 }
9578 
9579 void
9580 text::properties::update_text_extent (void)
9581 {
9582  int halign = 0;
9583  int valign = 0;
9584 
9585  if (horizontalalignment_is ("center"))
9586  halign = 1;
9587  else if (horizontalalignment_is ("right"))
9588  halign = 2;
9589 
9590  if (verticalalignment_is ("middle"))
9591  valign = 1;
9592  else if (verticalalignment_is ("top"))
9593  valign = 2;
9594  else if (verticalalignment_is ("baseline"))
9595  valign = 3;
9596  else if (verticalalignment_is ("cap"))
9597  valign = 4;
9598 
9599  Matrix bbox;
9600 
9601  // FIXME: string should be parsed only when modified, for efficiency
9602 
9603  octave_value string_prop = get_string ();
9604 
9605  string_vector sv = string_prop.string_vector_value ();
9606 
9607  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9608 
9609  octave::autolock guard (gh_mgr.graphics_lock ());
9610 
9611  m_txt_renderer.text_to_pixels (sv.join ("\n"), m_pixels, bbox,
9612  halign, valign, 0.0, get_interpreter ());
9613  // The bbox is relative to the text's position. We'll leave it that
9614  // way, because get_position does not return valid results when the
9615  // text is first constructed. Conversion to proper coordinates is
9616  // performed in get_extent.
9617  set_extent (bbox);
9618 
9619  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
9620  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
9621  update_autopos ("sync");
9622 }
9623 
9624 void
9625 text::properties::request_autopos (void)
9626 {
9627  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
9628  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
9629  update_autopos (get___autopos_tag__ ());
9630 }
9631 
9632 void
9633 text::properties::update_units (void)
9634 {
9635  if (! units_is ("data"))
9636  {
9637  set_xliminclude ("off");
9638  set_yliminclude ("off");
9639  set_zliminclude ("off");
9640  }
9641 
9642  Matrix pos = get_position ().matrix_value ();
9643 
9644  pos = convert_text_position (pos, *this, m_cached_units, get_units ());
9645 
9646  // FIXME: if the current axes view is 2D, then one should probably drop
9647  // the z-component of "pos" and leave "zliminclude" to "off".
9648 
9649  bool autopos = positionmode_is ("auto");
9650 
9651  set_position (pos);
9652 
9653  if (autopos)
9654  set_positionmode ("auto");
9655 
9656  if (units_is ("data"))
9657  {
9658  set_xliminclude ("on");
9659  set_yliminclude ("on");
9660  // FIXME: see above
9661  set_zliminclude ("off");
9662  }
9663 
9664  m_cached_units = get_units ();
9665 }
9666 
9667 double
9668 text::properties::get___fontsize_points__ (double box_pix_height) const
9669 {
9670  double fontsz = get_fontsize ();
9671  double parent_height = box_pix_height;
9672 
9673  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9674 
9675  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9676 
9677  if (fontunits_is ("normalized") && parent_height <= 0)
9678  {
9679  graphics_object ax (go.get_ancestor ("axes"));
9680 
9681  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
9682  }
9683 
9684  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
9685 }
9686 
9687 // ---------------------------------------------------------------------
9688 
9690 image::properties::get_color_data (void) const
9691 {
9692  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
9693 }
9694 
9695 // ---------------------------------------------------------------------
9696 
9697 void
9698 light::initialize (const graphics_object& go)
9699 {
9701 
9702  // trigger normals calculation for the respective children of this axes object
9703  axes::properties& parent_axes_prop
9704  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
9705  parent_axes_prop.trigger_normals_calc ();
9706 }
9707 
9708 void
9709 light::properties::update_visible (void)
9710 {
9711  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9712 
9713  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
9714 
9715  axes::properties& ax_props = dynamic_cast<axes::properties&>
9716  (go.get_ancestor ("axes").get_properties ());
9717  if (is_visible ())
9718  ax_props.increase_num_lights ();
9719  else
9720  ax_props.decrease_num_lights ();
9721 }
9722 
9723 // ---------------------------------------------------------------------
9724 
9725 bool
9726 patch::properties::get_do_lighting (void) const
9727 {
9728  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
9729 
9730  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
9731 
9732  axes::properties& ax_props = dynamic_cast<axes::properties&>
9733  (go.get_ancestor ("axes").get_properties ());
9734 
9735  return (ax_props.get_num_lights () > 0);
9736 }
9737 
9739 patch::properties::get_color_data (void) const
9740 {
9741  octave_value fvc = get_facevertexcdata ();
9742  if (fvc.is_undefined () || fvc.isempty ())
9743  return Matrix ();
9744  else
9745  return convert_cdata (*this, fvc, cdatamapping_is ("scaled"), 2);
9746 }
9747 
9748 static bool updating_patch_data = false;
9749 
9750 void
9751 patch::properties::update_fvc (void)
9752 {
9753  if (updating_patch_data)
9754  return;
9755 
9756  Matrix xd = get_xdata ().matrix_value ();
9757  Matrix yd = get_ydata ().matrix_value ();
9758  Matrix zd = get_zdata ().matrix_value ();
9759  NDArray cd = get_cdata ().array_value ();
9760 
9761  m_bad_data_msg = "";
9762  if (xd.dims () != yd.dims ()
9763  || (xd.dims () != zd.dims () && ! zd.isempty ()))
9764  {
9765  m_bad_data_msg = "x/y/zdata must have the same dimensions";
9766  return;
9767  }
9768 
9769  // Faces and Vertices
9770  dim_vector dv;
9771  bool is3D = false;
9772  octave_idx_type nr = xd.rows ();
9773  octave_idx_type nc = xd.columns ();
9774  if (nr == 1 && nc > 1)
9775  {
9776  nr = nc;
9777  nc = 1;
9778  xd = xd.as_column ();
9779  yd = yd.as_column ();
9780  zd = zd.as_column ();
9781  }
9782 
9783  dv(0) = nr * nc;
9784  if (zd.isempty ())
9785  dv(1) = 2;
9786  else
9787  {
9788  dv(1) = 3;
9789  is3D = true;
9790  }
9791 
9792  Matrix vert (dv);
9793  Matrix idx (nc, nr);
9794 
9795  octave_idx_type kk = 0;
9796  for (octave_idx_type jj = 0; jj < nc; jj++)
9797  {
9798  for (octave_idx_type ii = 0; ii < nr; ii++)
9799  {
9800  vert(kk, 0) = xd(ii, jj);
9801  vert(kk, 1) = yd(ii, jj);
9802  if (is3D)
9803  vert(kk, 2) = zd(ii, jj);
9804 
9805  idx(jj, ii) = static_cast<double> (kk+1);
9806 
9807  kk++;
9808  }
9809  }
9810 
9811  // facevertexcdata
9812  Matrix fvc;
9813  if (cd.ndims () == 3)
9814  {
9815  dv(0) = cd.rows () * cd.columns ();
9816  dv(1) = cd.dims ()(2);
9817  fvc = cd.reshape (dv);
9818  }
9819  else
9820  fvc = cd.as_column ();
9821 
9822  // FIXME: shouldn't we update facevertexalphadata here ?
9823 
9824  octave::unwind_protect_var<bool> restore_var (updating_patch_data, true);
9825 
9826  m_faces.set (idx);
9827  m_vertices.set (vert);
9828  m_facevertexcdata.set (fvc);
9829 }
9830 
9831 // core coplanar tester
9832 bool is_coplanar (const Matrix& cov)
9833 {
9834  // Accuracy note: this test will also accept single precision input (although
9835  // stored in double precision). This is because the error threshold is
9836  // sqrt(tol) = 1.5e-7.
9837  double tol = 100 * std::numeric_limits<double>::epsilon ();
9838  EIG eig (cov, false, false, true);
9839  ColumnVector ev = real (eig.eigenvalues ());
9840  return ev.min () <= tol * ev.max ();
9841 }
9842 
9843 std::vector<octave_idx_type>
9844 coplanar_partition (const Matrix& vert, const Matrix& idx,
9846 {
9847  std::vector<octave_idx_type> coplanar_ends;
9848 
9849  Matrix plane_pivot = Matrix (1, 3, 0.0);
9850  for (octave_idx_type i = 0; i < 3; i++)
9851  plane_pivot(0, i) = vert(idx(0, jj)-1, i);
9852 
9853  Matrix fc = Matrix (0, 3, 0.0); // face corner vertex coordinates
9854  Matrix fa = Matrix (1, 3, 0.0); // for append face corner
9855  Matrix coor_cov = Matrix (3, 3, 0.0);
9856 
9857  if (nc >= 5)
9858  {
9859  // Coplanar test that involves all points.
9860  // For nc == 4, this initial test is not beneficial at all.
9861  // If the probability of coplanar input is more than half, for
9862  // the best average performance, we should use nc >= 5.
9863  // Higher threshold is meaningful only when input is known to be
9864  // non-coplanar and nc is small.
9865 
9866  fc.resize (nc - 1, 3);
9867  for (octave_idx_type j = 1; j < nc; j++)
9868  for (octave_idx_type i = 0; i < 3; i++)
9869  fc(j-1, i) = vert(idx(j, jj)-1, i) - plane_pivot(i);
9870 
9871  coor_cov = fc.transpose () * fc;
9872  if (is_coplanar (coor_cov))
9873  {
9874  coplanar_ends.push_back (nc - 1);
9875  return coplanar_ends;
9876  }
9877  }
9878 
9879  fc.resize (3, 3);
9880  octave_idx_type i_start = 1;
9881  octave_idx_type i_end = 2;
9882 
9883  // Split the polygon into coplanar segments.
9884  // The first point is common corner of all planes.
9885  while (i_start < nc - 1)
9886  {
9887  i_end = i_start + 2;
9888  if (i_end > nc - 1)
9889  {
9890  coplanar_ends.push_back (nc - 1);
9891  break;
9892  }
9893 
9894  // Algorithm: Start from 3 points, keep adding points until the point set
9895  // is no more in a plane. Record the coplanar point set, then advance
9896  // i_start.
9897 
9898  // Prepare 1+3 points for coplanar test.
9899  // The first point is implicitly included.
9900  for (octave_idx_type j = 0; j < 3; j++)
9901  for (octave_idx_type i = 0; i < 3; i++)
9902  fc(j, i) = vert(idx(j+i_start, jj)-1, i) - plane_pivot(i);
9903 
9904  // covariance matrix between coordinates of vertices
9905  coor_cov = fc.transpose () * fc;
9906 
9907  while (true)
9908  {
9909  // coplanar test
9910  if (! is_coplanar (coor_cov))
9911  break;
9912 
9913  i_end++;
9914  if (i_end > nc - 1)
9915  break;
9916 
9917  // add a point to plane
9918  for (octave_idx_type i = 0; i < 3; i++)
9919  fa(0, i) = vert(idx(i_end, jj)-1, i) - plane_pivot(i);
9920  coor_cov += fa.transpose () * fa;
9921  }
9922 
9923  i_start = i_end - 1;
9924  coplanar_ends.push_back (i_start);
9925  }
9926  return coplanar_ends;
9927 }
9928 
9929 void
9930 patch::properties::update_data (void)
9931 {
9932  if (updating_patch_data)
9933  return;
9934 
9935  Matrix idx = get_faces ().matrix_value ().transpose ();
9936  Matrix vert = get_vertices ().matrix_value ();
9937  NDArray fvc = get_facevertexcdata ().array_value ();
9938 
9939  octave_idx_type nfaces = idx.columns ();
9940  octave_idx_type nvert = vert.rows ();
9941 
9942  // Check all vertices in faces are defined
9943  m_bad_data_msg = "";
9944  if (static_cast<double> (nvert) < idx.row_max ().max ())
9945  {
9946  m_bad_data_msg = R"(some vertices in "faces" property are undefined)";
9947  return;
9948  }
9949 
9950  // Replace NaNs
9951  if (idx.any_element_is_inf_or_nan ())
9952  {
9953  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
9954  {
9955  double valid_vert = idx(0, jj);
9956  bool turn_valid = false;
9957  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
9958  {
9959  if (octave::math::isnan (idx(ii, jj)) || turn_valid)
9960  {
9961  idx(ii, jj) = valid_vert;
9962  turn_valid = true;
9963  }
9964  else
9965  valid_vert = idx(ii, jj);
9966  }
9967  }
9968  }
9969 
9970  // check coplanarity for 3D-faces with more than 3 corners
9971  int fcmax = idx.rows ();
9972  if (fcmax > 3 && vert.columns () > 2
9973  && ! (facecolor_is ("none") && edgecolor_is ("none")))
9974  {
9975  m_coplanar_last_idx.resize (idx.columns ());
9976  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
9977  {
9978  if (octave::math::isnan (idx(3, jj)))
9979  continue;
9980 
9981  // find first element that is NaN to get number of corners
9982  octave_idx_type nc = 3;
9983  while (nc < fcmax && ! octave::math::isnan (idx(nc, jj)))
9984  nc++;
9985 
9986  // If any of the corners is NaN or Inf, skip coplanar test.
9987  // FIXME: Add support for non-coplanar faces with unclosed contour.
9988  bool is_unclosed = false;
9989  for (octave_idx_type j = 0; j < nc; j++)
9990  {
9991  const octave_idx_type k = idx(j, jj) - 1;
9992  if (! (octave::math::isfinite (vert(k, 0))
9993  && octave::math::isfinite (vert(k, 1))
9994  && octave::math::isfinite (vert(k, 2))))
9995  {
9996  is_unclosed = true;
9997  break;
9998  }
9999  }
10000  if (is_unclosed)
10001  continue;
10002 
10003  m_coplanar_last_idx[jj] = coplanar_partition (vert, idx, nc, jj);
10004  }
10005  }
10006  else
10007  m_coplanar_last_idx.resize (0);
10008 
10009  // Build cdata
10010  dim_vector dv = dim_vector::alloc (3);
10011  NDArray cd;
10012  bool pervertex = false;
10013 
10014  if (fvc.rows () == nfaces || fvc.rows () == 1)
10015  {
10016  dv(0) = 1;
10017  dv(1) = fvc.rows ();
10018  dv(2) = fvc.columns ();
10019  cd = fvc.reshape (dv);
10020  }
10021  else
10022  {
10023  if (! fvc.isempty ())
10024  {
10025  dv(0) = idx.rows ();
10026  dv(1) = nfaces;
10027  dv(2) = fvc.columns ();
10028  cd.resize (dv);
10029  pervertex = true;
10030  }
10031  }
10032 
10033  // Build x,y,zdata and eventually per vertex cdata
10034  Matrix xd (idx.dims ());
10035  Matrix yd (idx.dims ());
10036  Matrix zd;
10037  bool has_zd = false;
10038  if (vert.columns () > 2)
10039  {
10040  zd = Matrix (idx.dims ());
10041  has_zd = true;
10042  }
10043 
10044  for (octave_idx_type jj = 0; jj < nfaces; jj++)
10045  {
10046  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
10047  {
10048  octave_idx_type row = static_cast<octave_idx_type> (idx(ii, jj)-1);
10049  xd(ii, jj) = vert(row, 0);
10050  yd(ii, jj) = vert(row, 1);
10051 
10052  if (has_zd)
10053  zd(ii, jj) = vert(row, 2);
10054 
10055  if (pervertex)
10056  for (int kk = 0; kk < fvc.columns (); kk++)
10057  cd(ii, jj, kk) = fvc(row, kk);
10058  }
10059  }
10060 
10061  // Update normals
10062  update_normals (true);
10063 
10064  octave::unwind_protect_var<bool> restore_var (updating_patch_data, true);
10065 
10066  set_xdata (xd);
10067  set_ydata (yd);
10068  set_zdata (zd);
10069  set_cdata (cd);
10070 }
10071 
10072 inline void
10073 cross_product (double x1, double y1, double z1,
10074  double x2, double y2, double z2,
10075  double& x, double& y, double& z)
10076 {
10077  x += (y1 * z2 - z1 * y2);
10078  y += (z1 * x2 - x1 * z2);
10079  z += (x1 * y2 - y1 * x2);
10080 }
10081 
10082 void
10083 patch::properties::calc_face_normals (Matrix& fn)
10084 {
10085  Matrix v = get_vertices ().matrix_value ();
10086  Matrix f = get_faces ().matrix_value ();
10087 
10088  bool is_3D = (v.columns () == 3); // 2D or 3D patches
10089  octave_idx_type num_f = f.rows (); // number of faces
10090  octave_idx_type max_nc = f.columns (); // max. number of polygon corners
10091 
10092  // In which cases can we skip updating the normals?
10093  if (max_nc < 3)
10094  {
10095  fn = Matrix ();
10096  return;
10097  }
10098 
10099  // Calculate normals for all faces
10100  octave_idx_type i1, i2, i3;
10101  octave_idx_type j1, j2;
10102  for (octave_idx_type i = 0; i < num_f; i++)
10103  {
10104  bool is_coplanar = true;
10105  if (m_coplanar_last_idx.size () > 0 && m_coplanar_last_idx[i].size () > 1)
10106  is_coplanar = false;
10107 
10108  // get number of corners
10109  octave_idx_type nc = 3;
10110  if (max_nc > 3)
10111  {
10112  while (nc < max_nc && ! octave::math::isnan (f(i, nc)))
10113  nc++;
10114  }
10115 
10116  RowVector fnc (3, 0.0);
10117  double& nx = fnc(0);
10118  double& ny = fnc(1);
10119  double& nz = fnc(2);
10120 
10121  if (is_coplanar)
10122  {
10123  // fast way for coplanar polygons
10124  i1 = f(i, 0) - 1; i2 = f(i, 1) - 1; i3 = f(i, nc-1) - 1;
10125 
10126  if (is_3D)
10128  (v(i3, 0) - v(i1, 0), v(i3, 1) - v(i1, 1), v(i3, 2) - v(i1, 2),
10129  v(i2, 0) - v(i1, 0), v(i2, 1) - v(i1, 1), v(i2, 2) - v(i1, 2),
10130  nx, ny, nz);
10131  else
10132  {
10133  nz = (v(i2, 0) - v(i1, 0)) * (v(i3, 1) - v(i1, 1)) -
10134  (v(i2, 1) - v(i1, 1)) * (v(i3, 0) - v(i1, 0));
10135  // 2-d vertices always point towards +z
10136  nz = (nz < 0) ? -nz : nz;
10137  }
10138  }
10139  else
10140  {
10141  // more general for non-planar polygons
10142 
10143  // calculate face normal with Newell's method
10144  // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method
10145 
10146  j1 = nc - 1; j2 = 0;
10147  i1 = f(i, j1) - 1; i2 = f(i, j2) - 1;
10148 
10149  nx = (v(i2, 1) - v(i1, 1)) * (v(i1, 2) + v(i2, 2));
10150  ny = (v(i2, 2) - v(i1, 2)) * (v(i1, 0) + v(i2, 0));
10151  nz = (v(i2, 0) - v(i1, 0)) * (v(i1, 1) + v(i2, 1));
10152 
10153  for (octave_idx_type j = 1; j < nc; j++)
10154  {
10155  j1 = j-1; j2 = j;
10156  i1 = f(i, j1) - 1; i2 = f(i, j2) - 1;
10157 
10158  nx += (v(i2, 1) - v(i1, 1)) * (v(i1, 2) + v(i2, 2));
10159  ny += (v(i2, 2) - v(i1, 2)) * (v(i1, 0) + v(i2, 0));
10160  nz += (v(i2, 0) - v(i1, 0)) * (v(i1, 1) + v(i2, 1));
10161  }
10162  }
10163 
10164  // normalize normal vector
10165  double n_len = sqrt (nx*nx+ny*ny+nz*nz);
10166 
10167  // assign normal to current face
10168  if ( n_len < std::numeric_limits<double>::epsilon () )
10169  for (octave_idx_type j = 0; j < 3; j++)
10170  fn(i, j) = 0.0;
10171  else
10172  for (octave_idx_type j = 0; j < 3; j++)
10173  fn(i, j) = fnc(j) / n_len;
10174  }
10175 }
10176 
10177 void
10178 patch::properties::update_face_normals (bool reset, bool force)
10179 {
10180  if (updating_patch_data || ! facenormalsmode_is ("auto"))
10181  return;
10182 
10183  if (force || ((facelighting_is ("flat") || edgelighting_is ("flat"))
10184  && get_do_lighting ()))
10185  {
10186  Matrix f = get_faces ().matrix_value ();
10187 
10188  octave_idx_type num_f = f.rows (); // number of faces
10189  Matrix fn (num_f, 3, 0.0);
10190 
10191  calc_face_normals (fn);
10192  m_facenormals = fn;
10193  }
10194  else if (reset)
10195  m_facenormals = Matrix ();
10196 }
10197 
10198 void
10199 patch::properties::update_vertex_normals (bool reset, bool force)
10200 {
10201  if (updating_patch_data || ! vertexnormalsmode_is ("auto"))
10202  return;
10203 
10204  if (force || ((facelighting_is ("gouraud") || facelighting_is ("phong")
10205  || edgelighting_is ("gouraud") || edgelighting_is ("phong"))
10206  && get_do_lighting ()))
10207  {
10208  Matrix v = get_vertices ().matrix_value ();
10209  Matrix f = get_faces ().matrix_value ();
10210 
10211  octave_idx_type num_v = v.rows (); // number of vertices
10212  octave_idx_type num_f = f.rows (); // number of faces
10213  octave_idx_type max_nc = f.columns (); // max. number of polygon corners
10214 
10215  // In which cases can we skip updating the normals?
10216  if (max_nc < 3)
10217  return;
10218 
10219  // First step: Calculate the normals for all faces
10220  Matrix fn = get_facenormals ().matrix_value ();
10221  if ( fn.isempty () )
10222  {
10223  // calculate facenormals here
10224  fn = Matrix (num_f, 3, 0.0);
10225  calc_face_normals (fn);
10226  }
10227 
10228  // Second step: assign normals to the respective vertices
10229 
10230  // The following code collects the face normals for all faces adjacent to
10231  // each vertex. For this, a std::vector of length NUM_V (which might be
10232  // very large) is used so that memory is allocated from the heap rather
10233  // than the stack. Each element of this vector corresponds to one vertex
10234  // of the patch. The element itself is a variable length std::vector.
10235  // This second vector contains the face normals (of type RowVector) of
10236  // the adjacent faces.
10237  std::vector<std::vector<RowVector>> vec_vn (num_v);
10238  for (octave_idx_type i = 0; i < num_f; i++)
10239  {
10240  // get number of corners
10241  octave_idx_type nc = 3;
10242  if (max_nc > 3)
10243  {
10244  while (nc < max_nc && ! octave::math::isnan (f(i, nc)))
10245  nc++;
10246  }
10247 
10248  for (octave_idx_type j = 0; j < nc; j++)
10249  vec_vn[static_cast<octave_idx_type> (f(i, j) - 1)].push_back (fn.row (i));
10250  }
10251 
10252  // Third step: Calculate the normal for the vertices taking the average
10253  // of the normals determined from all adjacent faces
10254  Matrix vn (num_v, 3, 0.0);
10255  for (octave_idx_type i = 0; i < num_v; i++)
10256  {
10257  std::vector<RowVector>::iterator it = vec_vn[i].begin ();
10258 
10259  // The normal of unused vertices is NaN.
10260  RowVector vn0 (3, octave_NaN);
10261 
10262  if (it != vec_vn[i].end ())
10263  {
10264  // FIXME: Currently, the first vector also determines the
10265  // direction of the normal. How to determine the inner and outer
10266  // faces of all parts of the patch and point the normals outwards?
10267  // (Necessary for correct lighting with "backfacelighting" set to
10268  // "lit" or "unlit".) Matlab does not seem to do it correctly
10269  // either. So should we bother?
10270 
10271  vn0 = *it;
10272 
10273  for (++it; it != vec_vn[i].end (); ++it)
10274  {
10275  RowVector vn1 = *it;
10276  // Use sign of dot product to point vectors in a similar
10277  // direction before taking the average.
10278  double dir = (vn0(0)*vn1(0) + vn0(1)*vn1(1) + vn0(2)*vn1(2) < 0) ? -1 : 1;
10279  for (octave_idx_type j = 0; j < 3; j++)
10280  vn0(j) += dir * vn1(j);
10281  }
10282 
10283  // normalize normal vector
10284  double n_len = sqrt (vn0(0)*vn0(0)+vn0(1)*vn0(1)+vn0(2)*vn0(2));
10285 
10286  // save normal in matrix
10287  for (octave_idx_type j = 0; j < 3; j++)
10288  vn(i, j) = vn0(j)/n_len;
10289  }
10290  }
10291 
10292  m_vertexnormals = vn;
10293  }
10294  else if (reset)
10295  m_vertexnormals = Matrix ();
10296 }
10297 
10298 void
10299 patch::initialize (const graphics_object& go)
10300 {
10302 
10303  // calculate normals for default data
10304  // This is done because the normals for the default data do not match
10305  // get(0, "DefaultPatchVertexNormals") in Matlab.
10306  m_properties.update_normals (true);
10307 }
10308 
10309 
10310 void
10311 patch::reset_default_properties (void)
10312 {
10313  // empty list of local defaults
10314  m_default_properties = property_list ();
10315  xreset_default_properties (get_handle (), m_properties.factory_defaults ());
10316 
10317  // calculate normals for default data
10318  // This is done because the normals for the default data do not match
10319  // get(0, "DefaultPatchVertexNormals") in Matlab.
10320  m_properties.update_normals (true);
10321 }
10322 
10323 // ---------------------------------------------------------------------
10324 
10326 scatter::properties::get_color_data (void) const
10327 {
10328  octave_value c = get_cdata ();
10329  if (c.is_undefined () || c.isempty ())
10330  return Matrix ();
10331  else
10332  return convert_cdata (*this, c, c.columns () == 1, 2);
10333 }
10334 
10335 void
10336 scatter::properties::update_data (void)
10337 {
10338  Matrix xd = get_xdata ().matrix_value ();
10339  Matrix yd = get_ydata ().matrix_value ();
10340  Matrix zd = get_zdata ().matrix_value ();
10341  Matrix cd = get_cdata ().matrix_value ();
10342  Matrix sd = get_sizedata ().matrix_value ();
10343 
10344  m_bad_data_msg = "";
10345  if (xd.dims () != yd.dims ()
10346  || (xd.dims () != zd.dims () && ! zd.isempty ()))
10347  {
10348  m_bad_data_msg = "x/y/zdata must have the same dimensions";
10349  return;
10350  }
10351 
10352  octave_idx_type x_rows = xd.rows ();
10353  octave_idx_type c_cols = cd.columns ();
10354  octave_idx_type c_rows = cd.rows ();
10355 
10356  if (! cd.isempty () && (c_rows != 1 || c_cols != 3)
10357  && (c_rows != x_rows || (c_cols != 1 && c_cols != 3)))
10358  {
10359  m_bad_data_msg = "cdata must be an rgb triplet or have the same number "
10360  "of rows as X and one or three columns";
10361  return;
10362  }
10363 
10364  octave_idx_type s_rows = sd.rows ();
10365  if (s_rows != 1 && s_rows != x_rows)
10366  {
10367  m_bad_data_msg = "sizedata must be a scalar or a vector with the same "
10368  "dimensions as X";
10369  return;
10370  }
10371 }
10372 
10373 static bool updating_scatter_cdata = false;
10374 
10375 void
10376 scatter::properties::update_color (void)
10377 {
10379  return;
10380 
10381  Matrix series_idx = get_seriesindex ().matrix_value ();
10382  if (series_idx.isempty ())
10383  return;
10384 
10385  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
10386 
10387  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10388 
10389  axes::properties& parent_axes_prop
10390  = dynamic_cast<axes::properties&>
10391  (go.get_ancestor ("axes").get_properties ());
10392 
10393  Matrix color_order = parent_axes_prop.get_colororder ().matrix_value ();
10394  octave_idx_type s = (static_cast<octave_idx_type> (series_idx(0)) - 1)
10395  % color_order.rows ();
10396 
10397  Matrix color = Matrix (1, 3, 0.);
10398  color(0) = color_order(s, 0);
10399  color(1) = color_order(s, 1);
10400  color(2) = color_order(s, 2);
10401 
10402  octave::unwind_protect_var<bool> restore_var (updating_scatter_cdata, true);
10403 
10404  set_cdata (color);
10405  set_cdatamode ("auto");
10406 }
10407 
10408 void
10409 scatter::initialize (const graphics_object& go)
10410 {
10412 
10413  Matrix series_idx = m_properties.get_seriesindex ().matrix_value ();
10414  if (series_idx.isempty ())
10415  {
10416  // Increment series index counter in parent axes
10417  axes::properties& parent_axes_prop
10418  = dynamic_cast<axes::properties&>
10419  (go.get_ancestor ("axes").get_properties ());
10420 
10421  if (! parent_axes_prop.nextplot_is ("add"))
10422  parent_axes_prop.set_nextseriesindex (1);
10423 
10424  series_idx.resize (1, 1);
10425  series_idx(0) = parent_axes_prop.get_nextseriesindex ();
10426  m_properties.set_seriesindex (series_idx);
10427 
10428  parent_axes_prop.set_nextseriesindex
10429  (parent_axes_prop.get_nextseriesindex () + 1);
10430  }
10431 
10432  if (m_properties.cdatamode_is ("auto"))
10433  m_properties.update_color ();
10434 }
10435 
10436 // ---------------------------------------------------------------------
10437 
10439 surface::properties::get_color_data (void) const
10440 {
10441  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
10442 }
10443 
10444 bool
10445 surface::properties::get_do_lighting (void) const
10446 {
10447  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
10448 
10449  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10450 
10451  axes::properties& ax_prop = dynamic_cast<axes::properties&>
10452  (go.get_ancestor ("axes").get_properties ());
10453 
10454  return (ax_prop.get_num_lights () > 0);
10455 }
10456 
10457 void
10458 surface::properties::update_face_normals (bool reset, bool force)
10459 {
10460  if (! facenormalsmode_is ("auto"))
10461  return;
10462 
10463  if (force || ((facelighting_is ("flat") || edgelighting_is ("flat"))
10464  && get_do_lighting ()))
10465  {
10466  Matrix x = get_xdata ().matrix_value ();
10467  Matrix y = get_ydata ().matrix_value ();
10468  Matrix z = get_zdata ().matrix_value ();
10469 
10470  int p = z.columns ();
10471  int q = z.rows ();
10472 
10473  // FIXME: There might be a cleaner way to do this. When data is changed
10474  // the update_xdata, update_ydata, update_zdata routines are called in a
10475  // serial fashion. Until the final call to update_zdata the matrices
10476  // will be of mismatched dimensions which can cause an out-of-bound
10477  // indexing in the code below. This one-liner prevents calculating
10478  // normals until dimensions match.
10479  if (x.columns () != p || y.rows () != q)
10480  return;
10481 
10482  bool x_mat = (x.rows () == q);
10483  bool y_mat = (y.columns () == p);
10484 
10485  NDArray n (dim_vector (q-1, p-1, 3), 1);
10486 
10487  int i1, i2, j1, j2;
10488  i1 = i2 = 0;
10489  j1 = j2 = 0;
10490  double x0, x1, x2, x3, y0, y1, y2, y3, z0, z1, z2, z3;
10491  double x1m0, x2m1, x3m2, x0m3, y1m0, y2m1, y3m2, y0m3;
10492  double x1p0, x2p1, x3p2, x0p3, y1p0, y2p1, y3p2, y0p3;
10493  x3m2 = y0m3 = -1;
10494  x2m1 = x0m3 = y1m0 = y3m2 = 0;
10495  x1m0 = y2m1 = 1;
10496  x0p3 = y1p0 = 0;
10497  x1p0 = x3p2 = y2p1 = y0p3 = 1;
10498  x2p1 = y3p2 = 2;
10499 
10500  for (int i = 0; i < p-1; i++)
10501  {
10502  i1 = i;
10503  i2 = i + 1;
10504 
10505  for (int j = 0; j < q-1; j++)
10506  {
10507  j1 = j;
10508  j2 = j + 1;
10509 
10510  if (x_mat || y_mat)
10511  {
10512  x0 = x(x_mat?j1:0, y_mat?i1:0);
10513  x1 = x(x_mat?j1:0, y_mat?i2:0);
10514  x2 = x(x_mat?j2:0, y_mat?i2:0);
10515  x3 = x(x_mat?j2:0, y_mat?i1:0);
10516  x1m0 = x1 - x0;
10517  x2m1 = x2 - x1;
10518  x3m2 = x3 - x2;
10519  x0m3 = x0 - x3;
10520  x1p0 = x1 + x0;
10521  x2p1 = x2 + x1;
10522  x3p2 = x3 + x2;
10523  x0p3 = x0 + x3;
10524  y0 = y(x_mat?j1:0, y_mat?i1:0);
10525  y1 = y(x_mat?j1:0, y_mat?i2:0);
10526  y2 = y(x_mat?j2:0, y_mat?i2:0);
10527  y3 = y(x_mat?j2:0, y_mat?i1:0);
10528  y1m0 = y1 - y0;
10529  y2m1 = y2 - y1;
10530  y3m2 = y3 - y2;
10531  y0m3 = y0 - y3;
10532  y1p0 = y1 + y0;
10533  y2p1 = y2 + y1;
10534  y3p2 = y3 + y2;
10535  y0p3 = y0 + y3;
10536  }
10537 
10538  double& nx = n(j, i, 0);
10539  double& ny = n(j, i, 1);
10540  double& nz = n(j, i, 2);
10541 
10542  z0 = z(j1, i1);
10543  z1 = z(j1, i2);
10544  z2 = z(j2, i2);
10545  z3 = z(j2, i1);
10546 
10547  // calculate face normal with Newell's method
10548  // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method
10549 
10550  nx = y1m0 * (z1 + z0) + y2m1 * (z2 + z1)
10551  + y3m2 * (z3 + z2) + y0m3 * (z0 + z3);
10552  ny = (z1 - z0) * x1p0 + (z2 - z1) * x2p1
10553  + (z3 - z2) * x3p2 + (z0 - z3) * x0p3;
10554  nz = x1m0 * y1p0 + x2m1 * y2p1 + x3m2 * y3p2 + x0m3 * y0p3;
10555 
10556  double d = std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
10557 
10558  nx /= d;
10559  ny /= d;
10560  nz /= d;
10561  }
10562  }
10563  m_facenormals = n;
10564  }
10565  else if (reset)
10566  m_facenormals = Matrix ();
10567 }
10568 
10569 void
10570 surface::properties::update_vertex_normals (bool reset, bool force)
10571 {
10572  if (! vertexnormalsmode_is ("auto"))
10573  return;
10574 
10575  if (force || ((facelighting_is ("gouraud") || facelighting_is ("phong")
10576  || edgelighting_is ("gouraud") || edgelighting_is ("phong"))
10577  && get_do_lighting ()))
10578  {
10579  Matrix x = get_xdata ().matrix_value ();
10580  Matrix y = get_ydata ().matrix_value ();
10581  Matrix z = get_zdata ().matrix_value ();
10582 
10583  int p = z.columns ();
10584  int q = z.rows ();
10585 
10586  // FIXME: There might be a cleaner way to do this. When data is changed
10587  // the update_xdata, update_ydata, update_zdata routines are called in a
10588  // serial fashion. Until the final call to update_zdata the matrices
10589  // will be of mismatched dimensions which can cause an out-of-bound
10590  // indexing in the code below. This one-liner prevents calculating
10591  // normals until dimensions match.
10592  if (x.columns () != p || y.rows () != q)
10593  return;
10594 
10595  NDArray n (dim_vector (q, p, 3), 0.0);
10596 
10597  bool x_mat = (x.rows () == q);
10598  bool y_mat = (y.columns () == p);
10599 
10600  int i1, i2, i3, j1, j2, j3;
10601  i1 = i2 = i3 = 0;
10602  j1 = j2 = j3 = 0;
10603 
10604  for (int i = 0; i < p; i++)
10605  {
10606  if (y_mat)
10607  {
10608  i1 = i - 1;
10609  i2 = i;
10610  i3 = i + 1;
10611  }
10612 
10613  for (int j = 0; j < q; j++)
10614  {
10615  if (x_mat)
10616  {
10617  j1 = j - 1;
10618  j2 = j;
10619  j3 = j + 1;
10620  }
10621 
10622  double& nx = n(j, i, 0);
10623  double& ny = n(j, i, 1);
10624  double& nz = n(j, i, 2);
10625 
10626  if ((j > 0) && (i > 0))
10627  // upper left quadrangle
10629  (x(j1, i-1)-x(j2, i), y(j-1, i1)-y(j, i2), z(j-1, i-1)-z(j, i),
10630  x(j2, i-1)-x(j1, i), y(j, i1)-y(j-1, i2), z(j, i-1)-z(j-1, i),
10631  nx, ny, nz);
10632 
10633  if ((j > 0) && (i < (p -1)))
10634  // upper right quadrangle
10636  (x(j1, i+1)-x(j2, i), y(j-1, i3)-y(j, i2), z(j-1, i+1)-z(j, i),
10637  x(j1, i)-x(j2, i+1), y(j-1, i2)-y(j, i3), z(j-1, i)-z(j, i+1),
10638  nx, ny, nz);
10639 
10640  if ((j < (q - 1)) && (i > 0))
10641  // lower left quadrangle
10643  (x(j2, i-1)-x(j3, i), y(j, i1)-y(j+1, i2), z(j, i-1)-z(j+1, i),
10644  x(j3, i-1)-x(j2, i), y(j+1, i1)-y(j, i2), z(j+1, i-1)-z(j, i),
10645  nx, ny, nz);
10646 
10647  if ((j < (q - 1)) && (i < (p -1)))
10648  // lower right quadrangle
10650  (x(j3, i)-x(j2, i+1), y(j+1, i2)-y(j, i3), z(j+1, i)-z(j, i+1),
10651  x(j3, i+1)-x(j2, i), y(j+1, i3)-y(j, i2), z(j+1, i+1)-z(j, i),
10652  nx, ny, nz);
10653 
10654  double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
10655 
10656  nx /= d;
10657  ny /= d;
10658  nz /= d;
10659  }
10660  }
10661  m_vertexnormals = n;
10662  }
10663  else if (reset)
10664  m_vertexnormals = Matrix ();
10665 }
10666 
10667 DEFMETHOD (__update_normals__, interp, args, ,
10668  doc: /* -*- texinfo -*-
10669 @deftypefn {} {} __update_normals__ (@var{h})
10670 Update FaceNormals and VertexNormals of the patch or surface referred to by
10671 @var{h}.
10672 
10673 @end deftypefn */)
10674 {
10675  gh_manager& gh_mgr = interp.get_gh_manager ();
10676 
10677  octave::autolock guard (gh_mgr.graphics_lock ());
10678 
10679  if (args.length () != 1)
10680  print_usage ();
10681 
10682  octave_value val = args(0);
10683 
10684  graphics_object go = gh_mgr.get_object (val);
10685 
10686  if (go.isa ("surface"))
10687  {
10688  surface::properties& props
10689  = dynamic_cast <surface::properties&> (go.get_properties ());
10690  props.update_normals (false, true);
10691  }
10692  else if (go.isa ("patch"))
10693  {
10694  patch::properties& props
10695  = dynamic_cast <patch::properties&> (go.get_properties ());
10696  props.update_normals (false, true);
10697  }
10698  else
10699  error ("__update_normals__: "
10700  "H must be a handle to a valid surface or patch object.");
10701 
10702  return ovl ();
10703 }
10704 
10705 /*
10706 %!test
10707 %! hf = figure ("visible", "off");
10708 %! unwind_protect
10709 %! Z = peaks ();
10710 %! hs = surf (Z, "facelighting", "none");
10711 %! assert (isempty (get (hs, "vertexnormals")));
10712 %! assert (isempty (get (hs, "facenormals")));
10713 %! __update_normals__ (hs);
10714 %! assert (! isempty (get (hs, "vertexnormals")));
10715 %! assert (! isempty (get (hs, "facenormals")));
10716 %! unwind_protect_cleanup
10717 %! close (hf);
10718 %! end_unwind_protect
10719 
10720 %!test
10721 %! hf = figure ("visible", "off");
10722 %! unwind_protect
10723 %! hp = patch ("facelighting", "none");
10724 %! assert (isempty (get (hp, "vertexnormals")));
10725 %! assert (isempty (get (hp, "facenormals")));
10726 %! __update_normals__ (hp);
10727 %! assert (! isempty (get (hp, "vertexnormals")));
10728 %! assert (! isempty (get (hp, "facenormals")));
10729 %! unwind_protect_cleanup
10730 %! close (hf);
10731 %! end_unwind_protect
10732 */
10733 
10734 // ---------------------------------------------------------------------
10735 
10736 void
10737 hggroup::properties::remove_child (const graphics_handle& h, bool from_root)
10738 {
10739  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
10740 
10741  graphics_object go = gh_mgr.get_object (h);
10742 
10743  if (! from_root && go.isa ("light") && go.get_properties ().is_visible ())
10744  {
10745  axes::properties& ax_props
10746  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
10747  ax_props.decrease_num_lights ();
10748  }
10749  base_properties::remove_child (h, from_root);
10750  update_limits ();
10751 }
10752 
10753 void
10755 {
10756  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
10757 
10758  graphics_object go = gh_mgr.get_object (h);
10759 
10760  if (go.isa ("light") && go.get_properties ().is_visible ())
10761  {
10762  axes::properties& ax_props
10763  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
10764  ax_props.increase_num_lights ();
10765  }
10767  update_limits (h);
10768 }
10769 
10770 void
10771 hggroup::properties::update_limits (void) const
10772 {
10773  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
10774 
10775  graphics_object go = gh_mgr.get_object (m___myhandle__);
10776 
10777  if (go)
10778  {
10779  go.update_axis_limits ("xlim");
10780  go.update_axis_limits ("ylim");
10781  go.update_axis_limits ("zlim");
10782  go.update_axis_limits ("clim");
10783  go.update_axis_limits ("alim");
10784  }
10785 }
10786 
10787 void
10788 hggroup::properties::update_limits (const graphics_handle& h) const
10789 {
10790  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
10791 
10792  graphics_object go = gh_mgr.get_object (m___myhandle__);
10793 
10794  if (go)
10795  {
10796  go.update_axis_limits ("xlim", h);
10797  go.update_axis_limits ("ylim", h);
10798  go.update_axis_limits ("zlim", h);
10799  go.update_axis_limits ("clim", h);
10800  go.update_axis_limits ("alim", h);
10801  }
10802 }
10803 
10804 static bool updating_hggroup_limits = false;
10805 
10806 void
10807 hggroup::update_axis_limits (const std::string& axis_type,
10808  const graphics_handle& h)
10809 {
10811  return;
10812 
10813  Matrix kids = Matrix (1, 1, h.value ());
10814 
10815  double min_val = octave::numeric_limits<double>::Inf ();
10816  double max_val = -octave::numeric_limits<double>::Inf ();
10817  double min_pos = octave::numeric_limits<double>::Inf ();
10818  double max_neg = -octave::numeric_limits<double>::Inf ();
10819 
10820  Matrix limits;
10821  double val;
10822 
10823  char update_type = 0;
10824 
10825  if (axis_type == "xlim" || axis_type == "xliminclude")
10826  {
10827  limits = m_properties.get_xlim ().matrix_value ();
10828  update_type = 'x';
10829  }
10830  else if (axis_type == "ylim" || axis_type == "yliminclude")
10831  {
10832  limits = m_properties.get_ylim ().matrix_value ();
10833  update_type = 'y';
10834  }
10835  else if (axis_type == "zlim" || axis_type == "zliminclude")
10836  {
10837  limits = m_properties.get_zlim ().matrix_value ();
10838  update_type = 'z';
10839  }
10840  else if (axis_type == "clim" || axis_type == "climinclude")
10841  {
10842  limits = m_properties.get_clim ().matrix_value ();
10843  update_type = 'c';
10844  }
10845  else if (axis_type == "alim" || axis_type == "aliminclude")
10846  {
10847  limits = m_properties.get_alim ().matrix_value ();
10848  update_type = 'a';
10849  }
10850 
10851  if (limits.numel () == 4)
10852  {
10853  val = limits(0);
10854  if (octave::math::isfinite (val))
10855  min_val = val;
10856  val = limits(1);
10857  if (octave::math::isfinite (val))
10858  max_val = val;
10859  val = limits(2);
10860  if (octave::math::isfinite (val))
10861  min_pos = val;
10862  val = limits(3);
10863  if (octave::math::isfinite (val))
10864  max_neg = val;
10865  }
10866  else
10867  {
10868  limits.resize (1, 4);
10869  limits(0) = min_val;
10870  limits(1) = max_val;
10871  limits(2) = min_pos;
10872  limits(3) = max_neg;
10873  }
10874 
10875  get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
10876 
10877  octave::unwind_protect_var<bool> restore_var (updating_hggroup_limits, true);
10878 
10879  if (limits(0) != min_val || limits(1) != max_val
10880  || limits(2) != min_pos || limits(3) != max_neg)
10881  {
10882  limits(0) = min_val;
10883  limits(1) = max_val;
10884  limits(2) = min_pos;
10885  limits(3) = max_neg;
10886 
10887  switch (update_type)
10888  {
10889  case 'x':
10890  m_properties.set_xlim (limits);
10891  break;
10892 
10893  case 'y':
10894  m_properties.set_ylim (limits);
10895  break;
10896 
10897  case 'z':
10898  m_properties.set_zlim (limits);
10899  break;
10900 
10901  case 'c':
10902  m_properties.set_clim (limits);
10903  break;
10904 
10905  case 'a':
10906  m_properties.set_alim (limits);
10907  break;
10908 
10909  default:
10910  break;
10911  }
10912 
10913  graphics_handle hg = m_properties.get___myhandle__ ();
10914  base_graphics_object::update_axis_limits (axis_type, hg);
10915  }
10916 }
10917 
10918 void
10919 hggroup::update_axis_limits (const std::string& axis_type)
10920 {
10922  return;
10923 
10924  Matrix kids = m_properties.get_children ();
10925 
10926  double min_val = octave::numeric_limits<double>::Inf ();
10927  double max_val = -octave::numeric_limits<double>::Inf ();
10928  double min_pos = octave::numeric_limits<double>::Inf ();
10929  double max_neg = -octave::numeric_limits<double>::Inf ();
10930 
10931  char update_type = 0;
10932 
10933  if (axis_type == "xlim" || axis_type == "xliminclude")
10934  {
10935  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
10936 
10937  update_type = 'x';
10938  }
10939  else if (axis_type == "ylim" || axis_type == "yliminclude")
10940  {
10941  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
10942 
10943  update_type = 'y';
10944  }
10945  else if (axis_type == "zlim" || axis_type == "zliminclude")
10946  {
10947  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
10948 
10949  update_type = 'z';
10950  }
10951  else if (axis_type == "clim" || axis_type == "climinclude")
10952  {
10953  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
10954 
10955  update_type = 'c';
10956  }
10957  else if (axis_type == "alim" || axis_type == "aliminclude")
10958  {
10959  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
10960 
10961  update_type = 'a';
10962  }
10963 
10964  octave::unwind_protect_var<bool> restore_var (updating_hggroup_limits, true);
10965 
10966  Matrix limits (1, 4);
10967 
10968  limits(0) = min_val;
10969  limits(1) = max_val;
10970  limits(2) = min_pos;
10971  limits(3) = max_neg;
10972 
10973  switch (update_type)
10974  {
10975  case 'x':
10976  m_properties.set_xlim (limits);
10977  break;
10978 
10979  case 'y':
10980  m_properties.set_ylim (limits);
10981  break;
10982 
10983  case 'z':
10984  m_properties.set_zlim (limits);
10985  break;
10986 
10987  case 'c':
10988  m_properties.set_clim (limits);
10989  break;
10990 
10991  case 'a':
10992  m_properties.set_alim (limits);
10993  break;
10994 
10995  default:
10996  break;
10997  }
10998 
10999  base_graphics_object::update_axis_limits (axis_type);
11000 }
11001 
11002 // ---------------------------------------------------------------------
11003 
11004 void
11005 uicontextmenu::properties::update_beingdeleted (void)
11006 {
11007  // Clear the uicontextmenu property of dependent objects
11008  if (m_beingdeleted.is ("on"))
11009  {
11010  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11011 
11012  std::list<graphics_handle> lst = get_dependent_obj_list ();
11013 
11014  for (auto& hobj : lst)
11015  {
11016  graphics_object go = gh_mgr.get_object (hobj);
11017 
11018  if (go.valid_object ()
11019  && go.get ("contextmenu") == get___myhandle__ ())
11020  go.set ("contextmenu", Matrix ());
11021  }
11022  }
11023 }
11024 
11025 /*
11026 ## Test deletion/reset of uicontextmenu
11027 %!test
11028 %! hf = figure ("visible", "off");
11029 %! hax = axes ("parent", hf);
11030 %! unwind_protect
11031 %! hctx1 = uicontextmenu ("parent", hf);
11032 %! hctx2 = uicontextmenu ("parent", hf);
11033 %! set (hf, "uicontextmenu", hctx2);
11034 %! set (hax, "uicontextmenu", hctx2);
11035 %! assert (get (hf, "uicontextmenu"), hctx2);
11036 %! assert (get (hax, "uicontextmenu"), hctx2);
11037 %! assert (get (hf, "children"), [hctx2; hctx1; hax]);
11038 %! delete (hctx2);
11039 %! assert (get (hf, "uicontextmenu"), []);
11040 %! assert (get (hax, "uicontextmenu"), []);
11041 %! assert (get (hf, "children"), [hctx1; hax]);
11042 %! set (hf, "uicontextmenu", hctx1);
11043 %! assert (get (hf, "uicontextmenu"), hctx1);
11044 %! set (hf, "uicontextmenu", []);
11045 %! assert (get (hf, "uicontextmenu"), []);
11046 %! assert (get (hf, "children"), [hctx1; hax]);
11047 %! unwind_protect_cleanup
11048 %! close (hf);
11049 %! end_unwind_protect
11050 */
11051 
11052 // ---------------------------------------------------------------------
11053 
11055 uicontrol::properties::get_extent (void) const
11056 {
11057  Matrix m = m_extent.get ().matrix_value ();
11058 
11059  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11060 
11061  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11062 
11063  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11064  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11065 
11066  return convert_position (m, "pixels", get_units (), parent_size);
11067 }
11068 
11069 void
11070 uicontrol::properties::update_text_extent (void)
11071 {
11072  // FIXME: support multiline text
11073 
11074  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11075 
11076  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
11077 
11078  set_extent (go.get_toolkit ().get_text_extent (go));
11079 }
11080 
11081 void
11082 uicontrol::properties::update_units (void)
11083 {
11084  Matrix pos = get_position ().matrix_value ();
11085 
11086  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11087 
11088  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11089 
11090  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11091  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11092 
11093  pos = convert_position (pos, m_cached_units, get_units (), parent_size);
11094  set_position (pos);
11095 
11096  m_cached_units = get_units ();
11097 }
11098 
11099 void
11100 uicontrol::properties::set_style (const octave_value& st)
11101 {
11102  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11103 
11104  graphics_object go_parent = gh_mgr.get_object (get_parent ());
11105  if (go_parent.valid_object () && go_parent.isa ("uibuttongroup"))
11106  {
11107  bool was_button = style_is ("radiobutton") || style_is ("togglebutton");
11108  m_style = st;
11109  bool now_button = style_is ("radiobutton") || style_is ("togglebutton");
11110  uibuttongroup::properties& props =
11111  dynamic_cast<uibuttongroup::properties&> (go_parent.get_properties ());
11112  // update selectedobject
11113  if (! was_button && now_button && ! props.get_selectedobject ().ok ())
11114  {
11115  props.set_selectedobject (get___myhandle__ ().value ());
11116  m_value.set (octave_value (1));
11117  }
11118  else if (was_button && ! now_button
11119  && (props.get_selectedobject ().value ()
11120  == get___myhandle__ ().value ()))
11121  props.set_selectedobject (Matrix ());
11122  }
11123 
11124  // Don't notify the style change until the "value" property is fixed
11125  bool modified = m_style.set (st, true, false);
11126 
11127  // Override "value" property for listbox and popupmenu.
11128  if (modified)
11129  {
11130  if (style_is ("listbox") || style_is ("popupmenu"))
11131  {
11132  Matrix v = m_value.get ().matrix_value ();
11133  if (v.numel () == 1 && v(0) == 0)
11134  m_value.set (octave_value (1), true, false);
11135  }
11136 
11137  // Notify toolkit
11138 
11139  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
11140 
11141  if (go)
11142  go.update (m_style.get_id ());
11143  }
11144 }
11145 
11146 Matrix
11147 uicontrol::properties::get_boundingbox (bool,
11148  const Matrix& parent_pix_size) const
11149 {
11150  Matrix pos = get_position ().matrix_value ();
11151  Matrix parent_size (parent_pix_size);
11152 
11153  if (parent_size.isempty ())
11154  {
11155  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11156 
11157  graphics_object go = gh_mgr.get_object (get_parent ());
11158 
11159  if (go.valid_object ())
11160  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11161  else
11162  parent_size = default_figure_position ();
11163  }
11164 
11165  pos = convert_position (pos, get_units (), "pixels", parent_size);
11166 
11167  pos(0)--;
11168  pos(1)--;
11169  pos(1) = parent_size(1) - pos(1) - pos(3);
11170 
11171  return pos;
11172 }
11173 
11174 void
11175 uicontrol::properties::set_fontunits (const octave_value& val)
11176 {
11177  caseless_str old_fontunits = get_fontunits ();
11178 
11179  if (m_fontunits.set (val, true))
11180  {
11181  update_fontunits (old_fontunits);
11182  mark_modified ();
11183  }
11184 }
11185 
11186 void
11187 uicontrol::properties::update_fontunits (const caseless_str& old_units)
11188 {
11189  caseless_str new_units = get_fontunits ();
11190  double parent_height = get_boundingbox (false).elem (3);
11191  double fontsz = get_fontsize ();
11192 
11193  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11194 
11195  m_fontsize.set (octave_value (fontsz), true);
11196 }
11197 
11198 double
11199 uicontrol::properties::get___fontsize_points__ (double box_pix_height) const
11200 {
11201  double fontsz = get_fontsize ();
11202  double parent_height = box_pix_height;
11203 
11204  if (fontunits_is ("normalized") && parent_height <= 0)
11205  parent_height = get_boundingbox (false).elem (3);
11206 
11207  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11208 }
11209 
11210 // ---------------------------------------------------------------------
11211 
11212 Matrix
11213 uibuttongroup::properties::get_boundingbox (bool internal,
11214  const Matrix& parent_pix_size) const
11215 {
11216  Matrix pos = get_position ().matrix_value ();
11217  Matrix parent_size (parent_pix_size);
11218 
11219  if (parent_size.isempty ())
11220  {
11221  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11222 
11223  graphics_object go = gh_mgr.get_object (get_parent ());
11224 
11225  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11226  }
11227 
11228  pos = convert_position (pos, get_units (), "pixels", parent_size);
11229 
11230  pos(0)--;
11231  pos(1)--;
11232  pos(1) = parent_size(1) - pos(1) - pos(3);
11233 
11234  if (internal)
11235  {
11236  double outer_height = pos(3);
11237 
11238  pos(0) = pos(1) = 0;
11239 
11240  if (! bordertype_is ("none"))
11241  {
11242  double bw = get_borderwidth ();
11243  double mul = 1.0;
11244 
11245  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
11246  mul = 2.0;
11247 
11248  pos(0) += mul * bw;
11249  pos(1) += mul * bw;
11250  pos(2) -= 2 * mul * bw;
11251  pos(3) -= 2 * mul * bw;
11252  }
11253 
11254  if (! get_title ().empty ())
11255  {
11256  double fontsz = get_fontsize ();
11257 
11258  if (! fontunits_is ("pixels"))
11259  {
11260  double res = xget (0, "screenpixelsperinch").double_value ();
11261 
11262  if (fontunits_is ("points"))
11263  fontsz *= (res / 72.0);
11264  else if (fontunits_is ("inches"))
11265  fontsz *= res;
11266  else if (fontunits_is ("centimeters"))
11267  fontsz *= (res / 2.54);
11268  else if (fontunits_is ("normalized"))
11269  fontsz *= outer_height;
11270  }
11271 
11272  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
11273  || titleposition_is ("righttop"))
11274  pos(1) += (fontsz / 2);
11275  pos(3) -= (fontsz / 2);
11276  }
11277  }
11278 
11279  return pos;
11280 }
11281 
11282 void
11283 uibuttongroup::properties::set_position (const octave_value& v)
11284 {
11285  Matrix old_bb, new_bb;
11286  bool modified = false;
11287 
11288  old_bb = get_boundingbox (true);
11289  modified = m_position.set (v, false);
11290  new_bb = get_boundingbox (true);
11291 
11292  if (old_bb != new_bb)
11293  {
11294  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
11295  {
11296  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11297 
11298  if (! get_resizefcn ().isempty ())
11299  gh_mgr.post_callback (m___myhandle__, "resizefcn");
11300 
11301  if (! get_sizechangedfcn ().isempty ())
11302  gh_mgr.post_callback (m___myhandle__, "sizechangedfcn");
11303 
11304  update_boundingbox ();
11305  }
11306  }
11307 
11308  if (modified)
11309  {
11310  m_position.run_listeners (GCB_POSTSET);
11311  mark_modified ();
11312  }
11313 }
11314 
11315 void
11316 uibuttongroup::properties::set_units (const octave_value& val)
11317 {
11318  caseless_str old_units = get_units ();
11319 
11320  if (m_units.set (val, true))
11321  {
11322  update_units (old_units);
11323  mark_modified ();
11324  }
11325 }
11326 
11327 void
11328 uibuttongroup::properties::update_units (const caseless_str& old_units)
11329 {
11330  Matrix pos = get_position ().matrix_value ();
11331 
11332  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11333 
11334  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11335 
11336  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11337  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11338 
11339  pos = convert_position (pos, old_units, get_units (), parent_size);
11340  set_position (pos);
11341 }
11342 
11343 void
11344 uibuttongroup::properties::set_fontunits (const octave_value& val)
11345 {
11346  caseless_str old_fontunits = get_fontunits ();
11347 
11348  if (m_fontunits.set (val, true))
11349  {
11350  update_fontunits (old_fontunits);
11351  mark_modified ();
11352  }
11353 }
11354 
11355 void
11356 uibuttongroup::properties::update_fontunits (const caseless_str& old_units)
11357 {
11358  caseless_str new_units = get_fontunits ();
11359  double parent_height = get_boundingbox (false).elem (3);
11360  double fontsz = get_fontsize ();
11361 
11362  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11363 
11364  set_fontsize (octave_value (fontsz));
11365 }
11366 
11367 double
11368 uibuttongroup::properties::get___fontsize_points__ (double box_pix_height) const
11369 {
11370  double fontsz = get_fontsize ();
11371  double parent_height = box_pix_height;
11372 
11373  if (fontunits_is ("normalized") && parent_height <= 0)
11374  parent_height = get_boundingbox (false).elem (3);
11375 
11376  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11377 }
11378 
11379 void
11380 uibuttongroup::properties::set_selectedobject (const octave_value& v)
11381 {
11382  graphics_handle current_selectedobject = get_selectedobject();
11383  m_selectedobject = current_selectedobject;
11384  if (v.isempty ())
11385  {
11386  if (current_selectedobject.ok ())
11387  {
11388  m_selectedobject = graphics_handle ();
11389  mark_modified ();
11390  }
11391  return;
11392  }
11393 
11394  graphics_handle val (v);
11395  if (val.ok ())
11396  {
11397  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11398 
11399  graphics_object go (gh_mgr.get_object (val));
11400 
11401  base_properties& gop = go.get_properties ();
11402 
11403  if (go.valid_object ()
11404  && gop.get_parent () == get___myhandle__ ()
11405  && go.isa ("uicontrol"))
11406  {
11408  = dynamic_cast<uicontrol::properties&> (go.get_properties ());
11409  const caseless_str& style = cop.get_style ();
11410  if (style.compare ("radiobutton") || style.compare ("togglebutton"))
11411  {
11412  m_selectedobject = val;
11413  mark_modified ();
11414  return;
11415  }
11416  }
11417  }
11418  err_set_invalid ("selectedobject");
11419 }
11420 
11421 void
11422 uibuttongroup::properties::remove_child (const graphics_handle& h,
11423  bool from_root)
11424 {
11425  graphics_handle current_selected = get_selectedobject ();
11426  if (h.value () == current_selected.value ())
11427  set_selectedobject (Matrix ());
11428 
11429  base_properties::remove_child (h, from_root);
11430 }
11431 
11432 void
11434 {
11436 
11437  graphics_handle current_selected = get_selectedobject ();
11438  bool has_selected = current_selected.ok ();
11439 
11440  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11441 
11442  graphics_object go = gh_mgr.get_object (h);
11443 
11444  if (! has_selected && go.valid_object () && go.isa ("uicontrol"))
11445  {
11446  const uicontrol::properties& props =
11447  dynamic_cast<const uicontrol::properties&> (go.get_properties ());
11448  if (props.style_is ("radiobutton") || props.style_is ("togglebutton"))
11449  set_selectedobject (h.value ());
11450  }
11451 }
11452 
11453 // ---------------------------------------------------------------------
11454 
11455 Matrix
11456 uipanel::properties::get_boundingbox (bool internal,
11457  const Matrix& parent_pix_size) const
11458 {
11459  Matrix pos = get_position ().matrix_value ();
11460  Matrix parent_size (parent_pix_size);
11461 
11462  if (parent_size.isempty ())
11463  {
11464  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11465 
11466  graphics_object go = gh_mgr.get_object (get_parent ());
11467 
11468  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11469  }
11470 
11471  pos = convert_position (pos, get_units (), "pixels", parent_size);
11472 
11473  pos(0)--;
11474  pos(1)--;
11475  pos(1) = parent_size(1) - pos(1) - pos(3);
11476 
11477  if (internal)
11478  {
11479  double outer_height = pos(3);
11480 
11481  pos(0) = pos(1) = 0;
11482 
11483  if (! bordertype_is ("none"))
11484  {
11485  double bw = get_borderwidth ();
11486  double mul = 1.0;
11487 
11488  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
11489  mul = 2.0;
11490 
11491  pos(0) += mul * bw;
11492  pos(1) += mul * bw;
11493  pos(2) -= 2 * mul * bw;
11494  pos(3) -= 2 * mul * bw;
11495  }
11496 
11497  if (! get_title ().empty ())
11498  {
11499  double fontsz = get_fontsize ();
11500 
11501  if (! fontunits_is ("pixels"))
11502  {
11503  double res = xget (0, "screenpixelsperinch").double_value ();
11504 
11505  if (fontunits_is ("points"))
11506  fontsz *= (res / 72.0);
11507  else if (fontunits_is ("inches"))
11508  fontsz *= res;
11509  else if (fontunits_is ("centimeters"))
11510  fontsz *= (res / 2.54);
11511  else if (fontunits_is ("normalized"))
11512  fontsz *= outer_height;
11513  }
11514 
11515  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
11516  || titleposition_is ("righttop"))
11517  pos(1) += (fontsz / 2);
11518  pos(3) -= (fontsz / 2);
11519  }
11520  }
11521 
11522  return pos;
11523 }
11524 
11525 void
11526 uipanel::properties::set_position (const octave_value& v)
11527 {
11528  Matrix old_bb, new_bb;
11529  bool modified = false;
11530 
11531  old_bb = get_boundingbox (true);
11532  modified = m_position.set (v, false);
11533  new_bb = get_boundingbox (true);
11534 
11535  if (old_bb != new_bb)
11536  {
11537  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
11538  {
11539  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11540 
11541  if (! get_resizefcn ().isempty ())
11542  gh_mgr.post_callback (m___myhandle__, "resizefcn");
11543 
11544  if (! get_sizechangedfcn ().isempty ())
11545  gh_mgr.post_callback (m___myhandle__, "sizechangedfcn");
11546 
11547  update_boundingbox ();
11548  }
11549  }
11550 
11551  if (modified)
11552  {
11553  m_position.run_listeners (GCB_POSTSET);
11554  mark_modified ();
11555  }
11556 }
11557 
11558 
11559 void
11560 uipanel::properties::set_units (const octave_value& val)
11561 {
11562  caseless_str old_units = get_units ();
11563 
11564  if (m_units.set (val, true))
11565  {
11566  update_units (old_units);
11567  mark_modified ();
11568  }
11569 }
11570 
11571 void
11572 uipanel::properties::update_units (const caseless_str& old_units)
11573 {
11574  Matrix pos = get_position ().matrix_value ();
11575 
11576  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11577 
11578  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11579 
11580  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11581  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11582 
11583  pos = convert_position (pos, old_units, get_units (), parent_size);
11584  set_position (pos);
11585 }
11586 
11587 void
11588 uipanel::properties::set_fontunits (const octave_value& val)
11589 {
11590  caseless_str old_fontunits = get_fontunits ();
11591 
11592  if (m_fontunits.set (val, true))
11593  {
11594  update_fontunits (old_fontunits);
11595  mark_modified ();
11596  }
11597 }
11598 
11599 void
11600 uipanel::properties::update_fontunits (const caseless_str& old_units)
11601 {
11602  caseless_str new_units = get_fontunits ();
11603  double parent_height = get_boundingbox (false).elem (3);
11604  double fontsz = get_fontsize ();
11605 
11606  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11607 
11608  set_fontsize (octave_value (fontsz));
11609 }
11610 
11611 double
11612 uipanel::properties::get___fontsize_points__ (double box_pix_height) const
11613 {
11614  double fontsz = get_fontsize ();
11615  double parent_height = box_pix_height;
11616 
11617  if (fontunits_is ("normalized") && parent_height <= 0)
11618  parent_height = get_boundingbox (false).elem (3);
11619 
11620  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11621 }
11622 
11623 // ---------------------------------------------------------------------
11624 
11625 Matrix
11626 uitable::properties::get_boundingbox (bool,
11627  const Matrix& parent_pix_size) const
11628 {
11629  Matrix pos = get_position ().matrix_value ();
11630  Matrix parent_size (parent_pix_size);
11631 
11632  if (parent_size.isempty ())
11633  {
11634  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11635 
11636  graphics_object go = gh_mgr.get_object (get_parent ());
11637 
11638  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11639  }
11640 
11641  pos = convert_position (pos, get_units (), "pixels", parent_size);
11642 
11643  pos(0)--;
11644  pos(1)--;
11645  pos(1) = parent_size(1) - pos(1) - pos(3);
11646 
11647  return pos;
11648 }
11649 
11650 void
11651 uitable::properties::set_columnformat (const octave_value& val)
11652 {
11653  /* Matlab only allows certain values for ColumnFormat. Here we only check the
11654  * structure of the argument. Values will be checked in Table.cc */
11655 
11656  if (val.iscellstr ())
11657  {
11658  if (m_columnformat.set (val, true))
11659  mark_modified ();
11660  }
11661  else if (val.iscell ())
11662  {
11663  Cell cell_value = val.cell_value ();
11664 
11665  for (int i = 0; i < cell_value.numel (); i++)
11666  {
11667  octave_value v = cell_value(i);
11668  if (v.iscell ())
11669  {
11670  /* We are in a pop-up menu selection.
11671  * Matlab only allows non-empty strings here. */
11672  Cell popup = v.cell_value ();
11673  for (int j = 0; j < popup.numel (); j++)
11674  {
11675  octave_value p = popup(j);
11676  if (! p.is_string () || p.isempty ())
11677  error ("set: pop-up menu definitions must be non-empty strings");
11678  }
11679  }
11680  else if (! (v.is_string () || v.isempty ()))
11681  {
11682  error ("set: columnformat definintions must be a cellstr of "
11683  "either 'char', 'short [e|g|eng]?', 'long [e|g|eng]?', "
11684  "'numeric', 'bank', '+', 'rat', 'logical', "
11685  "or a cellstr of non-empty pop-up menu definitions.");
11686  }
11687  }
11688 
11689  if (m_columnformat.set (val, true))
11690  mark_modified ();
11691  }
11692  else if (val.isempty ())
11693  {
11694  if (m_columnformat.set (Cell (), true))
11695  mark_modified ();
11696  }
11697  else
11698  {
11699  error ("set: expecting cell of strings");
11700  }
11701 }
11702 
11703 void
11704 uitable::properties::set_columnwidth (const octave_value& val)
11705 {
11706  bool error_exists = false;
11707 
11708  if (val.is_string () && val.string_value (false) == "auto")
11709  error_exists = false;
11710  else if (val.iscell ())
11711  {
11712  Cell cell_value = val.cell_value ();
11713  for (int i = 0; i < cell_value.numel (); i++)
11714  {
11715  octave_value v = cell_value(i);
11716  if (v.is_string ())
11717  {
11718  if (v.string_value (false) != "auto")
11719  error_exists = true;
11720  }
11721  else if (v.iscell ())
11722  {
11723  error_exists = true;
11724  }
11725  else if (! v.is_scalar_type ())
11726  {
11727  error_exists = true;
11728  }
11729  }
11730  }
11731  else
11732  error_exists = true;
11733 
11734  if (error_exists)
11735  error ("set: expecting either 'auto' or a cell of pixel values or auto");
11736  else
11737  {
11738  if (m_columnwidth.set (val, true))
11739  mark_modified ();
11740  }
11741 }
11742 
11743 void
11744 uitable::properties::set_units (const octave_value& val)
11745 {
11746  caseless_str old_units = get_units ();
11747 
11748  if (m_units.set (val, true))
11749  {
11750  update_units (old_units);
11751  mark_modified ();
11752  }
11753 }
11754 
11755 void
11756 uitable::properties::update_units (const caseless_str& old_units)
11757 {
11758  Matrix pos = get_position ().matrix_value ();
11759 
11760  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11761 
11762  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11763 
11764  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11765  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11766 
11767  pos = convert_position (pos, old_units, get_units (), parent_size);
11768  set_position (pos);
11769 }
11770 
11771 void
11772 uitable::properties::set_fontunits (const octave_value& val)
11773 {
11774  caseless_str old_fontunits = get_fontunits ();
11775 
11776  if (m_fontunits.set (val, true))
11777  {
11778  update_fontunits (old_fontunits);
11779  mark_modified ();
11780  }
11781 }
11782 
11783 void
11784 uitable::properties::update_fontunits (const caseless_str& old_units)
11785 {
11786  caseless_str new_units = get_fontunits ();
11787  double parent_height = get_boundingbox (false).elem (3);
11788  double fontsz = get_fontsize ();
11789 
11790  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11791 
11792  set_fontsize (octave_value (fontsz));
11793 }
11794 
11795 double
11796 uitable::properties::get___fontsize_points__ (double box_pix_height) const
11797 {
11798  double fontsz = get_fontsize ();
11799  double parent_height = box_pix_height;
11800 
11801  if (fontunits_is ("normalized") && parent_height <= 0)
11802  parent_height = get_boundingbox (false).elem (3);
11803 
11804  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11805 }
11806 
11807 double
11808 uitable::properties::get_fontsize_pixels (double box_pix_height) const
11809 {
11810  double fontsz = get_fontsize ();
11811  double parent_height = box_pix_height;
11812 
11813  if (fontunits_is ("normalized") && parent_height <= 0)
11814  parent_height = get_boundingbox (false).elem (3);
11815 
11816  return convert_font_size (fontsz, get_fontunits (), "pixels", parent_height);
11817 }
11818 
11819 Matrix
11820 uitable::properties::get_backgroundcolor_rgb (void)
11821 {
11822  Matrix bg = m_backgroundcolor.get ().matrix_value ();
11823  return bg.row (0);
11824 }
11825 
11826 Matrix
11827 uitable::properties::get_alternatebackgroundcolor_rgb (void)
11828 {
11829  int i = 0;
11830  Matrix bg = m_backgroundcolor.get ().matrix_value ();
11831  if (bg.rows () > 1)
11832  i = 1;
11833 
11834  return bg.row (i);
11835 }
11836 
11837 Matrix
11838 uitable::properties::get_extent_matrix (void) const
11839 {
11840  return m_extent.get ().matrix_value ();
11841 }
11842 
11844 uitable::properties::get_extent (void) const
11845 {
11846  // FIXME: Is it really acceptable to just let the toolkit update the extent?
11847  Matrix m = m_extent.get ().matrix_value ();
11848 
11849  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11850 
11851  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11852 
11853  if (parent_go)
11854  {
11855  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11856  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11857 
11858  return convert_position (m, "pixels", get_units (), parent_size);
11859  }
11860 
11861  return m;
11862 }
11863 
11864 // ---------------------------------------------------------------------
11865 
11867 uitoolbar::get_default (const caseless_str& pname) const
11868 {
11869  octave_value retval = m_default_properties.lookup (pname);
11870 
11871  if (retval.is_undefined ())
11872  {
11873  graphics_handle parent_h = get_parent ();
11874 
11875  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11876 
11877  graphics_object parent_go = gh_mgr.get_object (parent_h);
11878 
11879  retval = parent_go.get_default (pname);
11880  }
11881 
11882  return retval;
11883 }
11884 
11885 void
11886 uitoolbar::reset_default_properties (void)
11887 {
11888  // empty list of local defaults
11889  m_default_properties = property_list ();
11890 
11891  remove_all_listeners ();
11892  xreset_default_properties (get_handle (), m_properties.factory_defaults ());
11893 }
11894 
11895 // ---------------------------------------------------------------------
11896 
11898 base_graphics_object::get_default (const caseless_str& pname) const
11899 {
11900  graphics_handle parent_h = get_parent ();
11901 
11902  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11903 
11904  graphics_object parent_go = gh_mgr.get_object (parent_h);
11905 
11906  return parent_go.get_default (type () + pname);
11907 }
11908 
11910 base_graphics_object::get_factory_default (const caseless_str& name) const
11911 {
11912  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
11913 
11914  graphics_object parent_go = gh_mgr.get_object (0);
11915 
11916  return parent_go.get_factory_default (type () + name);
11917 }
11918 
11919 // We use a random value for the handle to avoid issues with plots and
11920 // scalar values for the first argument.
11921 gh_manager::gh_manager (octave::interpreter& interp)
11922  : m_interpreter (interp), m_handle_map (), m_handle_free_list (),
11923  m_next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
11924  m_figure_list (), m_graphics_lock (), m_event_queue (),
11925  m_callback_objects (), m_event_processing (0)
11926 {
11927  m_handle_map[0] = graphics_object (new root_figure ());
11928 
11929  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
11930 
11931  // Make sure the default graphics toolkit is registered.
11932  gtk_mgr.default_toolkit ();
11933 }
11934 
11936 gh_manager::make_graphics_handle (const std::string& go_name,
11937  const graphics_handle& p,
11938  bool integer_figure_handle,
11939  bool call_createfcn, bool notify_toolkit)
11940 {
11941  graphics_handle h = get_handle (integer_figure_handle);
11942 
11943  base_graphics_object *bgo = make_graphics_object_from_type (go_name, h, p);
11944 
11945  if (! bgo)
11946  error ("gh_manager::make_graphics_handle: invalid object type '%s'",
11947  go_name.c_str ());
11948 
11949  graphics_object go (bgo);
11950 
11951  m_handle_map[h] = go;
11952 
11953  if (go_name == "axes")
11954  {
11955  // Handle defaults for labels since overriding defaults for
11956  // them can't work before the axes object is fully
11957  // constructed.
11958 
11959  axes::properties& props
11960  = dynamic_cast<axes::properties&> (go.get_properties ());
11961 
11962  graphics_object tgo;
11963 
11964  tgo = get_object (props.get_xlabel ());
11965  tgo.override_defaults ();
11966 
11967  tgo = get_object (props.get_ylabel ());
11968  tgo.override_defaults ();
11969 
11970  tgo = get_object (props.get_zlabel ());
11971  tgo.override_defaults ();
11972 
11973  tgo = get_object (props.get_title ());
11974  tgo.override_defaults ();
11975  }
11976 
11977  // Overriding defaults will work now because the handle is valid
11978  // and we can find parent objects (not just handles).
11979  go.override_defaults ();
11980 
11981  if (call_createfcn)
11982  bgo->get_properties ().execute_createfcn ();
11983 
11984  // Notify graphics toolkit.
11985  if (notify_toolkit)
11986  go.initialize ();
11987 
11988  return h;
11989 }
11990 
11992 gh_manager::make_figure_handle (double val, bool notify_toolkit)
11993 {
11994  graphics_handle h = val;
11995 
11996  base_graphics_object *bgo = new figure (h, 0);
11997  graphics_object go (bgo);
11998 
11999  m_handle_map[h] = go;
12000 
12001  // Notify graphics toolkit.
12002  if (notify_toolkit)
12003  go.initialize ();
12004 
12005  go.override_defaults ();
12006 
12007  return h;
12008 }
12009 
12010 void
12011 gh_manager::push_figure (const graphics_handle& h)
12012 {
12013  pop_figure (h);
12014 
12015  m_figure_list.push_front (h);
12016 }
12017 
12018 void
12019 gh_manager::pop_figure (const graphics_handle& h)
12020 {
12021  for (auto it = m_figure_list.begin (); it != m_figure_list.end (); it++)
12022  {
12023  if (*it == h)
12024  {
12025  m_figure_list.erase (it);
12026  break;
12027  }
12028  }
12029 }
12030 
12031 class
12033 {
12034 public:
12035  callback_event (const graphics_handle& h, const std::string& name,
12036  const octave_value& data = Matrix (),
12037  int busyaction = base_graphics_event::QUEUE)
12038  : base_graphics_event (busyaction), m_handle (h), m_callback_name (name),
12039  m_callback (), m_callback_data (data) { }
12040 
12042  const octave_value& data = Matrix (),
12043  int busyaction = base_graphics_event::QUEUE)
12044  : base_graphics_event (busyaction), m_handle (h), m_callback_name (),
12045  m_callback (cb), m_callback_data (data) { }
12046 
12047  void execute (void)
12048  {
12049  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12050 
12051  if (m_callback.is_defined ())
12052  gh_mgr.execute_callback (m_handle, m_callback, m_callback_data);
12053  else
12054  gh_mgr.execute_callback (m_handle, m_callback_name, m_callback_data);
12055  }
12056 
12057 private:
12059  : base_graphics_event (), m_handle (), m_callback_name (),
12060  m_callback_data ()
12061  { }
12062 
12063 private:
12065  std::string m_callback_name;
12068 };
12069 
12070 class
12072 {
12073 public:
12074  mcode_event (const graphics_handle& h, const std::string& cmd,
12075  int busyaction = base_graphics_event::QUEUE)
12076  : base_graphics_event (busyaction), m_handle (h), m_mcode (cmd)
12077  { }
12078 
12079  void execute (void)
12080  {
12081  if (! m_mcode.empty ())
12082  {
12083  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12084 
12085  graphics_object go = gh_mgr.get_object (m_handle);
12086 
12087  if (go.valid_object ())
12088  {
12089  octave_value cb (m_mcode);
12090  gh_mgr.execute_callback (m_handle, cb);
12091  }
12092  }
12093  }
12094 
12095 private:
12097  : base_graphics_event (), m_handle (), m_mcode ()
12098  { }
12099 
12100 private:
12102  std::string m_mcode;
12103 };
12104 
12105 class
12107 {
12108 public:
12109 
12110  // function_event objects must be created with at least a function.
12111 
12112  function_event (void) = delete;
12113 
12114  function_event (graphics_event::event_fcn fcn, void *data = nullptr)
12115  : base_graphics_event (), m_function (fcn), m_function_data (data)
12116  { }
12117 
12118  // No copying!
12119 
12120  function_event (const function_event&) = delete;
12121 
12122  function_event& operator = (const function_event&) = delete;
12123 
12124  void execute (void)
12125  {
12126  m_function (m_function_data);
12127  }
12128 
12129 private:
12130 
12131  graphics_event::event_fcn m_function;
12132 
12134 };
12135 
12136 class
12138 {
12139 public:
12140  set_event (const graphics_handle& h, const std::string& name,
12141  const octave_value& value, bool do_notify_toolkit = true,
12142  bool redraw_figure = false)
12143  : base_graphics_event (), m_handle (h), m_property_name (name),
12144  m_property_value (value), m_notify_toolkit (do_notify_toolkit),
12145  m_redraw_figure (redraw_figure)
12146  { }
12147 
12148  void execute (void)
12149  {
12150  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12151 
12152  octave::autolock guard (gh_mgr.graphics_lock ());
12153 
12154  graphics_object go = gh_mgr.get_object (m_handle);
12155 
12156  if (go)
12157  {
12158  property p = go.get_properties ().get_property (m_property_name);
12159 
12160  if (p.ok ())
12161  {
12162  // FIXME: figure position and outerposition properties set_xxx have
12163  // a signature that allows passing the notify_toolkit argument.
12164  // Should we change all set_xxx signatures and allow
12165  // base_properties::set to accept this also? This would allow for
12166  // the use of high level set_xxx instead of directly changing the
12167  // property value.
12168  if (go.isa ("figure") && m_property_name == "position")
12169  {
12170  figure::properties& fprops
12171  = dynamic_cast<figure::properties&> (go.get_properties ());
12172  fprops.set_position (m_property_value, m_notify_toolkit);
12173  }
12174  else if (go.isa ("figure") && m_property_name == "outerposition")
12175  {
12176  figure::properties& fprops
12177  = dynamic_cast<figure::properties&> (go.get_properties ());
12178  fprops.set_outerposition (m_property_value, m_notify_toolkit);
12179  }
12180  else
12181  p.set (m_property_value, true, m_notify_toolkit);
12182 
12183  if (m_redraw_figure)
12184  {
12185  if (! go.isa ("figure"))
12186  go = go.get_ancestor ("figure");
12187 
12188  if (go.valid_object ())
12189  {
12190  figure::properties& fprops
12191  = dynamic_cast<figure::properties&> (go.get_properties ());
12192  fprops.get_toolkit ().redraw_figure (go);
12193  }
12194  }
12195  }
12196  }
12197  }
12198 
12199 private:
12200  set_event (void)
12201  : base_graphics_event (), m_handle (), m_property_name (), m_property_value ()
12202  { }
12203 
12204 private:
12206  std::string m_property_name;
12210 };
12211 
12212 graphics_event
12213 graphics_event::create_callback_event (const graphics_handle& h,
12214  const std::string& name,
12215  const octave_value& data,
12216  int busyaction)
12217 {
12218  return graphics_event (new callback_event (h, name, data, busyaction));
12219 }
12220 
12221 graphics_event
12222 graphics_event::create_callback_event (const graphics_handle& h,
12223  const octave_value& cb,
12224  const octave_value& data,
12225  int busyaction)
12226 {
12227  return graphics_event (new callback_event (h, cb, data, busyaction));
12228 }
12229 
12230 graphics_event
12231 graphics_event::create_mcode_event (const graphics_handle& h,
12232  const std::string& cmd,
12233  int busyaction)
12234 {
12235  return graphics_event (new mcode_event (h, cmd, busyaction));
12236 }
12237 
12238 graphics_event
12239 graphics_event::create_function_event (graphics_event::event_fcn fcn,
12240  void *data)
12241 {
12242  return graphics_event (new function_event (fcn, data));
12243 }
12244 
12245 graphics_event
12246 graphics_event::create_set_event (const graphics_handle& h,
12247  const std::string& name,
12248  const octave_value& data,
12249  bool notify_toolkit, bool redraw_figure)
12250 {
12251  return graphics_event (new set_event (h, name, data, notify_toolkit,
12252  redraw_figure));
12253 }
12254 
12255 static void
12257 {
12258  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12259 
12260  graphics_object go = gh_mgr.get_object (0);
12261 
12263  = dynamic_cast<root_figure::properties&> (go.get_properties ());
12264 
12265  props.set_callbackobject (h.as_octave_value ());
12266 }
12267 
12268 void
12269 gh_manager::restore_gcbo (void)
12270 {
12271  octave::autolock guard (m_graphics_lock);
12272 
12273  m_callback_objects.pop_front ();
12274 
12275  xset_gcbo (m_callback_objects.empty ()
12276  ? graphics_handle () : m_callback_objects.front ().get_handle ());
12277 }
12278 
12279 void
12280 gh_manager::execute_listener (const graphics_handle& h, const octave_value& l)
12281 {
12282  if (octave::thread::is_thread ())
12283  execute_callback (h, l, octave_value ());
12284  else
12285  {
12286  octave::autolock guard (m_graphics_lock);
12287 
12288  post_event (graphics_event::create_callback_event (h, l));
12289  }
12290 }
12291 
12292 void
12293 gh_manager::execute_callback (const graphics_handle& h,
12294  const octave_value& cb_arg,
12295  const octave_value& data)
12296 {
12297  if (cb_arg.is_defined () && ! cb_arg.isempty ())
12298  {
12299  octave_value_list args;
12300  octave_value ov_fcn;
12301  octave_function *fcn = nullptr;
12302 
12303  args(0) = h.as_octave_value ();
12304  if (data.is_defined ())
12305  args(1) = data;
12306  else
12307  args(1) = Matrix ();
12308 
12309  octave::unwind_action_safe restore_gcbo_action
12310  (&gh_manager::restore_gcbo, this);
12311 
12312  graphics_object go (get_object (h));
12313  if (go)
12314  {
12315  // FIXME: Is the lock necessary when we're only calling a
12316  // const "get" method?
12317  octave::autolock guard (m_graphics_lock);
12318  m_callback_objects.push_front (go);
12319  xset_gcbo (h);
12320  }
12321 
12322  // Copy CB because "function_value" method is non-const.
12323  octave_value cb = cb_arg;
12324 
12325  if (cb.is_function ())
12326  fcn = cb.function_value ();
12327  else if (cb.is_function_handle ())
12328  ov_fcn = cb;
12329  else if (cb.is_string ())
12330  {
12331  int status;
12332  std::string s = cb.string_value ();
12333 
12334  try
12335  {
12336  m_interpreter.eval_string (s, false, status, 0);
12337  }
12338  catch (const octave::execution_exception& ee)
12339  {
12340  m_interpreter.handle_exception (ee);
12341  }
12342  }
12343  else if (cb.iscell () && cb.length () > 0
12344  && (cb.rows () == 1 || cb.columns () == 1)
12345  && (cb.cell_value ()(0).is_function ()
12346  || cb.cell_value ()(0).is_function_handle ()))
12347  {
12348  Cell c = cb.cell_value ();
12349 
12350  ov_fcn = c(0);
12351 
12352  for (int i = 1; i < c.numel () ; i++)
12353  args(1+i) = c(i);
12354  }
12355  else
12356  {
12357  std::string nm = cb.class_name ();
12358  error ("trying to execute non-executable object (class = %s)",
12359  nm.c_str ());
12360  }
12361 
12362  if (fcn || ov_fcn.is_defined ())
12363  try
12364  {
12365  if (ov_fcn.is_defined ())
12366  octave::feval (ov_fcn, args);
12367  else
12368  octave::feval (fcn, args);
12369  }
12370  catch (const octave::execution_exception& ee)
12371  {
12372  m_interpreter.handle_exception (ee);
12373  }
12374 
12375  // Redraw after interacting with a user-interface (ui*) object.
12376  if (Vdrawnow_requested)
12377  {
12378  if (go)
12379  {
12380  std::string go_name
12381  = go.get_properties ().graphics_object_name ();
12382 
12383  if (go_name.length () > 1
12384  && go_name[0] == 'u' && go_name[1] == 'i')
12385  {
12386  Fdrawnow (m_interpreter);
12387  Vdrawnow_requested = false;
12388  }
12389  }
12390  }
12391  }
12392 }
12393 
12394 static int
12396 {
12397  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12398 
12399  return gh_mgr.process_events ();
12400 }
12401 
12402 void
12403 gh_manager::post_event (const graphics_event& e)
12404 {
12405  m_event_queue.push_back (e);
12406 
12407  octave::command_editor::add_event_hook (process_graphics_events);
12408 }
12409 
12410 void
12411 gh_manager::post_callback (const graphics_handle& h, const std::string& name,
12412  const octave_value& data)
12413 {
12414  octave::autolock guard (m_graphics_lock);
12415 
12416  graphics_object go = get_object (h);
12417 
12418  if (go.valid_object ())
12419  {
12420  caseless_str cname (name);
12421  int busyaction = base_graphics_event::QUEUE;
12422 
12423  if (cname == "deletefcn" || cname == "createfcn"
12424  || cname == "closerequestfcn"
12425  || ((go.isa ("figure") || go.isa ("uipanel")
12426  || go.isa ("uibuttongroup"))
12427  && (cname == "resizefcn" || cname == "sizechangedfcn")))
12428  busyaction = base_graphics_event::INTERRUPT;
12429  else if (go.get_properties ().get_busyaction () == "cancel")
12430  busyaction = base_graphics_event::CANCEL;
12431 
12432  // The "closerequestfcn" callback must be executed once the figure has
12433  // been made current. Let "close" do the job.
12434  if (cname == "closerequestfcn")
12435  {
12436  std::string cmd ("close (gcbf ());");
12437  post_event (graphics_event::create_mcode_event (h, cmd, busyaction));
12438  }
12439  else
12440  post_event (graphics_event::create_callback_event (h, name, data,
12441  busyaction));
12442  }
12443 }
12444 
12445 void
12446 gh_manager::post_function (graphics_event::event_fcn fcn, void *fcn_data)
12447 {
12448  octave::autolock guard (m_graphics_lock);
12449 
12450  post_event (graphics_event::create_function_event (fcn, fcn_data));
12451 }
12452 
12453 void
12454 gh_manager::post_set (const graphics_handle& h, const std::string& name,
12455  const octave_value& value, bool notify_toolkit,
12456  bool redraw_figure)
12457 {
12458  octave::autolock guard (m_graphics_lock);
12459 
12460  post_event (graphics_event::create_set_event (h, name, value, notify_toolkit,
12461  redraw_figure));
12462 }
12463 
12464 int
12465 gh_manager::process_events (bool force)
12466 {
12467  graphics_event e;
12468  bool old_Vdrawnow_requested = Vdrawnow_requested;
12469  bool events_executed = false;
12470 
12471  do
12472  {
12473  e = graphics_event ();
12474 
12475  {
12476  octave::autolock guard (m_graphics_lock);
12477 
12478  if (! m_event_queue.empty ())
12479  {
12480  if (m_callback_objects.empty () || force)
12481  {
12482  e = m_event_queue.front ();
12483 
12484  m_event_queue.pop_front ();
12485  }
12486  else
12487  {
12488  const graphics_object& go = m_callback_objects.front ();
12489 
12490  if (go.get_properties ().is_interruptible ())
12491  {
12492  e = m_event_queue.front ();
12493 
12494  m_event_queue.pop_front ();
12495  }
12496  else
12497  {
12498  std::list<graphics_event>::iterator p = m_event_queue.begin ();
12499 
12500  while (p != m_event_queue.end ())
12501  if (p->get_busyaction () == base_graphics_event::CANCEL)
12502  {
12503  p = m_event_queue.erase (p);
12504  }
12505  else if (p->get_busyaction ()
12506  == base_graphics_event::INTERRUPT)
12507  {
12508  e = (*p);
12509  m_event_queue.erase (p);
12510  break;
12511  }
12512  else
12513  p++;
12514  }
12515  }
12516  }
12517  }
12518 
12519  if (e.ok ())
12520  {
12521  e.execute ();
12522  events_executed = true;
12523  }
12524  }
12525  while (e.ok ());
12526 
12527  {
12528  octave::autolock guard (m_graphics_lock);
12529 
12530  if (m_event_queue.empty () && m_event_processing == 0)
12531  octave::command_editor::remove_event_hook (process_graphics_events);
12532  }
12533 
12534  if (events_executed)
12536 
12537  if (Vdrawnow_requested && ! old_Vdrawnow_requested)
12538  {
12539  Fdrawnow (m_interpreter);
12540 
12541  Vdrawnow_requested = false;
12542  }
12543 
12544  return 0;
12545 }
12546 
12547 
12548 /*
12549 ## Test interruptible/busyaction properties
12550 %!function cb (h, ~)
12551 %! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]);
12552 %! drawnow ();
12553 %! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]);
12554 %!endfunction
12555 %!
12556 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
12557 %! hf = figure ("visible", "off", "resizefcn", @cb);
12558 %! graphics_toolkit (hf, "qt");
12559 %! unwind_protect
12560 %! ## Default
12561 %! hui1 = uicontrol ("parent", hf, "interruptible", "on", "callback", @cb);
12562 %! hui2 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb);
12563 %! hui3 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb);
12564 %! __go_post_callback__ (hui1, "callback");
12565 %! __go_post_callback__ (hui2, "callback");
12566 %! __go_post_callback__ (hui3, "callback");
12567 %!
12568 %! assert (getappdata (hf, "cb_exec"), []);
12569 %! drawnow ();
12570 %! assert (getappdata (hf, "cb_exec"), [hui1 hui2 hui3 hui3 hui2 hui1]);
12571 %!
12572 %! ## Interruptible off
12573 %! setappdata (hf, "cb_exec", []);
12574 %! set (hui1, "interruptible", "off");
12575 %! __go_post_callback__ (hui1, "callback");
12576 %! __go_post_callback__ (hui2, "callback");
12577 %! __go_post_callback__ (hui3, "callback");
12578 %! drawnow ();
12579 %! assert (getappdata (hf, "cb_exec"), [hui1 hui1 hui2 hui3 hui3 hui2]);
12580 %!
12581 %! ## "resizefcn" callback interrupts regardless of interruptible property
12582 %! setappdata (hf, "cb_exec", []);
12583 %! __go_post_callback__ (hui1, "callback");
12584 %! __go_post_callback__ (hf, "resizefcn");
12585 %! drawnow ();
12586 %! assert (getappdata (hf, "cb_exec"), [hui1 hf hf hui1]);
12587 %!
12588 %! ## test "busyaction" "cancel"
12589 %! setappdata (hf, "cb_exec", []);
12590 %! set (hui2, "busyaction", "cancel");
12591 %! __go_post_callback__ (hui1, "callback");
12592 %! __go_post_callback__ (hui2, "callback");
12593 %! __go_post_callback__ (hui3, "callback");
12594 %! __go_post_callback__ (hf, "resizefcn");
12595 %! drawnow ();
12596 %! assert (getappdata (hf, "cb_exec"), [hui1 hf hui3 hui3 hf hui1]);
12597 %! unwind_protect_cleanup
12598 %! close (hf)
12599 %! end_unwind_protect
12600 */
12601 
12602 void
12603 gh_manager::enable_event_processing (bool enable)
12604 {
12605  octave::autolock guard (m_graphics_lock);
12606 
12607  if (enable)
12608  {
12609  m_event_processing++;
12610 
12611  octave::command_editor::add_event_hook (process_graphics_events);
12612  }
12613  else
12614  {
12615  m_event_processing--;
12616 
12617  if (m_event_queue.empty () && m_event_processing == 0)
12618  octave::command_editor::remove_event_hook (process_graphics_events);
12619  }
12620 }
12621 
12622 property_list::plist_map_type
12623 root_figure::init_factory_properties (void)
12624 {
12625  property_list::plist_map_type plist_map;
12626 
12627  plist_map["figure"] = figure::properties::factory_defaults ();
12628  plist_map["axes"] = axes::properties::factory_defaults ();
12629  plist_map["line"] = line::properties::factory_defaults ();
12630  plist_map["text"] = text::properties::factory_defaults ();
12631  plist_map["image"] = image::properties::factory_defaults ();
12632  plist_map["patch"] = patch::properties::factory_defaults ();
12633  plist_map["scatter"] = scatter::properties::factory_defaults ();
12634  plist_map["surface"] = surface::properties::factory_defaults ();
12635  plist_map["light"] = light::properties::factory_defaults ();
12636  plist_map["hggroup"] = hggroup::properties::factory_defaults ();
12637  plist_map["uimenu"] = uimenu::properties::factory_defaults ();
12638  plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
12639  plist_map["uibuttongroup"] = uibuttongroup::properties::factory_defaults ();
12640  plist_map["uipanel"] = uipanel::properties::factory_defaults ();
12641  plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
12642  plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
12643  plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
12644  plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
12645 
12646  return plist_map;
12647 }
12648 
12649 // ---------------------------------------------------------------------
12650 
12651 DEFMETHOD (ishghandle, interp, args, ,
12652  doc: /* -*- texinfo -*-
12653 @deftypefn {} {@var{tf} =} ishghandle (@var{h})
12654 Return true if @var{h} is a graphics handle and false otherwise.
12655 
12656 @var{h} may also be a matrix of handles in which case a logical array is
12657 returned that is true where the elements of @var{h} are graphics handles and
12658 false where they are not.
12659 @seealso{isgraphics, isaxes, isfigure, ishandle}
12660 @end deftypefn */)
12661 {
12662  gh_manager& gh_mgr = interp.get_gh_manager ();
12663 
12664  octave::autolock guard (gh_mgr.graphics_lock ());
12665 
12666  if (args.length () != 1)
12667  print_usage ();
12668 
12669  return ovl (ishghandle (args(0)));
12670 }
12671 
12672 /*
12673 %!test
12674 %! hf = figure ("visible", "off");
12675 %! unwind_protect
12676 %! assert (ishghandle (hf));
12677 %! assert (! ishghandle (-hf));
12678 %! ax = gca ();
12679 %! l = line ();
12680 %! assert (ishghandle (ax));
12681 %! assert (! ishghandle (-ax));
12682 %! assert (ishghandle ([l, -1, ax, hf]), logical ([1, 0, 1, 1]));
12683 %! assert (ishghandle ([l, -1, ax, hf]'), logical ([1, 0, 1, 1]'));
12684 %! unwind_protect_cleanup
12685 %! close (hf);
12686 %! end_unwind_protect
12687 
12688 %!assert (ishghandle ([-1 0]), [false true])
12689 */
12690 
12691 static bool
12693 {
12694  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12695 
12696  return h.ok () && gh_mgr.is_handle_visible (h);
12697 }
12698 
12699 static bool
12700 is_handle_visible (double val)
12701 {
12702  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
12703 
12704  return is_handle_visible (gh_mgr.lookup (val));
12705 }
12706 
12707 static octave_value
12709 {
12710  octave_value retval = false;
12711 
12712  if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
12713  retval = true;
12714  else if (val.isnumeric () && val.isreal ())
12715  {
12716  const NDArray handles = val.array_value ();
12717 
12718  boolNDArray result (handles.dims ());
12719 
12720  for (octave_idx_type i = 0; i < handles.numel (); i++)
12721  result.xelem (i) = is_handle_visible (handles(i));
12722 
12723  retval = result;
12724  }
12725 
12726  return retval;
12727 }
12728 
12729 DEFUN (__is_handle_visible__, args, ,
12730  doc: /* -*- texinfo -*-
12731 @deftypefn {} {@var{tf} =} __is_handle_visible__ (@var{h})
12732 Undocumented internal function.
12733 @end deftypefn */)
12734 {
12735  if (args.length () != 1)
12736  print_usage ();
12737 
12738  return ovl (is_handle_visible (args(0)));
12739 }
12740 
12741 DEFMETHOD (reset, interp, args, ,
12742  doc: /* -*- texinfo -*-
12743 @deftypefn {} {} reset (@var{h})
12744 Reset the properties of the graphic object @var{h} to their default values.
12745 
12746 For figures, the properties @qcode{"position"}, @qcode{"units"},
12747 @qcode{"windowstyle"}, and @qcode{"paperunits"} are not affected.
12748 For axes, the properties @qcode{"position"} and @qcode{"units"} are
12749 not affected.
12750 
12751 The input @var{h} may also be a vector of graphic handles in which case
12752 each individual object will be reset.
12753 @seealso{cla, clf, newplot}
12754 @end deftypefn */)
12755 {
12756  if (args.length () != 1)
12757  print_usage ();
12758 
12759  // get vector of graphics handles
12760  ColumnVector hcv = args(0).xvector_value ("reset: H must be a graphics handle");
12761 
12762  gh_manager& gh_mgr = interp.get_gh_manager ();
12763 
12764  // loop over graphics objects
12765  for (octave_idx_type n = 0; n < hcv.numel (); n++)
12766  gh_mgr.get_object (hcv(n)).reset_default_properties ();
12767 
12768  Vdrawnow_requested = true;
12769 
12770  return ovl ();
12771 }
12772 
12773 /*
12774 
12775 %!test # line object
12776 %! hf = figure ("visible", "off");
12777 %! unwind_protect
12778 %! tol = 20 * eps;
12779 %! hax = axes ("defaultlinelinewidth", 3);
12780 %!
12781 %! hli = line (1:10, 1:10, 1:10, "marker", "o",
12782 %! "markerfacecolor", "b", "linestyle", ":");
12783 %!
12784 %! reset (hli);
12785 %! assert (get (hli, "marker"), get (0, "defaultlinemarker"));
12786 %! assert (get (hli, "markerfacecolor"),
12787 %! get (0, "defaultlinemarkerfacecolor"));
12788 %! assert (get (hli, "linestyle"), get (0, "defaultlinelinestyle"));
12789 %! assert (get (hli, "linewidth"), 3, tol); # parent axes defaults
12790 %!
12791 %! unwind_protect_cleanup
12792 %! close (hf);
12793 %! end_unwind_protect
12794 
12795 %!test # patch object
12796 %! hf = figure ("visible", "off");
12797 %! unwind_protect
12798 %! tol = 20 * eps;
12799 %! t1 = (1/16:1/8:1)' * 2*pi;
12800 %! t2 = ((1/16:1/16:1)' + 1/32) * 2*pi;
12801 %! x1 = sin (t1) - 0.8;
12802 %! y1 = cos (t1);
12803 %! x2 = sin (t2) + 0.8;
12804 %! y2 = cos (t2);
12805 %! vert = [x1, y1; x2, y2];
12806 %! fac = [1:8,NaN(1,8);9:24];
12807 %! hpa = patch ("Faces",fac, "Vertices",vert, "FaceColor","r");
12808 %!
12809 %! reset (hpa);
12810 %! assert (get (hpa, "faces"), get (0, "defaultpatchfaces"), tol);
12811 %! assert (get (hpa, "vertices"), get (0, "defaultpatchvertices"), tol);
12812 %! assert (get (hpa, "facevertexcdata"),
12813 %! get (0, "defaultpatchfacevertexcdata"), tol);
12814 %! unwind_protect_cleanup
12815 %! close (hf);
12816 %! end_unwind_protect
12817 
12818 %!test # surface object
12819 %! hf = figure ("visible", "off");
12820 %! unwind_protect
12821 %! tol = 20 * eps;
12822 %! hsu = surface (peaks, "edgecolor", "none");
12823 %!
12824 %! reset (hsu);
12825 %! assert (get (hsu, "xdata"), get (0, "defaultsurfacexdata"), tol);
12826 %! assert (get (hsu, "ydata"), get (0, "defaultsurfaceydata"), tol);
12827 %! assert (get (hsu, "zdata"), get (0, "defaultsurfacezdata"), tol);
12828 %! assert (get (hsu, "edgecolor"), get (0, "defaultsurfaceedgecolor"), tol);
12829 %! unwind_protect_cleanup
12830 %! close (hf);
12831 %! end_unwind_protect
12832 
12833 %!test # image object
12834 %! hf = figure ("visible", "off");
12835 %! unwind_protect
12836 %! tol = 20 * eps;
12837 %! him = image (rand (10,10), "cdatamapping", "scaled");
12838 %!
12839 %! reset (him);
12840 %! assert (get (him, "cdata"), get (0, "defaultimagecdata"), tol);
12841 %! assert (get (him, "cdatamapping"),
12842 %! get (0, "defaultimagecdatamapping"), tol);
12843 %! unwind_protect_cleanup
12844 %! close (hf);
12845 %! end_unwind_protect
12846 
12847 %!test # text object
12848 %! hf = figure ("visible", "off");
12849 %! unwind_protect
12850 %! tol = 20 * eps;
12851 %! hte = text (5, 5, "Hi!", "fontsize", 20 ,"color", "r");
12852 %!
12853 %! reset (hte);
12854 %! assert (get (hte, "position"), get (0, "defaulttextposition"), tol);
12855 %! assert (get (hte, "fontsize"), get (0, "defaulttextfontsize"), tol);
12856 %! assert (get (hte, "color"), get (0, "defaulttextcolor"), tol);
12857 %! unwind_protect_cleanup
12858 %! close (hf);
12859 %! end_unwind_protect
12860 
12861 %!test # axes object
12862 %! hf = figure ("visible", "off");
12863 %! unwind_protect
12864 %! tol = 20 * eps;
12865 %! pos = get (0, "defaultaxesposition") * .5;
12866 %! hax = axes ("linewidth", 2, "position", pos);
12867 %! title ("Reset me, please!");
12868 %!
12869 %! reset (hax);
12870 %! assert (get (hax, "linewidth"), get (0, "defaultaxeslinewidth"), tol);
12871 %! assert (get (hax, "position"), pos, tol); # axes position is unchanged
12872 %! assert (get (hax, "default"), struct ()); # no more axes' defaults
12873 %! assert (get (get (hax, "title"), "string"), "");
12874 %! unwind_protect_cleanup
12875 %! close (hf);
12876 %! end_unwind_protect
12877 
12878 %!test # root object
12879 %! set (0, "defaultfigurevisible", "off");
12880 %! hf = figure ("visible", "off", "paperunits", "centimeters",
12881 %! "papertype", "a4");
12882 %! unwind_protect
12883 %! reset (hf);
12884 %! assert (get (hf, "papertype"), get (0, "defaultfigurepapertype"));
12885 %! assert (get (hf, "paperunits"), "centimeters"); # paperunits is unchanged
12886 %! assert (get (hf, "visible"), get (0, "defaultfigurevisible"));
12887 %! unwind_protect_cleanup
12888 %! close (hf);
12889 %! set (0, "defaultfigurevisible", "remove");
12890 %! end_unwind_protect
12891 
12892 */
12893 
12894 DEFMETHOD (set, interp, args, nargout,
12895  doc: /* -*- texinfo -*-
12896 @deftypefn {} {} set (@var{h}, @var{property}, @var{value}, @dots{})
12897 @deftypefnx {} {} set (@var{h}, @{@var{properties}@}, @{@var{values}@})
12898 @deftypefnx {} {} set (@var{h}, @var{pv})
12899 @deftypefnx {} {@var{value_list} =} set (@var{h}, @var{property})
12900 @deftypefnx {} {@var{all_value_list} =} set (@var{h})
12901 Set named property values for the graphics handle (or vector of graphics
12902 handles) @var{h}.
12903 
12904 There are three ways to give the property names and values:
12905 
12906 @itemize
12907 @item as a comma-separated list of @var{property}, @var{value} pairs
12908 
12909 Each @var{property} is a string containing the property name, each @var{value}
12910 is a value of the appropriate type for the property. When there are multiple
12911 handles in @var{h}, each one is assigned the same @var{value}. For example:
12912 
12913 @example
12914 @group
12915 h = plot ([0, 1]);
12916 set (h, 'color', 'green');
12917 @end group
12918 @end example
12919 
12920 @item as a cell array of strings @var{properties} containing property names
12921 and a cell array @var{values} containing property values.
12922 
12923 In this case, the number of columns of @var{values} must match the number of
12924 elements in @var{properties}. The first column of @var{values} contains
12925 values for the first entry in @var{properties}, etc. The number of rows of
12926 @var{values} must be 1 or match the number of elements of @var{h}. In the
12927 first case, each handle in @var{h} will be assigned the same values. In the
12928 second case, the first handle in @var{h} will be assigned the values from
12929 the first row of @var{values} and so on. For example:
12930 
12931 @example
12932 @group
12933 h = plot ([0, 1; 1, 0]);
12934 set (h, @{'color'@}, @{'green'; 'red'@});
12935 @end group
12936 @end example
12937 
12938 @item as a structure @var{pv}
12939 
12940 This is the same as the first case where the field names of @var{pv} represent
12941 the property names, and the field values give the property values. As with
12942 the first case, it is only possible to set one value for a property which will
12943 be applied to all handles in @var{h}. For example:
12944 
12945 @example
12946 @group
12947 h = plot ([0, 1]);
12948 props.color = 'green';
12949 set (h, props);
12950 @end group
12951 @end example
12952 @end itemize
12953 
12954 @code{set} is also used to query the list of values a named property will
12955 take. @code{@var{clist} = set (@var{h}, "property")} will return the list
12956 of possible values for @qcode{"property"} in the cell list @var{clist}.
12957 If no output variable is used then the list is formatted and printed to the
12958 screen.
12959 
12960 If no property is specified (@code{@var{slist} = set (@var{h})}) then a
12961 structure @var{slist} is returned where the fieldnames are the properties of
12962 the object @var{h} and the fields are the list of possible values for each
12963 property. If no output variable is used then the list is formatted and
12964 printed to the screen.
12965 
12966 For example,
12967 
12968 @example
12969 @group
12970 hf = figure ();
12971 set (hf, "paperorientation")
12972 @result{} [ landscape | @{portrait@} ]
12973 @end group
12974 @end example
12975 
12976 @noindent
12977 shows the paperorientation property can take two values with the default
12978 being @qcode{"portrait"}.
12979 @seealso{get}
12980 @end deftypefn */)
12981 {
12982  gh_manager& gh_mgr = interp.get_gh_manager ();
12983 
12984  octave::autolock guard (gh_mgr.graphics_lock ());
12985 
12986  int nargin = args.length ();
12987 
12988  if (nargin == 0)
12989  print_usage ();
12990 
12991  octave_value retval;
12992 
12993  // get vector of graphics handles
12994  ColumnVector hcv = args(0).xvector_value ("set: H must be a graphics handle");
12995 
12996  bool request_drawnow = false;
12997 
12998  // loop over graphics objects
12999  for (octave_idx_type n = 0; n < hcv.numel (); n++)
13000  {
13001  graphics_object go = gh_mgr.get_object (hcv(n));
13002 
13003  if (! go)
13004  error ("set: invalid handle (= %g)", hcv(n));
13005 
13006  if (nargin == 3 && args(1).iscellstr () && args(2).iscell ())
13007  {
13008  if (args(2).cell_value ().rows () == 1)
13009  go.set (args(1).cellstr_value (), args(2).cell_value (), 0);
13010  else if (hcv.numel () == args(2).cell_value ().rows ())
13011  go.set (args(1).cellstr_value (), args(2).cell_value (), n);
13012  else
13013  error ("set: number of graphics handles must match number of "
13014  "value rows (%" OCTAVE_IDX_TYPE_FORMAT " != "
13015  "%" OCTAVE_IDX_TYPE_FORMAT ")",
13016  hcv.numel (), args(2).cell_value ().rows ());
13017  }
13018  else if (nargin == 2 && args(1).isstruct ())
13019  go.set (args(1).map_value ());
13020  else if (nargin == 2 && args(1).is_string ())
13021  {
13022  std::string property = args(1).string_value ();
13023  std::transform (property.begin (), property.end (),
13024  property.begin (), tolower);
13025 
13026  octave_map pmap = go.values_as_struct ();
13027 
13028  if (go.has_readonly_property (property))
13029  if (nargout != 0)
13030  retval = Matrix ();
13031  else
13032  octave_stdout << "set: " << property
13033  <<" is read-only" << std::endl;
13034  else if (pmap.isfield (property))
13035  {
13036  if (nargout != 0)
13037  retval = pmap.getfield (property)(0);
13038  else
13039  {
13040  std::string s = go.value_as_string (property);
13041 
13042  octave_stdout << s;
13043  }
13044  }
13045  else
13046  error (R"(set: unknown property "%s")", property.c_str ());
13047  }
13048  else if (nargin == 1)
13049  {
13050  if (nargout != 0)
13051  retval = go.values_as_struct ();
13052  else
13053  {
13054  std::string s = go.values_as_string ();
13055 
13056  octave_stdout << s;
13057  }
13058  }
13059  else
13060  {
13061  go.set (args.splice (0, 1));
13062  }
13063 
13064  request_drawnow = true;
13065  }
13066 
13067  if (request_drawnow)
13068  Vdrawnow_requested = true;
13069 
13070  return retval;
13071 }
13072 
13073 static std::string
13075 {
13076  std::string retval;
13077 
13078  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
13079 
13080  graphics_object go = gh_mgr.get_object (val);
13081 
13082  if (! go)
13083  error ("get: invalid handle (= %g)", val);
13084 
13085  return go.type ();
13086 }
13087 
13088 DEFMETHOD (get, interp, args, ,
13089  doc: /* -*- texinfo -*-
13090 @deftypefn {} {@var{val} =} get (@var{h})
13091 @deftypefnx {} {@var{val} =} get (@var{h}, @var{p})
13092 Return the value of the named property @var{p} from the graphics handle
13093 @var{h}.
13094 
13095 If @var{p} is omitted, return the complete property list for @var{h}.
13096 
13097 If @var{h} is a vector, return a cell array including the property values or
13098 lists respectively.
13099 @seealso{set}
13100 @end deftypefn */)
13101 {
13102  gh_manager& gh_mgr = interp.get_gh_manager ();
13103 
13104  octave::autolock guard (gh_mgr.graphics_lock ());
13105 
13106  int nargin = args.length ();
13107 
13108  if (nargin < 1 || nargin > 2)
13109  print_usage ();
13110 
13111  if (args(0).isempty ())
13112  return ovl (Matrix ());
13113 
13114  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
13115 
13116  octave_idx_type hcv_len = hcv.numel ();
13117 
13118  if (nargin == 1 && hcv_len > 1)
13119  {
13120  std::string typ0 = get_graphics_object_type (hcv(0));
13121 
13122  for (octave_idx_type n = 1; n < hcv_len; n++)
13123  {
13124  std::string typ = get_graphics_object_type (hcv(n));
13125 
13126  if (typ != typ0)
13127  error ("get: vector of handles must all have the same type");
13128  }
13129  }
13130 
13131  octave_value retval;
13132  Cell vals;
13133  bool use_cell_format = false;
13134 
13135  if (nargin > 1 && args(1).iscellstr ())
13136  {
13137  Array<std::string> plist = args(1).cellstr_value ();
13138 
13139  octave_idx_type plen = plist.numel ();
13140 
13141  use_cell_format = true;
13142 
13143  vals.resize (dim_vector (hcv_len, plen));
13144 
13145  for (octave_idx_type n = 0; n < hcv_len; n++)
13146  {
13147  graphics_object go = gh_mgr.get_object (hcv(n));
13148 
13149  if (! go)
13150  error ("get: invalid handle (= %g)", hcv(n));
13151 
13152  for (octave_idx_type m = 0; m < plen; m++)
13153  {
13154  caseless_str property = plist(m);
13155 
13156  vals(n, m) = go.get (property);
13157  }
13158  }
13159  }
13160  else
13161  {
13162  caseless_str property;
13163 
13164  if (nargin > 1)
13165  property = args(
13166  1).xstring_value ("get: second argument must be property name or cell array of property names");
13167 
13168  vals.resize (dim_vector (hcv_len, 1));
13169 
13170  for (octave_idx_type n = 0; n < hcv_len; n++)
13171  {
13172  graphics_object go = gh_mgr.get_object (hcv(n));
13173 
13174  if (! go)
13175  error ("get: invalid handle (= %g)", hcv(n));
13176 
13177  if (nargin == 1)
13178  vals(n) = go.get ();
13179  else
13180  vals(n) = go.get (property);
13181  }
13182  }
13183 
13184  if (use_cell_format)
13185  retval = vals;
13186  else
13187  {
13188  octave_idx_type vals_len = vals.numel ();
13189 
13190  if (vals_len == 0)
13191  retval = Matrix ();
13192  else if (vals_len == 1)
13193  retval = vals(0);
13194  else if (vals_len > 1 && nargin == 1)
13195  {
13196  OCTAVE_LOCAL_BUFFER (octave_scalar_map, tmp, vals_len);
13197 
13198  for (octave_idx_type n = 0; n < vals_len; n++)
13199  tmp[n] = vals(n).scalar_map_value ();
13200 
13201  retval = octave_map::cat (0, vals_len, tmp);
13202  }
13203  else
13204  retval = vals;
13205  }
13206 
13207  return retval;
13208 }
13209 
13210 /*
13211 %!assert (get (findobj (0, "Tag", "nonexistenttag"), "nonexistentproperty"), [])
13212 */
13213 
13214 // Return all properties from the graphics handle @var{h}.
13215 // If @var{h} is a vector, return a cell array including the
13216 // property values or lists respectively.
13217 
13218 DEFMETHOD (__get__, interp, args, ,
13219  doc: /* -*- texinfo -*-
13220 @deftypefn {} {@var{props} =} __get__ (@var{h})
13221 Undocumented internal function.
13222 @end deftypefn */)
13223 {
13224  gh_manager& gh_mgr = interp.get_gh_manager ();
13225 
13226  octave::autolock guard (gh_mgr.graphics_lock ());
13227 
13228  if (args.length () != 1)
13229  print_usage ();
13230 
13231  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
13232 
13233  octave_idx_type hcv_len = hcv.numel ();
13234 
13235  Cell vals (dim_vector (hcv_len, 1));
13236 
13237 // vals.resize (dim_vector (hcv_len, 1));
13238 
13239  for (octave_idx_type n = 0; n < hcv_len; n++)
13240  {
13241  graphics_object go = gh_mgr.get_object (hcv(n));
13242 
13243  if (! go)
13244  error ("get: invalid handle (= %g)", hcv(n));
13245 
13246  // Disable "Octave:deprecated-property" warnings
13247  int state = toggle_warn ("Octave:deprecated-property", false);
13248 
13249  vals(n) = go.get (true);
13250 
13251  toggle_warn ("Octave:deprecated-property", true, state);
13252  }
13253 
13254  octave_idx_type vals_len = vals.numel ();
13255 
13256  if (vals_len > 1)
13257  return ovl (vals);
13258  else if (vals_len == 1)
13259  return ovl (vals(0));
13260  else
13261  return ovl ();
13262 }
13263 
13264 static octave_value
13265 make_graphics_object (const std::string& go_name,
13266  bool integer_figure_handle,
13267  const octave_value_list& args)
13268 {
13269  octave_value retval;
13270 
13271  double val = octave::numeric_limits<double>::NaN ();
13272 
13273  octave_value_list xargs = args.splice (0, 1);
13274 
13275  caseless_str p ("parent");
13276 
13277  // Remove all "parent" property overrides of the first argument to function
13278  // and accept only the last one (bug #55322).
13279  for (int i = 0; i < xargs.length (); i += 2)
13280  {
13281  if (xargs(i).is_string () && p.compare (xargs(i).string_value ()))
13282  {
13283  if (i >= (xargs.length () - 1))
13284  error ("__go_%s__: missing value for parent property",
13285  go_name.c_str ());
13286 
13287  val = xargs(i+1).double_value ();
13288 
13289  xargs = xargs.splice (i, 2);
13290  i -= 2;
13291  }
13292  }
13293 
13294  if (octave::math::isnan (val))
13295  val = args(0).xdouble_value ("__go_%s__: invalid parent", go_name.c_str ());
13296 
13297  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
13298 
13299  graphics_handle parent = gh_mgr.lookup (val);
13300 
13301  if (! parent.ok ())
13302  error ("__go_%s__: invalid parent", go_name.c_str ());
13303 
13304  graphics_handle h;
13305 
13306  try
13307  {
13308  h = gh_mgr.make_graphics_handle (go_name, parent,
13309  integer_figure_handle, false, false);
13310  }
13311  catch (octave::execution_exception& ee)
13312  {
13313  error (ee, "__go_%s__: %s, unable to create graphics handle",
13314  go_name.c_str (), ee.message ().c_str ());
13315  }
13316 
13317  try
13318  {
13319  xset (h, xargs);
13320  }
13321  catch (octave::execution_exception& ee)
13322  {
13324  error (ee, "__go_%s__: %s, unable to create graphics handle",
13325  go_name.c_str (), ee.message ().c_str ());
13326  }
13327 
13328  adopt (parent, h);
13329 
13330  xcreatefcn (h);
13331  xinitialize (h);
13332 
13333  retval = h.value ();
13334 
13335  Vdrawnow_requested = true;
13336 
13337  return retval;
13338 }
13339 
13340 DEFMETHOD (__go_figure__, interp, args, ,
13341  doc: /* -*- texinfo -*-
13342 @deftypefn {} {@var{hfig} =} __go_figure__ (@var{fignum})
13343 Undocumented internal function.
13344 @end deftypefn */)
13345 {
13346  gh_manager& gh_mgr = interp.get_gh_manager ();
13347 
13348  octave::autolock guard (gh_mgr.graphics_lock ());
13349 
13350  if (args.length () == 0)
13351  print_usage ();
13352 
13353  double val = args(0).xdouble_value ("__go_figure__: figure number must be a double value");
13354 
13355  octave_value retval;
13356 
13357  if (isfigure (val))
13358  {
13359  graphics_handle h = gh_mgr.lookup (val);
13360 
13361  xset (h, args.splice (0, 1));
13362 
13363  retval = h.value ();
13364  }
13365  else
13366  {
13367  bool int_fig_handle = true;
13368 
13369  octave_value_list xargs = args.splice (0, 1);
13370 
13372 
13373  if (octave::math::isnan (val))
13374  {
13375  caseless_str pname ("integerhandle");
13376 
13377  for (int i = 0; i < xargs.length (); i++)
13378  {
13379  if (xargs(i).is_string ()
13380  && pname.compare (xargs(i).string_value ()))
13381  {
13382  if (i < (xargs.length () - 1))
13383  {
13384  std::string pval = xargs(i+1).string_value ();
13385 
13386  caseless_str on ("on");
13387  int_fig_handle = on.compare (pval);
13388  xargs = xargs.splice (i, 2);
13389 
13390  break;
13391  }
13392  }
13393  }
13394 
13395  h = gh_mgr.make_graphics_handle ("figure", 0, int_fig_handle,
13396  false, false);
13397 
13398  if (! int_fig_handle)
13399  {
13400  // We need to initialize the integerhandle property
13401  // without calling the set_integerhandle method,
13402  // because doing that will generate a new handle value...
13403  graphics_object go = gh_mgr.get_object (h);
13404  go.get_properties ().init_integerhandle ("off");
13405  }
13406  }
13407  else if (val > 0 && octave::math::x_nint (val) == val)
13408  h = gh_mgr.make_figure_handle (val, false);
13409 
13410  if (! h.ok ())
13411  error ("__go_figure__: failed to create figure handle");
13412 
13413  try
13414  {
13415  xset (h, xargs);
13416  }
13417  catch (octave::execution_exception& ee)
13418  {
13420  error (ee, "__go_figure__: unable to create figure handle");
13421  }
13422 
13423  adopt (0, h);
13424 
13425  gh_mgr.push_figure (h);
13426 
13427  xcreatefcn (h);
13428  xinitialize (h);
13429 
13430  retval = h.value ();
13431  }
13432 
13433  return retval;
13434 }
13435 
13436 #define GO_BODY(TYPE) \
13437  gh_manager& gh_mgr = interp.get_gh_manager (); \
13438  \
13439  octave::autolock guard (gh_mgr.graphics_lock ()); \
13440  \
13441  if (args.length () == 0) \
13442  print_usage (); \
13443  \
13444  return octave_value (make_graphics_object (#TYPE, false, args)); \
13445 
13446 int
13447 calc_dimensions (const graphics_object& go)
13448 {
13449  int nd = 2;
13450 
13451  if (go.isa ("surface"))
13452  nd = 3;
13453  else if ((go.isa ("line") || go.isa ("patch") || go.isa ("scatter"))
13454  && ! go.get ("zdata").isempty ())
13455  nd = 3;
13456  else
13457  {
13458  Matrix kids = go.get_properties ().get_children ();
13459 
13460  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
13461 
13462  for (octave_idx_type i = 0; i < kids.numel (); i++)
13463  {
13464  graphics_handle hkid = gh_mgr.lookup (kids(i));
13465 
13466  if (hkid.ok ())
13467  {
13468  const graphics_object& kid = gh_mgr.get_object (hkid);
13469 
13470  if (kid.valid_object ())
13471  nd = calc_dimensions (kid);
13472 
13473  if (nd == 3)
13474  break;
13475  }
13476  }
13477  }
13478 
13479  return nd;
13480 }
13481 
13482 DEFMETHOD (__calc_dimensions__, interp, args, ,
13483  doc: /* -*- texinfo -*-
13484 @deftypefn {} {@var{ndims} =} __calc_dimensions__ (@var{axes})
13485 Internal function.
13486 
13487 Determine the number of dimensions in a graphics object, either 2 or 3.
13488 @end deftypefn */)
13489 {
13490  gh_manager& gh_mgr = interp.get_gh_manager ();
13491 
13492  octave::autolock guard (gh_mgr.graphics_lock ());
13493 
13494  if (args.length () != 1)
13495  print_usage ();
13496 
13497  double h = args(0).xdouble_value ("__calc_dimensions__: first argument must be a graphics handle");
13498 
13499  return ovl (calc_dimensions (gh_mgr.get_object (h)));
13500 }
13501 
13502 DEFMETHOD (__go_axes__, interp, args, ,
13503  doc: /* -*- texinfo -*-
13504 @deftypefn {} {@var{hax} =} __go_axes__ (@var{parent})
13505 Undocumented internal function.
13506 @end deftypefn */)
13507 {
13508  GO_BODY (axes);
13509 }
13510 
13511 DEFMETHOD (__go_line__, interp, args, ,
13512  doc: /* -*- texinfo -*-
13513 @deftypefn {} {@var{hl} =} __go_line__ (@var{parent})
13514 Undocumented internal function.
13515 @end deftypefn */)
13516 {
13517  GO_BODY (line);
13518 }
13519 
13520 DEFMETHOD (__go_text__, interp, args, ,
13521  doc: /* -*- texinfo -*-
13522 @deftypefn {} {@var{ht} =} __go_text__ (@var{parent})
13523 Undocumented internal function.
13524 @end deftypefn */)
13525 {
13526  GO_BODY (text);
13527 }
13528 
13529 DEFMETHOD (__go_image__, interp, args, ,
13530  doc: /* -*- texinfo -*-
13531 @deftypefn {} {@var{hi} =} __go_image__ (@var{parent})
13532 Undocumented internal function.
13533 @end deftypefn */)
13534 {
13535  GO_BODY (image);
13536 }
13537 
13538 DEFMETHOD (__go_surface__, interp, args, ,
13539  doc: /* -*- texinfo -*-
13540 @deftypefn {} {@var{hs} =} __go_surface__ (@var{parent})
13541 Undocumented internal function.
13542 @end deftypefn */)
13543 {
13544  GO_BODY (surface);
13545 }
13546 
13547 DEFMETHOD (__go_patch__, interp, args, ,
13548  doc: /* -*- texinfo -*-
13549 @deftypefn {} {@var{hp} =} __go_patch__ (@var{parent})
13550 Undocumented internal function.
13551 @end deftypefn */)
13552 {
13553  GO_BODY (patch);
13554 }
13555 
13556 DEFMETHOD (__go_scatter__, interp, args, ,
13557  doc: /* -*- texinfo -*-
13558 @deftypefn {} {@var{hs} =} __go_scatter__ (@var{parent})
13559 Undocumented internal function.
13560 @end deftypefn */)
13561 {
13562  GO_BODY (scatter);
13563 }
13564 
13565 DEFMETHOD (__go_light__, interp, args, ,
13566  doc: /* -*- texinfo -*-
13567 @deftypefn {} {@var{hl} =} __go_light__ (@var{parent})
13568 Undocumented internal function.
13569 @end deftypefn */)
13570 {
13571  GO_BODY (light);
13572 }
13573 
13574 DEFMETHOD (__go_hggroup__, interp, args, ,
13575  doc: /* -*- texinfo -*-
13576 @deftypefn {} {@var{hgg} =} __go_hggroup__ (@var{parent})
13577 Undocumented internal function.
13578 @end deftypefn */)
13579 {
13580  GO_BODY (hggroup);
13581 }
13582 
13583 DEFMETHOD (__go_uimenu__, interp, args, ,
13584  doc: /* -*- texinfo -*-
13585 @deftypefn {} {@var{hui} =} __go_uimenu__ (@var{parent})
13586 Undocumented internal function.
13587 @end deftypefn */)
13588 {
13589  GO_BODY (uimenu);
13590 }
13591 
13592 DEFMETHOD (__go_uicontrol__, interp, args, ,
13593  doc: /* -*- texinfo -*-
13594 @deftypefn {} {@var{hui} =} __go_uicontrol__ (@var{parent})
13595 Undocumented internal function.
13596 @end deftypefn */)
13597 {
13598  GO_BODY (uicontrol);
13599 }
13600 
13601 DEFMETHOD (__go_uibuttongroup__, interp, args, ,
13602  doc: /* -*- texinfo -*-
13603 @deftypefn {} {@var{hui} =} __go_uibuttongroup__ (@var{parent})
13604 Undocumented internal function.
13605 @end deftypefn */)
13606 {
13607  GO_BODY (uibuttongroup);
13608 }
13609 
13610 DEFMETHOD (__go_uipanel__, interp, args, ,
13611  doc: /* -*- texinfo -*-
13612 @deftypefn {} {@var{hui} =} __go_uipanel__ (@var{parent})
13613 Undocumented internal function.
13614 @end deftypefn */)
13615 {
13616  GO_BODY (uipanel);
13617 }
13618 
13619 DEFMETHOD (__go_uicontextmenu__, interp, args, ,
13620  doc: /* -*- texinfo -*-
13621 @deftypefn {} {@var{hui} =} __go_uicontextmenu__ (@var{parent})
13622 Undocumented internal function.
13623 @end deftypefn */)
13624 {
13625  GO_BODY (uicontextmenu);
13626 }
13627 
13628 DEFMETHOD (__go_uitable__, interp, args, ,
13629  doc: /* -*- texinfo -*-
13630 @deftypefn {} {@var{hui} =} __go_uitable__ (@var{parent})
13631 Undocumented internal function.
13632 @end deftypefn */)
13633 {
13634  GO_BODY (uitable);
13635 }
13636 
13637 DEFMETHOD (__go_uitoolbar__, interp, args, ,
13638  doc: /* -*- texinfo -*-
13639 @deftypefn {} {@var{hui} =} __go_uitoolbar__ (@var{parent})
13640 Undocumented internal function.
13641 @end deftypefn */)
13642 {
13643  GO_BODY (uitoolbar);
13644 }
13645 
13646 DEFMETHOD (__go_uipushtool__, interp, args, ,
13647  doc: /* -*- texinfo -*-
13648 @deftypefn {} {@var{hui} =} __go_uipushtool__ (@var{parent})
13649 Undocumented internal function.
13650 @end deftypefn */)
13651 {
13652  GO_BODY (uipushtool);
13653 }
13654 
13655 DEFMETHOD (__go_uitoggletool__, interp, args, ,
13656  doc: /* -*- texinfo -*-
13657 @deftypefn {} {@var{hui} =} __go_uitoggletool__ (@var{parent})
13658 Undocumented internal function.
13659 @end deftypefn */)
13660 {
13661  GO_BODY (uitoggletool);
13662 }
13663 
13664 DEFMETHOD (__go_delete__, interp, args, ,
13665  doc: /* -*- texinfo -*-
13666 @deftypefn {} {} __go_delete__ (@var{h})
13667 Undocumented internal function.
13668 @end deftypefn */)
13669 {
13670  gh_manager& gh_mgr = interp.get_gh_manager ();
13671 
13672  octave::autolock guard (gh_mgr.graphics_lock ());
13673 
13674  if (args.length () != 1)
13675  print_usage ();
13676 
13678 
13679  const NDArray vals = args(0).xarray_value ("delete: invalid graphics object");
13680 
13681  // Check all the handles to delete are valid first,
13682  // as callbacks might delete one of the handles we later want to delete.
13683  for (octave_idx_type i = 0; i < vals.numel (); i++)
13684  {
13685  h = gh_mgr.lookup (vals(i));
13686 
13687  if (! h.ok ())
13688  error ("delete: invalid graphics object (= %g)", vals(i));
13689  }
13690 
13691  delete_graphics_objects (vals);
13692 
13693  return ovl ();
13694 }
13695 
13696 DEFMETHOD (__go_handles__, interp, args, ,
13697  doc: /* -*- texinfo -*-
13698 @deftypefn {} {@var{hg_list} =} __go_handles__ (@var{show_hidden})
13699 Undocumented internal function.
13700 @end deftypefn */)
13701 {
13702  gh_manager& gh_mgr = interp.get_gh_manager ();
13703 
13704  octave::autolock guard (gh_mgr.graphics_lock ());
13705 
13706  bool show_hidden = false;
13707 
13708  if (args.length () > 0)
13709  show_hidden = args(0).bool_value ();
13710 
13711  return ovl (gh_mgr.handle_list (show_hidden));
13712 }
13713 
13714 DEFMETHOD (__go_figure_handles__, interp, args, ,
13715  doc: /* -*- texinfo -*-
13716 @deftypefn {} {@var{hfig_list} =} __go_figure_handles__ (@var{show_hidden})
13717 Undocumented internal function.
13718 @end deftypefn */)
13719 {
13720  gh_manager& gh_mgr = interp.get_gh_manager ();
13721 
13722  octave::autolock guard (gh_mgr.graphics_lock ());
13723 
13724  bool show_hidden = false;
13725 
13726  if (args.length () > 0)
13727  show_hidden = args(0).bool_value ();
13728 
13729  return ovl (gh_mgr.figure_handle_list (show_hidden));
13730 }
13731 
13732 DEFMETHOD (__go_execute_callback__, interp, args, ,
13733  doc: /* -*- texinfo -*-
13734 @deftypefn {} {} __go_execute_callback__ (@var{h}, @var{name})
13735 @deftypefnx {} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})
13736 Undocumented internal function.
13737 @end deftypefn */)
13738 {
13739  int nargin = args.length ();
13740 
13741  if (nargin < 2 || nargin > 3)
13742  print_usage ();
13743 
13744  const NDArray vals = args(0).xarray_value ("__go_execute_callback__: invalid graphics object");
13745 
13746  std::string name = args(1).xstring_value ("__go_execute_callback__: invalid callback name");
13747 
13748  gh_manager& gh_mgr = interp.get_gh_manager ();
13749 
13750  for (octave_idx_type i = 0; i < vals.numel (); i++)
13751  {
13752  double val = vals(i);
13753 
13754  graphics_handle h = gh_mgr.lookup (val);
13755 
13756  if (! h.ok ())
13757  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
13758 
13759  if (nargin == 2)
13760  gh_mgr.execute_callback (h, name);
13761  else
13762  gh_mgr.execute_callback (h, name, args(2));
13763  }
13764 
13765  return ovl ();
13766 }
13767 
13768 DEFMETHOD (__go_post_callback__, interp, args, ,
13769  doc: /* -*- texinfo -*-
13770 @deftypefn {} {} __go_post_callback__ (@var{h}, @var{name})
13771 @deftypefnx {} {} __go_post_callback__ (@var{h}, @var{name}, @var{param})
13772 Undocumented internal function.
13773 @end deftypefn */)
13774 {
13775  int nargin = args.length ();
13776 
13777  if (nargin < 2 || nargin > 3)
13778  print_usage ();
13779 
13780  const NDArray vals = args(0).xarray_value ("__go_post_callback__: invalid graphics object");
13781 
13782  std::string name = args(1).xstring_value ("__go_post_callback__: invalid callback name");
13783 
13784  gh_manager& gh_mgr = interp.get_gh_manager ();
13785 
13786  for (octave_idx_type i = 0; i < vals.numel (); i++)
13787  {
13788  double val = vals(i);
13789 
13790  graphics_handle h = gh_mgr.lookup (val);
13791 
13792  if (! h.ok ())
13793  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
13794 
13795  if (nargin == 2)
13796  gh_mgr.post_callback (h, name);
13797  else
13798  gh_mgr.post_callback (h, name, args(2));
13799  }
13800 
13801  return ovl ();
13802 }
13803 
13804 DEFMETHOD (__image_pixel_size__, interp, args, ,
13805  doc: /* -*- texinfo -*-
13806 @deftypefn {} {@var{sz} =} __image_pixel_size__ (@var{h})
13807 Internal function: returns the pixel size of the image in normalized units.
13808 @end deftypefn */)
13809 {
13810  if (args.length () != 1)
13811  print_usage ();
13812 
13813  gh_manager& gh_mgr = interp.get_gh_manager ();
13814 
13815  double h = args(0).xdouble_value ("__image_pixel_size__: argument is not a handle");
13816 
13817  graphics_object go = gh_mgr.get_object (h);
13818 
13819  if (! go || ! go.isa ("image"))
13820  error ("__image_pixel_size__: object is not an image");
13821 
13822  image::properties& ip
13823  = dynamic_cast<image::properties&> (go.get_properties ());
13824 
13825  Matrix dp = Matrix (1, 2);
13826  dp(0) = ip.pixel_xsize ();
13827  dp(1) = ip.pixel_ysize ();
13828  return ovl (dp);
13829 }
13830 
13831 DEFMETHOD (available_graphics_toolkits, interp, , ,
13832  doc: /* -*- texinfo -*-
13833 @deftypefn {} {@var{toolkits} =} available_graphics_toolkits ()
13834 Return a cell array of registered graphics toolkits.
13835 @seealso{graphics_toolkit, register_graphics_toolkit}
13836 @end deftypefn */)
13837 {
13838  gh_manager& gh_mgr = interp.get_gh_manager ();
13839 
13840  octave::autolock guard (gh_mgr.graphics_lock ());
13841 
13842  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13843 
13844  return ovl (gtk_mgr.available_toolkits_list ());
13845 }
13846 
13847 DEFMETHOD (register_graphics_toolkit, interp, args, ,
13848  doc: /* -*- texinfo -*-
13849 @deftypefn {} {} register_graphics_toolkit ("@var{toolkit}")
13850 List @var{toolkit} as an available graphics toolkit.
13851 
13852 Programming Note: No input validation is done on the input string; it is simply
13853 added to the list of possible graphics toolkits.
13854 @seealso{available_graphics_toolkits}
13855 @end deftypefn */)
13856 {
13857  gh_manager& gh_mgr = interp.get_gh_manager ();
13858 
13859  octave::autolock guard (gh_mgr.graphics_lock ());
13860 
13861  if (args.length () != 1)
13862  print_usage ();
13863 
13864  std::string name = args(0).xstring_value ("register_graphics_toolkit: TOOLKIT must be a string");
13865 
13866  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13867 
13868  gtk_mgr.register_toolkit (name);
13869 
13870  return ovl ();
13871 }
13872 
13873 DEFMETHOD (loaded_graphics_toolkits, interp, , ,
13874  doc: /* -*- texinfo -*-
13875 @deftypefn {} {@var{toolkits} =} loaded_graphics_toolkits ()
13876 Return a cell array of the currently loaded graphics toolkits.
13877 @seealso{available_graphics_toolkits}
13878 @end deftypefn */)
13879 {
13880  gh_manager& gh_mgr = interp.get_gh_manager ();
13881 
13882  octave::autolock guard (gh_mgr.graphics_lock ());
13883 
13884  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13885 
13886  return ovl (gtk_mgr.loaded_toolkits_list ());
13887 }
13888 
13889 DEFMETHOD (__show_figure__, interp, args, ,
13890  doc: /* -*- texinfo -*-
13891 @deftypefn {} {} __show_figure__ (@var{n})
13892 Undocumented internal function.
13893 @end deftypefn */)
13894 {
13895  if (args.length () != 1)
13896  print_usage ();
13897 
13898  gh_manager& gh_mgr = interp.get_gh_manager ();
13899 
13900  double h = args(0).xdouble_value ("__show_figure__: invalid handle H");
13901 
13902  graphics_handle gh = gh_mgr.lookup (h);
13903 
13904  if (! gh.ok ())
13905  error ("__show_figure__: invalid graphics object (= %g)", h);
13906 
13907  graphics_object go = gh_mgr.get_object (gh);
13908 
13909  figure::properties& fprops
13910  = dynamic_cast<figure::properties&> (go.get_properties ());
13911 
13912  fprops.get_toolkit ().show_figure (go);
13913 
13914  return ovl ();
13915 }
13916 
13917 DEFMETHOD (drawnow, interp, args, ,
13918  doc: /* -*- texinfo -*-
13919 @deftypefn {} {} drawnow ()
13920 @deftypefnx {} {} drawnow ("expose")
13921 @deftypefnx {} {} drawnow (@var{term}, @var{file}, @var{debug_file})
13922 Update figure windows and their children.
13923 
13924 The event queue is flushed and any callbacks generated are executed.
13925 
13926 With the optional argument @qcode{"expose"}, only graphic objects are
13927 updated and no other events or callbacks are processed.
13928 
13929 The third calling form of @code{drawnow} is for debugging and is
13930 undocumented.
13931 @seealso{refresh}
13932 @end deftypefn */)
13933 {
13934  if (args.length () > 3)
13935  print_usage ();
13936 
13937  octave::unwind_protect_var<bool> restore_var (Vdrawnow_requested, false);
13938 
13939  // Redraw unless we are in the middle of a deletion.
13940 
13941  if (! delete_executing)
13942  {
13943  gh_manager& gh_mgr = interp.get_gh_manager ();
13944 
13945  octave::autolock guard (gh_mgr.graphics_lock ());
13946 
13947  if (args.length () <= 1)
13948  {
13949  // First process events so that the redraw happens when all
13950  // objects are in their definite state.
13951  bool do_events = true;
13952 
13953  if (args.length () == 1)
13954  {
13955  caseless_str val (args(0).xstring_value ("drawnow: first argument must be a string"));
13956 
13957  if (val.compare ("expose"))
13958  do_events = false;
13959  else
13960  error ("drawnow: invalid argument, 'expose' is only valid option");
13961  }
13962 
13963  if (do_events)
13964  {
13965  gh_mgr.unlock ();
13966 
13967  gh_mgr.process_events ();
13968 
13969  gh_mgr.lock ();
13970  }
13971 
13972  Matrix hlist = gh_mgr.figure_handle_list (true);
13973 
13974  // Redraw modified figures
13975  for (int i = 0; i < hlist.numel (); i++)
13976  {
13977  graphics_handle h = gh_mgr.lookup (hlist(i));
13978 
13979  if (h.ok () && h != 0)
13980  {
13981  graphics_object go = gh_mgr.get_object (h);
13982  figure::properties& fprops
13983  = dynamic_cast<figure::properties&> (go.get_properties ());
13984 
13985  if (fprops.is_modified ())
13986  {
13987  if (fprops.is_visible ())
13988  {
13989  gh_mgr.unlock ();
13990 
13991  fprops.get_toolkit ().redraw_figure (go);
13992 
13993  gh_mgr.lock ();
13994  }
13995 
13996  fprops.set_modified (false);
13997  }
13998  }
13999 
14000  }
14001  }
14002  else if (args.length () >= 2 && args.length () <= 3)
14003  {
14004  std::string term, file, debug_file;
14005 
14006  term = args(0).xstring_value ("drawnow: TERM must be a string");
14007 
14008  file = args(1).xstring_value ("drawnow: FILE must be a string");
14009 
14010  if (file.empty ())
14011  error ("drawnow: empty output ''");
14012  else if (file.length () == 1 && file[0] == '|')
14013  error ("drawnow: empty pipe '|'");
14014  else if (file[0] != '|')
14015  {
14016  std::size_t pos = file.find_last_of (octave::sys::file_ops::dir_sep_chars ());
14017 
14018  if (pos != std::string::npos)
14019  {
14020  std::string dirname = file.substr (0, pos+1);
14021 
14022  octave::sys::file_stat fs (dirname);
14023 
14024  if (! fs || ! fs.is_dir ())
14025  error ("drawnow: nonexistent directory '%s'",
14026  dirname.c_str ());
14027 
14028  }
14029  }
14030 
14031  debug_file = (args.length () > 2 ? args(2).xstring_value ("drawnow: DEBUG_FILE must be a string") :
14032  "");
14033 
14034  graphics_handle h = gcf ();
14035 
14036  if (! h.ok ())
14037  error ("drawnow: nothing to draw");
14038 
14039  graphics_object go = gh_mgr.get_object (h);
14040 
14041  gh_mgr.unlock ();
14042 
14043  go.get_toolkit ().print_figure (go, term, file, debug_file);
14044 
14045  gh_mgr.lock ();
14046  }
14047  }
14048 
14049  return ovl ();
14050 }
14051 
14052 DEFMETHOD (addlistener, interp, args, ,
14053  doc: /* -*- texinfo -*-
14054 @deftypefn {} {} addlistener (@var{h}, @var{prop}, @var{fcn})
14055 Register @var{fcn} as listener for the property @var{prop} of the graphics
14056 object @var{h}.
14057 
14058 Property listeners are executed (in order of registration) when the property
14059 is set. The new value is already available when the listeners are executed.
14060 
14061 @var{prop} must be a string naming a valid property in @var{h}.
14062 
14063 @var{fcn} can be a function handle, a string or a cell array whose first
14064 element is a function handle. If @var{fcn} is a function handle, the
14065 corresponding function should accept at least 2 arguments, that will be
14066 set to the object handle and the empty matrix respectively. If @var{fcn}
14067 is a string, it must be any valid octave expression. If @var{fcn} is a cell
14068 array, the first element must be a function handle with the same signature
14069 as described above. The next elements of the cell array are passed
14070 as additional arguments to the function.
14071 
14072 Example:
14073 
14074 @example
14075 @group
14076 function my_listener (h, dummy, p1)
14077  fprintf ("my_listener called with p1=%s\n", p1);
14078 endfunction
14079 
14080 addlistener (gcf, "position", @{@@my_listener, "my string"@})
14081 @end group
14082 @end example
14083 
14084 @seealso{dellistener, addproperty, hggroup}
14085 @end deftypefn */)
14086 {
14087  gh_manager& gh_mgr = interp.get_gh_manager ();
14088 
14089  octave::autolock guard (gh_mgr.graphics_lock ());
14090 
14091  int nargin = args.length ();
14092 
14093  if (nargin < 3 || nargin > 4)
14094  print_usage ();
14095 
14096  double h = args(0).xdouble_value ("addlistener: invalid handle H");
14097 
14098  std::string pname = args(1).xstring_value ("addlistener: PROP must be a string");
14099 
14100  graphics_handle gh = gh_mgr.lookup (h);
14101 
14102  if (! gh.ok ())
14103  error ("addlistener: invalid graphics object (= %g)", h);
14104 
14105  graphics_object go = gh_mgr.get_object (gh);
14106 
14107  go.add_property_listener (pname, args(2), GCB_POSTSET);
14108 
14109  if (args.length () == 4)
14110  {
14111  caseless_str persistent = args(3).string_value ();
14112  if (persistent.compare ("persistent"))
14113  go.add_property_listener (pname, args(2), GCB_PERSISTENT);
14114  }
14115 
14116  return ovl ();
14117 }
14118 
14119 DEFMETHOD (dellistener, interp, args, ,
14120  doc: /* -*- texinfo -*-
14121 @deftypefn {} {} dellistener (@var{h}, @var{prop}, @var{fcn})
14122 Remove the registration of @var{fcn} as a listener for the property
14123 @var{prop} of the graphics object @var{h}.
14124 
14125 The function @var{fcn} must be the same variable (not just the same value),
14126 as was passed to the original call to @code{addlistener}.
14127 
14128 If @var{fcn} is not defined then all listener functions of @var{prop}
14129 are removed.
14130 
14131 Example:
14132 
14133 @example
14134 @group
14135 function my_listener (h, dummy, p1)
14136  fprintf ("my_listener called with p1=%s\n", p1);
14137 endfunction
14138 
14139 c = @{@@my_listener, "my string"@};
14140 addlistener (gcf, "position", c);
14141 dellistener (gcf, "position", c);
14142 @end group
14143 @end example
14144 
14145 @seealso{addlistener}
14146 @end deftypefn */)
14147 {
14148  gh_manager& gh_mgr = interp.get_gh_manager ();
14149 
14150  octave::autolock guard (gh_mgr.graphics_lock ());
14151 
14152  if (args.length () < 2 || args.length () > 3)
14153  print_usage ();
14154 
14155  double h = args(0).xdouble_value ("dellistener: invalid handle");
14156 
14157  std::string pname = args(1).xstring_value ("dellistener: PROP must be a string");
14158 
14159  graphics_handle gh = gh_mgr.lookup (h);
14160 
14161  if (! gh.ok ())
14162  error ("dellistener: invalid graphics object (= %g)", h);
14163 
14164  graphics_object go = gh_mgr.get_object (gh);
14165 
14166  if (args.length () == 2)
14167  go.delete_property_listener (pname, octave_value (), GCB_POSTSET);
14168  else
14169  {
14170  if (args(2).is_string ()
14171  && args(2).string_value () == "persistent")
14172  {
14173  go.delete_property_listener (pname, octave_value (),
14174  GCB_PERSISTENT);
14175  go.delete_property_listener (pname, octave_value (),
14176  GCB_POSTSET);
14177  }
14178  else
14179  go.delete_property_listener (pname, args(2), GCB_POSTSET);
14180  }
14181 
14182  return ovl ();
14183 }
14184 
14185 DEFMETHOD (addproperty, interp, args, ,
14186  doc: /* -*- texinfo -*-
14187 @deftypefn {} {} addproperty (@var{name}, @var{h}, @var{type})
14188 @deftypefnx {} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})
14189 Create a new property named @var{name} in graphics object @var{h}.
14190 
14191 @var{type} determines the type of the property to create. @var{args}
14192 usually contains the default value of the property, but additional
14193 arguments might be given, depending on the type of the property.
14194 
14195 The supported property types are:
14196 
14197 @table @code
14198 @item string
14199 A string property. @var{arg} contains the default string value.
14200 
14201 @item any
14202 An @nospell{un-typed} property. This kind of property can hold any octave
14203 value. @var{args} contains the default value.
14204 
14205 @item radio
14206 A string property with a limited set of accepted values. The first
14207 argument must be a string with all accepted values separated by
14208 a vertical bar ('|'). The default value can be marked by enclosing
14209 it with a '@{' '@}' pair. The default value may also be given as
14210 an optional second string argument.
14211 
14212 @item boolean
14213 A boolean property. This property type is equivalent to a radio
14214 property with "on|off" as accepted values. @var{arg} contains
14215 the default property value.
14216 
14217 @item double
14218 A scalar double property. @var{arg} contains the default value.
14219 
14220 @item handle
14221 A handle property. This kind of property holds the handle of a
14222 graphics object. @var{arg} contains the default handle value.
14223 When no default value is given, the property is initialized to
14224 the empty matrix.
14225 
14226 @item data
14227 A data (matrix) property. @var{arg} contains the default data
14228 value. When no default value is given, the data is initialized to
14229 the empty matrix.
14230 
14231 @item color
14232 A color property. @var{arg} contains the default color value.
14233 When no default color is given, the property is set to black.
14234 An optional second string argument may be given to specify an
14235 additional set of accepted string values (like a radio property).
14236 @end table
14237 
14238 @var{type} may also be the concatenation of a core object type and
14239 a valid property name for that object type. The property created
14240 then has the same characteristics as the referenced property (type,
14241 possible values, hidden state@dots{}). This allows one to clone an
14242 existing property into the graphics object @var{h}.
14243 
14244 Examples:
14245 
14246 @example
14247 @group
14248 addproperty ("my_property", gcf, "string", "a string value");
14249 addproperty ("my_radio", gcf, "radio", "val_1|val_2|@{val_3@}");
14250 addproperty ("my_style", gcf, "linelinestyle", "--");
14251 @end group
14252 @end example
14253 
14254 @seealso{addlistener, hggroup}
14255 @end deftypefn */)
14256 {
14257  gh_manager& gh_mgr = interp.get_gh_manager ();
14258 
14259  octave::autolock guard (gh_mgr.graphics_lock ());
14260 
14261  if (args.length () < 3)
14262  print_usage ();
14263 
14264  std::string name = args(0).xstring_value ("addproperty: NAME must be a string");
14265 
14266  double h = args(1).xdouble_value ("addproperty: invalid handle H");
14267 
14268  graphics_handle gh = gh_mgr.lookup (h);
14269 
14270  if (! gh.ok ())
14271  error ("addproperty: invalid graphics object (= %g)", h);
14272 
14273  graphics_object go = gh_mgr.get_object (gh);
14274 
14275  std::string type = args(2).xstring_value ("addproperty: TYPE must be a string");
14276 
14277  if (go.get_properties ().has_property (name))
14278  error ("addproperty: a '%s' property already exists in the graphics object",
14279  name.c_str ());
14280 
14281  property p = property::create (name, gh, type, args.splice (0, 3));
14282 
14283  go.get_properties ().insert_property (name, p);
14284 
14285  return ovl ();
14286 }
14287 
14289 get_property_from_handle (double handle, const std::string& property,
14290  const std::string& fcn)
14291 {
14292  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
14293 
14294  octave::autolock guard (gh_mgr.graphics_lock ());
14295 
14296  graphics_object go = gh_mgr.get_object (handle);
14297 
14298  if (! go)
14299  error ("%s: invalid handle (= %g)", fcn.c_str (), handle);
14300 
14301  return go.get (caseless_str (property));
14302 }
14303 
14304 bool
14305 set_property_in_handle (double handle, const std::string& property,
14306  const octave_value& arg, const std::string& fcn)
14307 {
14308  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
14309 
14310  octave::autolock guard (gh_mgr.graphics_lock ());
14311 
14312  graphics_object go = gh_mgr.get_object (handle);
14313 
14314  if (! go)
14315  error ("%s: invalid handle (= %g)", fcn.c_str (), handle);
14316 
14317  go.set (caseless_str (property), arg);
14318 
14319  return true;
14320 }
14321 
14322 static bool
14324 {
14325  octave_value_list args(2);
14326 
14327  args(0) = ov1;
14328  args(1) = ov2;
14329 
14330  octave_value_list result = octave::feval ("isequal", args, 1);
14331 
14332  if (result.length () > 0)
14333  return result(0).bool_value ();
14334 
14335  return false;
14336 }
14337 
14338 static std::map<uint32_t, bool> waitfor_results;
14339 
14340 static void
14341 cleanup_waitfor_id (uint32_t id)
14342 {
14343  waitfor_results.erase (id);
14344 }
14345 
14346 static void
14348  listener_mode mode = GCB_POSTSET)
14349 {
14350  Cell c = listener.cell_value ();
14351 
14352  if (c.numel () >= 4)
14353  {
14354  double h = c(2).double_value ();
14355 
14356  caseless_str pname = c(3).string_value ();
14357 
14358  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
14359 
14360  octave::autolock guard (gh_mgr.graphics_lock ());
14361 
14362  graphics_handle gh = gh_mgr.lookup (h);
14363 
14364  if (gh.ok ())
14365  {
14366  graphics_object go = gh_mgr.get_object (gh);
14367 
14368  if (go.get_properties ().has_property (pname))
14369  {
14370  go.get_properties ().delete_listener (pname, listener, mode);
14371 
14372  if (mode == GCB_POSTSET)
14373  go.get_properties ().delete_listener (pname, listener,
14374  GCB_PERSISTENT);
14375  }
14376  }
14377  }
14378 }
14379 
14380 static void
14382 { do_cleanup_waitfor_listener (listener, GCB_POSTSET); }
14383 
14384 static void
14386 { do_cleanup_waitfor_listener (listener, GCB_PREDELETE); }
14387 
14389 {
14390  if (args.length () > 3)
14391  {
14392  uint32_t id = args(2).uint32_scalar_value ().value ();
14393 
14394  if (args.length () > 5)
14395  {
14396  double h = args(0).double_value ();
14397 
14398  caseless_str pname = args(4).string_value ();
14399 
14400  gh_manager& gh_mgr = octave::__get_gh_manager__ ();
14401 
14402  octave::autolock guard (gh_mgr.graphics_lock ());
14403 
14404  graphics_handle gh = gh_mgr.lookup (h);
14405 
14406  if (gh.ok ())
14407  {
14408  graphics_object go = gh_mgr.get_object (gh);
14409  octave_value pvalue = go.get (pname);
14410 
14411  if (compare_property_values (pvalue, args(5)))
14412  waitfor_results[id] = true;
14413  }
14414  }
14415  else
14416  waitfor_results[id] = true;
14417  }
14418 
14419  return ovl ();
14420 }
14421 
14423 {
14424  if (args.length () > 2)
14425  {
14426  uint32_t id = args(2).uint32_scalar_value ().value ();
14427 
14428  waitfor_results[id] = true;
14429  }
14430 
14431  return ovl ();
14432 }
14433 
14434 DEFMETHOD (waitfor, interp, args, ,
14435  doc: /* -*- texinfo -*-
14436 @deftypefn {} {} waitfor (@var{h})
14437 @deftypefnx {} {} waitfor (@var{h}, @var{prop})
14438 @deftypefnx {} {} waitfor (@var{h}, @var{prop}, @var{value})
14439 @deftypefnx {} {} waitfor (@dots{}, "timeout", @var{timeout})
14440 Suspend the execution of the current program until a condition is
14441 satisfied on the graphics handle @var{h}.
14442 
14443 While the program is suspended graphics events are still processed normally,
14444 allowing callbacks to modify the state of graphics objects. This function
14445 is reentrant and can be called from a callback, while another @code{waitfor}
14446 call is pending at the top-level.
14447 
14448 In the first form, program execution is suspended until the graphics object
14449 @var{h} is destroyed. If the graphics handle is invalid or if @var{h} is
14450 the root graphics handle and no property @var{prop} was provided, the function
14451 returns immediately.
14452 
14453 In the second form, execution is suspended until the graphics object is
14454 destroyed or the property named @var{prop} is modified. If the graphics
14455 handle is invalid or the property does not exist, the function returns
14456 immediately.
14457 
14458 In the third form, execution is suspended until the graphics object is
14459 destroyed or the property named @var{prop} is set to @var{value}. The
14460 function @code{isequal} is used to compare property values. If the graphics
14461 handle is invalid, the property does not exist or the property is already
14462 set to @var{value}, the function returns immediately.
14463 
14464 An optional timeout can be specified using the property @qcode{"timeout"}.
14465 This timeout value is the number of seconds to wait for the condition to be
14466 true. @var{timeout} must be at least 1. If a smaller value is specified, a
14467 warning is issued and a value of 1 is used instead. If the timeout value is
14468 not an integer, it is truncated towards 0.
14469 
14470 To define a condition on a property named @qcode{"timeout"}, use the string
14471 @qcode{'@backslashchar{}timeout'} instead.
14472 
14473 In all cases, typing CTRL-C stops program execution immediately.
14474 @seealso{waitforbuttonpress, isequal}
14475 @end deftypefn */)
14476 {
14477  if (args.length () == 0)
14478  print_usage ();
14479 
14480  // return immediately if the graphics handle is invalid
14481  if (args(0).isempty ())
14482  return ovl ();
14483 
14484  double h = args(0).xdouble_value ("waitfor: invalid handle value");
14485 
14486  if (! ishghandle (h) || (h == 0 && args.length () == 1))
14487  return ovl ();
14488 
14489  caseless_str pname;
14490 
14491  octave::unwind_action cleanup_waitfor_id_action;
14492  octave::unwind_action cleanup_waitfor_postset_listener_action;
14493  octave::unwind_action cleanup_waitfor_predelete_listener_action;
14494 
14495  static uint32_t id_counter = 0;
14496  uint32_t id = 0;
14497 
14498  int max_arg_index = 0;
14499  int timeout_index = -1;
14500 
14501  double timeout = 0;
14502 
14503  gh_manager& gh_mgr = interp.get_gh_manager ();
14504 
14505  if (args.length () > 1)
14506  {
14507  pname = args(1).xstring_value ("waitfor: PROP must be a string");
14508 
14509  if (pname.empty ())
14510  error ("waitfor: PROP must be a non-empty string");
14511 
14512  if (pname != "timeout")
14513  {
14514  if (pname.compare (R"(\timeout)"))
14515  pname = "timeout";
14516 
14517  static octave_value wf_listener;
14518 
14519  if (! wf_listener.is_defined ())
14520  wf_listener
14522  "waitfor_listener"));
14523 
14524  max_arg_index++;
14525  if (args.length () > 2)
14526  {
14527  if (args(2).is_string ())
14528  {
14529  caseless_str s = args(2).string_value ();
14530 
14531  if (s.compare ("timeout"))
14532  timeout_index = 2;
14533  else
14534  max_arg_index++;
14535  }
14536  else
14537  max_arg_index++;
14538  }
14539 
14540  Cell listener (1, max_arg_index >= 2 ? 5 : 4);
14541 
14542  id = id_counter++;
14543  cleanup_waitfor_id_action.set (cleanup_waitfor_id, id);
14544  waitfor_results[id] = false;
14545 
14546  listener(0) = wf_listener;
14547  listener(1) = octave_uint32 (id);
14548  listener(2) = h;
14549  listener(3) = pname;
14550 
14551  if (max_arg_index >= 2)
14552  listener(4) = args(2);
14553 
14554  octave_value ov_listener (listener);
14555 
14556  octave::autolock guard (gh_mgr.graphics_lock ());
14557 
14558  graphics_handle gh = gh_mgr.lookup (h);
14559 
14560  if (gh.ok ())
14561  {
14562  graphics_object go = gh_mgr.get_object (gh);
14563 
14564  if (max_arg_index >= 2
14565  && compare_property_values (go.get (pname), args(2)))
14566  waitfor_results[id] = true;
14567  else
14568  {
14569  cleanup_waitfor_postset_listener_action.set
14570  (cleanup_waitfor_postset_listener, ov_listener);
14571 
14572  go.add_property_listener (pname, ov_listener, GCB_POSTSET);
14573  go.add_property_listener (pname, ov_listener, GCB_PERSISTENT);
14574 
14575  if (go.get_properties ().has_dynamic_property (pname))
14576  {
14577  static octave_value wf_del_listener;
14578 
14579  if (! wf_del_listener.is_defined ())
14580  wf_del_listener
14583  "waitfor_del_listener"));
14584 
14585  Cell del_listener (1, 4);
14586 
14587  del_listener(0) = wf_del_listener;
14588  del_listener(1) = octave_uint32 (id);
14589  del_listener(2) = h;
14590  del_listener(3) = pname;
14591 
14592  octave_value ov_del_listener (del_listener);
14593 
14594  cleanup_waitfor_predelete_listener_action.set
14595  (cleanup_waitfor_predelete_listener, ov_del_listener);
14596 
14597  go.add_property_listener (pname, ov_del_listener,
14598  GCB_PREDELETE);
14599  }
14600  }
14601  }
14602  }
14603  }
14604 
14605  if (timeout_index < 0 && args.length () > (max_arg_index + 1))
14606  {
14607  caseless_str s = args(max_arg_index +
14608  1).xstring_value ("waitfor: invalid parameter, expected 'timeout'");
14609 
14610  if (! s.compare ("timeout"))
14611  error ("waitfor: invalid parameter '%s'", s.c_str ());
14612 
14613  timeout_index = max_arg_index + 1;
14614  }
14615 
14616  if (timeout_index >= 0)
14617  {
14618  if (args.length () <= (timeout_index + 1))
14619  error ("waitfor: missing TIMEOUT value");
14620 
14621  timeout = args(timeout_index + 1).xscalar_value ("waitfor: TIMEOUT must be a scalar >= 1");
14622 
14623  if (timeout < 1)
14624  {
14625  warning ("waitfor: TIMEOUT value must be >= 1, using 1 instead");
14626  timeout = 1;
14627  }
14628  }
14629 
14630  // FIXME: There is still a "hole" in the following loop. The code
14631  // assumes that an object handle is unique, which is a fair
14632  // assumption, except for figures. If a figure is destroyed
14633  // then recreated with the same figure ID, within the same
14634  // run of event hooks, then the figure destruction won't be
14635  // caught and the loop will not stop. This is an unlikely
14636  // possibility in practice, though.
14637  //
14638  // Using deletefcn callback is also unreliable as it could be
14639  // modified during a callback execution and the waitfor loop
14640  // would not stop.
14641  //
14642  // The only "good" implementation would require object
14643  // listeners, similar to property listeners.
14644 
14645  octave::sys::time start;
14646 
14647  if (timeout > 0)
14648  start.stamp ();
14649 
14650  while (true)
14651  {
14652  if (true)
14653  {
14654  octave::autolock guard (gh_mgr.graphics_lock ());
14655 
14656  graphics_handle gh = gh_mgr.lookup (h);
14657 
14658  if (gh.ok ())
14659  {
14660  if (! pname.empty () && waitfor_results[id])
14661  break;
14662  }
14663  else
14664  break;
14665  }
14666 
14667  octave::sleep (0.1); // FIXME: really needed?
14668 
14669  octave_quit ();
14670 
14671  octave::command_editor::run_event_hooks ();
14672 
14673  if (timeout > 0)
14674  {
14675  octave::sys::time now;
14676 
14677  if (start + timeout < now)
14678  break;
14679  }
14680  }
14681 
14682  return ovl ();
14683 }
14684 
14685 DEFMETHOD (__zoom__, interp, args, ,
14686  doc: /* -*- texinfo -*-
14687 @deftypefn {} {} __zoom__ (@var{axes}, @var{mode}, @var{factor})
14688 @deftypefnx {} {} __zoom__ (@var{axes}, "out")
14689 @deftypefnx {} {} __zoom__ (@var{axes}, "reset")
14690 Undocumented internal function.
14691 @end deftypefn */)
14692 {
14693  int nargin = args.length ();
14694 
14695  if (nargin != 2 && nargin != 3)
14696  print_usage ();
14697 
14698  double h = args(0).double_value ();
14699 
14700  gh_manager& gh_mgr = interp.get_gh_manager ();
14701 
14702  octave::autolock guard (gh_mgr.graphics_lock ());
14703 
14704  graphics_handle handle = gh_mgr.lookup (h);
14705 
14706  if (! handle.ok ())
14707  error ("__zoom__: invalid handle");
14708 
14709  graphics_object ax = gh_mgr.get_object (handle);
14710 
14711  axes::properties& ax_props
14712  = dynamic_cast<axes::properties&> (ax.get_properties ());
14713 
14714  if (nargin == 2)
14715  {
14716  std::string opt = args(1).string_value ();
14717 
14718  if (opt == "out" || opt == "reset")
14719  {
14720  if (opt == "out")
14721  {
14722  ax_props.clear_zoom_stack ();
14723  Vdrawnow_requested = true;
14724  }
14725  else
14726  ax_props.clear_zoom_stack (false);
14727  }
14728  }
14729  else
14730  {
14731  std::string mode = args(1).string_value ();
14732  double factor = args(2).scalar_value ();
14733 
14734  ax_props.zoom (mode, factor);
14735  Vdrawnow_requested = true;
14736  }
14737 
14738  return ovl ();
14739 }
14740 
14741 DEFMETHOD (__get_frame__, interp, args, ,
14742  doc: /* -*- texinfo -*-
14743 @deftypefn {} {@var{cdata} =} __get_frame__ (@var{hfig})
14744 Internal function.
14745 
14746 Return the pixel cdata of figure hfig in the form of a height-by-width-by-3
14747 uint8 array.
14748 @end deftypefn */)
14749 {
14750  if (args.length () != 1)
14751  print_usage ();
14752 
14753  double h = args(0).xdouble_value ("__get_frame__: HFIG is not a handle");
14754 
14755  gh_manager& gh_mgr = interp.get_gh_manager ();
14756 
14757  graphics_object go = gh_mgr.get_object (h);
14758 
14759  if (! go || ! go.isa ("figure"))
14760  error ("__get_frame__: HFIG is not a figure");
14761 
14762  // For Matlab compatibility, getframe must flush the event queue.
14763  gh_mgr.process_events ();
14764 
14765  return ovl (go.get_toolkit ().get_pixels (go));
14766 }
14767 
14768 DEFMETHOD (__get_position__, interp, args, ,
14769  doc: /* -*- texinfo -*-
14770 @deftypefn {} {@var{pos} =} __get_position__ (@var{h}, @var{units})
14771 Internal function.
14772 
14773 Return the position of the graphics object @var{h} in the specified
14774 @var{units}.
14775 @end deftypefn */)
14776 {
14777  if (args.length () != 2)
14778  print_usage ();
14779 
14780  double h
14781  = args(0).xdouble_value ("__get_position__: H must be a graphics handle");
14782 
14783  std::string units
14784  = args(1).xstring_value ("__get_position__: UNITS must be a string");
14785 
14786  gh_manager& gh_mgr = interp.get_gh_manager ();
14787 
14788  graphics_object go = gh_mgr.get_object (h);
14789 
14790  if (h == 0 || ! go)
14791  error ("__get_position__: H must be a handle to a valid graphics object");
14792 
14793  graphics_object parent_go = gh_mgr.get_object (go.get_parent ());
14794  Matrix bbox = parent_go.get_properties ().get_boundingbox (true)
14795  .extract_n (0, 2, 1, 2);
14796 
14797  Matrix pos = convert_position (go.get ("position").matrix_value (),
14798  go.get ("units").string_value (),
14799  units, bbox);
14800 
14801  return ovl (pos);
14802 }
14803 
14804 DEFUN (__get_system_fonts__, args, ,
14805  doc: /* -*- texinfo -*-
14806 @deftypefn {} {@var{font_struct} =} __get_system_fonts__ ()
14807 Internal function.
14808 @end deftypefn */)
14809 {
14810  if (args.length () != 0)
14811  print_usage ();
14812 
14813  octave::text_renderer txt_renderer;
14814 
14815  return ovl (txt_renderer.get_system_fonts ());
14816 }
14817 
OCTAVE_END_NAMESPACE(octave)
#define Inf
Definition: Faddeeva.cc:260
#define NaN
Definition: Faddeeva.cc:261
octave_idx_type lookup(const T *x, octave_idx_type n, T y)
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type columns(void) const
Definition: Array.h:471
OCTARRAY_OVERRIDABLE_FUNC_API const T * data(void) const
Size of the specified dimension.
Definition: Array.h:663
OCTARRAY_OVERRIDABLE_FUNC_API bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:651
OCTARRAY_OVERRIDABLE_FUNC_API Array< T, Alloc > as_column(void) const
Return the array as a column vector.
Definition: Array.h:420
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
OCTARRAY_OVERRIDABLE_FUNC_API const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:503
OCTARRAY_API void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array-base.cc:1032
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type rows(void) const
Definition: Array.h:459
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array-base.cc:1766
OCTARRAY_OVERRIDABLE_FUNC_API int ndims(void) const
Size of the specified dimension.
Definition: Array.h:677
OCTARRAY_OVERRIDABLE_FUNC_API T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:562
OCTARRAY_API octave_idx_type lookup(const T &value, sortmode mode=UNSORTED) const
Do a binary lookup in a sorted array.
Definition: Array-base.cc:2160
OCTARRAY_OVERRIDABLE_FUNC_API T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
Definition: Cell.h:43
OCTAVE_API double max(void) const
Definition: dColVector.cc:261
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:112
OCTAVE_API RowVector transpose(void) const
Definition: dColVector.cc:125
OCTAVE_API double min(void) const
Definition: dColVector.cc:245
OCTAVE_API ColumnVector extract_n(octave_idx_type r1, octave_idx_type n) const
Definition: dColVector.cc:166
Definition: EIG.h:41
ComplexColumnVector eigenvalues(void) const
Definition: EIG.h:121
MArray< T > reshape(const dim_vector &new_dims) const
Definition: MArray.h:87
Definition: dMatrix.h:42
OCTAVE_API ColumnVector row_max(void) const
Definition: dMatrix.cc:2484
OCTAVE_API RowVector row(octave_idx_type i) const
Definition: dMatrix.cc:416
OCTAVE_API Matrix extract_n(octave_idx_type r1, octave_idx_type c1, octave_idx_type nr, octave_idx_type nc) const
Definition: dMatrix.cc:407
OCTAVE_API ColumnVector row_min(void) const
Definition: dMatrix.cc:2429
Matrix transpose(void) const
Definition: dMatrix.h:140
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:158
OCTAVE_API bool any_element_is_inf_or_nan(void) const
Definition: dNDArray.cc:324
callback_event(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix(), int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:12035
std::string m_callback_name
Definition: graphics.cc:12065
void execute(void)
Definition: graphics.cc:12047
graphics_handle m_handle
Definition: graphics.cc:12064
callback_event(void)
Definition: graphics.cc:12058
callback_event(const graphics_handle &h, const octave_value &cb, const octave_value &data=Matrix(), int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:12041
octave_value m_callback_data
Definition: graphics.cc:12067
octave_value m_callback
Definition: graphics.cc:12066
~callback_props(void)=default
callback_props(const callback_props &)=delete
bool empty(void) const
Definition: graphics.cc:1948
callback_props(void)
Definition: graphics.cc:1940
void erase(const callback_property *ptr)
Definition: graphics.cc:1955
callback_props & operator=(const callback_props &)=delete
std::set< intptr_t > m_set
Definition: graphics.cc:1967
bool contains(const callback_property *ptr) const
Definition: graphics.cc:1960
void insert(const callback_property *ptr)
Definition: graphics.cc:1950
bool compare(const std::string &s, std::size_t limit=std::string::npos) const
Definition: caseless-str.h:78
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
void resize(int n, int fill_value=0)
Definition: dim-vector.h:272
static dim_vector alloc(int n)
Definition: dim-vector.h:202
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:257
graphics_event::event_fcn m_function
Definition: graphics.cc:12131
void * m_function_data
Definition: graphics.cc:12133
void execute(void)
Definition: graphics.cc:12124
function_event(graphics_event::event_fcn fcn, void *data=nullptr)
Definition: graphics.cc:12114
function_event(void)=delete
function_event(const function_event &)=delete
void execute(void)
Definition: graphics.cc:12079
mcode_event(void)
Definition: graphics.cc:12096
mcode_event(const graphics_handle &h, const std::string &cmd, int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:12074
graphics_handle m_handle
Definition: graphics.cc:12101
std::string m_mcode
Definition: graphics.cc:12102
octave_value as_octave_value(void) const
Definition: oct-handle.h:80
double value(void) const
Definition: oct-handle.h:78
bool ok(void) const
Definition: oct-handle.h:113
Cell getfield(const std::string &key) const
Definition: oct-map.cc:275
bool isfield(const std::string &name) const
Definition: oct-map.h:347
static octave_map cat(int dim, octave_idx_type n, const octave_scalar_map *map_list)
Definition: oct-map.cc:688
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:190
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:238
octave_value getfield(const std::string &key) const
Definition: oct-map.cc:183
octave_idx_type length(void) const
Definition: ovl.h:113
octave_value_list splice(octave_idx_type offset, octave_idx_type len, const octave_value_list &lst=octave_value_list()) const
Definition: ovl.cc:139
bool is_function(void) const
Definition: ov.h:822
OCTINTERP_API octave_function * function_value(bool silent=false) const
bool iscellstr(void) const
Definition: ov.h:652
bool iscell(void) const
Definition: ov.h:649
bool isreal(void) const
Definition: ov.h:783
bool is_uint16_type(void) const
Definition: ov.h:766
bool is_bool_scalar(void) const
Definition: ov.h:667
bool is_int8_type(void) const
Definition: ov.h:751
octave_idx_type rows(void) const
Definition: ov.h:590
bool isnumeric(void) const
Definition: ov.h:795
octave_idx_type numel(void) const
Definition: ov.h:604
bool is_scalar_type(void) const
Definition: ov.h:789
bool is_string(void) const
Definition: ov.h:682
bool is_defined(void) const
Definition: ov.h:637
bool is_double_type(void) const
Definition: ov.h:740
Cell cell_value(void) const
bool is_function_handle(void) const
Definition: ov.h:813
std::string class_name(void) const
Definition: ov.h:1454
bool is_uint32_type(void) const
Definition: ov.h:769
octave_idx_type columns(void) const
Definition: ov.h:592
bool is_int64_type(void) const
Definition: ov.h:760
octave_value reshape(const dim_vector &dv) const
Definition: ov.h:616
std::string string_value(bool force=false) const
Definition: ov.h:1019
bool is_matrix_type(void) const
Definition: ov.h:792
bool is_int32_type(void) const
Definition: ov.h:757
bool is_uint64_type(void) const
Definition: ov.h:772
bool is_int16_type(void) const
Definition: ov.h:754
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:1022
bool isempty(void) const
Definition: ov.h:646
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:904
bool is_single_type(void) const
Definition: ov.h:743
bool is_real_scalar(void) const
Definition: ov.h:655
bool is_undefined(void) const
Definition: ov.h:640
OCTINTERP_API octave_idx_type length(void) const
bool is_uint8_type(void) const
Definition: ov.h:763
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:898
bool iscomplex(void) const
Definition: ov.h:786
double double_value(bool frc_str_conv=false) const
Definition: ov.h:886
std::string type_name(void) const
Definition: ov.h:1452
bool islogical(void) const
Definition: ov.h:780
dim_vector dims(void) const
Definition: ov.h:586
OCTINTERP_API double xdouble_value(const char *fmt,...) const
Definition: oct-rand.h:45
bool m_notify_toolkit
Definition: graphics.cc:12208
void execute(void)
Definition: graphics.cc:12148
bool m_redraw_figure
Definition: graphics.cc:12209
std::string m_property_name
Definition: graphics.cc:12206
set_event(const graphics_handle &h, const std::string &name, const octave_value &value, bool do_notify_toolkit=true, bool redraw_figure=false)
Definition: graphics.cc:12140
set_event(void)
Definition: graphics.cc:12200
graphics_handle m_handle
Definition: graphics.cc:12205
octave_value m_property_value
Definition: graphics.cc:12207
string_vector & append(const std::string &s)
Definition: str-vec.cc:110
std::ostream & list_in_columns(std::ostream &, int width=0, const std::string &prefix="") const
Definition: str-vec.cc:201
std::string join(const std::string &sep="") const
Definition: str-vec.cc:137
octave_idx_type numel(void) const
Definition: str-vec.h:100
static octave_idx_type find(octave_idx_type i, octave_idx_type *pp)
Definition: colamd.cc:106
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
T eps(const T &x)
Definition: data.cc:4942
#define DECLARE_STATIC_FUNX(name, args_name, nargout_name)
Definition: defun-int.h:161
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:111
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void interpreter_try(unwind_protect &frame)
Definition: error.cc:2177
void warning(const char *fmt,...)
Definition: error.cc:1054
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1069
void error(const char *fmt,...)
Definition: error.cc:979
octave_value_list set_warning_state(const std::string &id, const std::string &state)
Definition: error.cc:1814
void disable_warning(const std::string &id)
Definition: error.cc:1843
int warning_enabled(const std::string &id)
Definition: error.cc:1835
void error_unless(bool cond)
Definition: error.h:549
std::string dirname(const std::string &path)
Definition: file-ops.cc:360
octave_handle graphics_handle
static double default_screendepth(void)
Definition: graphics.cc:225
static Matrix default_patch_vertices(void)
Definition: graphics.cc:403
static void xset(const graphics_handle &h, const caseless_str &pname, const octave_value &val)
Definition: graphics.cc:2973
static graphics_handle reparent(const octave_value &ov, const std::string &who, const std::string &pname, const graphics_handle &new_parent, bool adopt=true)
Definition: graphics.cc:3007
static Matrix default_surface_ydata(void)
Definition: graphics.cc:362
static Matrix default_light_position(void)
Definition: graphics.cc:581
static bool compare_property_values(const octave_value &ov1, const octave_value &ov2)
Definition: graphics.cc:14323
static Matrix default_patch_ydata(void)
Definition: graphics.cc:425
static double make_handle_fraction(void)
Definition: graphics.cc:2822
void cross_product(double x1, double y1, double z1, double x2, double y2, double z2, double &x, double &y, double &z)
Definition: graphics.cc:10073
Matrix unit_cube(void)
Definition: graphics.cc:5916
double dot(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5892
static bool updating_zlabel_position
Definition: graphics.cc:6624
static Matrix default_image_cdata(void)
Definition: graphics.cc:334
void translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5857
static std::set< double > updating_aspectratios
Definition: graphics.cc:6851
OCTAVE_EXPORT octave_value_list Fdrawnow(octave::interpreter &interp, const octave_value_list &args, int)
Definition: graphics.cc:13932
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:87
static Matrix default_data(void)
Definition: graphics.cc:310
bool set_property_in_handle(double handle, const std::string &property, const octave_value &arg, const std::string &fcn)
Definition: graphics.cc:14305
#define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL)
static graphics_handle make_graphics_handle(const std::string &go_name, const graphics_handle &parent, bool integer_figure_handle=false, bool call_createfcn=true, bool notify_toolkit=true)
Definition: graphics.cc:619
static bool updating_hggroup_limits
Definition: graphics.cc:10804
ColumnVector cross(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5904
static void delete_graphics_objects(const NDArray vals, bool from_root=false)
Definition: graphics.cc:3092
static void finalize_r(const graphics_handle &h)
Definition: graphics.cc:2128
static Matrix default_figure_paperposition(void)
Definition: graphics.cc:522
static Matrix default_figure_papersize(void)
Definition: graphics.cc:511
graphics_handle gca(void)
Definition: graphics.cc:3050
static Matrix default_table_backgroundcolor(void)
Definition: graphics.cc:606
static octave_value convert_ticklabel_string(const octave_value &val)
Definition: graphics.cc:7137
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:8422
#define FIX_LIMITS
static Matrix screen_size_pixels(void)
Definition: graphics.cc:925
double norm(const ColumnVector &v)
Definition: graphics.cc:5898
static void xreset_default_properties(graphics_handle h, property_list::pval_map_type factory_pval)
Definition: graphics.cc:3257
static bool updating_patch_data
Definition: graphics.cc:9748
void xform(ColumnVector &v, const Matrix &m)
Definition: graphics.cc:5863
static Matrix default_axes_position(void)
Definition: graphics.cc:435
static Matrix default_screensize(void)
Definition: graphics.cc:233
void normalize(ColumnVector &v)
Definition: graphics.cc:5885
static Matrix default_axes_view(void)
Definition: graphics.cc:461
static Matrix default_surface_xdata(void)
Definition: graphics.cc:350
static void close_figure(const graphics_handle &h)
Definition: graphics.cc:3102
Matrix xform_scale(double x, double y, double z)
Definition: graphics.cc:5825
static void normalized_aspectratios(Matrix &aspectratios, const Matrix &scalefactors, double xlength, double ylength, double zlength)
Definition: graphics.cc:6815
static std::map< caseless_str, graphics_object > dprop_obj_map
Definition: graphics.cc:2000
static Matrix default_control_position(void)
Definition: graphics.cc:544
static Matrix do_translate(double x0, double x1, const Matrix &lims, bool is_logscale)
Definition: graphics.cc:9097
static Matrix default_axes_tick(void)
Definition: graphics.cc:472
static bool updating_scatter_cdata
Definition: graphics.cc:10373
std::vector< octave_idx_type > coplanar_partition(const Matrix &vert, const Matrix &idx, octave_idx_type nc, octave_idx_type jj)
Definition: graphics.cc:9844
static void update_text_pos(graphics_handle h)
Definition: graphics.cc:2266
static callback_props executing_callbacks
Definition: graphics.cc:1974
static double device_pixel_ratio(graphics_handle h)
Definition: graphics.cc:938
static Matrix default_control_sliderstep(void)
Definition: graphics.cc:557
ColumnVector cam2xform(const Array< double > &m)
Definition: graphics.cc:5937
static void delete_graphics_object(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:3059
static OCTAVE_NORETURN void err_set_invalid(const std::string &pname)
Definition: graphics.cc:77
int calc_dimensions(const graphics_object &go)
Definition: graphics.cc:13447
static bool updating_ylabel_position
Definition: graphics.cc:6520
static bool ishghandle(const graphics_handle &h)
Definition: graphics.cc:3178
RowVector xform2cam(const ColumnVector &v)
Definition: graphics.cc:5947
static bool delete_executing
Definition: graphics.cc:3089
static void magform(double x, double &a, int &b)
Definition: graphics.cc:7479
static Matrix papersize_from_type(const caseless_str punits, const caseless_str ptype)
Definition: graphics.cc:4419
static std::map< uint32_t, bool > waitfor_results
Definition: graphics.cc:14338
static bool updating_axes_layout
Definition: graphics.cc:6166
static Matrix default_patch_xdata(void)
Definition: graphics.cc:415
static void check_limit_vals(double &min_val, double &max_val, double &min_pos, double &max_neg, const octave_value &data)
Definition: graphics.cc:7440
static bool lookup_object_name(const caseless_str &name, caseless_str &go_name, caseless_str &rest)
Definition: graphics.cc:1126
static Matrix default_lim(bool logscale=false)
Definition: graphics.cc:291
static void force_close_figure(const graphics_handle &h)
Definition: graphics.cc:3112
static Matrix default_table_position(void)
Definition: graphics.cc:593
static int process_graphics_events(void)
Definition: graphics.cc:12395
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:632
static Matrix viridis_colormap(void)
Definition: graphics.cc:141
static void get_array_limits(const Array< T > &m, double &emin, double &emax, double &eminp, double &emaxp)
Definition: graphics.cc:1097
#define GO_BODY(TYPE)
Definition: graphics.cc:13436
static void cleanup_waitfor_predelete_listener(const octave_value &listener)
Definition: graphics.cc:14385
static octave_value_list waitfor_listener(const octave_value_list &args, int)
Definition: graphics.cc:14388
static std::set< double > updating_axis_limits
Definition: graphics.cc:8507
static void xset_gcbo(const graphics_handle &h)
Definition: graphics.cc:12256
static ColumnVector convert_label_position(const ColumnVector &p, const text::properties &props, const graphics_xform &xform, const Matrix &bbox)
Definition: graphics.cc:6388
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5851
static octave_value convert_linestyleorder_string(const octave_value &val)
Definition: graphics.cc:7235
Matrix xform_translate(double x, double y, double z)
Definition: graphics.cc:5838
static std::string default_graphics_toolkit(void)
Definition: graphics.cc:536
static void initialize_r(const graphics_handle &h)
Definition: graphics.cc:2146
static void cleanup_waitfor_id(uint32_t id)
Definition: graphics.cc:14341
static std::string get_graphics_object_type(double val)
Definition: graphics.cc:13074
Matrix xform_matrix(void)
Definition: graphics.cc:5786
static double default_screenpixelsperinch(void)
Definition: graphics.cc:248
static bool updating_xlabel_position
Definition: graphics.cc:6416
ColumnVector xform_vector(void)
Definition: graphics.cc:5797
static Matrix default_surface_cdata(void)
Definition: graphics.cc:385
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5819
graphics_handle gcf(void)
Definition: graphics.cc:3040
static void do_cleanup_waitfor_listener(const octave_value &listener, listener_mode mode=GCB_POSTSET)
Definition: graphics.cc:14347
static void xinitialize(const graphics_handle &h)
Definition: graphics.cc:3226
static octave_value xget(const graphics_handle &h, const caseless_str &name)
Definition: graphics.cc:2997
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:953
static Matrix default_data_lim(void)
Definition: graphics.cc:321
#define CHECK_ARRAY_EQUAL(T, F, A)
static Matrix default_panel_position(void)
Definition: graphics.cc:568
static octave_value_list waitfor_del_listener(const octave_value_list &args, int)
Definition: graphics.cc:14422
bool is_coplanar(const Matrix &cov)
Definition: graphics.cc:9832
static Matrix do_zoom(double val, double factor, const Matrix &lims, bool is_logscale)
Definition: graphics.cc:8952
static bool updating_title_position
Definition: graphics.cc:6750
static bool is_handle_visible(const graphics_handle &h)
Definition: graphics.cc:12692
static Matrix default_colororder(void)
Definition: graphics.cc:256
static octave_value convert_cdata(const base_properties &props, const octave_value &cdata, bool is_scaled, int cdim)
Definition: graphics.cc:995
static void adopt(const graphics_handle &parent_h, const graphics_handle &h)
Definition: graphics.cc:3168
static Matrix default_axes_ticklength(void)
Definition: graphics.cc:487
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:1206
double force_in_range(double x, double lower, double upper)
Definition: graphics.cc:8941
static Matrix default_surface_zdata(void)
Definition: graphics.cc:374
static void max_axes_scale(double &s, Matrix &limits, const Matrix &kids, double pbfactor, double dafactor, char limit_type, bool tight)
Definition: graphics.cc:6830
static void xcreatefcn(const graphics_handle &h)
Definition: graphics.cc:3216
static Matrix default_axes_outerposition(void)
Definition: graphics.cc:448
static Matrix convert_position(const Matrix &pos, const caseless_str &from_units, const caseless_str &to_units, const Matrix &parent_dim)
Definition: graphics.cc:690
octave_value get_property_from_handle(double handle, const std::string &property, const std::string &fcn)
Definition: graphics.cc:14289
static Matrix default_patch_faces(void)
Definition: graphics.cc:391
static octave_value make_graphics_object(const std::string &go_name, bool integer_figure_handle, const octave_value_list &args)
Definition: graphics.cc:13265
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:833
static int toggle_warn(std::string id, bool on, int state=-1)
Definition: graphics.cc:3239
static bool isfigure(double val)
Definition: graphics.cc:2871
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:985
static Matrix default_figure_position(void)
Definition: graphics.cc:498
static void cleanup_waitfor_postset_listener(const octave_value &listener)
Definition: graphics.cc:14381
bool Vdrawnow_requested
Definition: input.cc:91
gtk_manager & __get_gtk_manager__(void)
interpreter & __get_interpreter__(void)
display_info & __get_display_info__(void)
gh_manager & __get_gh_manager__(void)
#define octave_NaN
Definition: lo-ieee.h:44
Complex atan(const Complex &x)
Definition: lo-mappers.h:71
bool isfinite(double x)
Definition: lo-mappers.h:192
bool isinf(double x)
Definition: lo-mappers.h:203
double signum(double x)
Definition: lo-mappers.h:229
double round(double x)
Definition: lo-mappers.h:136
bool isnan(bool)
Definition: lo-mappers.h:178
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
std::complex< T > ceil(const std::complex< T > &x)
Definition: lo-mappers.h:103
double fix(double x)
Definition: lo-mappers.h:118
T x_nint(T x)
Definition: lo-mappers.h:269
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
class OCTAVE_API NDArray
Definition: mx-fwd.h:38
class OCTAVE_API Matrix
Definition: mx-fwd.h:31
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
T::properties & properties(graphics_object obj)
octave_int< uint32_t > octave_uint32
octave_int< T > pow(const octave_int< T > &a, const octave_int< T > &b)
octave_int< T > xmin(const octave_int< T > &x, const octave_int< T > &y)
octave_int< T > xmax(const octave_int< T > &x, const octave_int< T > &y)
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:10370
void free(void *)
static void initialize(void)
static char default_im_data[]
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
const octave_char_matrix & v2
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
void flush_stdout(void)
Definition: pager.cc:260
#define octave_stdout
Definition: pager.h:314
static T abs(T x)
Definition: pr-output.cc:1678
static uint32_t state[624]
Definition: randmtzig.cc:193
static std::string dir_sep_chars
Definition: shared-fcns.h:96
void sleep(double seconds, bool do_graphics_events)
Definition: utils.cc:1532
F77_RET_T len
Definition: xerbla.cc:61