GNU Octave  6.2.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-2021 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 
70 // forward declarations
71 static octave_value xget (const graphics_handle& h, const caseless_str& name);
72 
73 OCTAVE_NORETURN static
74 void
75 err_set_invalid (const std::string& pname)
76 {
77  error ("set: invalid value for %s property", pname.c_str ());
78 }
79 
80 // Check to see that PNAME matches just one of PNAMES uniquely.
81 // Return the full name of the match, or an empty caseless_str object
82 // if there is no match, or the match is ambiguous.
83 
84 static caseless_str
85 validate_property_name (const std::string& who, const std::string& what,
86  const std::set<std::string>& pnames,
87  const caseless_str& pname)
88 {
89  size_t len = pname.length ();
90  std::set<std::string> matches;
91 
92  // Find exact or partial matches to property name
93  for (const auto& propnm : pnames)
94  {
95  if (pname.compare (propnm, len))
96  {
97  if (len == propnm.length ())
98  return pname; // Exact match.
99 
100  matches.insert (propnm);
101  }
102  }
103 
104  size_t num_matches = matches.size ();
105 
106  if (num_matches == 0)
107  error ("%s: unknown %s property %s",
108  who.c_str (), what.c_str (), pname.c_str ());
109  else if (num_matches > 1)
110  {
111  string_vector sv (matches);
112 
113  std::ostringstream os;
114 
115  sv.list_in_columns (os);
116 
117  std::string match_list = os.str ();
118 
119  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
120  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
121  }
122  else // num_matches == 1
123  {
124  // Exact match was handled above.
125  std::string possible_match = *(matches.begin ());
126 
127  warning_with_id ("Octave:abbreviated-property-match",
128  "%s: allowing %s to match %s property %s",
129  who.c_str (), pname.c_str (), what.c_str (),
130  possible_match.c_str ());
131 
132  return possible_match;
133  }
134 
135  return caseless_str ();
136 }
137 
138 static Matrix
140 {
141  // The values below have been produced by viridis (64)(:)
142  // It would be nice to be able to feval the
143  // viridis function but since there is a static property object that includes
144  // a colormap_property object, we need to initialize this before main is
145  // even called, so calling an interpreted function is not possible.
146 
147  const double cmapv[] =
148  {
149  2.67004010000000e-01, 2.72651720952381e-01, 2.77106307619048e-01,
150  2.80356151428571e-01, 2.82390045238095e-01, 2.83204606666667e-01,
151  2.82809341428571e-01, 2.81230763333333e-01, 2.78516153333333e-01,
152  2.74735528571429e-01, 2.69981791904762e-01, 2.64368580952381e-01,
153  2.58026184285714e-01, 2.51098684761905e-01, 2.43732853333333e-01,
154  2.36073294285714e-01, 2.28263191428571e-01, 2.20424955714286e-01,
155  2.12666598571429e-01, 2.05079113809524e-01, 1.97721880952381e-01,
156  1.90631350000000e-01, 1.83819438571429e-01, 1.77272360952381e-01,
157  1.70957518571429e-01, 1.64832915714286e-01, 1.58845368095238e-01,
158  1.52951235714286e-01, 1.47131626666667e-01, 1.41402210952381e-01,
159  1.35832975714286e-01, 1.30582113809524e-01, 1.25898377619048e-01,
160  1.22163105714286e-01, 1.19872409523810e-01, 1.19626570000000e-01,
161  1.22045948571429e-01, 1.27667691904762e-01, 1.36834947142857e-01,
162  1.49643331428571e-01, 1.65967274285714e-01, 1.85538397142857e-01,
163  2.08030450000000e-01, 2.33127309523809e-01, 2.60531475238095e-01,
164  2.90000730000000e-01, 3.21329971428571e-01, 3.54355250000000e-01,
165  3.88930322857143e-01, 4.24933143333333e-01, 4.62246770476190e-01,
166  5.00753620000000e-01, 5.40336957142857e-01, 5.80861172380952e-01,
167  6.22170772857143e-01, 6.64087320476191e-01, 7.06403823333333e-01,
168  7.48885251428571e-01, 7.91273132857143e-01, 8.33302102380952e-01,
169  8.74717527142857e-01, 9.15296319047619e-01, 9.54839555238095e-01,
170  9.93247890000000e-01, 4.87433000000000e-03, 2.58456800000000e-02,
171  5.09139004761905e-02, 7.42014957142857e-02, 9.59536042857143e-02,
172  1.16893314761905e-01, 1.37350195714286e-01, 1.57479940000000e-01,
173  1.77347967619048e-01, 1.96969168571429e-01, 2.16330337619048e-01,
174  2.35404660952381e-01, 2.54161735714286e-01, 2.72573219047619e-01,
175  2.90619516666667e-01, 3.08291041428571e-01, 3.25586450952381e-01,
176  3.42517215238095e-01, 3.59102207142857e-01, 3.75366067142857e-01,
177  3.91340913333333e-01, 4.07061480000000e-01, 4.22563764285714e-01,
178  4.37885543809524e-01, 4.53062984285714e-01, 4.68129543809524e-01,
179  4.83117059523810e-01, 4.98052961428571e-01, 5.12959473333333e-01,
180  5.27854311428571e-01, 5.42750087142857e-01, 5.57652481904762e-01,
181  5.72563073333333e-01, 5.87476284285714e-01, 6.02382410952381e-01,
182  6.17265840000000e-01, 6.32106955714286e-01, 6.46881817142857e-01,
183  6.61562926190476e-01, 6.76119717142857e-01, 6.90518987142857e-01,
184  7.04725181904762e-01, 7.18700950000000e-01, 7.32406441904762e-01,
185  7.45802021904762e-01, 7.58846480000000e-01, 7.71497934761905e-01,
186  7.83714033809524e-01, 7.95453081428571e-01, 8.06673890000000e-01,
187  8.17337565714286e-01, 8.27409135714286e-01, 8.36858167619048e-01,
188  8.45663399523809e-01, 8.53815582857143e-01, 8.61321019047619e-01,
189  8.68206316666667e-01, 8.74522215714286e-01, 8.80346158571429e-01,
190  8.85780083333333e-01, 8.90945338571429e-01, 8.95973498571429e-01,
191  9.01005800000000e-01, 9.06156570000000e-01, 3.29415190000000e-01,
192  3.53367293333333e-01, 3.76236064761905e-01, 3.97901482857143e-01,
193  4.18250757142857e-01, 4.37178920000000e-01, 4.54595888571429e-01,
194  4.70433883333333e-01, 4.84653865714286e-01, 4.97250492857143e-01,
195  5.08254501428571e-01, 5.17731949047619e-01, 5.25780221428571e-01,
196  5.32522206190476e-01, 5.38097133333333e-01, 5.42651800000000e-01,
197  5.46335411904762e-01, 5.49287148571429e-01, 5.51635008571429e-01,
198  5.53493173333333e-01, 5.54953478571429e-01, 5.56089070000000e-01,
199  5.56952166666667e-01, 5.57576145714286e-01, 5.57974025714286e-01,
200  5.58142745238095e-01, 5.58058673809524e-01, 5.57684744285714e-01,
201  5.56973310000000e-01, 5.55864478571429e-01, 5.54288677142857e-01,
202  5.52175699047619e-01, 5.49445382857143e-01, 5.46023368571429e-01,
203  5.41830633809524e-01, 5.36795616666667e-01, 5.30847985714286e-01,
204  5.23924198571429e-01, 5.15966779523810e-01, 5.06924262857143e-01,
205  4.96751861428571e-01, 4.85412122857143e-01, 4.72873300000000e-01,
206  4.59105875238095e-01, 4.44095883333333e-01, 4.27825852857143e-01,
207  4.10292713809524e-01, 3.91487632857143e-01, 3.71420688571429e-01,
208  3.50098750000000e-01, 3.27544678571429e-01, 3.03798967142857e-01,
209  2.78916748571429e-01, 2.53000856190476e-01, 2.26223670000000e-01,
210  1.98879439523810e-01, 1.71494930000000e-01, 1.45037631428572e-01,
211  1.21291048571429e-01, 1.03326155238095e-01, 9.53507900000000e-02,
212  1.00469958095238e-01, 1.17876387142857e-01, 1.43936200000000e-01
213  };
214 
215  // It would be nice if Matrix had a ctor allowing to do the
216  // following without a copy
217  Matrix cmap (64, 3, 0.0);
218  std::copy (cmapv, cmapv + (64*3), cmap.fortran_vec ());
219  return cmap;
220 }
221 
222 static double
224 {
225  octave::display_info& dpy_info
226  = octave::__get_display_info__ ("default_screendepth");
227 
228  return dpy_info.depth ();
229 }
230 
231 static Matrix
233 {
234  Matrix retval (1, 4);
235 
236  octave::display_info& dpy_info
237  = octave::__get_display_info__ ("default_screensize");
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
251  = octave::__get_display_info__ ("default_screenpixelsperinch");
252 
253  return (dpy_info.x_dpi () + dpy_info.y_dpi ()) / 2;
254 }
255 
256 static Matrix
258 {
259  Matrix retval (7, 3, 0.0);
260 
261  retval(0,1) = 0.447;
262  retval(0,2) = 0.741;
263 
264  retval(1,0) = 0.850;
265  retval(1,1) = 0.325;
266  retval(1,2) = 0.098;
267 
268  retval(2,0) = 0.929;
269  retval(2,1) = 0.694;
270  retval(2,2) = 0.125;
271 
272  retval(3,0) = 0.494;
273  retval(3,1) = 0.184;
274  retval(3,2) = 0.556;
275 
276  retval(4,0) = 0.466;
277  retval(4,1) = 0.674;
278  retval(4,2) = 0.188;
279 
280  retval(5,0) = 0.301;
281  retval(5,1) = 0.745;
282  retval(5,2) = 0.933;
283 
284  retval(6,0) = 0.635;
285  retval(6,1) = 0.078;
286  retval(6,2) = 0.184;
287 
288  return retval;
289 }
290 
291 static Matrix
292 default_lim (bool logscale = false)
293 {
294  Matrix m (1, 2);
295 
296  if (logscale)
297  {
298  m(0) = 0.1;
299  m(1) = 1.0;
300  }
301  else
302  {
303  m(0) = 0.0;
304  m(1) = 1.0;
305  }
306 
307  return m;
308 }
309 
310 static Matrix
312 {
313  Matrix retval (1, 2);
314 
315  retval(0) = 0;
316  retval(1) = 1;
317 
318  return retval;
319 }
320 
321 static Matrix
323 {
324  Matrix retval (1, 4);
325 
326  retval(0) = 0;
327  retval(1) = 1;
328  retval(2) = 1; // minimum positive
329  retval(3) = -octave::numeric_limits<double>::Inf (); // maximum negative
330 
331  return retval;
332 }
333 
334 static Matrix
336 {
337  Matrix m (64, 64);
338 
339  int i = 0;
340  for (int col = 0; col < 64; col++)
341  for (int row = 0; row < 64; row++)
342  {
343  m(col,row) = static_cast<double> (default_im_data[i]);
344  i++;
345  }
346 
347  return m;
348 }
349 
350 static Matrix
352 {
353  Matrix m (3, 3);
354 
355  for (int col = 0; col < 3; col++)
356  for (int row = 0; row < 3; row++)
357  m(row,col) = col+1;
358 
359  return m;
360 }
361 
362 static Matrix
364 {
365  Matrix m (3, 3);
366 
367  for (int row = 0; row < 3; row++)
368  for (int col = 0; col < 3; col++)
369  m(row,col) = row+1;
370 
371  return m;
372 }
373 
374 static Matrix
376 {
377  Matrix m (3, 3, 0.0);
378 
379  for (int row = 0; row < 3; row++)
380  m(row,row) = 1.0;
381 
382  return m;
383 }
384 
385 static Matrix
387 {
388  return default_surface_zdata ();
389 }
390 
391 static Matrix
393 {
394  Matrix m (1, 3);
395 
396  m(0) = 1.0;
397  m(1) = 2.0;
398  m(2) = 3.0;
399 
400  return m;
401 }
402 
403 static Matrix
405 {
406  Matrix m (3, 2, 0.0);
407 
408  m(1) = 1.0;
409  m(3) = 1.0;
410  m(4) = 1.0;
411 
412  return m;
413 }
414 
415 static Matrix
417 {
418  Matrix m (3, 1, 0.0);
419 
420  m(1) = 1.0;
421 
422  return m;
423 }
424 
425 static Matrix
427 {
428  Matrix m (3, 1, 1.0);
429 
430  m(2) = 0.0;
431 
432  return m;
433 }
434 
435 static Matrix
437 {
438  Matrix m (1, 4);
439 
440  m(0) = 0.13;
441  m(1) = 0.11;
442  m(2) = 0.775;
443  m(3) = 0.815;
444 
445  return m;
446 }
447 
448 static Matrix
450 {
451  Matrix m (1, 4);
452 
453  m(0) = 0.0;
454  m(1) = 0.0;
455  m(2) = 1.0;
456  m(3) = 1.0;
457 
458  return m;
459 }
460 
461 static Matrix
463 {
464  Matrix m (1, 2);
465 
466  m(0) = 0.0;
467  m(1) = 90.0;
468 
469  return m;
470 }
471 
472 static Matrix
474 {
475  Matrix m (1, 6);
476 
477  m(0) = 0.0;
478  m(1) = 0.2;
479  m(2) = 0.4;
480  m(3) = 0.6;
481  m(4) = 0.8;
482  m(5) = 1.0;
483 
484  return m;
485 }
486 
487 static Matrix
489 {
490  Matrix m (1, 2);
491 
492  m(0) = 0.01;
493  m(1) = 0.025;
494 
495  return m;
496 }
497 
498 static Matrix
500 {
501  Matrix m (1, 4);
502 
503  m(0) = 300;
504  m(1) = 200;
505  m(2) = 560;
506  m(3) = 420;
507 
508  return m;
509 }
510 
511 static Matrix
513 {
514  Matrix m (1, 2);
515 
516  m(0) = 8.5;
517  m(1) = 11.0;
518 
519  return m;
520 }
521 
522 static Matrix
524 {
525  Matrix m (1, 4);
526 
527  // Update if default_figure_position or default_figure_papersize change
528  m(0) = 1.3421852580027660;
529  m(1) = 3.3191389435020748;
530  m(2) = 5.8156294839944680;
531  m(3) = 4.3617221129958503;
532 
533  return m;
534 }
535 
536 static std::string
538 {
539  octave::gtk_manager& gtk_mgr
540  = octave::__get_gtk_manager__ ("default_graphics_toolkit");
541 
542  return gtk_mgr.default_toolkit ();
543 }
544 
545 static Matrix
547 {
548  Matrix retval (1, 4);
549 
550  retval(0) = 0;
551  retval(1) = 0;
552  retval(2) = 80;
553  retval(3) = 30;
554 
555  return retval;
556 }
557 
558 static Matrix
560 {
561  Matrix retval (1, 2);
562 
563  retval(0) = 0.01;
564  retval(1) = 0.1;
565 
566  return retval;
567 }
568 
569 static Matrix
571 {
572  Matrix retval (1, 4);
573 
574  retval(0) = 0;
575  retval(1) = 0;
576  retval(2) = 1;
577  retval(3) = 1;
578 
579  return retval;
580 }
581 
582 static Matrix
584 {
585  Matrix m (1, 3);
586 
587  m(0) = 1.0;
588  m(1) = 0.0;
589  m(2) = 1.0;
590 
591  return m;
592 }
593 
594 static Matrix
596 {
597  Matrix retval (1, 4);
598 
599  retval(0) = 20;
600  retval(1) = 20;
601  retval(2) = 300;
602  retval(3) = 300;
603 
604  return retval;
605 }
606 
607 static Matrix
609 {
610  Matrix retval (2, 3);
611  retval(0, 0) = 1;
612  retval(0, 1) = 1;
613  retval(0, 2) = 1;
614  retval(1, 0) = 0.94;
615  retval(1, 1) = 0.94;
616  retval(1, 2) = 0.94;
617  return retval;
618 }
619 
620 static graphics_handle
621 make_graphics_handle (const std::string& go_name,
622  const graphics_handle& parent,
623  bool integer_figure_handle = false,
624  bool call_createfcn = true,
625  bool notify_toolkit = true)
626 {
627  gh_manager& gh_mgr = octave::__get_gh_manager__ ("make_graphics_handle");
628 
629  return gh_mgr.make_graphics_handle (go_name, parent, integer_figure_handle,
630  call_createfcn, notify_toolkit);
631 }
632 
633 static double
634 convert_font_size (double font_size, const caseless_str& from_units,
635  const caseless_str& to_units, double parent_height = 0)
636 {
637  // Simple case where from_units == to_units
638 
639  if (from_units.compare (to_units))
640  return font_size;
641 
642  // Converts the given fontsize using the following transformation:
643  // <old_font_size> => points => <new_font_size>
644 
645  double points_size = 0;
646  double res = 0;
647 
648  if (from_units.compare ("points"))
649  points_size = font_size;
650  else
651  {
652  res = xget (0, "screenpixelsperinch").double_value ();
653 
654  if (from_units.compare ("pixels"))
655  points_size = font_size * 72.0 / res;
656  else if (from_units.compare ("inches"))
657  points_size = font_size * 72.0;
658  else if (from_units.compare ("centimeters"))
659  points_size = font_size * 72.0 / 2.54;
660  else if (from_units.compare ("normalized"))
661  points_size = font_size * parent_height * 72.0 / res;
662  }
663 
664  double new_font_size = 0;
665 
666  if (to_units.compare ("points"))
667  new_font_size = points_size;
668  else
669  {
670  if (res <= 0)
671  res = xget (0, "screenpixelsperinch").double_value ();
672 
673  if (to_units.compare ("pixels"))
674  new_font_size = points_size * res / 72.0;
675  else if (to_units.compare ("inches"))
676  new_font_size = points_size / 72.0;
677  else if (to_units.compare ("centimeters"))
678  new_font_size = points_size * 2.54 / 72.0;
679  else if (to_units.compare ("normalized"))
680  {
681  // Avoid setting font size to (0/0) = NaN
682 
683  if (parent_height > 0)
684  new_font_size = points_size * res / (parent_height * 72.0);
685  }
686  }
687 
688  return new_font_size;
689 }
690 
691 static Matrix
692 convert_position (const Matrix& pos, const caseless_str& from_units,
693  const caseless_str& to_units, const Matrix& parent_dim)
694 {
695  Matrix retval (1, pos.numel ());
696  double res = 0;
697  bool is_rectangle = (pos.numel () == 4);
698  bool is_2D = (pos.numel () == 2);
699 
700  if (from_units.compare ("pixels"))
701  retval = pos;
702  else if (from_units.compare ("normalized"))
703  {
704  retval(0) = pos(0) * parent_dim(0) + 1;
705  retval(1) = pos(1) * parent_dim(1) + 1;
706  if (is_rectangle)
707  {
708  retval(2) = pos(2) * parent_dim(0);
709  retval(3) = pos(3) * parent_dim(1);
710  }
711  else if (! is_2D)
712  retval(2) = 0;
713  }
714  else if (from_units.compare ("characters"))
715  {
716  if (res <= 0)
717  res = xget (0, "screenpixelsperinch").double_value ();
718 
719  double f = 0.0;
720 
721  // FIXME: this assumes the system font is Helvetica 10pt
722  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
723  f = 12.0 * res / 74.951;
724 
725  if (f > 0)
726  {
727  retval(0) = 0.5 * pos(0) * f;
728  retval(1) = pos(1) * f;
729  if (is_rectangle)
730  {
731  retval(2) = 0.5 * pos(2) * f;
732  retval(3) = pos(3) * f;
733  }
734  else if (! is_2D)
735  retval(2) = 0;
736  }
737  }
738  else
739  {
740  if (res <= 0)
741  res = xget (0, "screenpixelsperinch").double_value ();
742 
743  double f = 0.0;
744 
745  if (from_units.compare ("points"))
746  f = res / 72.0;
747  else if (from_units.compare ("inches"))
748  f = res;
749  else if (from_units.compare ("centimeters"))
750  f = res / 2.54;
751 
752  if (f > 0)
753  {
754  retval(0) = pos(0) * f + 1;
755  retval(1) = pos(1) * f + 1;
756  if (is_rectangle)
757  {
758  retval(2) = pos(2) * f;
759  retval(3) = pos(3) * f;
760  }
761  else if (! is_2D)
762  retval(2) = 0;
763  }
764  }
765 
766  if (! to_units.compare ("pixels"))
767  {
768  if (to_units.compare ("normalized"))
769  {
770  retval(0) = (retval(0) - 1) / parent_dim(0);
771  retval(1) = (retval(1) - 1) / parent_dim(1);
772  if (is_rectangle)
773  {
774  retval(2) /= parent_dim(0);
775  retval(3) /= parent_dim(1);
776  }
777  else if (! is_2D)
778  retval(2) = 0;
779  }
780  else if (to_units.compare ("characters"))
781  {
782  if (res <= 0)
783  res = xget (0, "screenpixelsperinch").double_value ();
784 
785  double f = 0.0;
786 
787  f = 12.0 * res / 74.951;
788 
789  if (f > 0)
790  {
791  retval(0) = 2 * retval(0) / f;
792  retval(1) = retval(1) / f;
793  if (is_rectangle)
794  {
795  retval(2) = 2 * retval(2) / f;
796  retval(3) = retval(3) / f;
797  }
798  else if (! is_2D)
799  retval(2) = 0;
800  }
801  }
802  else
803  {
804  if (res <= 0)
805  res = xget (0, "screenpixelsperinch").double_value ();
806 
807  double f = 0.0;
808 
809  if (to_units.compare ("points"))
810  f = res / 72.0;
811  else if (to_units.compare ("inches"))
812  f = res;
813  else if (to_units.compare ("centimeters"))
814  f = res / 2.54;
815 
816  if (f > 0)
817  {
818  retval(0) = (retval(0) - 1) / f;
819  retval(1) = (retval(1) - 1) / f;
820  if (is_rectangle)
821  {
822  retval(2) /= f;
823  retval(3) /= f;
824  }
825  else if (! is_2D)
826  retval(2) = 0;
827  }
828  }
829  }
830  else if (! is_rectangle && ! is_2D)
831  retval(2) = 0;
832 
833  return retval;
834 }
835 
836 static Matrix
838  const caseless_str& from_units,
839  const caseless_str& to_units)
840 {
841  gh_manager& gh_mgr = octave::__get_gh_manager__ ("convert_text_position");
842 
843  graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
844 
845  graphics_object ax = go.get_ancestor ("axes");
846 
847  Matrix retval;
848 
849  if (ax.valid_object ())
850  {
851  const axes::properties& ax_props
852  = dynamic_cast<const axes::properties&> (ax.get_properties ());
853  graphics_xform ax_xform = ax_props.get_transform ();
854  bool is_rectangle = (pos.numel () == 4);
855  Matrix ax_bbox = ax_props.get_boundingbox (true),
856  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
857 
858  if (from_units.compare ("data"))
859  {
860  if (is_rectangle)
861  {
862  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
863  v2 = ax_xform.transform (pos(0) + pos(2),
864  pos(1) + pos(3), 0);
865 
866  retval.resize (1, 4);
867 
868  retval(0) = v1(0) - ax_bbox(0) + 1;
869  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
870  retval(2) = v2(0) - v1(0);
871  retval(3) = v1(1) - v2(1);
872  }
873  else
874  {
875  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
876 
877  retval.resize (1, 3);
878 
879  retval(0) = v(0) - ax_bbox(0) + 1;
880  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
881  retval(2) = 0;
882  }
883  }
884  else
885  retval = convert_position (pos, from_units, "pixels", ax_size);
886 
887  if (! to_units.compare ("pixels"))
888  {
889  if (to_units.compare ("data"))
890  {
891  if (is_rectangle)
892  {
893  ColumnVector v1, v2;
894  v1 = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
895  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
896  v2 = ax_xform.untransform (retval(0) + retval(2) + ax_bbox(0) - 1,
897  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
898 
899  retval.resize (1, 4);
900 
901  retval(0) = v1(0);
902  retval(1) = v1(1);
903  retval(2) = v2(0) - v1(0);
904  retval(3) = v2(1) - v1(1);
905  }
906  else
907  {
908  ColumnVector v;
909  v = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
910  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
911 
912  retval.resize (1, 3);
913 
914  retval(0) = v(0);
915  retval(1) = v(1);
916  retval(2) = v(2);
917  }
918  }
919  else
920  retval = convert_position (retval, "pixels", to_units, ax_size);
921  }
922  }
923 
924  return retval;
925 }
926 
927 // This function always returns the screensize in pixels
928 static Matrix
930 {
931  gh_manager& gh_mgr = octave::__get_gh_manager__ ("scren_size_pixels");
932 
933  graphics_object obj = gh_mgr.get_object (0);
934 
935  Matrix sz = obj.get ("screensize").matrix_value ();
936 
937  return convert_position (sz, obj.get ("units").string_value (), "pixels",
938  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
939 }
940 
941 static double
943 {
944  double retval = 1.0;
945 
946  gh_manager& gh_mgr = octave::__get_gh_manager__ ("device_pixel_ratio");
947 
948  graphics_object fig = gh_mgr.get_object (h).get_ancestor ("figure");
949 
950  if (fig.valid_object ())
951  retval = fig.get ("__device_pixel_ratio__").double_value ();
952 
953  return retval;
954 }
955 
956 static void
957 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
958  const double *cmapv, double x, octave_idx_type lda,
959  octave_idx_type nc, octave_idx_type i, double *av)
960 {
961  if (is_scaled)
962  x = octave::math::fix (nc * (x - clim_0) / (clim_1 - clim_0));
963  else if (is_real)
964  x = octave::math::fix (x - 1);
965 
966  if (octave::math::isnan (x))
967  {
968  av[i] = x;
969  av[i+lda] = x;
970  av[i+2*lda] = x;
971  }
972  else
973  {
974  if (x < 0)
975  x = 0;
976  else if (x >= nc)
977  x = (nc - 1);
978 
979  octave_idx_type idx = static_cast<octave_idx_type> (x);
980 
981  av[i] = cmapv[idx];
982  av[i+lda] = cmapv[idx+nc];
983  av[i+2*lda] = cmapv[idx+2*nc];
984  }
985 }
986 
987 template <typename T>
988 void
989 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
990  const double *cmapv, const T *cv, octave_idx_type lda,
991  octave_idx_type nc, double *av)
992 {
993  for (octave_idx_type i = 0; i < lda; i++)
994  convert_cdata_2 (is_scaled, is_real,
995  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
996 }
997 
998 static octave_value
999 convert_cdata (const base_properties& props, const octave_value& cdata,
1000  bool is_scaled, int cdim)
1001 {
1002  dim_vector dv (cdata.dims ());
1003 
1004  // TrueColor data doesn't require conversion
1005  if (dv.ndims () == cdim && dv(cdim-1) == 3)
1006  return cdata;
1007 
1008  Matrix cmap (1, 3, 0.0);
1009  Matrix clim (1, 2, 0.0);
1010 
1011  gh_manager& gh_mgr = octave::__get_gh_manager__ ("convert_cdata");
1012 
1013  graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
1014  graphics_object ax = go.get_ancestor ("axes");
1015 
1016  if (ax.valid_object ())
1017  {
1018  Matrix _cmap = ax.get (caseless_str ("colormap")).matrix_value ();
1019 
1020  cmap = _cmap;
1021 
1022  if (is_scaled)
1023  {
1024  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
1025 
1026  clim = _clim;
1027  }
1028  }
1029 
1030  dv.resize (cdim);
1031  dv(cdim-1) = 3;
1032 
1033  NDArray a (dv);
1034 
1035  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
1036  octave_idx_type nc = cmap.rows ();
1037 
1038  double *av = a.fortran_vec ();
1039  const double *cmapv = cmap.data ();
1040 
1041  double clim_0 = clim(0);
1042  double clim_1 = clim(1);
1043 
1044  // FIXME: There is a lot of processing time spent just on data conversion
1045  // both here in graphics.cc and again in gl-render.cc. There must
1046  // be room for improvement! Here a macro expands to a templated
1047  // function which in turn calls another function (covert_cdata_2).
1048  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
1049  // is supported anyways so there is another double for loop across
1050  // height and width to convert all of the input data to GLfloat.
1051 
1052 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
1053  do \
1054  { \
1055  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
1056  \
1057  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
1058  tmp.data (), lda, nc, av); \
1059  } \
1060  while (0)
1061 
1062  if (cdata.is_int8_type ())
1063  CONVERT_CDATA_1 (int8NDArray, int8_, false);
1064  else if (cdata.is_int16_type ())
1065  CONVERT_CDATA_1 (int16NDArray, int16_, false);
1066  else if (cdata.is_int32_type ())
1067  CONVERT_CDATA_1 (int32NDArray, int32_, false);
1068  else if (cdata.is_int64_type ())
1069  CONVERT_CDATA_1 (int64NDArray, int64_, false);
1070  else if (cdata.is_uint8_type ())
1071  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
1072  else if (cdata.is_uint16_type ())
1073  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
1074  else if (cdata.is_uint32_type ())
1075  CONVERT_CDATA_1 (uint32NDArray, uint32_, false);
1076  else if (cdata.is_uint64_type ())
1077  CONVERT_CDATA_1 (uint64NDArray, uint64_, false);
1078  else if (cdata.is_double_type ())
1079  CONVERT_CDATA_1 (NDArray, , true);
1080  else if (cdata.is_single_type ())
1081  CONVERT_CDATA_1 (FloatNDArray, float_, true);
1082  else if (cdata.islogical ())
1083  CONVERT_CDATA_1 (boolNDArray, bool_, false);
1084  else
1085  {
1086  // Don't throw an error; leads to an incomplete FLTK object (bug #46933).
1087  warning ("unsupported type for cdata (= %s). "
1088  "Valid types are int8, int16, int32, int64, uint8, uint16, "
1089  "uint32, uint64, double, single, and bool.",
1090  cdata.type_name ().c_str ());
1091  a = NDArray (dv, 0); // return 0 instead
1092  }
1093 
1094 #undef CONVERT_CDATA_1
1095 
1096  return octave_value (a);
1097 }
1098 
1099 template <typename T>
1100 static void
1101 get_array_limits (const Array<T>& m, double& emin, double& emax,
1102  double& eminp, double& emaxp)
1103 {
1104  const T *data = m.data ();
1105  octave_idx_type n = m.numel ();
1106 
1107  for (octave_idx_type i = 0; i < n; i++)
1108  {
1109  double e = double (data[i]);
1110 
1111  // Don't need to test for NaN here as NaN>x and NaN<x is always false
1112  if (! octave::math::isinf (e))
1113  {
1114  if (e < emin)
1115  emin = e;
1116 
1117  if (e > emax)
1118  emax = e;
1119 
1120  if (e > 0 && e < eminp)
1121  eminp = e;
1122 
1123  if (e < 0 && e > emaxp)
1124  emaxp = e;
1125  }
1126  }
1127 }
1128 
1129 static bool
1131  caseless_str& rest)
1132 {
1133  int len = name.length ();
1134  int offset = 0;
1135  bool result = false;
1136 
1137  if (len >= 4)
1138  {
1139  caseless_str pfx = name.substr (0, 4);
1140 
1141  if (pfx.compare ("axes") || pfx.compare ("line")
1142  || pfx.compare ("text"))
1143  offset = 4;
1144  else if (len >= 5)
1145  {
1146  pfx = name.substr (0, 5);
1147 
1148  if (pfx.compare ("image") || pfx.compare ("patch"))
1149  offset = 5;
1150  else if (len >= 6)
1151  {
1152  pfx = name.substr (0, 6);
1153 
1154  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1155  offset = 6;
1156  else if (len >= 7)
1157  {
1158  pfx = name.substr (0, 7);
1159 
1160  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1161  || pfx.compare ("uipanel") || pfx.compare ("uitable"))
1162  offset = 7;
1163  else if (len >= 9)
1164  {
1165  pfx = name.substr (0, 9);
1166 
1167  if (pfx.compare ("uicontrol")
1168  || pfx.compare ("uitoolbar"))
1169  offset = 9;
1170  else if (len >= 10)
1171  {
1172  pfx = name.substr (0, 10);
1173 
1174  if (pfx.compare ("uipushtool"))
1175  offset = 10;
1176  else if (len >= 12)
1177  {
1178  pfx = name.substr (0, 12);
1179 
1180  if (pfx.compare ("uitoggletool"))
1181  offset = 12;
1182  else if (len >= 13)
1183  {
1184  pfx = name.substr (0, 13);
1185 
1186  if (pfx.compare ("uicontextmenu")
1187  || pfx.compare ("uibuttongroup"))
1188  offset = 13;
1189  }
1190  }
1191  }
1192  }
1193  }
1194  }
1195  }
1196 
1197  if (offset > 0)
1198  {
1199  go_name = pfx;
1200  rest = name.substr (offset);
1201  result = true;
1202  }
1203  }
1204 
1205  return result;
1206 }
1207 
1208 static base_graphics_object*
1210  const graphics_handle& h = graphics_handle (),
1211  const graphics_handle& p = graphics_handle ())
1212 {
1213  base_graphics_object *go = nullptr;
1214 
1215  if (type.compare ("figure"))
1216  go = new figure (h, p);
1217  else if (type.compare ("axes"))
1218  go = new axes (h, p);
1219  else if (type.compare ("line"))
1220  go = new line (h, p);
1221  else if (type.compare ("text"))
1222  go = new text (h, p);
1223  else if (type.compare ("image"))
1224  go = new image (h, p);
1225  else if (type.compare ("light"))
1226  go = new light (h, p);
1227  else if (type.compare ("patch"))
1228  go = new patch (h, p);
1229  else if (type.compare ("surface"))
1230  go = new surface (h, p);
1231  else if (type.compare ("hggroup"))
1232  go = new hggroup (h, p);
1233  else if (type.compare ("uimenu"))
1234  go = new uimenu (h, p);
1235  else if (type.compare ("uicontrol"))
1236  go = new uicontrol (h, p);
1237  else if (type.compare ("uipanel"))
1238  go = new uipanel (h, p);
1239  else if (type.compare ("uibuttongroup"))
1240  go = new uibuttongroup (h, p);
1241  else if (type.compare ("uicontextmenu"))
1242  go = new uicontextmenu (h, p);
1243  else if (type.compare ("uitable"))
1244  go = new uitable (h, p);
1245  else if (type.compare ("uitoolbar"))
1246  go = new uitoolbar (h, p);
1247  else if (type.compare ("uipushtool"))
1248  go = new uipushtool (h, p);
1249  else if (type.compare ("uitoggletool"))
1250  go = new uitoggletool (h, p);
1251  return go;
1252 }
1253 
1254 // ---------------------------------------------------------------------
1255 
1256 bool
1257 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1258 {
1259  if (do_set (v))
1260  {
1261  // Notify graphics toolkit.
1262  if (id >= 0 && do_notify_toolkit)
1263  {
1264  gh_manager& gh_mgr
1265  = octave::__get_gh_manager__ ("base_property::set");
1266 
1267  graphics_object go = gh_mgr.get_object (parent);
1268  if (go)
1269  go.update (id);
1270  }
1271 
1272  // run listeners
1273  if (do_run)
1275 
1276  return true;
1277  }
1278 
1279  return false;
1280 }
1281 
1282 void
1284 {
1285  const octave_value_list& l = listeners[mode];
1286 
1287  gh_manager& gh_mgr
1288  = octave::__get_gh_manager__ ("base_property::run_listeners");
1289 
1290  for (int i = 0; i < l.length (); i++)
1291  gh_mgr.execute_listener (parent, l(i));
1292 }
1293 
1294 radio_values::radio_values (const std::string& opt_string)
1295  : default_val (), possible_vals ()
1296 {
1297  size_t beg = 0;
1298  size_t len = opt_string.length ();
1299  bool done = len == 0;
1300 
1301  while (! done)
1302  {
1303  size_t end = opt_string.find ('|', beg);
1304 
1305  if (end == std::string::npos)
1306  {
1307  end = len;
1308  done = true;
1309  }
1310 
1311  std::string t = opt_string.substr (beg, end-beg);
1312 
1313  // Might want more error checking here...
1314  if (t[0] == '{')
1315  {
1316  t = t.substr (1, t.length () - 2);
1317  default_val = t;
1318  }
1319  else if (beg == 0) // ensure default value
1320  default_val = t;
1321 
1322  possible_vals.insert (t);
1323 
1324  beg = end + 1;
1325  }
1326 }
1327 
1328 std::string
1330 {
1331  std::string retval;
1332 
1333  for (const auto& val : possible_vals)
1334  {
1335  if (retval.empty ())
1336  {
1337  if (val == default_value ())
1338  retval = '{' + val + '}';
1339  else
1340  retval = val;
1341  }
1342  else
1343  {
1344  if (val == default_value ())
1345  retval += " | {" + val + '}';
1346  else
1347  retval += " | " + val;
1348  }
1349  }
1350 
1351  if (! retval.empty ())
1352  retval = "[ " + retval + " ]";
1353 
1354  return retval;
1355 }
1356 
1357 Cell
1359 {
1360  octave_idx_type i = 0;
1361  Cell retval (nelem (), 1);
1362 
1363  for (const auto& val : possible_vals)
1364  retval(i++) = std::string (val);
1365 
1366  return retval;
1367 }
1368 
1369 bool
1370 color_values::str2rgb (const std::string& str_arg)
1371 {
1372  bool retval = true;
1373 
1374  double tmp_rgb[3] = {0, 0, 0};
1375 
1376  std::string str = str_arg;
1377  unsigned int len = str.length ();
1378 
1379  std::transform (str.begin (), str.end (), str.begin (), tolower);
1380 
1381  if (str.compare (0, len, "blue", 0, len) == 0)
1382  tmp_rgb[2] = 1;
1383  else if (str.compare (0, len, "black", 0, len) == 0
1384  || str.compare (0, len, "k", 0, len) == 0)
1385  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1386  else if (str.compare (0, len, "red", 0, len) == 0)
1387  tmp_rgb[0] = 1;
1388  else if (str.compare (0, len, "green", 0, len) == 0)
1389  tmp_rgb[1] = 1;
1390  else if (str.compare (0, len, "yellow", 0, len) == 0)
1391  tmp_rgb[0] = tmp_rgb[1] = 1;
1392  else if (str.compare (0, len, "magenta", 0, len) == 0)
1393  tmp_rgb[0] = tmp_rgb[2] = 1;
1394  else if (str.compare (0, len, "cyan", 0, len) == 0)
1395  tmp_rgb[1] = tmp_rgb[2] = 1;
1396  else if (str.compare (0, len, "white", 0, len) == 0
1397  || str.compare (0, len, "w", 0, len) == 0)
1398  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1399  else
1400  retval = false;
1401 
1402  if (retval)
1403  {
1404  for (int i = 0; i < 3; i++)
1405  xrgb(i) = tmp_rgb[i];
1406  }
1407 
1408  return retval;
1409 }
1410 
1411 bool
1413 {
1414  if (val.is_string ())
1415  {
1416  std::string s = val.string_value ();
1417 
1418  if (s.empty ())
1419  error (R"(invalid value for color property "%s")",
1420  get_name ().c_str ());
1421 
1422  std::string match;
1423 
1424  if (radio_val.contains (s, match))
1425  {
1426  if (current_type != radio_t || match != current_val)
1427  {
1428  if (s.length () != match.length ())
1429  warning_with_id ("Octave:abbreviated-property-match",
1430  "%s: allowing %s to match %s value %s",
1431  "set", s.c_str (), get_name ().c_str (),
1432  match.c_str ());
1433  current_val = match;
1435  return true;
1436  }
1437  }
1438  else
1439  {
1440  try
1441  {
1442  color_values col (s);
1443 
1444  if (current_type != color_t || col != color_val)
1445  {
1446  color_val = col;
1448  return true;
1449  }
1450  }
1451  catch (octave::execution_exception& e)
1452  {
1453  error (e, R"(invalid value for color property "%s" (value = %s))",
1454  get_name ().c_str (), s.c_str ());
1455  }
1456  }
1457  }
1458  else if (val.isnumeric ())
1459  {
1460  Matrix m = val.matrix_value ();
1461 
1462  if (m.numel () != 3)
1463  error (R"(invalid value for color property "%s")",
1464  get_name ().c_str ());
1465 
1466  color_values col (m(0), m(1), m(2));
1467 
1468  if (current_type != color_t || col != color_val)
1469  {
1470  color_val = col;
1472  return true;
1473  }
1474  }
1475  else
1476  error (R"(invalid value for color property "%s")",
1477  get_name ().c_str ());
1478 
1479  return false;
1480 }
1481 
1482 bool
1484 {
1485  if (val.is_string ())
1486  {
1487  std::string s = val.string_value ();
1488  std::string match;
1489 
1490  if (s.empty () || ! radio_val.contains (s, match))
1491  error (R"(invalid value for double_radio property "%s")",
1492  get_name ().c_str ());
1493 
1494  if (current_type != radio_t || match != current_val)
1495  {
1496  if (s.length () != match.length ())
1497  warning_with_id ("Octave:abbreviated-property-match",
1498  "%s: allowing %s to match %s value %s",
1499  "set", s.c_str (), get_name ().c_str (),
1500  match.c_str ());
1501  current_val = match;
1503  return true;
1504  }
1505  }
1506  else if (val.is_scalar_type () && val.isreal ())
1507  {
1508  double new_dval = val.double_value ();
1509 
1510  if (current_type != double_t || new_dval != dval)
1511  {
1512  dval = new_dval;
1514  return true;
1515  }
1516  }
1517  else
1518  error (R"(invalid value for double_radio property "%s")",
1519  get_name ().c_str ());
1520 
1521  return false;
1522 }
1523 
1524 bool
1526 {
1527  bool xok = false;
1528 
1529  // check value type
1530  if (type_constraints.size () > 0)
1531  {
1532  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1533  xok = true;
1534 
1535  // check if complex is allowed (it's also of class "double", so
1536  // checking that alone is not enough to ensure real type)
1537  if (type_constraints.find ("real") != type_constraints.end ()
1538  && v.iscomplex ())
1539  xok = false;
1540  }
1541  else
1542  xok = v.isnumeric () || v.is_bool_scalar ();
1543 
1544  if (xok && size_constraints.size () > 0)
1545  {
1546  dim_vector vdims = v.dims ();
1547  int vlen = vdims.ndims ();
1548 
1549  xok = false;
1550 
1551  // check dimensional size constraints until a match is found
1552  for (auto it = size_constraints.cbegin ();
1553  ! xok && it != size_constraints.cend ();
1554  ++it)
1555  {
1556  dim_vector itdims = (*it);
1557 
1558  if (itdims.ndims () == vlen)
1559  {
1560  xok = true;
1561 
1562  for (int i = 0; xok && i < vlen; i++)
1563  {
1564  if (itdims(i) > 0)
1565  {
1566  if (itdims(i) != vdims(i))
1567  xok = false;
1568  }
1569  else if (itdims(i) == 0)
1570  {
1571  if (! v.isempty ())
1572  xok = false;
1573  break;
1574  }
1575  }
1576  }
1577  }
1578  }
1579 
1580  if (xok)
1581  {
1582  NDArray v_mat = v.array_value ();
1583  // Check min and max
1584  if (! octave::math::isnan (minval.first))
1585  {
1586  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1587  if (minval.second && minval.first > v_mat(i))
1588  error (R"(set: "%s" must be greater than or equal to %g)",
1589  get_name ().c_str (), minval.first);
1590  else if (! minval.second && minval.first >= v_mat(i))
1591  error (R"(set: "%s" must be greater than %g)",
1592  get_name ().c_str (), minval.first);
1593  }
1594 
1595  if (! octave::math::isnan (maxval.first))
1596  {
1597  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1598  if (maxval.second && maxval.first < v_mat(i))
1599  error (R"(set: "%s" must be less than or equal to %g)",
1600  get_name ().c_str (), maxval.first);
1601  else if (! maxval.second && maxval.first <= v_mat(i))
1602  error (R"(set: "%s" must be less than %g)",
1603  get_name ().c_str (), maxval.first);
1604  }
1605 
1606  if (finite_constraint == NO_CHECK) { /* do nothing */ }
1607  else if (finite_constraint == FINITE)
1608  {
1609  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1610  if (! octave::math::isfinite (v_mat(i)))
1611  error (R"(set: "%s" must be finite)", get_name ().c_str ());
1612  }
1613  else if (finite_constraint == NOT_NAN)
1614  {
1615  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1616  if (octave::math::isnan (v_mat(i)))
1617  error (R"(set: "%s" must not be nan)", get_name ().c_str ());
1618  }
1619  else if (finite_constraint == NOT_INF)
1620  {
1621  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1622  if (octave::math::isinf (v_mat(i)))
1623  error (R"(set: "%s" must not be infinite)", get_name ().c_str ());
1624  }
1625 
1626  }
1627 
1628  return xok;
1629 }
1630 
1631 bool
1633 {
1634  if (data.type_name () == v.type_name ())
1635  {
1636  if (data.dims () == v.dims ())
1637  {
1638 
1639 #define CHECK_ARRAY_EQUAL(T, F, A) \
1640  { \
1641  if (data.numel () == 1) \
1642  return data.F ## scalar_value () == \
1643  v.F ## scalar_value (); \
1644  else \
1645  { \
1646  /* Keep copy of array_value to allow */ \
1647  /* sparse/bool arrays that are converted, to */ \
1648  /* not be deallocated early */ \
1649  const A m1 = data.F ## array_value (); \
1650  const T *d1 = m1.data (); \
1651  const A m2 = v.F ## array_value (); \
1652  const T *d2 = m2.data (); \
1653  \
1654  bool flag = true; \
1655  \
1656  for (int i = 0; flag && i < data.numel (); i++) \
1657  if (d1[i] != d2[i]) \
1658  flag = false; \
1659  \
1660  return flag; \
1661  } \
1662  }
1663 
1664  if (data.is_double_type () || data.islogical ())
1665  CHECK_ARRAY_EQUAL (double, , NDArray)
1666  else if (data.is_single_type ())
1667  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1668  else if (data.is_int8_type ())
1670  else if (data.is_int16_type ())
1672  else if (data.is_int32_type ())
1674  else if (data.is_int64_type ())
1676  else if (data.is_uint8_type ())
1678  else if (data.is_uint16_type ())
1680  else if (data.is_uint32_type ())
1682  else if (data.is_uint64_type ())
1684  }
1685  }
1686 
1687  return false;
1688 }
1689 
1690 void
1692 {
1695 
1696  if (! data.isempty ())
1697  {
1698  if (data.isinteger ())
1699  {
1700  if (data.is_int8_type ())
1702  xmin, xmax, xminp, xmaxp);
1703  else if (data.is_uint8_type ())
1705  xmin, xmax, xminp, xmaxp);
1706  else if (data.is_int16_type ())
1708  xmin, xmax, xminp, xmaxp);
1709  else if (data.is_uint16_type ())
1711  xmin, xmax, xminp, xmaxp);
1712  else if (data.is_int32_type ())
1714  xmin, xmax, xminp, xmaxp);
1715  else if (data.is_uint32_type ())
1717  xmin, xmax, xminp, xmaxp);
1718  else if (data.is_int64_type ())
1720  xmin, xmax, xminp, xmaxp);
1721  else if (data.is_uint64_type ())
1723  xmin, xmax, xminp, xmaxp);
1724  }
1725  else
1727  }
1728 }
1729 
1730 bool
1732 {
1733  // Users may want to use empty matrix to reset a handle property
1734  if (v.isempty ())
1735  {
1736  if (! get ().isempty ())
1737  {
1739  return true;
1740  }
1741  else
1742  return false;
1743  }
1744 
1745  double dv = v.xdouble_value (R"(set: invalid graphics handle for property "%s")",
1746  get_name ().c_str ());
1747 
1748  gh_manager& gh_mgr = octave::__get_gh_manager__ ("handle_property::do_set");
1749 
1750  graphics_handle gh = gh_mgr.lookup (dv);
1751 
1752  // Check the object type if necessary
1753  bool type_ok = true;
1754  if (gh.ok () && ! type_constraints.empty ())
1755  {
1756  type_ok = false;
1757  graphics_object obj = gh_mgr.get_object (gh);
1758 
1759  for (const auto& type : type_constraints)
1760  if (obj.isa (type))
1761  {
1762  type_ok = true;
1763  break;
1764  }
1765  }
1766 
1767  if (! octave::math::isnan (gh.value ()) && ! (gh.ok () && type_ok))
1768  {
1769  if (type_ok)
1770  error (R"(set: invalid graphics handle (= %g) for property "%s")",
1771  dv, get_name ().c_str ());
1772  else
1773  error (R"(set: invalid graphics object type for property "%s")",
1774  get_name ().c_str ());
1775  }
1776 
1777  if (current_val != gh)
1778  {
1779  current_val = gh;
1780  return true;
1781  }
1782 
1783  return false;
1784 }
1785 
1786 /*
1787 ## Test validation of uicontextmenu property
1788 %!test
1789 %! hf = figure ("visible", "off");
1790 %! unwind_protect
1791 %! hax = axes ("parent", hf);
1792 %! hpa = patch ("parent", hax);
1793 %! try
1794 %! set (hax, "uicontextmenu", hpa);
1795 %! catch
1796 %! err = lasterr ();
1797 %! end_try_catch
1798 %! assert (err, 'set: invalid graphics object type for property "uicontextmenu"');
1799 %! unwind_protect_cleanup
1800 %! delete (hf);
1801 %! end_unwind_protect
1802 */
1803 
1804 Matrix
1805 children_property::do_get_children (bool return_hidden) const
1806 {
1807  Matrix retval (children_list.size (), 1);
1808  octave_idx_type k = 0;
1809 
1810  gh_manager& gh_mgr
1811  = octave::__get_gh_manager__ ("children_property::do_get_children");
1812 
1813  graphics_object go = gh_mgr.get_object (0);
1814 
1816  = dynamic_cast<root_figure::properties&> (go.get_properties ());
1817 
1818  if (! props.is_showhiddenhandles ())
1819  {
1820  for (const auto& hchild : children_list)
1821  {
1822  graphics_handle kid = hchild;
1823 
1824  if (gh_mgr.is_handle_visible (kid))
1825  {
1826  if (! return_hidden)
1827  retval(k++) = hchild;
1828  }
1829  else if (return_hidden)
1830  retval(k++) = hchild;
1831  }
1832 
1833  retval.resize (k, 1);
1834  }
1835  else
1836  {
1837  for (const auto& hchild : children_list)
1838  retval(k++) = hchild;
1839  }
1840 
1841  return retval;
1842 }
1843 
1844 void
1845 children_property::do_delete_children (bool clear, bool from_root)
1846 {
1847  gh_manager& gh_mgr
1848  = octave::__get_gh_manager__ ("children_property::do_delete_children");
1849 
1850  if (from_root)
1851  {
1852  for (graphics_handle hchild : children_list)
1853  {
1854  graphics_object go = gh_mgr.get_object (hchild);
1855  if (go.valid_object ()
1856  && ! go.get_properties ().is_beingdeleted ())
1857  gh_mgr.free (hchild, from_root);
1858  }
1859  children_list.clear ();
1860  }
1861  else
1862  while (! children_list.empty ())
1863  {
1864  // gh_mgr.free removes hchild from children_list
1865  graphics_handle hchild = children_list.front ();
1866  graphics_object go = gh_mgr.get_object (hchild);
1867  if (go.valid_object ()
1868  && ! go.get_properties ().is_beingdeleted ())
1869  gh_mgr.free (hchild, from_root);
1870  }
1871 
1872  // FIXME: children_list should be clear anyway at this point.
1873  if (clear)
1874  children_list.clear ();
1875 }
1876 
1877 bool
1879 {
1880  // case 1: empty matrix
1881  // case 2: function handle
1882  // case 3: string corresponding to known function name
1883  // case 4: string that can be eval()'ed
1884  // case 5: cell array with first element being a function handle
1885 
1886  if (v.isempty ())
1887  return true;
1888  else if (v.is_function_handle ())
1889  return true;
1890  else if (v.is_string ())
1891  // complete validation will be done at execution-time
1892  return true;
1893  else if (v.iscell () && (v.rows () == 1 || v.columns () == 1)
1894  && v.cell_value ()(0).is_function_handle ())
1895  return true;
1896 
1897  return false;
1898 }
1899 
1901 {
1902 public:
1903 
1904  callback_props (void) : m_set () { }
1905 
1906  callback_props (const callback_props&) = delete;
1907 
1909 
1910  ~callback_props (void) = default;
1911 
1912  bool empty (void) const { return m_set.empty (); }
1913 
1914  void insert (const callback_property *ptr)
1915  {
1916  m_set.insert (reinterpret_cast<intptr_t> (ptr));
1917  }
1918 
1919  void erase (const callback_property *ptr)
1920  {
1921  m_set.erase (reinterpret_cast<intptr_t> (ptr));
1922  }
1923 
1924  bool contains (const callback_property *ptr) const
1925  {
1926  return m_set.find (reinterpret_cast<intptr_t> (ptr)) != m_set.end ();
1927  }
1928 
1929 private:
1930 
1931  std::set<intptr_t> m_set;
1932 };
1933 
1934 // Elements of this set are pointers to currently executing
1935 // callback_property objects. Used to determine handle visibility
1936 // inside callback functions.
1937 
1939 
1940 void
1942 {
1943  octave::unwind_protect frame;
1944 
1945  // We are executing a callback function, so allow handles that have
1946  // their handlevisibility property set to "callback" to be visible.
1947 
1949 
1950  if (! executing_callbacks.contains (this))
1951  {
1952  executing_callbacks.insert (this);
1953 
1954  if (callback.is_defined () && ! callback.isempty ())
1955  {
1956  gh_manager& gh_mgr
1957  = octave::__get_gh_manager__ ("callback_property::execute");
1958 
1959  gh_mgr.execute_callback (get_parent (), callback, data);
1960  }
1961  }
1962 }
1963 
1964 // Used to cache dummy graphics objects from which dynamic properties can be
1965 // cloned.
1966 static std::map<caseless_str, graphics_object> dprop_obj_map;
1967 
1968 property
1969 property::create (const std::string& name, const graphics_handle& h,
1970  const caseless_str& type, const octave_value_list& args)
1971 {
1972  property retval;
1973 
1974  if (type.compare ("string"))
1975  {
1976  std::string sv = (args.length () > 0 ? args(0).string_value () : "");
1977 
1978  retval = property (new string_property (name, h, sv));
1979  }
1980  else if (type.compare ("any"))
1981  {
1982  octave_value ov = (args.length () > 0 ? args(0)
1983  : octave_value (Matrix ()));
1984 
1985  retval = property (new any_property (name, h, ov));
1986  }
1987  else if (type.compare ("radio"))
1988  {
1989  if (args.length () < 1)
1990  error ("addproperty: missing possible values for radio property");
1991 
1992  std::string sv = args(0).xstring_value ("addproperty: argument for radio property must be a string");
1993 
1994  retval = property (new radio_property (name, h, sv));
1995 
1996  if (args.length () > 1)
1997  retval.set (args(1));
1998  }
1999  else if (type.compare ("double"))
2000  {
2001  double dv = (args.length () > 0 ? args(0).double_value () : 0.0);
2002 
2003  retval = property (new double_property (name, h, dv));
2004  }
2005  else if (type.compare ("handle"))
2006  {
2007  double hv = (args.length () > 0 ? args(0).double_value ()
2009 
2010  graphics_handle gh (hv);
2011 
2012  retval = property (new handle_property (name, h, gh));
2013  }
2014  else if (type.compare ("boolean"))
2015  {
2016  retval = property (new bool_property (name, h, false));
2017 
2018  if (args.length () > 0)
2019  retval.set (args(0));
2020  }
2021  else if (type.compare ("data"))
2022  {
2023  retval = property (new array_property (name, h, Matrix ()));
2024 
2025  if (args.length () > 0)
2026  {
2027  retval.set (args(0));
2028  // FIXME: additional argument could define constraints,
2029  // but is this really useful?
2030  }
2031  }
2032  else if (type.compare ("color"))
2033  {
2034  color_values cv (0, 0, 0);
2035  radio_values rv;
2036 
2037  if (args.length () > 1)
2038  rv = radio_values (args(1).string_value ());
2039 
2040  retval = property (new color_property (name, h, cv, rv));
2041 
2042  if (args.length () > 0 && ! args(0).isempty ())
2043  retval.set (args(0));
2044  else
2045  retval.set (rv.default_value ());
2046  }
2047  else
2048  {
2049  caseless_str go_name, go_rest;
2050 
2051  if (! lookup_object_name (type, go_name, go_rest))
2052  error ("addproperty: unsupported type for dynamic property (= %s)",
2053  type.c_str ());
2054 
2055  graphics_object go;
2056 
2057  std::map<caseless_str, graphics_object>::const_iterator it
2058  = dprop_obj_map.find (go_name);
2059 
2060  if (it == dprop_obj_map.end ())
2061  {
2063 
2064  if (bgo)
2065  {
2066  go = graphics_object (bgo);
2067 
2068  dprop_obj_map[go_name] = go;
2069  }
2070  }
2071  else
2072  go = it->second;
2073 
2074  if (! go.valid_object ())
2075  error ("addproperty: invalid object type (= %s)",
2076  go_name.c_str ());
2077 
2078  property prop = go.get_properties ().get_property (go_rest);
2079 
2080  retval = prop.clone ();
2081 
2082  retval.set_parent (h);
2083  retval.set_name (name);
2084 
2085  if (args.length () > 0)
2086  retval.set (args(0));
2087  }
2088 
2089  return retval;
2090 }
2091 
2092 static void
2094 {
2095  gh_manager& gh_mgr = octave::__get_gh_manager__ ("finalize_r");
2096 
2097  graphics_object go = gh_mgr.get_object (h);
2098 
2099  if (go)
2100  {
2101  Matrix children = go.get_properties ().get_all_children ();
2102 
2103  for (int k = 0; k < children.numel (); k++)
2104  finalize_r (children(k));
2105 
2106  go.finalize ();
2107  }
2108 }
2109 
2110 static void
2112 {
2113  gh_manager& gh_mgr = octave::__get_gh_manager__ ("initialize_r");
2114 
2115  graphics_object go = gh_mgr.get_object (h);
2116 
2117  if (go)
2118  {
2119  Matrix children = go.get_properties ().get_all_children ();
2120 
2121  go.initialize ();
2122 
2123  for (int k = 0; k < children.numel (); k++)
2124  initialize_r (children(k));
2125  }
2126 }
2127 
2128 void
2130 {
2131  if (toolkit)
2132  finalize_r (get___myhandle__ ());
2133 
2134  toolkit = b;
2135  __graphics_toolkit__ = b.get_name ();
2136  __plot_stream__ = Matrix ();
2137 
2138  if (toolkit)
2139  initialize_r (get___myhandle__ ());
2140 
2141  mark_modified ();
2142 }
2143 
2144 void
2145 figure::properties::set___mouse_mode__ (const octave_value& val_arg)
2146 {
2147  std::string direction = "in";
2148 
2149  octave_value val = val_arg;
2150 
2151  if (val.is_string ())
2152  {
2153  std::string modestr = val.string_value ();
2154 
2155  if (modestr == "zoom in")
2156  {
2157  val = modestr = "zoom";
2158  direction = "in";
2159  }
2160  else if (modestr == "zoom out")
2161  {
2162  val = modestr = "zoom";
2163  direction = "out";
2164  }
2165 
2166  if (__mouse_mode__.set (val, true))
2167  {
2168  std::string mode = __mouse_mode__.current_value ();
2169 
2170  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
2171  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
2172  set___pan_mode__ (pm);
2173 
2174  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
2175  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
2176  set___rotate_mode__ (rm);
2177 
2178  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2179  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
2180  zm.setfield ("Direction", direction);
2181  set___zoom_mode__ (zm);
2182 
2183  mark_modified ();
2184  }
2185  else if (modestr == "zoom")
2186  {
2187  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2188  std::string curr_direction
2189  = zm.getfield ("Direction").string_value ();
2190 
2191  if (direction != curr_direction)
2192  {
2193  zm.setfield ("Direction", direction);
2194  set___zoom_mode__ (zm);
2195 
2196  mark_modified ();
2197  }
2198  }
2199  }
2200 }
2201 
2202 void
2203 figure::properties::update_handlevisibility (void)
2204 {
2205  if (! is_handle_visible ())
2206  {
2207  gh_manager& gh_mgr
2208  = octave::__get_gh_manager__ ("figure::properties::update_handlevisibility");
2209 
2210  octave_value cf = gh_mgr.get_object (0).get ("currentfigure");
2211 
2212  if (! cf.isempty () && cf.double_value () == __myhandle__)
2213  {
2214  octave::autolock guard (gh_mgr.graphics_lock ());
2215 
2216  octave_value kids = gh_mgr.get_object (0).get ("children");
2217 
2218  if (kids.isempty ())
2219  gh_mgr.get_object (0).set ("currentfigure", Matrix ());
2220  else
2221  {
2222  NDArray kidsarray = kids.array_value ();
2223  gh_mgr.get_object (0).set ("currentfigure", kidsarray(0));
2224  }
2225  }
2226  }
2227 
2228  base_properties::update_handlevisibility ();
2229 }
2230 
2231 static void
2233 {
2234  gh_manager& gh_mgr = octave::__get_gh_manager__ ("update_text_pos");
2235 
2236  graphics_object go = gh_mgr.get_object (h);
2237 
2238  if (go.isa ("text"))
2239  {
2240  text::properties& tp
2241  = dynamic_cast<text::properties&> (go.get_properties ());
2242  tp.update_font ();
2243  tp.update_text_extent ();
2244  }
2245  else if (go.isa ("figure") || go.isa ("uipanel") || go.isa ("axes")
2246  || go.isa ("hggroup"))
2247  {
2248  Matrix ch = go.get_properties ().get_all_children ();
2249  for (octave_idx_type ii = 0; ii < ch.numel (); ii++)
2250  update_text_pos (graphics_handle (ch(ii)));
2251 
2252  if (go.isa ("axes"))
2253  {
2254  axes::properties& ap
2255  = dynamic_cast<axes::properties&> (go.get_properties ());
2256  ap.update_font ();
2257  ap.sync_positions ();
2258  }
2259  }
2260 }
2261 
2262 void
2263 figure::properties::update___device_pixel_ratio__ (void)
2264 {
2265  update_text_pos (get___myhandle__ ());
2266 }
2267 
2268 // ---------------------------------------------------------------------
2269 
2270 void
2272 {
2273  size_t offset = 0;
2274 
2275  size_t len = name.length ();
2276 
2277  if (len > 4)
2278  {
2279  caseless_str pfx = name.substr (0, 4);
2280 
2281  if (pfx.compare ("axes") || pfx.compare ("line")
2282  || pfx.compare ("text"))
2283  offset = 4;
2284  else if (len > 5)
2285  {
2286  pfx = name.substr (0, 5);
2287 
2288  if (pfx.compare ("image") || pfx.compare ("patch"))
2289  offset = 5;
2290  else if (len > 6)
2291  {
2292  pfx = name.substr (0, 6);
2293 
2294  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2295  offset = 6;
2296  else if (len > 7)
2297  {
2298  pfx = name.substr (0, 7);
2299 
2300  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2301  || pfx.compare ("uipanel") || pfx.compare ("uitable"))
2302  offset = 7;
2303  else if (len > 9)
2304  {
2305  pfx = name.substr (0, 9);
2306 
2307  if (pfx.compare ("uicontrol")
2308  || pfx.compare ("uitoolbar"))
2309  offset = 9;
2310  else if (len > 10)
2311  {
2312  pfx = name.substr (0, 10);
2313 
2314  if (pfx.compare ("uipushtool"))
2315  offset = 10;
2316  else if (len > 12)
2317  {
2318  pfx = name.substr (0, 12);
2319 
2320  if (pfx.compare ("uitoogletool"))
2321  offset = 12;
2322  else if (len > 13)
2323  {
2324  pfx = name.substr (0, 13);
2325 
2326  if (pfx.compare ("uicontextmenu")
2327  || pfx.compare ("uibuttongroup"))
2328  offset = 13;
2329  }
2330  }
2331  }
2332  }
2333  }
2334  }
2335  }
2336 
2337  if (offset > 0)
2338  {
2339  // FIXME: should we validate property names and values here?
2340 
2341  std::string pname = name.substr (offset);
2342 
2343  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2344  std::transform (pname.begin (), pname.end (), pname.begin (),
2345  tolower);
2346 
2347  bool has_property = false;
2348  if (pfx == "axes")
2349  has_property = axes::properties::has_core_property (pname);
2350  else if (pfx == "figure")
2351  has_property = figure::properties::has_core_property (pname);
2352  else if (pfx == "line")
2353  has_property = line::properties::has_core_property (pname);
2354  else if (pfx == "text")
2355  has_property = text::properties::has_core_property (pname);
2356  else if (pfx == "image")
2357  has_property = image::properties::has_core_property (pname);
2358  else if (pfx == "patch")
2359  has_property = patch::properties::has_core_property (pname);
2360  else if (pfx == "surface")
2361  has_property = surface::properties::has_core_property (pname);
2362  else if (pfx == "hggroup")
2363  has_property = hggroup::properties::has_core_property (pname);
2364  else if (pfx == "uimenu")
2365  has_property = uimenu::properties::has_core_property (pname);
2366  else if (pfx == "uicontrol")
2367  has_property = uicontrol::properties::has_core_property (pname);
2368  else if (pfx == "uibuttongroup")
2369  has_property = uibuttongroup::properties::has_core_property (pname);
2370  else if (pfx == "uipanel")
2371  has_property = uipanel::properties::has_core_property (pname);
2372  else if (pfx == "uicontextmenu")
2373  has_property = uicontextmenu::properties::has_core_property (pname);
2374  else if (pfx == "uitable")
2375  has_property = uitable::properties::has_core_property (pname);
2376  else if (pfx == "uitoolbar")
2377  has_property = uitoolbar::properties::has_core_property (pname);
2378  else if (pfx == "uipushtool")
2379  has_property = uipushtool::properties::has_core_property (pname);
2380 
2381  if (! has_property)
2382  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2383 
2384  bool remove = false;
2385  if (val.is_string ())
2386  {
2387  std::string sval = val.string_value ();
2388 
2389  remove = (sval == "remove");
2390  }
2391 
2392  pval_map_type& pval_map = plist_map[pfx];
2393 
2394  if (remove)
2395  {
2396  auto p = pval_map.find (pname);
2397 
2398  if (p != pval_map.end ())
2399  pval_map.erase (p);
2400  }
2401  else
2402  pval_map[pname] = val;
2403  }
2404  }
2405 
2406  if (offset == 0)
2407  error ("invalid default property specification");
2408 }
2409 
2412 {
2414 
2415  size_t offset = 0;
2416 
2417  size_t len = name.length ();
2418 
2419  if (len > 4)
2420  {
2421  caseless_str pfx = name.substr (0, 4);
2422 
2423  if (pfx.compare ("axes") || pfx.compare ("line")
2424  || pfx.compare ("text"))
2425  offset = 4;
2426  else if (len > 5)
2427  {
2428  pfx = name.substr (0, 5);
2429 
2430  if (pfx.compare ("image") || pfx.compare ("patch"))
2431  offset = 5;
2432  else if (len > 6)
2433  {
2434  pfx = name.substr (0, 6);
2435 
2436  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2437  offset = 6;
2438  else if (len > 7)
2439  {
2440  pfx = name.substr (0, 7);
2441 
2442  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2443  || pfx.compare ("uipanel") || pfx.compare ("uitable"))
2444  offset = 7;
2445  else if (len > 9)
2446  {
2447  pfx = name.substr (0, 9);
2448 
2449  if (pfx.compare ("uicontrol")
2450  || pfx.compare ("uitoolbar"))
2451  offset = 9;
2452  else if (len > 10)
2453  {
2454  pfx = name.substr (0, 10);
2455 
2456  if (pfx.compare ("uipushtool"))
2457  offset = 10;
2458  else if (len > 12)
2459  {
2460  pfx = name.substr (0, 12);
2461 
2462  if (pfx.compare ("uitoggletool"))
2463  offset = 12;
2464  else if (len > 13)
2465  {
2466  pfx = name.substr (0, 13);
2467 
2468  if (pfx.compare ("uicontextmenu")
2469  || pfx.compare ("uibuttongroup"))
2470  offset = 13;
2471  }
2472  }
2473  }
2474  }
2475  }
2476  }
2477  }
2478 
2479  if (offset > 0)
2480  {
2481  std::string pname = name.substr (offset);
2482 
2483  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2484  std::transform (pname.begin (), pname.end (), pname.begin (),
2485  tolower);
2486 
2487  plist_map_const_iterator p = find (pfx);
2488 
2489  if (p != end ())
2490  {
2491  const pval_map_type& pval_map = p->second;
2492 
2493  pval_map_const_iterator q = pval_map.find (pname);
2494 
2495  if (q != pval_map.end ())
2496  retval = q->second;
2497  }
2498  }
2499  }
2500 
2501  return retval;
2502 }
2503 
2505 property_list::as_struct (const std::string& prefix_arg) const
2506 {
2508 
2509  for (auto p = begin (); p != end (); p++)
2510  {
2511  std::string prefix = prefix_arg + p->first;
2512 
2513  for (const auto& prop_val : p->second)
2514  m.assign (prefix + prop_val.first, prop_val.second);
2515  }
2516 
2517  return m;
2518 }
2519 
2520 // Set properties given as a cs-list of name, value pairs.
2521 
2522 void
2524 {
2525  int nargin = args.length ();
2526 
2527  if (nargin == 0)
2528  error ("graphics_object::set: Nothing to set");
2529 
2530  for (int i = 0; i < nargin; )
2531  {
2532  if (args(i).isstruct () )
2533  {
2534  set (args(i).map_value ());
2535  i++;
2536  }
2537  else if (i < nargin - 1)
2538  {
2539  caseless_str pname = args(i).xstring_value ("set: argument %d must be a property name", i);
2540  octave_value val = args(i+1);
2541  set_value_or_default (pname, val);
2542  i += 2;
2543  }
2544  else
2545  error ("set: invalid number of arguments");
2546  }
2547 }
2548 
2549 /*
2550 ## test set with name, value pairs
2551 %!test
2552 %! hf = figure ("visible", "off");
2553 %! h = plot (1:10, 10:-1:1);
2554 %! set (h, "linewidth", 10, "marker", "x");
2555 %! lw = get (h, "linewidth");
2556 %! mk = get (h, "marker");
2557 %! close (hf);
2558 %! assert (lw, 10);
2559 %! assert (mk, "x");
2560 */
2561 
2562 // Set properties given in two cell arrays containing names and values.
2563 void
2565  const Cell& values, octave_idx_type row)
2566 {
2567  if (pnames.numel () != values.columns ())
2568  error ("set: number of names must match number of value columns "
2569  "(%" OCTAVE_IDX_TYPE_FORMAT " != %" OCTAVE_IDX_TYPE_FORMAT ")",
2570  pnames.numel (), values.columns ());
2571 
2572  octave_idx_type k = pnames.columns ();
2573 
2574  for (octave_idx_type column = 0; column < k; column++)
2575  {
2576  caseless_str pname = pnames(column);
2577  octave_value val = values(row, column);
2578 
2579  set_value_or_default (pname, val);
2580  }
2581 }
2582 
2583 /*
2584 ## test set with cell array arguments
2585 %!test
2586 %! hf = figure ("visible", "off");
2587 %! h = plot (1:10, 10:-1:1);
2588 %! set (h, {"linewidth", "marker"}, {10, "x"});
2589 %! lw = get (h, "linewidth");
2590 %! mk = get (h, "marker");
2591 %! close (hf);
2592 %! assert (lw, 10);
2593 %! assert (mk, "x");
2594 
2595 ## test set with multiple handles and cell array arguments
2596 %!test
2597 %! hf = figure ("visible", "off");
2598 %! unwind_protect
2599 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2600 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2601 %! assert (get (h, "linewidth"), {10; 5});
2602 %! assert (get (h, "marker"), {"x"; "o"});
2603 %! set (h, {"linewidth", "marker"}, {10, "x"});
2604 %! assert (get (h, "linewidth"), {10; 10});
2605 %! assert (get (h, "marker"), {"x"; "x"});
2606 %! unwind_protect_cleanup
2607 %! close (hf);
2608 %! end_unwind_protect;
2609 
2610 %!error <set: number of graphics handles must match number of value rows>
2611 %! hf = figure ("visible", "off");
2612 %! unwind_protect
2613 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2614 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2615 %! unwind_protect_cleanup
2616 %! close (hf);
2617 %! end_unwind_protect
2618 
2619 %!error <set: number of names must match number of value columns>
2620 %! hf = figure ("visible", "off");
2621 %! unwind_protect
2622 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2623 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2624 %! unwind_protect_cleanup
2625 %! close (hf);
2626 %! end_unwind_protect
2627 */
2628 
2629 // Set properties given in a struct array
2630 void
2632 {
2633  for (octave_idx_type p = 0; p < m.nfields (); p++)
2634  {
2635  // FIXME: Would it be better to extract all the keys at once rather than
2636  // repeatedly call keys() inside a for loop?
2637  caseless_str pname = m.keys ()[p];
2638 
2639  octave_value val = octave_value (m.contents (pname).elem (m.numel () - 1));
2640 
2641  set_value_or_default (pname, val);
2642  }
2643 }
2644 
2645 /*
2646 ## test set ticklabels for compatibility
2647 %!test
2648 %! hf = figure ("visible", "off");
2649 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2650 %! xticklabel = get (gca (), "xticklabel");
2651 %! close (hf);
2652 %! assert (class (xticklabel), "char");
2653 %! assert (size (xticklabel), [6, 3]);
2654 
2655 %!test
2656 %! hf = figure ("visible", "off");
2657 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2658 %! xticklabel = get (gca (), "xticklabel");
2659 %! close (hf);
2660 %! assert (class (xticklabel), "char");
2661 %! assert (size (xticklabel), [6, 3]);
2662 
2663 %!test
2664 %! hf = figure ("visible", "off");
2665 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2666 %! xticklabel = get (gca (), "xticklabel");
2667 %! close (hf);
2668 %! assert (class (xticklabel), "char");
2669 %! assert (size (xticklabel), [6, 3]);
2670 
2671 %!test
2672 %! hf = figure ("visible", "off");
2673 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2674 %! xticklabel = get (gca (), "xticklabel");
2675 %! close (hf);
2676 %! assert (class (xticklabel), "cell");
2677 %! assert (size (xticklabel), [6, 1]);
2678 */
2679 
2680 /*
2681 ## test set with struct arguments
2682 %!test
2683 %! hf = figure ("visible", "off");
2684 %! unwind_protect
2685 %! h = plot (1:10, 10:-1:1);
2686 %! set (h, struct ("linewidth", 10, "marker", "x"));
2687 %! assert (get (h, "linewidth"), 10);
2688 %! assert (get (h, "marker"), "x");
2689 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2690 %! set (h, struct ("linewidth", {5, 10}));
2691 %! assert (get (h, "linewidth"), {10; 10});
2692 %! unwind_protect_cleanup
2693 %! close (hf);
2694 %! end_unwind_protect
2695 
2696 ## test ordering
2697 %!test
2698 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2699 %! hf = figure ("visible", "off");
2700 %! unwind_protect
2701 %! h = line ();
2702 %! set (h, "userdata", {});
2703 %! addlistener (h, "color", {markchanged, "color"});
2704 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2705 %! ## "linewidth" first
2706 %! props.linewidth = 2;
2707 %! props.color = "r";
2708 %! set (h, props);
2709 %! assert (get (h, "userdata"), fieldnames (props));
2710 %! clear props;
2711 %! clf ();
2712 %! h = line ();
2713 %! set (h, "userdata", {});
2714 %! addlistener (h, "color", {markchanged, "color"});
2715 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2716 %! ## "color" first
2717 %! props.color = "r";
2718 %! props.linewidth = 2;
2719 %! set (h, props);
2720 %! assert (get (h, "userdata"), fieldnames (props));
2721 %! unwind_protect_cleanup
2722 %! close (hf);
2723 %! end_unwind_protect
2724 */
2725 
2726 // Set a property to a value or to its (factory) default value.
2727 
2728 void
2730  const octave_value& val)
2731 {
2732  if (val.is_string ())
2733  {
2734  std::string sval = val.string_value ();
2735 
2736  octave_value default_val;
2737 
2738  if (sval == "default")
2739  {
2740  default_val = get_default (pname);
2741 
2742  rep->set (pname, default_val);
2743  }
2744  else if (sval == "factory")
2745  {
2746  default_val = get_factory_default (pname);
2747 
2748  rep->set (pname, default_val);
2749  }
2750  else
2751  {
2752  // Matlab specifically uses "\default" to escape string setting
2753  if (sval == R"(\default)")
2754  rep->set (pname, "default");
2755  else if (sval == R"(\factory)")
2756  rep->set (pname, "factory");
2757  else
2758  rep->set (pname, val);
2759  }
2760  }
2761  else
2762  rep->set (pname, val);
2763 }
2764 
2765 /*
2766 ## test setting of default values
2767 %!test
2768 %! old_lw = get (0, "defaultlinelinewidth");
2769 %! unwind_protect
2770 %! hf = figure ("visible", "off");
2771 %! h = plot (1:10, 10:-1:1);
2772 %! set (0, "defaultlinelinewidth", 20);
2773 %! set (h, "linewidth", "default");
2774 %! assert (get (h, "linewidth"), 20);
2775 %! set (h, "linewidth", "factory");
2776 %! assert (get (h, "linewidth"), 0.5);
2777 %! unwind_protect_cleanup
2778 %! close (hf);
2779 %! set (0, "defaultlinelinewidth", old_lw);
2780 %! end_unwind_protect
2781 */
2782 
2783 static double
2785 {
2786  static double maxrand = RAND_MAX + 2.0;
2787 
2788  return (rand () + 1.0) / maxrand;
2789 }
2790 
2792 gh_manager::get_handle (bool integer_figure_handle)
2793 {
2795 
2796  if (integer_figure_handle)
2797  {
2798  // Figure handles are positive integers corresponding
2799  // to the figure number.
2800 
2801  // We always want the lowest unused figure number.
2802 
2803  retval = 1;
2804 
2805  while (m_handle_map.find (retval) != m_handle_map.end ())
2806  retval++;
2807  }
2808  else
2809  {
2810  // Other graphics handles are negative integers plus some random
2811  // fractional part. To avoid running out of integers, we recycle the
2812  // integer part but tack on a new random part each time.
2813 
2814  auto p = m_handle_free_list.begin ();
2815 
2816  if (p != m_handle_free_list.end ())
2817  {
2818  retval = *p;
2819  m_handle_free_list.erase (p);
2820  }
2821  else
2822  {
2823  retval = graphics_handle (m_next_handle);
2824 
2825  m_next_handle = std::ceil (m_next_handle) - 1.0 - make_handle_fraction ();
2826  }
2827  }
2828 
2829  return retval;
2830 }
2831 
2832 static bool
2833 isfigure (double val)
2834 {
2835  gh_manager& gh_mgr = octave::__get_gh_manager__ ("isfigure");
2836 
2837  graphics_object go = gh_mgr.get_object (val);
2838 
2839  return go && go.isa ("figure");
2840 }
2841 
2842 void
2843 gh_manager::free (const graphics_handle& h, bool from_root)
2844 {
2845  if (h.ok ())
2846  {
2847  if (h.value () == 0)
2848  error ("graphics_handle::free: can't delete root object");
2849 
2850  auto p = m_handle_map.find (h);
2851 
2852  if (p == m_handle_map.end ())
2853  error ("graphics_handle::free: invalid object %g", h.value ());
2854 
2855  base_properties& bp = p->second.get_properties ();
2856 
2857  if (! p->second.valid_object () || bp.is_beingdeleted ())
2858  return;
2859 
2860  graphics_handle parent_h = p->second.get_parent ();
2861  graphics_object parent_go = nullptr;
2862  if (! from_root || isfigure (h.value ()))
2863  parent_go = get_object (parent_h);
2864 
2865  bp.set_beingdeleted (true);
2866 
2867  // delete listeners before invalidating object
2868  p->second.remove_all_listeners ();
2869 
2870  bp.delete_children (true, from_root);
2871 
2872  // NOTE: Call the delete function while the object's state is still valid.
2873  octave_value val = bp.get_deletefcn ();
2874 
2875  bp.execute_deletefcn ();
2876 
2877  // Notify graphics toolkit.
2878  p->second.finalize ();
2879 
2880 
2881  // NOTE: Call remove_child before erasing the go from the map if not
2882  // removing from groot.
2883  // A callback function might have already deleted the parent
2884  if ((! from_root || isfigure (h.value ())) && parent_go.valid_object ()
2885  && h.ok ())
2886  parent_go.remove_child (h);
2887 
2888  // Note: this will be valid only for first explicitly deleted
2889  // object. All its children will then have an
2890  // unknown graphics toolkit.
2891 
2892  // Graphics handles for non-figure objects are negative
2893  // integers plus some random fractional part. To avoid
2894  // running out of integers, we recycle the integer part
2895  // but tack on a new random part each time.
2896 
2897  m_handle_map.erase (p);
2898 
2899  if (h.value () < 0)
2900  m_handle_free_list.insert
2901  (std::ceil (h.value ()) - make_handle_fraction ());
2902  }
2903 }
2904 
2905 void
2907  const graphics_handle& new_gh)
2908 {
2909  auto p = m_handle_map.find (old_gh);
2910 
2911  if (p == m_handle_map.end ())
2912  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2913 
2914  graphics_object go = p->second;
2915 
2916  m_handle_map.erase (p);
2917 
2918  m_handle_map[new_gh] = go;
2919 
2920  if (old_gh.value () < 0)
2921  m_handle_free_list.insert (std::ceil (old_gh.value ())
2922  - make_handle_fraction ());
2923 
2924  for (auto& hfig : m_figure_list)
2925  {
2926  if (hfig == old_gh)
2927  {
2928  hfig = new_gh;
2929  break;
2930  }
2931  }
2932 }
2933 
2934 static void
2935 xset (const graphics_handle& h, const caseless_str& pname,
2936  const octave_value& val)
2937 {
2938  gh_manager& gh_mgr = octave::__get_gh_manager__ ("xset");
2939 
2940  graphics_object go = gh_mgr.get_object (h);
2941 
2942  go.set (pname, val);
2943 }
2944 
2945 static void
2946 xset (const graphics_handle& h, const octave_value_list& args)
2947 {
2948  if (args.length () > 0)
2949  {
2950  gh_manager& gh_mgr = octave::__get_gh_manager__ ("xset");
2951 
2952  graphics_object go = gh_mgr.get_object (h);
2953 
2954  go.set (args);
2955  }
2956 }
2957 
2958 static octave_value
2959 xget (const graphics_handle& h, const caseless_str& pname)
2960 {
2961  gh_manager& gh_mgr = octave::__get_gh_manager__ ("xget");
2962 
2963  graphics_object go = gh_mgr.get_object (h);
2964 
2965  return go.get (pname);
2966 }
2967 
2968 static graphics_handle
2969 reparent (const octave_value& ov, const std::string& who,
2970  const std::string& pname, const graphics_handle& new_parent,
2971  bool adopt = true)
2972 {
2973  double hv = ov.xdouble_value ("%s: %s must be a graphics handle",
2974  who.c_str (), pname.c_str ());
2975 
2976  gh_manager& gh_mgr = octave::__get_gh_manager__ ("reparent");
2977 
2978  graphics_handle h = gh_mgr.lookup (hv);
2979 
2980  if (! h.ok ())
2981  error ("%s: invalid graphics handle (= %g) for %s",
2982  who.c_str (), hv, pname.c_str ());
2983 
2984  graphics_object go = gh_mgr.get_object (h);
2985 
2986  graphics_handle parent_h = go.get_parent ();
2987 
2988  graphics_object parent_go = gh_mgr.get_object (parent_h);
2989 
2990  parent_go.remove_child (h);
2991 
2992  if (adopt)
2993  go.set ("parent", new_parent.value ());
2994  else
2995  go.reparent (new_parent);
2996 
2997  return h;
2998 }
2999 
3000 // This function is NOT equivalent to the scripting language function gcf.
3002 gcf (void)
3003 {
3004  octave_value val = xget (0, "currentfigure");
3005 
3006  return val.isempty () ? octave::numeric_limits<double>::NaN ()
3007  : val.double_value ();
3008 }
3009 
3010 // This function is NOT equivalent to the scripting language function gca.
3012 gca (void)
3013 {
3014  octave_value val = xget (gcf (), "currentaxes");
3015 
3016  return val.isempty () ? octave::numeric_limits<double>::NaN ()
3017  : val.double_value ();
3018 }
3019 
3020 static void
3021 delete_graphics_object (const graphics_handle& h, bool from_root = false)
3022 {
3023  if (h.ok ())
3024  {
3025  gh_manager& gh_mgr
3026  = octave::__get_gh_manager__ ("delete_graphics_object");
3027 
3028  graphics_object go = gh_mgr.get_object (h);
3029 
3030  // Don't do recursive deleting, due to callbacks
3031  if (! go.get_properties ().is_beingdeleted ())
3032  {
3033  // NOTE: Freeing the handle also calls any deletefcn. It also calls
3034  // the parent's delete_child function.
3035 
3036  gh_mgr.free (h, from_root || go.isa ("figure"));
3037 
3038  Vdrawnow_requested = true;
3039  }
3040  }
3041 }
3042 
3043 static void
3044 delete_graphics_object (double val, bool from_root = false)
3045 {
3046  gh_manager& gh_mgr = octave::__get_gh_manager__ ("delete_graphics_object");
3047 
3048  delete_graphics_object (gh_mgr.lookup (val), from_root || isfigure (val));
3049 }
3050 
3051 // Flag to stop redraws due to callbacks while deletion is in progress.
3052 static bool delete_executing = false;
3053 
3054 static void
3055 delete_graphics_objects (const NDArray vals, bool from_root = false)
3056 {
3057  // Prevent redraw of partially deleted objects.
3058  octave::unwind_protect frame;
3059  frame.protect_var (delete_executing);
3060  delete_executing = true;
3061 
3062  for (octave_idx_type i = 0; i < vals.numel (); i++)
3063  delete_graphics_object (vals.elem (i), from_root);
3064 }
3065 
3066 static void
3068 {
3069  octave_value closerequestfcn = xget (h, "closerequestfcn");
3070 
3071  gh_manager& gh_mgr = octave::__get_gh_manager__ ("close_figure");
3072 
3073  gh_mgr.execute_callback (h, closerequestfcn);
3074 }
3075 
3076 static void
3078 {
3079  // Remove the deletefcn and closerequestfcn callbacks
3080  // and delete the object directly.
3081 
3082  xset (h, "deletefcn", Matrix ());
3083  xset (h, "closerequestfcn", Matrix ());
3084 
3085  delete_graphics_object (h, true);
3086 }
3087 
3088 void
3090 {
3091  // FIXME: should we process or discard pending events?
3092 
3093  m_event_queue.clear ();
3094 
3095  // Don't use m_figure_list_iterator because we'll be removing elements
3096  // from the list elsewhere.
3097 
3098  Matrix hlist = figure_handle_list (true);
3099 
3100  for (octave_idx_type i = 0; i < hlist.numel (); i++)
3101  {
3102  graphics_handle h = lookup (hlist(i));
3103 
3104  if (h.ok ())
3105  close_figure (h);
3106  }
3107 
3108  // They should all be closed now. If not, force them to close.
3109 
3110  hlist = figure_handle_list (true);
3111 
3112  for (octave_idx_type i = 0; i < hlist.numel (); i++)
3113  {
3114  graphics_handle h = lookup (hlist(i));
3115 
3116  if (h.ok ())
3117  force_close_figure (h);
3118  }
3119 
3120  // None left now, right?
3121 
3122  hlist = figure_handle_list (true);
3123 
3124  if (hlist.numel () != 0)
3125  warning ("gh_manager::close_all_figures: some graphics elements failed to close.");
3126 
3127  // Clear all callback objects from our list.
3128 
3129  m_callback_objects.clear ();
3130 }
3131 
3132 static void
3133 adopt (const graphics_handle& parent_h, const graphics_handle& h)
3134 {
3135  gh_manager& gh_mgr = octave::__get_gh_manager__ ("adopt");
3136 
3137  graphics_object parent_go = gh_mgr.get_object (parent_h);
3138 
3139  parent_go.adopt (h);
3140 }
3141 
3142 static bool
3144 {
3145  return h.ok ();
3146 }
3147 
3148 static bool
3149 ishghandle (double val)
3150 {
3151  gh_manager& gh_mgr = octave::__get_gh_manager__ ("ishghandle");
3152 
3153  graphics_handle h = gh_mgr.lookup (val);
3154 
3155  return h.ok ();
3156 }
3157 
3158 static octave_value
3160 {
3161  octave_value retval = false;
3162 
3163  if (val.is_real_scalar () && ishghandle (val.double_value ()))
3164  retval = true;
3165  else if (val.isnumeric () && val.isreal ())
3166  {
3167  const NDArray handles = val.array_value ();
3168 
3169  boolNDArray result (handles.dims ());
3170 
3171  for (octave_idx_type i = 0; i < handles.numel (); i++)
3172  result.xelem (i) = ishghandle (handles(i));
3173 
3174  retval = result;
3175  }
3176 
3177  return retval;
3178 }
3179 
3180 static void
3182 {
3183  gh_manager& gh_mgr = octave::__get_gh_manager__ ("xcreatefcn");
3184 
3185  graphics_object go = gh_mgr.get_object (h);
3186 
3187  go.get_properties ().execute_createfcn ();
3188 }
3189 
3190 static void
3192 {
3193  gh_manager& gh_mgr = octave::__get_gh_manager__ ("xinitialize");
3194 
3195  graphics_object go = gh_mgr.get_object (h);
3196 
3197  if (go)
3198  go.initialize ();
3199 }
3200 
3201 // ---------------------------------------------------------------------
3202 
3203 static int
3204 toggle_warn (std::string id, bool on, int state = -1)
3205 {
3206  if (! on)
3207  {
3208  state = warning_enabled (id);
3209  disable_warning (id);
3210  }
3211  else
3212  {
3213  if (state == 1)
3214  set_warning_state (id, "on");
3215  else if (state == 2)
3216  set_warning_state (id, "error");
3217  }
3218  return state;
3219 }
3220 
3221 static void
3223  property_list::pval_map_type factory_pval)
3224 {
3225  gh_manager& gh_mgr
3226  = octave::__get_gh_manager__ ("xreset_default_properties");
3227 
3228  graphics_object go = gh_mgr.get_object (h);
3229 
3230  // Replace factory defaults by user defined ones
3231  std::string go_name = go.get_properties ().graphics_object_name ();
3233  go.build_user_defaults_map (pval, go_name);
3234 
3235  for (const auto& p : pval)
3236  factory_pval[p.first] = p.second;
3237 
3238  // Save warning state of "Octave:deprecated-property"
3239  int state = toggle_warn ("Octave:deprecated-property", false);
3240 
3241  // Reset defaults
3242  for (const auto& p : factory_pval)
3243  {
3244  std::string pname = p.first;
3245 
3246  // Don't reset internal properties and handle_properties
3247  if (! go.has_readonly_property (pname)
3248  && pname.find ("__") != 0 && pname.find ("current") != 0
3249  && pname != "uicontextmenu" && pname != "parent")
3250  {
3251  // Store *mode prop/val in order to set them last
3252  if (pname.find ("mode") == (pname.length () - 4))
3253  pval[pname] = p.second;
3254  else
3255  go.set (pname, p.second);
3256  }
3257  }
3258 
3259  // set *mode properties
3260  for (const auto& p : pval)
3261  go.set (p.first, p.second);
3262 
3263  toggle_warn ("Octave:deprecated-property", true, state);
3264 }
3265 
3266 // ---------------------------------------------------------------------
3267 
3268 void
3271 {
3272  std::string go_name = graphics_object_name ();
3273 
3274  property_list::plist_map_const_iterator plist = defaults.find (go_name);
3275 
3276  if (plist != defaults.end ())
3277  {
3278  const property_list::pval_map_type pval_map = plist->second;
3279 
3280  for (const auto& prop_val : pval_map)
3281  {
3282  std::string pname = prop_val.first;
3283 
3284  try
3285  {
3286  bgo.set (pname, prop_val.second);
3287  }
3288  catch (octave::execution_exception& e)
3289  {
3290  error (e, "error setting default property %s", pname.c_str ());
3291  }
3292  }
3293  }
3294 }
3295 
3296 /*
3297 ## test defaults are set in the order they were stored
3298 %!test
3299 %! set(0, "defaultfigureunits", "normalized");
3300 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
3301 %! hf = figure ("visible", "off");
3302 %! tol = 20 * eps;
3303 %! unwind_protect
3304 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
3305 %! unwind_protect_cleanup
3306 %! close (hf);
3307 %! set(0, "defaultfigureunits", "remove");
3308 %! set(0, "defaultfigureposition", "remove");
3309 %! end_unwind_protect
3310 */
3311 
3314 {
3315  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it
3316  = all_props.find (pname);
3317 
3318  if (it == all_props.end ())
3319  error (R"(get: unknown property "%s")", pname.c_str ());
3320 
3321  return it->second.get ();
3322 }
3323 
3326 {
3328 
3329  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
3330  it = all_props.begin (); it != all_props.end (); ++it)
3331  if (all || ! it->second.is_hidden ())
3332  m.assign (it->second.get_name (), it->second.get ());
3333 
3334  return m;
3335 }
3336 
3337 std::set<std::string>
3339 {
3340  return dynamic_properties;
3341 }
3342 
3343 bool
3344 base_properties::has_dynamic_property (const std::string& pname) const
3345 {
3346  const std::set<std::string>& dynprops = dynamic_property_names ();
3347 
3348  if (dynprops.find (pname) != dynprops.end ())
3349  return true;
3350  else
3351  return all_props.find (pname) != all_props.end ();
3352 }
3353 
3354 void
3356  const octave_value& val)
3357 {
3358  auto it = all_props.find (pname);
3359 
3360  if (it == all_props.end ())
3361  error (R"(set: unknown property "%s")", pname.c_str ());
3362 
3363  it->second.set (val);
3364 
3365  dynamic_properties.insert (pname);
3366 
3367  mark_modified ();
3368 }
3369 
3370 property
3372 {
3373  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it
3374  = all_props.find (pname);
3375 
3376  if (it == all_props.end ())
3377  error (R"(get_property: unknown property "%s")", pname.c_str ());
3378 
3379  return it->second;
3380 }
3381 
3382 void
3384 {
3385  double hp = val.xdouble_value ("set: parent must be a graphics handle");
3386  if (hp == __myhandle__)
3387  error ("set: can not set object parent to be object itself");
3388 
3389  gh_manager& gh_mgr
3390  = octave::__get_gh_manager__ ("base_properties::set_parent");
3391 
3392  graphics_handle new_parent = gh_mgr.lookup (hp);
3393  if (! new_parent.ok ())
3394  error ("set: invalid graphics handle (= %g) for parent", hp);
3395 
3396  // Remove child from current parent
3397  graphics_object old_parent_go;
3398  old_parent_go = gh_mgr.get_object (get_parent ());
3399 
3400  if (old_parent_go.get_handle () != hp)
3401  old_parent_go.remove_child (__myhandle__);
3402  else
3403  return; // Do nothing more
3404 
3405  // Check new parent's parent is not this child to avoid recursion
3406  graphics_object new_parent_go;
3407  new_parent_go = gh_mgr.get_object (new_parent);
3408  if (new_parent_go.get_parent () == __myhandle__)
3409  {
3410  // new parent's parent gets child's original parent
3411  new_parent_go.get_properties ().set_parent (get_parent ().as_octave_value ());
3412  }
3413 
3414  // Set parent property to new_parent and do adoption
3415  parent = new_parent.as_octave_value ();
3416  ::adopt (parent.handle_value (), __myhandle__);
3417 }
3418 
3419 /*
3420 %!test
3421 %! hf = figure ("visible", "off");
3422 %! unwind_protect
3423 %! hax = gca ();
3424 %! set (hax, "parent", gcf ());
3425 %! assert (gca (), hax);
3426 %! unwind_protect_cleanup
3427 %! close (hf);
3428 %! end_unwind_protect
3429 */
3430 
3431 void
3433 {
3434  // Mark existing object as modified
3435  __modified__ = "on";
3436 
3437  // Attempt to mark parent object as modified if it exists
3438 
3439  gh_manager& gh_mgr
3440  = octave::__get_gh_manager__ ("base_properties::mark_modified");
3441 
3442  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3443 
3444  if (parent_go)
3445  parent_go.mark_modified ();
3446 }
3447 
3448 void
3450 {
3451  gh_manager& gh_mgr
3452  = octave::__get_gh_manager__ ("base_properties::override_defaults");
3453 
3454  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3455 
3456  if (parent_go)
3457  parent_go.override_defaults (obj);
3458 }
3459 
3460 void
3461 base_properties::update_axis_limits (const std::string& axis_type) const
3462 {
3463  gh_manager& gh_mgr
3464  = octave::__get_gh_manager__ ("base_properties::update_axis_limits");
3465 
3466  graphics_object go = gh_mgr.get_object (__myhandle__);
3467 
3468  if (go)
3469  go.update_axis_limits (axis_type);
3470 }
3471 
3472 void
3473 base_properties::update_axis_limits (const std::string& axis_type,
3474  const graphics_handle& h) const
3475 {
3476  gh_manager& gh_mgr
3477  = octave::__get_gh_manager__ ("base_properties::update_axis_limits");
3478 
3479  graphics_object go = gh_mgr.get_object (__myhandle__);
3480 
3481  if (go)
3482  go.update_axis_limits (axis_type, h);
3483 }
3484 
3485 void
3487 {
3488  if (uicontextmenu.get ().isempty ())
3489  return;
3490 
3491  gh_manager& gh_mgr
3492  = octave::__get_gh_manager__ ("base_properties::update_uicontextmenu");
3493 
3494  graphics_object go = gh_mgr.get_object (uicontextmenu.get ());
3495 
3496  if (go && go.isa ("uicontextmenu"))
3497  {
3499  = reinterpret_cast<uicontextmenu::properties&> (go.get_properties ());
3500  props.add_dependent_obj (__myhandle__);
3501  }
3502 }
3503 
3504 bool
3506 {
3507  return (handlevisibility.is ("on")
3508  || (! executing_callbacks.empty () && ! handlevisibility.is ("off")));
3509 }
3510 
3513 {
3514  gh_manager& gh_mgr
3515  = octave::__get_gh_manager__ ("base_properties::get_toolkit");
3516 
3517  graphics_object go = gh_mgr.get_object (get_parent ());
3518 
3519  if (go)
3520  return go.get_toolkit ();
3521  else
3522  return octave::graphics_toolkit ();
3523 }
3524 
3525 void
3527 {
3528  Matrix kids = get_children ();
3529 
3530  gh_manager& gh_mgr
3531  = octave::__get_gh_manager__ ("base_properties::update_boundingbox");
3532 
3533  for (int i = 0; i < kids.numel (); i++)
3534  {
3535  graphics_object go = gh_mgr.get_object (kids(i));
3536 
3537  if (go.valid_object ())
3539  }
3540 }
3541 
3542 void
3543 base_properties::update_autopos (const std::string& elem_type)
3544 {
3545  gh_manager& gh_mgr
3546  = octave::__get_gh_manager__ ("base_properties::update_autopos");
3547 
3548  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3549 
3550  if (parent_go.valid_object ())
3551  parent_go.get_properties ().update_autopos (elem_type);
3552 }
3553 
3554 void
3555 base_properties::update_handlevisibility (void)
3556 {
3557  if (is_handle_visible ())
3558  return;
3559 
3560  // This object should not be the figure "currentobject"
3561 
3562  gh_manager& gh_mgr
3563  = octave::__get_gh_manager__ ("base_properties::update_handlevisibility");
3564 
3565  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
3566 
3567  graphics_object fig (go.get_ancestor ("figure"));
3568 
3569  if (fig.valid_object ())
3570  {
3571  octave_value co = fig.get ("currentobject");
3572  if (! co.isempty () && co.double_value () == __myhandle__)
3573  {
3574  octave::autolock guard (gh_mgr.graphics_lock ());
3575 
3576  auto& fig_props = dynamic_cast<figure::properties&> (fig.get_properties ());
3577  fig_props.set_currentobject (Matrix ());
3578  }
3579  }
3580 }
3581 
3582 /*
3583 ## test current figure and current axes have visible handles
3584 %!test
3585 %! hf1 = figure ("visible", "off");
3586 %! hf2 = figure ("visible", "off");
3587 %! hax1 = axes ();
3588 %! hax2 = axes ();
3589 %! unwind_protect
3590 %! assert (get (0, "currentfigure"), hf2);
3591 %! assert (get (hf2, "currentaxes"), hax2);
3592 %! set (hf2, "handlevisibility", "off");
3593 %! assert (get (0, "currentfigure"), hf1);
3594 %! set (hax2, "handlevisibility", "off");
3595 %! assert (get (hf2, "currentaxes"), hax1);
3596 %! assert (get (hf2, "currentobject"), []);
3597 %! unwind_protect_cleanup
3598 %! close ([hf1, hf2]);
3599 %! end_unwind_protect;
3600 */
3601 
3602 /*
3603 ## test current callback object have visible handle
3604 %!test
3605 %! hf = figure ("handlevisibility", "off", "visible", "off");
3606 %! hax = axes ("parent", hf, "handlevisibility", "off");
3607 %! unwind_protect
3608 %! fcn = @(h) setappdata (h, "testdata", gcbo ());
3609 %! addlistener (hf, "color", fcn);
3610 %! addlistener (hax, "color", fcn);
3611 %! set (hf, "color", "b");
3612 %! set (hax, "color", "b");
3613 %! assert (getappdata (hf, "testdata"), hf)
3614 %! assert (getappdata (hax, "testdata"), hax)
3615 %! unwind_protect_cleanup
3616 %! close (hf);
3617 %! end_unwind_protect;
3618 */
3619 
3620 void
3622  const octave_value& val,
3623  listener_mode mode)
3624 {
3625  property p = get_property (pname);
3626 
3627  if (p.ok ())
3628  p.add_listener (val, mode);
3629 }
3630 
3631 void
3633  const octave_value& val,
3634  listener_mode mode)
3635 {
3636  property p = get_property (pname);
3637 
3638  if (p.ok ())
3639  p.delete_listener (val, mode);
3640 }
3641 
3642 void
3644  bool get_invisible,
3645  bool traverse,
3646  std::list<graphics_object> &children_list) const
3647 {
3648  gh_manager& gh_mgr
3649  = octave::__get_gh_manager__ ("base_properties::get_children_of_type");
3650 
3651  Matrix ch = get_children ();
3652 
3653  for (octave_idx_type i = 0; i < ch.numel (); i++)
3654  {
3655  graphics_handle hkid = gh_mgr.lookup (ch(i));
3656 
3657  if (hkid.ok ())
3658  {
3659  graphics_object go = gh_mgr.get_object (hkid);
3660  if ( get_invisible || go.get_properties ().is_visible () )
3661  {
3662  if (go.isa (chtype))
3663  children_list.push_back (go);
3664  else if (traverse && go.isa ("hggroup"))
3665  go.get_properties ().get_children_of_type (chtype,
3666  get_invisible,
3667  traverse,
3668  children_list);
3669  }
3670  }
3671  }
3672 }
3673 
3674 // ---------------------------------------------------------------------
3675 
3676 void
3677 base_graphics_object::update_axis_limits (const std::string& axis_type)
3678 {
3679  if (! valid_object ())
3680  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3681 
3682  gh_manager& gh_mgr
3683  = octave::__get_gh_manager__ ("base_graphics_object::update_axis_limits");
3684 
3685  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3686 
3687  if (parent_go)
3688  parent_go.update_axis_limits (axis_type);
3689 }
3690 
3691 void
3692 base_graphics_object::update_axis_limits (const std::string& axis_type,
3693  const graphics_handle& h)
3694 {
3695  if (! valid_object ())
3696  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3697 
3698  gh_manager& gh_mgr
3699  = octave::__get_gh_manager__ ("base_graphics_object::update_axis_limits");
3700 
3701  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3702 
3703  if (parent_go)
3704  parent_go.update_axis_limits (axis_type, h);
3705 }
3706 
3707 void
3709 {
3710  int state = toggle_warn ("Octave:deprecated-property", false);
3711  octave_map m = get (true).map_value ();
3712  toggle_warn ("Octave:deprecated-property", true, state);
3713 
3714  for (const auto& pm : m)
3715  {
3716  // FIXME: there has to be a better way. I think we want to
3717  // ask whether it is OK to delete the listener for the given
3718  // property. How can we know in advance that it will be OK?
3719 
3720  octave::unwind_protect frame;
3721 
3722  interpreter_try (frame);
3723 
3724  try
3725  {
3726  property p = get_properties ().get_property (pm.first);
3727 
3728  if (p.ok ())
3729  p.delete_listener ();
3730  }
3731  catch (const octave::execution_exception&)
3732  {
3733  octave::interpreter& interp
3734  = octave::__get_interpreter__ ("remove_all_listeners");
3735 
3736  interp.recover_from_exception ();
3737  }
3738  }
3739 }
3740 
3741 void
3743 {
3744  property_list local_defaults = get_defaults_list ();
3745  const auto it = local_defaults.find (go_name);
3746 
3747  if (it != local_defaults.end ())
3748  {
3749  property_list::pval_map_type pval_lst = it->second;
3750  for (const auto& prop_val : pval_lst)
3751  {
3752  std::string pname = prop_val.first;
3753  if (def.find (pname) == def.end ())
3754  def[pname] = prop_val.second;
3755  }
3756  }
3757 
3758  gh_manager& gh_mgr
3759  = octave::__get_gh_manager__ ("base_graphics_object::build_user_defaults_map");
3760 
3761  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3762 
3763  if (parent_go)
3764  parent_go.build_user_defaults_map (def, go_name);
3765 }
3766 
3767 void
3769 {
3770  if (valid_object ())
3771  {
3772  gh_manager& gh_mgr
3773  = octave::__get_gh_manager__ ("base_graphics_object::reset_default_properties");
3774 
3775  property_list::pval_map_type factory_pval
3776  = gh_mgr.get_object (0).get_factory_defaults_list ().find (type ())->second;
3777 
3779  xreset_default_properties (get_handle (), factory_pval);
3780  }
3781 }
3782 
3783 std::string
3785 {
3786  if (! valid_object ())
3787  error ("base_graphics_object::values_as_string: invalid graphics object");
3788 
3789  std::string retval;
3790  octave_map m = get ().map_value ();
3791 
3792  gh_manager& gh_mgr
3793  = octave::__get_gh_manager__ ("base_graphics_object::values_as_string");
3794 
3795  graphics_object go = gh_mgr.get_object (get_handle ());
3796 
3797  for (const auto& pm : m)
3798  {
3799  const auto& pname = pm.first;
3800  if (pname != "children" && ! go.has_readonly_property (pname))
3801  {
3802  property p = get_properties ().get_property (pname);
3803 
3804  if (p.ok () && ! p.is_hidden ())
3805  {
3806  retval += "\n\t" + std::string (pname) + ": ";
3807  if (p.is_radio ())
3808  retval += p.values_as_string ();
3809  }
3810  }
3811  }
3812 
3813  if (! retval.empty ())
3814  retval += '\n';
3815 
3816  return retval;
3817 }
3818 
3819 std::string
3820 base_graphics_object::value_as_string (const std::string& prop)
3821 {
3822  std::string retval;
3823 
3824  if (! valid_object ())
3825  error ("base_graphics_object::value_as_string: invalid graphics object");
3826 
3827  gh_manager& gh_mgr
3828  = octave::__get_gh_manager__ ("base_graphics_object::value_as_string");
3829 
3830  graphics_object go = gh_mgr.get_object (get_handle ());
3831 
3832  if (prop != "children" && ! go.has_readonly_property (prop))
3833  {
3834  property p = get_properties ().get_property (prop);
3835 
3836  if (p.ok () && ! p.is_hidden ())
3837  {
3838  if (p.is_radio ())
3839  retval += p.values_as_string ();
3840  }
3841  }
3842 
3843  if (! retval.empty ())
3844  retval += '\n';
3845 
3846  return retval;
3847 }
3848 
3851 {
3853 
3854  if (! valid_object ())
3855  error ("base_graphics_object::values_as_struct: invalid graphics object");
3856 
3858 
3859  gh_manager& gh_mgr
3860  = octave::__get_gh_manager__ ("base_graphics_object::values_as_struct");
3861 
3862  graphics_object go = gh_mgr.get_object (get_handle ());
3863 
3864  for (const auto& pm : m)
3865  {
3866  const auto& pname = pm.first;
3867  if (pname != "children" && ! go.has_readonly_property (pname))
3868  {
3869  property p = get_properties ().get_property (pname);
3870 
3871  if (p.ok () && ! p.is_hidden ())
3872  {
3873  if (p.is_radio ())
3874  retval.assign (p.get_name (), p.values_as_cell ());
3875  else
3876  retval.assign (p.get_name (), Cell ());
3877  }
3878  }
3879  }
3880 
3881  return retval;
3882 }
3883 
3884 /*
3885 %!test
3886 %! hfig = figure ("visible", "off");
3887 %! unwind_protect
3888 %! hax = axes ();
3889 %! ret = set (hax, "tightinset");
3890 %! assert (isempty (ret));
3891 %! ret = set (hax, "type");
3892 %! assert (isempty (ret));
3893 %! ret = set (hfig, "tag");
3894 %! assert (isempty (ret));
3895 %! ret = set (0, "commandwindowsize");
3896 %! assert (isempty (ret));
3897 %! ret = set (0);
3898 %! assert (! isfield (ret, "commandwindowsize"));
3899 %! unwind_protect_cleanup
3900 %! close (hfig);
3901 %! end_unwind_protect
3902 */
3903 
3905 graphics_object::get_ancestor (const std::string& obj_type) const
3906 {
3907  if (valid_object ())
3908  {
3909  if (isa (obj_type))
3910  return *this;
3911  else
3912  {
3913  gh_manager& gh_mgr
3914  = octave::__get_gh_manager__ ("graphics_object::get_ancestor");
3915 
3916  return gh_mgr.get_object (get_parent ()).get_ancestor (obj_type);
3917  }
3918  }
3919  else
3920  return graphics_object ();
3921 }
3922 
3923 // ---------------------------------------------------------------------
3924 
3925 #include "graphics-props.cc"
3926 
3927 // ---------------------------------------------------------------------
3928 
3929 void
3930 root_figure::properties::set_callbackobject (const octave_value& v)
3931 {
3932  graphics_handle val (v);
3933 
3934  if (octave::math::isnan (val.value ()))
3935  callbackobject = graphics_handle ();
3936  else if (ishghandle (val))
3937  callbackobject = val;
3938  else
3939  err_set_invalid ("callbackobject");
3940 }
3941 
3942 void
3943 root_figure::properties::set_currentfigure (const octave_value& v)
3944 {
3945  graphics_handle val (v);
3946 
3947  if (octave::math::isnan (val.value ()) || ishghandle (val))
3948  {
3949  currentfigure = val;
3950 
3951  if (val.ok ())
3952  {
3953  gh_manager& gh_mgr
3954  = octave::__get_gh_manager__ ("root_figure::properties::set_currentfigure");
3955 
3956  gh_mgr.push_figure (val);
3957  }
3958  }
3959  else
3960  err_set_invalid ("currentfigure");
3961 }
3962 
3963 void
3964 figure::properties::set_integerhandle (const octave_value& val)
3965 {
3966  if (integerhandle.set (val, true))
3967  {
3968  bool int_fig_handle = integerhandle.is_on ();
3969 
3970  gh_manager& gh_mgr
3971  = octave::__get_gh_manager__ ("figure::properties::set_integerhandle");
3972 
3973  graphics_object this_go = gh_mgr.get_object (__myhandle__);
3974 
3975  graphics_handle old_myhandle = __myhandle__;
3976 
3977  __myhandle__ = gh_mgr.get_handle (int_fig_handle);
3978 
3979  gh_mgr.renumber_figure (old_myhandle, __myhandle__);
3980 
3981  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3982 
3983  base_properties& props = parent_go.get_properties ();
3984 
3985  props.renumber_child (old_myhandle, __myhandle__);
3986 
3987  Matrix kids = get_children ();
3988 
3989  for (octave_idx_type i = 0; i < kids.numel (); i++)
3990  {
3991  graphics_object kid = gh_mgr.get_object (kids(i));
3992 
3993  kid.get_properties ().renumber_parent (__myhandle__);
3994  }
3995 
3996  graphics_handle cf = gh_mgr.current_figure ();
3997 
3998  if (__myhandle__ == cf)
3999  xset (0, "currentfigure", __myhandle__.value ());
4000 
4001  this_go.update (integerhandle.get_id ());
4002 
4003  mark_modified ();
4004  }
4005 }
4006 
4007 // FIXME: This should update monitorpositions and pointerlocation, but as these
4008 // properties aren't yet used, it doesn't matter that they aren't set either.
4009 void
4010 root_figure::properties::update_units (void)
4011 {
4012  std::string xunits = get_units ();
4013 
4014  Matrix scrn_sz = default_screensize ();
4015 
4016  double dpi = get_screenpixelsperinch ();
4017 
4018  if (xunits == "pixels")
4019  {
4020  // Most common case (default).
4021  // Don't need to convert anything, but short-circuit if/else tree.
4022  }
4023  else if (xunits == "normalized")
4024  {
4025  scrn_sz = Matrix (1, 4, 1.0);
4026  scrn_sz(0) = 0;
4027  scrn_sz(1) = 0;
4028  }
4029  else if (xunits == "inches")
4030  {
4031  scrn_sz(0) = 0;
4032  scrn_sz(1) = 0;
4033  scrn_sz(2) /= dpi;
4034  scrn_sz(3) /= dpi;
4035  }
4036  else if (xunits == "centimeters")
4037  {
4038  scrn_sz(0) = 0;
4039  scrn_sz(1) = 0;
4040  scrn_sz(2) *= 2.54 / dpi;
4041  scrn_sz(3) *= 2.54 / dpi;
4042  }
4043  else if (xunits == "points")
4044  {
4045  scrn_sz(0) = 0;
4046  scrn_sz(1) = 0;
4047  scrn_sz(2) *= 72 / dpi;
4048  scrn_sz(3) *= 72 / dpi;
4049  }
4050  else if (xunits == "characters")
4051  {
4052  scrn_sz(0) = 0;
4053  scrn_sz(1) = 0;
4054  // FIXME: this assumes the system font is Helvetica 10pt
4055  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
4056  scrn_sz(2) *= 74.951 / 12.0 / dpi;
4057  scrn_sz(3) *= 74.951 / 12.0 / dpi;
4058  }
4059 
4060  set_screensize (scrn_sz);
4061 }
4062 
4063 Matrix
4065 {
4066  Matrix screen_size = screen_size_pixels ();
4067  Matrix pos = Matrix (1, 4, 0.0);
4068 
4069  pos(2) = screen_size(0);
4070  pos(3) = screen_size(1);
4071 
4072  return pos;
4073 }
4074 
4075 /*
4076 %!test
4077 %! old_units = get (0, "units");
4078 %! unwind_protect
4079 %! set (0, "units", "pixels");
4080 %! sz = get (0, "screensize") - [1, 1, 0, 0];
4081 %! dpi = get (0, "screenpixelsperinch");
4082 %! set (0, "units", "inches");
4083 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
4084 %! set (0, "units", "centimeters");
4085 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
4086 %! set (0, "units", "points");
4087 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
4088 %! set (0, "units", "normalized");
4089 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
4090 %! set (0, "units", "pixels");
4091 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
4092 %! set (0, "units", "characters");
4093 %! assert (get (0, "screensize"), sz / dpi * (74.951 / 12.0), 0.5 / dpi * (74.951 / 12.0));
4094 %! unwind_protect_cleanup
4095 %! set (0, "units", old_units);
4096 %! end_unwind_protect
4097 */
4098 
4099 void
4101 {
4102  gh_manager& gh_mgr
4103  = octave::__get_gh_manager__ ("root_figure::properties::remove_child");
4104 
4105  gh_mgr.pop_figure (h);
4106 
4107  graphics_handle cf = gh_mgr.current_figure ();
4108 
4109  xset (0, "currentfigure", cf.value ());
4110 
4112 }
4113 
4114 void
4116 {
4117  // empty list of local defaults
4119 
4123 }
4124 
4125 // ---------------------------------------------------------------------
4126 
4127 void
4128 figure::properties::set_currentaxes (const octave_value& val)
4129 {
4130  graphics_handle hax (val);
4131 
4132  if (octave::math::isnan (hax.value ()) || ishghandle (hax))
4133  currentaxes = hax;
4134  else
4135  err_set_invalid ("currentaxes");
4136 }
4137 
4138 void
4140 {
4141  base_properties::remove_child (h, from_root);
4142 
4143  if (h == currentaxes.handle_value ())
4144  {
4145  graphics_handle new_currentaxes;
4146 
4147  Matrix kids = get_children ();
4148 
4149  gh_manager& gh_mgr
4150  = octave::__get_gh_manager__ ("root_figure::properties::remove_child");
4151 
4152  for (octave_idx_type i = 0; i < kids.numel (); i++)
4153  {
4154  graphics_handle kid = kids(i);
4155 
4156  graphics_object go = gh_mgr.get_object (kid);
4157 
4158  if (go.isa ("axes"))
4159  {
4160  new_currentaxes = kid;
4161  break;
4162  }
4163  }
4164 
4165  currentaxes = new_currentaxes;
4166  }
4167 }
4168 
4170 figure::properties::get_number (void) const
4171 {
4172  if (integerhandle.is_on ())
4173  return __myhandle__.value ();
4174  else
4175  return Matrix ();
4176 }
4177 
4180 {
4181  return toolkit;
4182 }
4183 
4184 void
4186 {
4187  if (! val.is_string ())
4188  error ("set___graphics_toolkit__ must be a string");
4189 
4190  std::string nm = val.string_value ();
4191 
4192  octave::gtk_manager& gtk_mgr
4193  = octave::__get_gtk_manager__ ("figure::properties::set___graphics_toolkit__");
4194 
4195  octave::graphics_toolkit b = gtk_mgr.find_toolkit (nm);
4196 
4197  if (b.get_name () != nm)
4198  error ("set___graphics_toolkit__: invalid graphics toolkit");
4199 
4200  if (nm != get___graphics_toolkit__ ())
4201  {
4202  set_toolkit (b);
4203  mark_modified ();
4204  }
4205 }
4206 
4207 void
4209 {
4211 
4212  if (! get_currentaxes ().ok ())
4213  {
4214  gh_manager& gh_mgr
4215  = octave::__get_gh_manager__ ("figure::properties::adopt");
4216 
4217  graphics_object go = gh_mgr.get_object (h);
4218 
4219  if (go.type () == "axes")
4220  set_currentaxes (h.as_octave_value ());
4221  }
4222 }
4223 
4224 /*
4225 %!test
4226 %! hf1 = figure ("visible", "off");
4227 %! ax1 = subplot (1,2,1);
4228 %! ax2 = subplot (1,2,2);
4229 %! hf2 = figure ("visible", "off");
4230 %! unwind_protect
4231 %! set (ax2, "parent", hf2);
4232 %! assert (get (hf2, "currentaxes"), ax2);
4233 %! assert (get (hf1, "currentaxes"), ax1);
4234 %! set (ax1, "parent", hf2);
4235 %! assert (get (hf2, "currentaxes"), ax2);
4236 %! unwind_protect_cleanup
4237 %! close (hf1);
4238 %! close (hf2);
4239 %! end_unwind_protect
4240 */
4241 
4242 void
4244 {
4245  std::string sval = val.string_value ();
4246 
4247  if (sval == "on")
4248  xset (0, "currentfigure", __myhandle__.value ());
4249 
4250  visible = val;
4251 }
4252 
4253 Matrix
4254 figure::properties::get_boundingbox (bool internal, const Matrix&) const
4255 {
4256  Matrix screen_size = screen_size_pixels ();
4257  Matrix pos = (internal ?
4258  get_position ().matrix_value () :
4259  get_outerposition ().matrix_value ());
4260 
4261  pos = convert_position (pos, get_units (), "pixels", screen_size);
4262 
4263  pos(0)--;
4264  pos(1)--;
4265  pos(1) = screen_size(1) - pos(1) - pos(3);
4266 
4267  return pos;
4268 }
4269 
4270 Matrix
4272 {
4273  Matrix screen_size = screen_size_pixels ();
4274  Matrix pos = bb;
4275 
4276  pos(1) = screen_size(1) - pos(1) - pos(3);
4277  pos(1)++;
4278  pos(0)++;
4279  pos = convert_position (pos, "pixels", get_units (), screen_size);
4280  return pos;
4281 }
4282 
4283 void
4285  bool do_notify_toolkit)
4286 {
4287  Matrix screen_size = screen_size_pixels ();
4288  Matrix pos = bbox2position (bb);
4289 
4290  if (internal)
4291  set_position (pos, do_notify_toolkit);
4292  else
4293  set_outerposition (pos, do_notify_toolkit);
4294 }
4295 
4296 Matrix
4298 {
4299  Matrix bb = get_boundingbox (true);
4300  Matrix pos (1, 2, 0.0);
4301 
4302  pos(0) = x;
4303  pos(1) = y;
4304 
4305  pos(1) = bb(3) - pos(1);
4306  pos(0)++;
4307  pos = convert_position (pos, "pixels", get_units (),
4308  bb.extract_n (0, 2, 1, 2));
4309 
4310  return pos;
4311 }
4312 
4313 Matrix
4315 {
4316  Matrix bb = get_boundingbox (true);
4317  Matrix pos (1, 2, 0.0);
4318 
4319  pos(0) = x;
4320  pos(1) = y;
4321 
4322  pos = convert_position (pos, get_units (), "pixels",
4323  bb.extract_n (0, 2, 1, 2));
4324  pos(0)--;
4325  pos(1) = bb(3) - pos(1);
4326 
4327  return pos;
4328 }
4329 
4330 void
4332  bool do_notify_toolkit)
4333 {
4334  Matrix old_bb, new_bb;
4335  bool modified = false;
4336 
4337  old_bb = get_boundingbox (true);
4338  modified = position.set (v, false, do_notify_toolkit);
4339  new_bb = get_boundingbox (true);
4340 
4341  if (old_bb != new_bb)
4342  {
4343  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
4344  {
4345  gh_manager& gh_mgr
4346  = octave::__get_gh_manager__ ("figure::properties::set_position");
4347 
4348  if (! get_resizefcn ().isempty ())
4349  gh_mgr.post_callback (__myhandle__, "resizefcn");
4350 
4351  if (! get_sizechangedfcn ().isempty ())
4352  gh_mgr.post_callback (__myhandle__, "sizechangedfcn");
4353 
4354  update_boundingbox ();
4355  }
4356  }
4357 
4358  if (modified)
4359  {
4360  position.run_listeners (GCB_POSTSET);
4361  mark_modified ();
4362  }
4363 
4364  if (paperpositionmode.is ("auto"))
4365  paperposition.set (get_auto_paperposition ());
4366 }
4367 
4368 void
4370  bool do_notify_toolkit)
4371 {
4372  if (outerposition.set (v, true, do_notify_toolkit))
4373  mark_modified ();
4374 }
4375 
4376 void
4377 figure::properties::set_paperunits (const octave_value& val)
4378 {
4379  caseless_str punits = val.string_value ();
4380  caseless_str ptype = get_papertype ();
4381 
4382  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4383  error ("set: can't set paperunits to normalized when papertype is custom");
4384 
4385  caseless_str old_paperunits = get_paperunits ();
4386  if (paperunits.set (val, true))
4387  {
4388  update_paperunits (old_paperunits);
4389  mark_modified ();
4390  }
4391 }
4392 
4393 void
4394 figure::properties::set_papertype (const octave_value& val)
4395 {
4396  caseless_str ptype = val.string_value ();
4397  caseless_str punits = get_paperunits ();
4398 
4399  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4400  error ("set: can't set paperunits to normalized when papertype is custom");
4401 
4402  if (papertype.set (val, true))
4403  {
4404  update_papertype ();
4405  mark_modified ();
4406  }
4407 }
4408 
4409 static Matrix
4410 papersize_from_type (const caseless_str punits, const caseless_str ptype)
4411 {
4412  Matrix retval (1, 2, 1.0);
4413 
4414  if (! punits.compare ("normalized"))
4415  {
4416  double in2units;
4417  double mm2units;
4418 
4419  if (punits.compare ("inches"))
4420  {
4421  in2units = 1.0;
4422  mm2units = 1 / 25.4;
4423  }
4424  else if (punits.compare ("centimeters"))
4425  {
4426  in2units = 2.54;
4427  mm2units = 1 / 10.0;
4428  }
4429  else // points
4430  {
4431  in2units = 72.0;
4432  mm2units = 72.0 / 25.4;
4433  }
4434 
4435  if (ptype.compare ("usletter"))
4436  {
4437  retval(0) = 8.5 * in2units;
4438  retval(1) = 11.0 * in2units;
4439  }
4440  else if (ptype.compare ("uslegal"))
4441  {
4442  retval(0) = 8.5 * in2units;
4443  retval(1) = 14.0 * in2units;
4444  }
4445  else if (ptype.compare ("tabloid"))
4446  {
4447  retval(0) = 11.0 * in2units;
4448  retval(1) = 17.0 * in2units;
4449  }
4450  else if (ptype.compare ("a0"))
4451  {
4452  retval(0) = 841.0 * mm2units;
4453  retval(1) = 1189.0 * mm2units;
4454  }
4455  else if (ptype.compare ("a1"))
4456  {
4457  retval(0) = 594.0 * mm2units;
4458  retval(1) = 841.0 * mm2units;
4459  }
4460  else if (ptype.compare ("a2"))
4461  {
4462  retval(0) = 420.0 * mm2units;
4463  retval(1) = 594.0 * mm2units;
4464  }
4465  else if (ptype.compare ("a3"))
4466  {
4467  retval(0) = 297.0 * mm2units;
4468  retval(1) = 420.0 * mm2units;
4469  }
4470  else if (ptype.compare ("a4"))
4471  {
4472  retval(0) = 210.0 * mm2units;
4473  retval(1) = 297.0 * mm2units;
4474  }
4475  else if (ptype.compare ("a5"))
4476  {
4477  retval(0) = 148.0 * mm2units;
4478  retval(1) = 210.0 * mm2units;
4479  }
4480  else if (ptype.compare ("b0"))
4481  {
4482  retval(0) = 1029.0 * mm2units;
4483  retval(1) = 1456.0 * mm2units;
4484  }
4485  else if (ptype.compare ("b1"))
4486  {
4487  retval(0) = 728.0 * mm2units;
4488  retval(1) = 1028.0 * mm2units;
4489  }
4490  else if (ptype.compare ("b2"))
4491  {
4492  retval(0) = 514.0 * mm2units;
4493  retval(1) = 728.0 * mm2units;
4494  }
4495  else if (ptype.compare ("b3"))
4496  {
4497  retval(0) = 364.0 * mm2units;
4498  retval(1) = 514.0 * mm2units;
4499  }
4500  else if (ptype.compare ("b4"))
4501  {
4502  retval(0) = 257.0 * mm2units;
4503  retval(1) = 364.0 * mm2units;
4504  }
4505  else if (ptype.compare ("b5"))
4506  {
4507  retval(0) = 182.0 * mm2units;
4508  retval(1) = 257.0 * mm2units;
4509  }
4510  else if (ptype.compare ("arch-a"))
4511  {
4512  retval(0) = 9.0 * in2units;
4513  retval(1) = 12.0 * in2units;
4514  }
4515  else if (ptype.compare ("arch-b"))
4516  {
4517  retval(0) = 12.0 * in2units;
4518  retval(1) = 18.0 * in2units;
4519  }
4520  else if (ptype.compare ("arch-c"))
4521  {
4522  retval(0) = 18.0 * in2units;
4523  retval(1) = 24.0 * in2units;
4524  }
4525  else if (ptype.compare ("arch-d"))
4526  {
4527  retval(0) = 24.0 * in2units;
4528  retval(1) = 36.0 * in2units;
4529  }
4530  else if (ptype.compare ("arch-e"))
4531  {
4532  retval(0) = 36.0 * in2units;
4533  retval(1) = 48.0 * in2units;
4534  }
4535  else if (ptype.compare ("a"))
4536  {
4537  retval(0) = 8.5 * in2units;
4538  retval(1) = 11.0 * in2units;
4539  }
4540  else if (ptype.compare ("b"))
4541  {
4542  retval(0) = 11.0 * in2units;
4543  retval(1) = 17.0 * in2units;
4544  }
4545  else if (ptype.compare ("c"))
4546  {
4547  retval(0) = 17.0 * in2units;
4548  retval(1) = 22.0 * in2units;
4549  }
4550  else if (ptype.compare ("d"))
4551  {
4552  retval(0) = 22.0 * in2units;
4553  retval(1) = 34.0 * in2units;
4554  }
4555  else if (ptype.compare ("e"))
4556  {
4557  retval(0) = 34.0 * in2units;
4558  retval(1) = 43.0 * in2units;
4559  }
4560  }
4561 
4562  return retval;
4563 }
4564 
4565 Matrix
4566 figure::properties::get_auto_paperposition (void)
4567 {
4568  Matrix pos = get_position ().matrix_value ();
4569  Matrix sz;
4570 
4571  caseless_str funits = get_units ();
4572  caseless_str punits = get_paperunits ();
4573 
4574  // Convert position from figure units to paperunits
4575  if (funits == "normalized" || punits == "normalized")
4576  {
4577  sz = screen_size_pixels ();
4578  pos = convert_position (pos, funits, "inches", sz);
4579 
4580  if (punits == "normalized")
4581  sz = papersize_from_type ("points", get_papertype ());
4582 
4583  pos = convert_position (pos, "inches", punits, sz);
4584  }
4585  else
4586  pos = convert_position (pos, funits, punits, sz);
4587 
4588  // Center the figure on the page
4589  sz = get_papersize ().matrix_value ();
4590 
4591  pos(0) = sz(0)/2 - pos(2)/2;
4592  pos(1) = sz(1)/2 - pos(3)/2;
4593 
4594  return pos;
4595 }
4596 
4597 /*
4598 %!test
4599 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4600 %! in_pos = [0 0 4 5];
4601 %! tol = 20 * eps ();
4602 %! unwind_protect
4603 %! ## paperpositionmode "auto" converts figure size to paper units
4604 %! set (hf, "units", "inches");
4605 %! set (hf, "position", in_pos);
4606 %! set (hf, "paperunits", "centimeters");
4607 %! psz = get (hf, "papersize");
4608 %! fsz = in_pos(3:4) * 2.54;
4609 %! pos = [(psz/2 .- fsz/2) fsz];
4610 %! set (hf, "paperpositionmode", "auto");
4611 %! assert (get (hf, "paperposition"), pos, tol);
4612 %! unwind_protect_cleanup
4613 %! close (hf);
4614 %! end_unwind_protect
4615 
4616 %!test
4617 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4618 %! in_pos = [0 0 4 5];
4619 %! tol = 20 * eps ();
4620 %! unwind_protect
4621 %! ## likewise with normalized units
4622 %! set (hf, "units", "inches");
4623 %! set (hf, "position", in_pos);
4624 %! psz = get (hf, "papersize");
4625 %! set (hf, "paperunits", "normalized");
4626 %! fsz = in_pos(3:4) ./ psz;
4627 %! pos = [([0.5 0.5] .- fsz/2) fsz];
4628 %! assert (get (hf, "paperposition"), pos, tol);
4629 %! unwind_protect_cleanup
4630 %! close (hf);
4631 %! end_unwind_protect
4632 
4633 %!test
4634 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4635 %! in_pos = [0 0 4 5];
4636 %! tol = 20 * eps ();
4637 %! unwind_protect
4638 %! ## changing papertype updates paperposition
4639 %! set (hf, "units", "inches");
4640 %! set (hf, "position", in_pos);
4641 %! set (hf, "papertype", "a4");
4642 %! psz = get (hf, "papersize");
4643 %! fsz = in_pos(3:4);
4644 %! pos = [(psz/2 .- fsz/2) fsz];
4645 %! assert (get (hf, "paperposition"), pos, tol);
4646 %! unwind_protect_cleanup
4647 %! close (hf);
4648 %! end_unwind_protect
4649 
4650 %!test
4651 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4652 %! in_pos = [0 0 4 5];
4653 %! tol = 20 * eps ();
4654 %! unwind_protect
4655 %! ## lanscape updates paperposition
4656 %! set (hf, "units", "inches");
4657 %! set (hf, "position", in_pos);
4658 %! set (hf, "paperorientation", "landscape");
4659 %! psz = get (hf, "papersize");
4660 %! fsz = in_pos(3:4);
4661 %! pos = [(psz/2 .- fsz/2) fsz];
4662 %! assert (get (hf, "paperposition"), pos, tol);
4663 %! unwind_protect_cleanup
4664 %! close (hf);
4665 %! end_unwind_protect
4666 
4667 %!test
4668 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4669 %! in_pos = [0 0 4 5];
4670 %! unwind_protect
4671 %! ## back to manual mode
4672 %! set (hf, "paperposition", in_pos * 1.1);
4673 %! assert (get (hf, "paperpositionmode"), "manual");
4674 %! assert (get (hf, "paperposition"), in_pos * 1.1);
4675 %! unwind_protect_cleanup
4676 %! close (hf);
4677 %! end_unwind_protect
4678 */
4679 
4680 void
4682 {
4683  Matrix pos = get_paperposition ().matrix_value ();
4684  Matrix sz = get_papersize ().matrix_value ();
4685 
4686  pos(0) /= sz(0);
4687  pos(1) /= sz(1);
4688  pos(2) /= sz(0);
4689  pos(3) /= sz(1);
4690 
4691  std::string porient = get_paperorientation ();
4692  caseless_str punits = get_paperunits ();
4693  caseless_str ptype = get_papertype ();
4694 
4695  if (ptype.compare ("<custom>"))
4696  {
4697  if (old_paperunits.compare ("centimeters"))
4698  {
4699  sz(0) /= 2.54;
4700  sz(1) /= 2.54;
4701  }
4702  else if (old_paperunits.compare ("points"))
4703  {
4704  sz(0) /= 72.0;
4705  sz(1) /= 72.0;
4706  }
4707 
4708  if (punits.compare ("centimeters"))
4709  {
4710  sz(0) *= 2.54;
4711  sz(1) *= 2.54;
4712  }
4713  else if (punits.compare ("points"))
4714  {
4715  sz(0) *= 72.0;
4716  sz(1) *= 72.0;
4717  }
4718  }
4719  else
4720  {
4721  sz = papersize_from_type (punits, ptype);
4722  if (porient == "landscape")
4723  std::swap (sz(0), sz(1));
4724  }
4725 
4726  pos(0) *= sz(0);
4727  pos(1) *= sz(1);
4728  pos(2) *= sz(0);
4729  pos(3) *= sz(1);
4730 
4731  papersize.set (octave_value (sz));
4732  paperposition.set (octave_value (pos));
4733 }
4734 
4735 void
4736 figure::properties::update_papertype (void)
4737 {
4738  std::string typ = get_papertype ();
4739  if (typ != "<custom>")
4740  {
4741  Matrix sz = papersize_from_type (get_paperunits (), typ);
4742  if (get_paperorientation () == "landscape")
4743  std::swap (sz(0), sz(1));
4744  // Call papersize.set rather than set_papersize to avoid loops
4745  // between update_papersize and update_papertype.
4746  papersize.set (octave_value (sz));
4747  }
4748 
4749  if (paperpositionmode.is ("auto"))
4750  paperposition.set (get_auto_paperposition ());
4751 }
4752 
4753 void
4754 figure::properties::update_papersize (void)
4755 {
4756  Matrix sz = get_papersize ().matrix_value ();
4757  if (sz(0) > sz(1))
4758  {
4759  std::swap (sz(0), sz(1));
4760  papersize.set (octave_value (sz));
4761  paperorientation.set (octave_value ("landscape"));
4762  }
4763  else
4764  {
4765  paperorientation.set ("portrait");
4766  }
4767 
4768  std::string punits = get_paperunits ();
4769  if (punits == "centimeters")
4770  {
4771  sz(0) /= 2.54;
4772  sz(1) /= 2.54;
4773  }
4774  else if (punits == "points")
4775  {
4776  sz(0) /= 72.0;
4777  sz(1) /= 72.0;
4778  }
4779  if (punits == "normalized")
4780  {
4781  if (get_papertype () == "<custom>")
4782  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4783  }
4784  else
4785  {
4786  // FIXME: The papersizes info is also in papersize_from_type().
4787  // Both should be rewritten to avoid the duplication.
4788  // Don't Repeat Yourself (DRY) principle.
4789  std::string ptype = "<custom>";
4790  const double mm2in = 1.0 / 25.4;
4791  const double tol = 0.01;
4792 
4793  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4794  ptype = "usletter";
4795  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4796  ptype = "uslegal";
4797  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4798  ptype = "tabloid";
4799  else if (std::abs (sz(0) - 841.0 * mm2in)
4800  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4801  ptype = "a0";
4802  else if (std::abs (sz(0) - 594.0 * mm2in)
4803  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4804  ptype = "a1";
4805  else if (std::abs (sz(0) - 420.0 * mm2in)
4806  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4807  ptype = "a2";
4808  else if (std::abs (sz(0) - 297.0 * mm2in)
4809  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4810  ptype = "a3";
4811  else if (std::abs (sz(0) - 210.0 * mm2in)
4812  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4813  ptype = "a4";
4814  else if (std::abs (sz(0) - 148.0 * mm2in)
4815  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4816  ptype = "a5";
4817  else if (std::abs (sz(0) - 1029.0 * mm2in)
4818  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4819  ptype = "b0";
4820  else if (std::abs (sz(0) - 728.0 * mm2in)
4821  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4822  ptype = "b1";
4823  else if (std::abs (sz(0) - 514.0 * mm2in)
4824  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4825  ptype = "b2";
4826  else if (std::abs (sz(0) - 364.0 * mm2in)
4827  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4828  ptype = "b3";
4829  else if (std::abs (sz(0) - 257.0 * mm2in)
4830  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4831  ptype = "b4";
4832  else if (std::abs (sz(0) - 182.0 * mm2in)
4833  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4834  ptype = "b5";
4835  else if (std::abs (sz(0) - 9.0)
4836  + std::abs (sz(1) - 12.0) < tol)
4837  ptype = "arch-a";
4838  else if (std::abs (sz(0) - 12.0)
4839  + std::abs (sz(1) - 18.0) < tol)
4840  ptype = "arch-b";
4841  else if (std::abs (sz(0) - 18.0)
4842  + std::abs (sz(1) - 24.0) < tol)
4843  ptype = "arch-c";
4844  else if (std::abs (sz(0) - 24.0)
4845  + std::abs (sz(1) - 36.0) < tol)
4846  ptype = "arch-d";
4847  else if (std::abs (sz(0) - 36.0)
4848  + std::abs (sz(1) - 48.0) < tol)
4849  ptype = "arch-e";
4850  else if (std::abs (sz(0) - 8.5)
4851  + std::abs (sz(1) - 11.0) < tol)
4852  ptype = "a";
4853  else if (std::abs (sz(0) - 11.0)
4854  + std::abs (sz(1) - 17.0) < tol)
4855  ptype = "b";
4856  else if (std::abs (sz(0) - 17.0)
4857  + std::abs (sz(1) - 22.0) < tol)
4858  ptype = "c";
4859  else if (std::abs (sz(0) - 22.0)
4860  + std::abs (sz(1) - 34.0) < tol)
4861  ptype = "d";
4862  else if (std::abs (sz(0) - 34.0)
4863  + std::abs (sz(1) - 43.0) < tol)
4864  ptype = "e";
4865  // Call papertype.set rather than set_papertype to avoid loops between
4866  // update_papersize and update_papertype
4867  papertype.set (ptype);
4868  }
4869  if (punits == "centimeters")
4870  {
4871  sz(0) *= 2.54;
4872  sz(1) *= 2.54;
4873  }
4874  else if (punits == "points")
4875  {
4876  sz(0) *= 72.0;
4877  sz(1) *= 72.0;
4878  }
4879  if (get_paperorientation () == "landscape")
4880  {
4881  std::swap (sz(0), sz(1));
4882  papersize.set (octave_value (sz));
4883  }
4884 
4885  if (paperpositionmode.is ("auto"))
4886  paperposition.set (get_auto_paperposition ());
4887 }
4888 
4889 /*
4890 %!test
4891 %! hf = figure ("visible", "off");
4892 %! unwind_protect
4893 %! set (hf, "paperunits", "inches");
4894 %! set (hf, "papersize", [5, 4]);
4895 %! set (hf, "paperunits", "points");
4896 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4897 %! papersize = get (hf, "papersize");
4898 %! set (hf, "papersize", papersize + 1);
4899 %! set (hf, "papersize", papersize);
4900 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4901 %! unwind_protect_cleanup
4902 %! close (hf);
4903 %! end_unwind_protect
4904 
4905 %!test
4906 %! hf = figure ("visible", "off");
4907 %! unwind_protect
4908 %! set (hf, "paperunits", "inches");
4909 %! set (hf, "papersize", [5, 4]);
4910 %! set (hf, "paperunits", "centimeters");
4911 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4912 %! papersize = get (hf, "papersize");
4913 %! set (hf, "papersize", papersize + 1);
4914 %! set (hf, "papersize", papersize);
4915 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4916 %! unwind_protect_cleanup
4917 %! close (hf);
4918 %! end_unwind_protect
4919 */
4920 
4921 void
4922 figure::properties::update_paperorientation (void)
4923 {
4924  std::string porient = get_paperorientation ();
4925  Matrix sz = get_papersize ().matrix_value ();
4926  if ((sz(0) > sz(1) && porient == "portrait")
4927  || (sz(0) < sz(1) && porient == "landscape"))
4928  {
4929  std::swap (sz(0), sz(1));
4930  // Call papertype.set rather than set_papertype to avoid loops
4931  // between update_papersize and update_papertype
4932  papersize.set (octave_value (sz));
4933  }
4934 
4935  if (paperpositionmode.is ("auto"))
4936  paperposition.set (get_auto_paperposition ());
4937 }
4938 
4939 /*
4940 %!test
4941 %! hf = figure ("visible", "off");
4942 %! unwind_protect
4943 %! tol = 100 * eps ();
4944 %! ## UPPER case and MiXed case is part of test and should not be changed.
4945 %! set (hf, "paperorientation", "PORTRAIT");
4946 %! set (hf, "paperunits", "inches");
4947 %! set (hf, "papertype", "USletter");
4948 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4949 %! set (hf, "paperorientation", "Landscape");
4950 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4951 %! set (hf, "paperunits", "centimeters");
4952 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4953 %! set (hf, "papertype", "a4");
4954 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4955 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4956 %! assert (get (hf, "papertype"), "usletter");
4957 %! assert (get (hf, "paperorientation"), "portrait");
4958 %! set (hf, "papersize", [11.0, 8.5]);
4959 %! assert (get (hf, "papertype"), "usletter");
4960 %! assert (get (hf, "paperorientation"), "landscape");
4961 %! unwind_protect_cleanup
4962 %! close (hf);
4963 %! end_unwind_protect
4964 */
4965 
4966 void
4967 figure::properties::set_units (const octave_value& val)
4968 {
4969  caseless_str old_units = get_units ();
4970 
4971  if (units.set (val, true))
4972  {
4973  update_units (old_units);
4974  mark_modified ();
4975  }
4976 }
4977 
4978 void
4980 {
4981  position.set (convert_position (get_position ().matrix_value (), old_units,
4982  get_units (), screen_size_pixels ()), false);
4983 }
4984 
4985 /*
4986 %!test
4987 %! hf = figure ("visible", "off");
4988 %! old_units = get (0, "units");
4989 %! unwind_protect
4990 %! set (0, "units", "pixels");
4991 %! rsz = get (0, "screensize");
4992 %! set (gcf (), "units", "pixels");
4993 %! fsz = get (gcf (), "position");
4994 %! set (gcf (), "units", "normalized");
4995 %! pos = get (gcf (), "position");
4996 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
4997 %! unwind_protect_cleanup
4998 %! close (hf);
4999 %! set (0, "units", old_units);
5000 %! end_unwind_protect
5001 */
5002 
5003 std::string
5005 {
5006  std::string title;
5007  if (! get_number ().isempty () && is_numbertitle ())
5008  {
5009  std::ostringstream os;
5010  std::string nm = get_name ();
5011 
5012  os << "Figure " << __myhandle__.value ();
5013  if (! nm.empty ())
5014  os << ": " << get_name ();
5015 
5016  title = os.str ();
5017  }
5018  else
5019  title = get_name ();
5020 
5021  // Qt will use QCoreApplication name (set in main-window.cc)
5022  // if the name is empty, so force blank.
5023  if (title.empty ())
5024  title = " ";
5025 
5026  return title;
5027 }
5028 
5031 {
5033 
5034  if (retval.is_undefined ())
5035  {
5036  graphics_handle parent_h = get_parent ();
5037 
5038  gh_manager& gh_mgr = octave::__get_gh_manager__ ("figure::get_default");
5039 
5040  graphics_object parent_go = gh_mgr.get_object (parent_h);
5041 
5042  retval = parent_go.get_default (name);
5043  }
5044 
5045  return retval;
5046 }
5047 
5048 void
5050 {
5051  // empty list of local defaults
5054 
5055  plist.erase ("units");
5056  plist.erase ("position");
5057  plist.erase ("outerposition");
5058  plist.erase ("paperunits");
5059  plist.erase ("paperposition");
5060  plist.erase ("windowstyle");
5061 
5064 }
5065 
5066 // ---------------------------------------------------------------------
5067 
5068 void
5069 axes::properties::init (void)
5070 {
5071  position.add_constraint (dim_vector (1, 4));
5072  outerposition.add_constraint (dim_vector (1, 4));
5073  tightinset.add_constraint (dim_vector (1, 4));
5074  looseinset.add_constraint (dim_vector (1, 4));
5075  colororder.add_constraint (dim_vector (-1, 3));
5076  dataaspectratio.add_constraint (3);
5077  dataaspectratio.add_constraint ("min", 0, false);
5078  dataaspectratio.add_constraint (FINITE);
5079  plotboxaspectratio.add_constraint (3);
5080  plotboxaspectratio.add_constraint ("min", 0, false);
5081  plotboxaspectratio.add_constraint (FINITE);
5082  // FIXME: Should these use dimension vectors? Currently can set 'xlim' to
5083  // any matrix size, but only first two elements are used.
5084  alim.add_constraint (2);
5085  alim.add_constraint (NOT_NAN);
5086  clim.add_constraint (2);
5087  clim.add_constraint (NOT_NAN);
5088  xlim.add_constraint (2);
5089  xlim.add_constraint (NOT_NAN);
5090  ylim.add_constraint (2);
5091  ylim.add_constraint (NOT_NAN);
5092  zlim.add_constraint (2);
5093  zlim.add_constraint (NOT_NAN);
5094  xtick.add_constraint (dim_vector (1, -1));
5095  xtick.add_constraint (FINITE);
5096  ytick.add_constraint (dim_vector (1, -1));
5097  ytick.add_constraint (FINITE);
5098  ztick.add_constraint (dim_vector (1, -1));
5099  ztick.add_constraint (FINITE);
5100  ticklength.add_constraint (dim_vector (1, 2));
5101  Matrix vw (1, 2, 0);
5102  vw(1) = 90;
5103  view = vw;
5104  view.add_constraint (dim_vector (1, 2));
5105  cameraposition.add_constraint (3);
5106  cameraposition.add_constraint (FINITE);
5107  cameratarget.add_constraint (3);
5108  cameratarget.add_constraint (FINITE);
5109  Matrix upv (1, 3, 0.0);
5110  upv(2) = 1.0;
5111  cameraupvector = upv;
5112  cameraupvector.add_constraint (3);
5113  cameraupvector.add_constraint (FINITE);
5114  cameraviewangle.add_constraint (FINITE);
5115  currentpoint.add_constraint (dim_vector (2, 3));
5116 
5117  // Range constraints for double properties
5118  fontsize.add_constraint ("min", 0.0, false);
5119  gridalpha.add_constraint ("min", 0.0, true);
5120  gridalpha.add_constraint ("max", 1.0, true);
5121  labelfontsizemultiplier.add_constraint ("min", 0.0, false);
5122  linewidth.add_constraint ("min", 0.0, false);
5123  minorgridalpha.add_constraint ("min", 0.0, true);
5124  minorgridalpha.add_constraint ("max", 1.0, true);
5125  titlefontsizemultiplier.add_constraint ("min", 0.0, false);
5126 
5127  // No constraints for hidden transform properties
5128  update_font ();
5129 
5130  x_zlim.resize (1, 2);
5131 
5132  sx = "linear";
5133  sy = "linear";
5134  sz = "linear";
5135 
5136  calc_ticklabels (xtick, xticklabel, xscale.is ("log"),
5137  xaxislocation_is ("origin"),
5138  yscale.is ("log") ? 2 : (yaxislocation_is ("origin") ? 0 :
5139  (yaxislocation_is ("left") ? -1 : 1)), xlim);
5140  calc_ticklabels (ytick, yticklabel, yscale.is ("log"),
5141  yaxislocation_is ("origin"),
5142  xscale.is ("log") ? 2 : (xaxislocation_is ("origin") ? 0 :
5143  (xaxislocation_is ("bottom") ? -1 : 1)), ylim);
5144  calc_ticklabels (ztick, zticklabel, zscale.is ("log"), false, 2, zlim);
5145 
5146  xset (xlabel.handle_value (), "handlevisibility", "off");
5147  xset (ylabel.handle_value (), "handlevisibility", "off");
5148  xset (zlabel.handle_value (), "handlevisibility", "off");
5149  xset (title.handle_value (), "handlevisibility", "off");
5150 
5151  xset (xlabel.handle_value (), "horizontalalignment", "center");
5152  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5153  xset (ylabel.handle_value (), "horizontalalignment", "center");
5154  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5155  xset (zlabel.handle_value (), "horizontalalignment", "right");
5156  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5157  xset (title.handle_value (), "horizontalalignment", "center");
5158  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5159 
5160  xset (xlabel.handle_value (), "verticalalignment", "top");
5161  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5162  xset (ylabel.handle_value (), "verticalalignment", "bottom");
5163  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5164  xset (title.handle_value (), "verticalalignment", "bottom");
5165  xset (title.handle_value (), "verticalalignmentmode", "auto");
5166 
5167  xset (ylabel.handle_value (), "rotation", 90.0);
5168  xset (ylabel.handle_value (), "rotationmode", "auto");
5169 
5170  xset (zlabel.handle_value (), "visible", "off");
5171 
5172  xset (xlabel.handle_value (), "clipping", "off");
5173  xset (ylabel.handle_value (), "clipping", "off");
5174  xset (zlabel.handle_value (), "clipping", "off");
5175  xset (title.handle_value (), "clipping", "off");
5176 
5177  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5178  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5179  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5180  xset (title.handle_value (), "__autopos_tag__", "title");
5181 
5182  double fs = labelfontsizemultiplier.double_value () *
5183  fontsize.double_value ();
5184  xset (xlabel.handle_value (), "fontsize", octave_value (fs));
5185  xset (ylabel.handle_value (), "fontsize", octave_value (fs));
5186  xset (zlabel.handle_value (), "fontsize", octave_value (fs));
5187  fs = titlefontsizemultiplier.double_value () * fontsize.double_value ();
5188  xset (title.handle_value (), "fontsize", octave_value (fs));
5189  xset (title.handle_value (), "fontweight", titlefontweight.get ());
5190 
5191  adopt (xlabel.handle_value ());
5192  adopt (ylabel.handle_value ());
5193  adopt (zlabel.handle_value ());
5194  adopt (title.handle_value ());
5195 
5196  Matrix tlooseinset = default_axes_position ();
5197  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
5198  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
5199  looseinset = tlooseinset;
5200 }
5201 
5202 /*
5203 ## Test validation of axes double properties range
5204 %!test
5205 %! hf = figure ("visible", "off");
5206 %! unwind_protect
5207 %! hax = axes ("parent", hf);
5208 %! try
5209 %! set (hax, "linewidth", -1);
5210 %! catch
5211 %! err = lasterr ();
5212 %! end_try_catch
5213 %! assert (err, 'set: "linewidth" must be greater than 0');
5214 %! try
5215 %! set (hax, "minorgridalpha", 1.5);
5216 %! catch
5217 %! err = lasterr ();
5218 %! end_try_catch
5219 %! assert (err, 'set: "minorgridalpha" must be less than or equal to 1');
5220 %! unwind_protect_cleanup
5221 %! delete (hf);
5222 %! end_unwind_protect
5223 */
5224 
5225 Matrix
5226 axes::properties::calc_tightbox (const Matrix& init_pos)
5227 {
5228  Matrix pos = init_pos;
5229 
5230  gh_manager& gh_mgr
5231  = octave::__get_gh_manager__ ("axes::properties::calc_tightbox");
5232 
5233  graphics_object go = gh_mgr.get_object (get_parent ());
5234 
5235  Matrix parent_bb = go.get_properties ().get_boundingbox (true);
5236 
5237  // FIXME: The layout should be clean at this stage and we should not have to
5238  // update ticks and labels positions here again. See bug #48718.
5239  update_ticklength ();
5240 
5241  Matrix ext = get_extent (true, true);
5242  ext(1) = parent_bb(3) - ext(1) - ext(3);
5243  ext(0)++;
5244  ext(1)++;
5245  ext = convert_position (ext, "pixels", get_units (),
5246  parent_bb.extract_n (0, 2, 1, 2));
5247  if (ext(0) < pos(0))
5248  {
5249  pos(2) += pos(0)-ext(0);
5250  pos(0) = ext(0);
5251  }
5252  if (ext(0)+ext(2) > pos(0)+pos(2))
5253  pos(2) = ext(0)+ext(2)-pos(0);
5254 
5255  if (ext(1) < pos(1))
5256  {
5257  pos(3) += pos(1)-ext(1);
5258  pos(1) = ext(1);
5259  }
5260  if (ext(1)+ext(3) > pos(1)+pos(3))
5261  pos(3) = ext(1)+ext(3)-pos(1);
5262 
5263  return pos;
5264 }
5265 
5266 void
5268 {
5269  // First part is equivalent to 'update_tightinset ()'
5270  if (activepositionproperty.is ("position"))
5271  update_position ();
5272  else
5273  update_outerposition ();
5274  caseless_str old_units = get_units ();
5275  set_units ("normalized");
5276  Matrix pos = position.get ().matrix_value ();
5277  Matrix outpos = outerposition.get ().matrix_value ();
5278  Matrix tightpos = calc_tightbox (pos);
5279  Matrix tinset (1, 4, 1.0);
5280  tinset(0) = pos(0)-tightpos(0);
5281  tinset(1) = pos(1)-tightpos(1);
5282  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
5283  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
5284  tightinset = tinset;
5285  set_units (old_units);
5286  update_transform ();
5287  if (activepositionproperty.is ("position"))
5288  update_position ();
5289  else
5290  update_outerposition ();
5291 }
5292 
5293 /*
5294 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
5295 %! hf = figure ("visible", "off");
5296 %! graphics_toolkit (hf, "qt");
5297 %! unwind_protect
5298 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
5299 %! hax = findall (gcf (), "type", "axes");
5300 %! positions = cell2mat (get (hax, "position"));
5301 %! outerpositions = cell2mat (get (hax, "outerposition"));
5302 %! looseinsets = cell2mat (get (hax, "looseinset"));
5303 %! tightinsets = cell2mat (get (hax, "tightinset"));
5304 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
5305 %! hax = findall (gcf (), "type", "axes");
5306 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
5307 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
5308 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
5309 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
5310 %! unwind_protect_cleanup
5311 %! close (hf);
5312 %! end_unwind_protect
5313 
5314 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
5315 %! hf = figure ("visible", "off");
5316 %! graphics_toolkit (hf, "qt");
5317 %! fpos = get (hf, "position");
5318 %! unwind_protect
5319 %! plot (rand (3));
5320 %! position = get (gca, "position");
5321 %! outerposition = get (gca, "outerposition");
5322 %! looseinset = get (gca, "looseinset");
5323 %! tightinset = get (gca, "tightinset");
5324 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
5325 %! set (hf, "position", fpos);
5326 %! assert (get (gca, "outerposition"), outerposition, 0.001);
5327 %! assert (get (gca, "position"), position, 0.001);
5328 %! assert (get (gca, "looseinset"), looseinset, 0.001);
5329 %! assert (get (gca, "tightinset"), tightinset, 0.001);
5330 %! unwind_protect_cleanup
5331 %! close (hf);
5332 %! end_unwind_protect
5333 
5334 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
5335 %! hf = figure ("visible", "off");
5336 %! graphics_toolkit (hf, "qt");
5337 %! fpos = get (hf, "position");
5338 %! set (gca, "activepositionproperty", "position");
5339 %! unwind_protect
5340 %! plot (rand (3));
5341 %! position = get (gca, "position");
5342 %! outerposition = get (gca, "outerposition");
5343 %! looseinset = get (gca, "looseinset");
5344 %! tightinset = get (gca, "tightinset");
5345 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
5346 %! set (hf, "position", fpos);
5347 %! assert (get (gca, "position"), position, 0.001);
5348 %! assert (get (gca, "outerposition"), outerposition, 0.001);
5349 %! assert (get (gca, "looseinset"), looseinset, 0.001);
5350 %! assert (get (gca, "tightinset"), tightinset, 0.001);
5351 %! unwind_protect_cleanup
5352 %! close (hf);
5353 %! end_unwind_protect
5354 */
5355 
5356 void
5358  const std::string& who,
5359  const octave_value& v)
5360 {
5361  if (v.is_string ())
5362  {
5363  xset (hp.handle_value (), "string", v);
5364  return;
5365  }
5366 
5367  graphics_handle val;
5368 
5369  gh_manager& gh_mgr
5370  = octave::__get_gh_manager__ ("axes::properties::set_text_child");
5371 
5372  graphics_object go = gh_mgr.get_object (gh_mgr.lookup (v));
5373 
5374  if (go.isa ("text"))
5375  val = ::reparent (v, "set", who, __myhandle__, false);
5376  else
5377  {
5378  std::string cname = v.class_name ();
5379 
5380  error ("set: expecting text graphics object or character string for %s property, found %s",
5381  who.c_str (), cname.c_str ());
5382  }
5383 
5384  xset (val, "handlevisibility", "off");
5385 
5386  gh_mgr.free (hp.handle_value ());
5387 
5388  hp = val;
5389 
5390  adopt (hp.handle_value ());
5391 }
5392 
5393 void
5394 axes::properties::set_xlabel (const octave_value& v)
5395 {
5396  set_text_child (xlabel, "xlabel", v);
5397  xset (xlabel.handle_value (), "positionmode", "auto");
5398  xset (xlabel.handle_value (), "rotationmode", "auto");
5399  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5400  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5401  xset (xlabel.handle_value (), "clipping", "off");
5402  xset (xlabel.handle_value (), "color", get_xcolor ());
5403  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5404  update_xlabel_position ();
5405 }
5406 
5407 void
5408 axes::properties::set_ylabel (const octave_value& v)
5409 {
5410  set_text_child (ylabel, "ylabel", v);
5411  xset (ylabel.handle_value (), "positionmode", "auto");
5412  xset (ylabel.handle_value (), "rotationmode", "auto");
5413  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5414  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5415  xset (ylabel.handle_value (), "clipping", "off");
5416  xset (ylabel.handle_value (), "color", get_ycolor ());
5417  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5418  update_ylabel_position ();
5419 }
5420 
5421 void
5422 axes::properties::set_zlabel (const octave_value& v)
5423 {
5424  set_text_child (zlabel, "zlabel", v);
5425  xset (zlabel.handle_value (), "positionmode", "auto");
5426  xset (zlabel.handle_value (), "rotationmode", "auto");
5427  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5428  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
5429  xset (zlabel.handle_value (), "clipping", "off");
5430  xset (zlabel.handle_value (), "color", get_zcolor ());
5431  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5432  update_zlabel_position ();
5433 }
5434 
5435 void
5436 axes::properties::set_title (const octave_value& v)
5437 {
5438  set_text_child (title, "title", v);
5439  xset (title.handle_value (), "positionmode", "auto");
5440  xset (title.handle_value (), "horizontalalignment", "center");
5441  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5442  xset (title.handle_value (), "verticalalignment", "bottom");
5443  xset (title.handle_value (), "verticalalignmentmode", "auto");
5444  xset (title.handle_value (), "clipping", "off");
5445  xset (title.handle_value (), "__autopos_tag__", "title");
5446  update_title_position ();
5447 }
5448 
5449 void
5451  const std::string& mode)
5452 {
5453  // FIXME: Should this have all properties in it?
5454  // Including ones we do don't implement?
5455 
5456  // FIXME: This function is probably never called without mode == "reset"
5457  // Check that this is the case with an assert statement (1/6/2017).
5458  // If there are reports of problems then figure out what code is
5459  // calling it with the mode set to something else.
5460  assert (mode == "reset");
5461 
5462  Matrix tlim (1, 2, 0.0);
5463  tlim(1) = 1;
5464  alim = tlim;
5465  xlim = tlim;
5466  ylim = tlim;
5467  zlim = tlim;
5468 
5469  alimmode = "auto";
5470  climmode = "auto";
5471  xlimmode = "auto";
5472  ylimmode = "auto";
5473  zlimmode = "auto";
5474 
5475  ambientlightcolor = Matrix (1, 3, 1.0);
5476 
5477  box = "off";
5478  boxstyle = "back";
5479 
5480  // Note: camera properties (not mode) will be set in update_transform
5481  camerapositionmode = "auto";
5482  cameratargetmode = "auto";
5483  cameraupvectormode = "auto";
5484  cameraviewanglemode = "auto";
5485 
5486  Matrix cl (1, 2, 0.0);
5487  cl(1) = 1;
5488  clim = cl;
5489 
5490  clippingstyle = "3dbox";
5491 
5492  color = color_values ("white");
5493  colororder = default_colororder ();
5494  colororderindex = 1.0;
5495 
5496  // Note: dataspectratio (not mode) will be set through update_aspectratios
5497  dataaspectratiomode = "auto";
5498 
5499  fontangle = "normal";
5500  fontname = OCTAVE_DEFAULT_FONTNAME;
5501  fontsize = 10;
5502  fontunits = "points";
5503  fontsmoothing = "on";
5504  fontweight = "normal";
5505 
5506  gridalpha = 0.15;
5507  gridalphamode = "auto";
5508  gridcolor = color_values (0.15, 0.15, 0.15);
5509  gridcolormode = "auto";
5510  gridlinestyle = "-";
5511 
5512  labelfontsizemultiplier = 1.1;
5513 
5514  layer = "bottom";
5515 
5516  linestyleorder = "-";
5517  linestyleorderindex = 1.0;
5518 
5519  linewidth = 0.5;
5520 
5521  minorgridalpha = 0.25;
5522  minorgridalphamode = "auto";
5523  minorgridcolor = color_values (0.1, 0.1, 0.1);
5524  minorgridcolormode = "auto";
5525  minorgridlinestyle = ":";
5526 
5527  nextplot = "replace";
5528 
5529  // Note: plotboxaspectratio will be set through update_aspectratios
5530  plotboxaspectratiomode = "auto";
5531  projection = "orthographic";
5532 
5533  sortmethod = "depth";
5534 
5535  tickdir = "in";
5536  tickdirmode = "auto";
5537  ticklabelinterpreter = "tex";
5538  ticklength = default_axes_ticklength ();
5539 
5540  tightinset = Matrix (1, 4, 0.0);
5541 
5542  titlefontsizemultiplier = 1.1;
5543  titlefontweight = "bold";
5544 
5545  Matrix tview (1, 2, 0.0);
5546  tview(1) = 90;
5547  view = tview;
5548 
5549  xaxislocation = "bottom";
5550 
5551  xcolor = color_values (0.15, 0.15, 0.15);
5552  xcolormode = "auto";
5553  xdir = "normal";
5554  xgrid = "off";
5555  xminorgrid = "off";
5556  xminortick = "off";
5557  xscale = "linear";
5558  xtick = Matrix ();
5559  xticklabel = "";
5560  xticklabelmode = "auto";
5561  xticklabelrotation = 0.0;
5562  xtickmode = "auto";
5563 
5564  yaxislocation = "left";
5565 
5566  ycolor = color_values (0.15, 0.15, 0.15);
5567  ycolormode = "auto";
5568  ydir = "normal";
5569  ygrid = "off";
5570  yminorgrid = "off";
5571  yminortick = "off";
5572  yscale = "linear";
5573  ytick = Matrix ();
5574  yticklabel = "";
5575  yticklabelmode = "auto";
5576  yticklabelrotation = 0.0;
5577  ytickmode = "auto";
5578 
5579  zcolor = color_values (0.15, 0.15, 0.15);
5580  zcolormode = "auto";
5581  zdir = "normal";
5582  zgrid = "off";
5583  zminorgrid = "off";
5584  zminortick = "off";
5585  zscale = "linear";
5586  ztick = Matrix ();
5587  zticklabel = "";
5588  zticklabelmode = "auto";
5589  zticklabelrotation = 0.0;
5590  ztickmode = "auto";
5591 
5592  sx = "linear";
5593  sy = "linear";
5594  sz = "linear";
5595 
5596  visible = "on";
5597 
5598  gh_manager& gh_mgr
5599  = octave::__get_gh_manager__ ("axes::properties::set_defaults");
5600 
5601  graphics_object go = gh_mgr.get_object (xlabel.handle_value ());
5603  go = gh_mgr.get_object (ylabel.handle_value ());
5605  go = gh_mgr.get_object (zlabel.handle_value ());
5607  go = gh_mgr.get_object (title.handle_value ());
5609 
5610  xset (xlabel.handle_value (), "handlevisibility", "off");
5611  xset (ylabel.handle_value (), "handlevisibility", "off");
5612  xset (zlabel.handle_value (), "handlevisibility", "off");
5613  xset (title.handle_value (), "handlevisibility", "off");
5614 
5615  xset (xlabel.handle_value (), "horizontalalignment", "center");
5616  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5617  xset (ylabel.handle_value (), "horizontalalignment", "center");
5618  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5619  xset (zlabel.handle_value (), "horizontalalignment", "right");
5620  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5621  xset (title.handle_value (), "horizontalalignment", "center");
5622  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5623 
5624  xset (xlabel.handle_value (), "verticalalignment", "top");
5625  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5626  xset (ylabel.handle_value (), "verticalalignment", "bottom");
5627  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5628  xset (title.handle_value (), "verticalalignment", "bottom");
5629  xset (title.handle_value (), "verticalalignmentmode", "auto");
5630 
5631  xset (ylabel.handle_value (), "rotation", 90.0);
5632  xset (ylabel.handle_value (), "rotationmode", "auto");
5633 
5634  xset (zlabel.handle_value (), "visible", "off");
5635 
5636  xset (xlabel.handle_value (), "clipping", "off");
5637  xset (ylabel.handle_value (), "clipping", "off");
5638  xset (zlabel.handle_value (), "clipping", "off");
5639  xset (title.handle_value (), "clipping", "off");
5640 
5641  xset (xlabel.handle_value (), "__autopos_tag__", "xlabel");
5642  xset (ylabel.handle_value (), "__autopos_tag__", "ylabel");
5643  xset (zlabel.handle_value (), "__autopos_tag__", "zlabel");
5644  xset (title.handle_value (), "__autopos_tag__", "title");
5645 
5646  double fs;
5647  fs = labelfontsizemultiplier.double_value () * fontsize.double_value ();
5648  xset (xlabel.handle_value (), "fontsize", octave_value (fs));
5649  xset (ylabel.handle_value (), "fontsize", octave_value (fs));
5650  xset (zlabel.handle_value (), "fontsize", octave_value (fs));
5651  fs = titlefontsizemultiplier.double_value () * fontsize.double_value ();
5652  xset (title.handle_value (), "fontsize", octave_value (fs));
5653  xset (title.handle_value (), "fontweight", titlefontweight.get ());
5654 
5655  update_transform ();
5656  sync_positions ();
5657  override_defaults (bgo);
5658 }
5659 
5661 axes::properties::get_colormap (void) const
5662 {
5663  if (__colormap__.get ().isempty ())
5664  {
5665  gh_manager& gh_mgr
5666  = octave::__get_gh_manager__ ("axes::properties::get_colormap");
5667 
5668  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
5669  graphics_object go_f (go.get_ancestor ("figure"));
5670  figure::properties& figure_props
5671  = reinterpret_cast<figure::properties&> (go_f.get_properties ());
5672  return figure_props.get_colormap ();
5673  }
5674 
5675  return get___colormap__ ();
5676 }
5677 
5678 void
5680 {
5681  graphics_handle h = hp.handle_value ();
5682 
5683  gh_manager& gh_mgr
5684  = octave::__get_gh_manager__ ("axes::properties::delete_text_child");
5685 
5686  if (h.ok ())
5687  {
5688  graphics_object go = gh_mgr.get_object (h);
5689 
5690  if (go.valid_object ())
5691  gh_mgr.free (h, from_root);
5692  }
5693 
5694  // FIXME: is it necessary to check whether the axes object is
5695  // being deleted now? I think this function is only called when an
5696  // individual child object is delete and not when the parent axes
5697  // object is deleted.
5698 
5699  if (! is_beingdeleted ())
5700  {
5701  hp = gh_mgr.make_graphics_handle ("text", __myhandle__, false, false);
5702 
5703  xset (hp.handle_value (), "handlevisibility", "off");
5704 
5705  adopt (hp.handle_value ());
5706  }
5707 }
5708 
5709 void
5711 {
5712  gh_manager& gh_mgr
5713  = octave::__get_gh_manager__ ("axes::properties::remove_child");
5714 
5715  graphics_object go = gh_mgr.get_object (h);
5716 
5717  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
5718  {
5719  delete_text_child (xlabel, from_root);
5720  update_xlabel_position ();
5721  }
5722  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
5723  {
5724  delete_text_child (ylabel, from_root);
5725  update_ylabel_position ();
5726  }
5727  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
5728  {
5729  delete_text_child (zlabel, from_root);
5730  update_zlabel_position ();
5731  }
5732  else if (title.handle_value ().ok () && h == title.handle_value ())
5733  {
5734  delete_text_child (title, from_root);
5735  update_title_position ();
5736  }
5737  else if (get_num_lights () > 0 && go.isa ("light")
5738  && go.get_properties ().is_visible ())
5739  decrease_num_lights ();
5740 
5741  if (go.valid_object ())
5742  base_properties::remove_child (h, from_root);
5743 
5744 }
5745 
5746 void
5748 {
5749  gh_manager& gh_mgr = octave::__get_gh_manager__ ("axes::properties::adopt");
5750 
5751  graphics_object go (gh_mgr.get_object (h));
5752 
5753  if (go.isa ("light") && go.get_properties ().is_visible ())
5754  increase_num_lights ();
5755 
5757 
5758  // FIXME: For performance reasons, we would like to call
5759  // update_axis_limits ("xlim", h);
5760  // which updates the limits based ONLY on the new data from h.
5761  // But this isn't working properly at the moment, so we
5762  // call the other form which invokes a full tree traversal of all
5763  // of the axes children.
5764  if (xlimmode_is ("auto"))
5765  update_axis_limits ("xlim");
5766 
5767  if (ylimmode_is ("auto"))
5768  update_axis_limits ("ylim");
5769 
5770  if (zlimmode_is ("auto"))
5771  update_axis_limits ("zlim");
5772 
5773  if (climmode_is ("auto"))
5774  update_axis_limits ("clim");
5775 
5776  if (climmode_is ("auto"))
5777  update_axis_limits ("alim");
5778 }
5779 
5780 inline Matrix
5782 {
5783  Matrix m (4, 4, 0.0);
5784 
5785  for (int i = 0; i < 4; i++)
5786  m(i,i) = 1;
5787 
5788  return m;
5789 }
5790 
5791 inline ColumnVector
5793 {
5794  ColumnVector v (4, 0.0);
5795 
5796  v(3) = 1;
5797 
5798  return v;
5799 }
5800 
5801 inline ColumnVector
5802 xform_vector (double x, double y, double z)
5803 {
5804  ColumnVector v (4, 1.0);
5805 
5806  v(0) = x;
5807  v(1) = y;
5808  v(2) = z;
5809 
5810  return v;
5811 }
5812 
5813 inline ColumnVector
5814 transform (const Matrix& m, double x, double y, double z)
5815 {
5816  return (m * xform_vector (x, y, z));
5817 }
5818 
5819 inline Matrix
5820 xform_scale (double x, double y, double z)
5821 {
5822  Matrix m (4, 4, 0.0);
5823 
5824  m(0,0) = x;
5825  m(1,1) = y;
5826  m(2,2) = z;
5827  m(3,3) = 1;
5828 
5829  return m;
5830 }
5831 
5832 inline Matrix
5833 xform_translate (double x, double y, double z)
5834 {
5835  Matrix m = xform_matrix ();
5836 
5837  m(0,3) = x;
5838  m(1,3) = y;
5839  m(2,3) = z;
5840  m(3,3) = 1;
5841 
5842  return m;
5843 }
5844 
5845 inline void
5846 scale (Matrix& m, double x, double y, double z)
5847 {
5848  m = m * xform_scale (x, y, z);
5849 }
5850 
5851 inline void
5852 translate (Matrix& m, double x, double y, double z)
5853 {
5854  m = m * xform_translate (x, y, z);
5855 }
5856 
5857 inline void
5859 {
5860  v = m * v;
5861 }
5862 
5863 inline void
5864 scale (ColumnVector& v, double x, double y, double z)
5865 {
5866  v(0) *= x;
5867  v(1) *= y;
5868  v(2) *= z;
5869 }
5870 
5871 inline void
5872 translate (ColumnVector& v, double x, double y, double z)
5873 {
5874  v(0) += x;
5875  v(1) += y;
5876  v(2) += z;
5877 }
5878 
5879 inline void
5881 {
5882  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5883  scale (v, fact, fact, fact);
5884 }
5885 
5886 inline double
5887 dot (const ColumnVector& v1, const ColumnVector& v2)
5888 {
5889  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5890 }
5891 
5892 inline double
5893 norm (const ColumnVector& v)
5894 {
5895  return sqrt (dot (v, v));
5896 }
5897 
5898 inline ColumnVector
5899 cross (const ColumnVector& v1, const ColumnVector& v2)
5900 {
5902 
5903  r(0) = v1(1)*v2(2) - v1(2)*v2(1);
5904  r(1) = v1(2)*v2(0) - v1(0)*v2(2);
5905  r(2) = v1(0)*v2(1) - v1(1)*v2(0);
5906 
5907  return r;
5908 }
5909 
5910 inline Matrix
5912 {
5913  static double data[32] =
5914  {
5915  0,0,0,1,
5916  1,0,0,1,
5917  0,1,0,1,
5918  0,0,1,1,
5919  1,1,0,1,
5920  1,0,1,1,
5921  0,1,1,1,
5922  1,1,1,1
5923  };
5924  Matrix m (4, 8);
5925 
5926  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5927 
5928  return m;
5929 }
5930 
5931 inline ColumnVector
5933 {
5934  ColumnVector retval (4, 1.0);
5935 
5936  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
5937 
5938  return retval;
5939 }
5940 
5941 inline RowVector
5943 {
5944  return v.extract_n (0, 3).transpose ();
5945 }
5946 
5947 void
5949 {
5950  double xd = (xdir_is ("normal") ? 1 : -1);
5951  double yd = (ydir_is ("normal") ? 1 : -1);
5952  double zd = (zdir_is ("normal") ? 1 : -1);
5953 
5954  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
5955  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
5956  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
5957 
5958  double xo = xlimits(xd > 0 ? 0 : 1);
5959  double yo = ylimits(yd > 0 ? 0 : 1);
5960  double zo = zlimits(zd > 0 ? 0 : 1);
5961 
5962  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5963 
5964  bool autocam = (camerapositionmode_is ("auto")
5965  && cameratargetmode_is ("auto")
5966  && cameraupvectormode_is ("auto")
5967  && cameraviewanglemode_is ("auto"));
5968  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5969  && plotboxaspectratiomode_is ("auto"));
5970 
5971  ColumnVector c_eye (xform_vector ());
5972  ColumnVector c_center (xform_vector ());
5973  ColumnVector c_upv (xform_vector ());
5974 
5975  if (cameratargetmode_is ("auto"))
5976  {
5977  c_center(0) = (xlimits(0) + xlimits(1)) / 2;
5978  c_center(1) = (ylimits(0) + ylimits(1)) / 2;
5979  c_center(2) = (zlimits(0) + zlimits(1)) / 2;
5980 
5981  cameratarget = xform2cam (c_center);
5982  }
5983  else
5984  c_center = cam2xform (get_cameratarget ().matrix_value ());
5985 
5986  if (camerapositionmode_is ("auto"))
5987  {
5988  Matrix tview = get_view ().matrix_value ();
5989  double az = tview(0);
5990  double el = tview(1);
5991  double d = 5 * sqrt (pb(0)*pb(0) + pb(1)*pb(1) + pb(2)*pb(2));
5992 
5993  if (el == 90 || el == -90)
5994  c_eye(2) = d*octave::math::signum (el);
5995  else
5996  {
5997  az *= M_PI/180.0;
5998  el *= M_PI/180.0;
5999  c_eye(0) = d * cos (el) * sin (az);
6000  c_eye(1) = -d* cos (el) * cos (az);
6001  c_eye(2) = d * sin (el);
6002  }
6003  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
6004  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
6005  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
6006 
6007  cameraposition = xform2cam (c_eye);
6008  }
6009  else
6010  c_eye = cam2xform (get_cameraposition ().matrix_value ());
6011 
6012  if (cameraupvectormode_is ("auto"))
6013  {
6014  Matrix tview = get_view ().matrix_value ();
6015  double az = tview(0);
6016  double el = tview(1);
6017 
6018  if (el == 90 || el == -90)
6019  {
6020  c_upv(0) = -octave::math::signum (el)
6021  * sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
6022  c_upv(1) = octave::math::signum (el)
6023  * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
6024  }
6025  else
6026  c_upv(2) = 1;
6027 
6028  cameraupvector = xform2cam (c_upv);
6029  }
6030  else
6031  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
6032 
6033  Matrix x_view = xform_matrix ();
6034  Matrix x_projection = xform_matrix ();
6035  Matrix x_viewport = xform_matrix ();
6036  Matrix x_normrender;
6037  Matrix x_pre = xform_matrix ();
6038 
6039  x_render = xform_matrix ();
6040  x_render_inv = xform_matrix ();
6041 
6042  scale (x_pre, pb(0), pb(1), pb(2));
6043  translate (x_pre, -0.5, -0.5, -0.5);
6044  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
6045  zd/(zlimits(1)-zlimits(0)));
6046  translate (x_pre, -xo, -yo, -zo);
6047 
6048  xform (c_eye, x_pre);
6049  xform (c_center, x_pre);
6050  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
6051  pb(2)/(zlimits(1)-zlimits(0)));
6052  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
6053 
6054  ColumnVector F (c_center), f (F), UP (c_upv);
6055  normalize (f);
6056  normalize (UP);
6057 
6058  if (std::abs (dot (f, UP)) > 1e-15)
6059  {
6060  double fa = 1 / sqrt (1 - f(2)*f(2));
6061  scale (UP, fa, fa, fa);
6062  }
6063 
6064  ColumnVector s = cross (f, UP);
6065  ColumnVector u = cross (s, f);
6066 
6067  scale (x_view, 1, 1, -1);
6068  Matrix l = xform_matrix ();
6069  l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
6070  l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
6071  l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
6072  x_view = x_view * l;
6073  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
6074  scale (x_view, pb(0), pb(1), pb(2));
6075  translate (x_view, -0.5, -0.5, -0.5);
6076 
6077  Matrix x_cube = x_view * unit_cube ();
6078  ColumnVector cmin = x_cube.row_min ();
6079  ColumnVector cmax = x_cube.row_max ();
6080  double xM = cmax(0) - cmin(0);
6081  double yM = cmax(1) - cmin(1);
6082 
6083  Matrix bb = get_boundingbox (true);
6084 
6085  double v_angle;
6086 
6087  if (cameraviewanglemode_is ("auto"))
6088  {
6089  double af;
6090 
6091  // FIXME: was this really needed? When compared to Matlab, it
6092  // does not seem to be required. Need investigation with concrete
6093  // graphics toolkit to see results visually.
6094  if (false && dowarp)
6095  af = (1.0 / (xM > yM ? xM : yM));
6096  else
6097  {
6098  if ((bb(2)/bb(3)) > (xM/yM))
6099  af = 1.0 / yM;
6100  else
6101  af = 1.0 / xM;
6102  }
6103  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
6104 
6105  cameraviewangle = v_angle;
6106  }
6107  else
6108  v_angle = get_cameraviewangle ();
6109 
6110  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
6111  scale (x_projection, pf, pf, 1);
6112 
6113  if (dowarp)
6114  {
6115  xM *= pf;
6116  yM *= pf;
6117  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
6118  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
6119  }
6120  else
6121  {
6122  double pix = 1;
6123  if (autocam)
6124  {
6125  if ((bb(2)/bb(3)) > (xM/yM))
6126  pix = bb(3);
6127  else
6128  pix = bb(2);
6129  }
6130  else
6131  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
6132  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
6133  scale (x_viewport, pix, -pix, 1);
6134  }
6135 
6136  x_normrender = x_viewport * x_projection * x_view;
6137 
6138  x_cube = x_normrender * unit_cube ();
6139  cmin = x_cube.row_min ();
6140  cmax = x_cube.row_max ();
6141  x_zlim.resize (1, 2);
6142  x_zlim(0) = cmin(2);
6143  x_zlim(1) = cmax(2);
6144 
6145  x_render = x_normrender;
6146  scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
6147  zd/(zlimits(1)-zlimits(0)));
6148  translate (x_render, -xo, -yo, -zo);
6149 
6150  x_render_inv = x_render.inverse ();
6151 
6152  // Note: these matrices are a slight modified version of the regular matrices,
6153  // more suited for OpenGL rendering (x_gl_mat1 => light => x_gl_mat2)
6154  x_gl_mat1 = x_view;
6155  scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
6156  zd/(zlimits(1)-zlimits(0)));
6157  translate (x_gl_mat1, -xo, -yo, -zo);
6158  x_gl_mat2 = x_viewport * x_projection;
6159 }
6160 
6161 static bool updating_axes_layout = false;
6162 
6163 void
6165 {
6167  return;
6168 
6169  graphics_xform xform = get_transform ();
6170 
6171  double xd = (xdir_is ("normal") ? 1 : -1);
6172  double yd = (ydir_is ("normal") ? 1 : -1);
6173  double zd = (zdir_is ("normal") ? 1 : -1);
6174 
6175  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
6176  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
6177  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
6178 
6179  double x_min, x_max, y_min, y_max, z_min, z_max;
6180  x_min = xlims(0), x_max = xlims(1);
6181  y_min = ylims(0), y_max = ylims(1);
6182  z_min = zlims(0), z_max = zlims(1);
6183 
6184  ColumnVector p1, p2, dir (3);
6185 
6186  xstate = ystate = zstate = AXE_ANY_DIR;
6187 
6188  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
6189  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
6190  dir(0) = octave::math::round (p2(0) - p1(0));
6191  dir(1) = octave::math::round (p2(1) - p1(1));
6192  dir(2) = (p2(2) - p1(2));
6193  if (dir(0) == 0 && dir(1) == 0)
6194  xstate = AXE_DEPTH_DIR;
6195  else if (dir(2) == 0)
6196  {
6197  if (dir(0) == 0)
6198  xstate = AXE_VERT_DIR;
6199  else if (dir(1) == 0)
6200  xstate = AXE_HORZ_DIR;
6201  }
6202 
6203  if (dir(2) == 0)
6204  {
6205  if (dir(1) == 0)
6206  xPlane = (dir(0) > 0 ? x_max : x_min);
6207  else
6208  xPlane = (dir(1) < 0 ? x_max : x_min);
6209  }
6210  else
6211  xPlane = (dir(2) < 0 ? x_min : x_max);
6212 
6213  xPlaneN = (xPlane == x_min ? x_max : x_min);
6214  fx = (x_max - x_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
6215 
6216  p1 = xform.transform ((x_min + x_max)/2, y_min, (z_min + z_max)/2, false);
6217  p2 = xform.transform ((x_min + x_max)/2, y_max, (z_min + z_max)/2, false);
6218  dir(0) = octave::math::round (p2(0) - p1(0));
6219  dir(1) = octave::math::round (p2(1) - p1(1));
6220  dir(2) = (p2(2) - p1(2));
6221  if (dir(0) == 0 && dir(1) == 0)
6222  ystate = AXE_DEPTH_DIR;
6223  else if (dir(2) == 0)
6224  {
6225  if (dir(0) == 0)
6226  ystate = AXE_VERT_DIR;
6227  else if (dir(1) == 0)
6228  ystate = AXE_HORZ_DIR;
6229  }
6230 
6231  if (dir(2) == 0)
6232  {
6233  if (dir(1) == 0)
6234  yPlane = (dir(0) > 0 ? y_max : y_min);
6235  else
6236  yPlane = (dir(1) < 0 ? y_max : y_min);
6237  }
6238  else
6239  yPlane = (dir(2) < 0 ? y_min : y_max);
6240 
6241  yPlaneN = (yPlane == y_min ? y_max : y_min);
6242  fy = (y_max - y_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
6243 
6244  p1 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_min, false);
6245  p2 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_max, false);
6246  dir(0) = octave::math::round (p2(0) - p1(0));
6247  dir(1) = octave::math::round (p2(1) - p1(1));
6248  dir(2) = (p2(2) - p1(2));
6249  if (dir(0) == 0 && dir(1) == 0)
6250  zstate = AXE_DEPTH_DIR;
6251  else if (dir(2) == 0)
6252  {
6253  if (dir(0) == 0)
6254  zstate = AXE_VERT_DIR;
6255  else if (dir(1) == 0)
6256  zstate = AXE_HORZ_DIR;
6257  }
6258 
6259  if (dir(2) == 0)
6260  {
6261  if (dir(1) == 0)
6262  zPlane = (dir(0) > 0 ? z_min : z_max);
6263  else
6264  zPlane = (dir(1) < 0 ? z_min : z_max);
6265  }
6266  else
6267  zPlane = (dir(2) < 0 ? z_min : z_max);
6268 
6269  zPlaneN = (zPlane == z_min ? z_max : z_min);
6270  fz = (z_max - z_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
6271 
6272  octave::unwind_protect frame;
6274  updating_axes_layout = true;
6275 
6276  xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
6277  zSign = (zd*(zPlane-zPlaneN) <= 0);
6278  xyzSym = (zSign ? xySym : ! xySym);
6279  xpTick = (zSign ? xPlaneN : xPlane);
6280  ypTick = (zSign ? yPlaneN : yPlane);
6281  zpTick = (zSign ? zPlane : zPlaneN);
6282  xpTickN = (zSign ? xPlane : xPlaneN);
6283  ypTickN = (zSign ? yPlane : yPlaneN);
6284  zpTickN = (zSign ? zPlaneN : zPlane);
6285 
6286  // 2D mode
6287  x2Dtop = false;
6288  y2Dright = false;
6289  layer2Dtop = false;
6290  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
6291  {
6292  Matrix ylimits = get_ylim ().matrix_value ();
6293  if (xaxislocation_is ("top")
6294  || (yscale_is ("log") && xaxislocation_is ("origin")
6295  && (ylimits(1) < 0.)))
6296  {
6297  std::swap (yPlane, yPlaneN);
6298  x2Dtop = true;
6299  }
6300  ypTick = yPlaneN;
6301  ypTickN = yPlane;
6302  Matrix xlimits = get_xlim ().matrix_value ();
6303  if (yaxislocation_is ("right")
6304  || (xscale_is ("log") && yaxislocation_is ("origin")
6305  && (xlimits(1) < 0.)))
6306  {
6307  std::swap (xPlane, xPlaneN);
6308  y2Dright = true;
6309  }
6310  xpTick = xPlaneN;
6311  xpTickN = xPlane;
6312  if (layer_is ("top"))
6313  {
6314  zpTick = zPlaneN;
6315  layer2Dtop = true;
6316  }
6317  else
6318  zpTick = zPlane;
6319  }
6320 
6321  Matrix viewmat = get_view ().matrix_value ();
6322  nearhoriz = std::abs (viewmat(1)) <= 5;
6323  is2D = viewmat(1) == 90;
6324 
6325  update_ticklength ();
6326 }
6327 
6328 void
6329 axes::properties::update_ticklength (void)
6330 {
6331  bool mode2D = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
6332  (ystate > AXE_DEPTH_DIR ? 1 : 0) +
6333  (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
6334 
6335  if (tickdirmode_is ("auto"))
6336  tickdir.set (mode2D ? "in" : "out", true);
6337 
6338  double ticksign = (tickdir_is ("in") ? -1 : 1);
6339 
6340  Matrix bbox = get_boundingbox (true);
6341  Matrix ticklen = get_ticklength ().matrix_value ();
6342  ticklen(0) *= std::max (bbox(2), bbox(3));
6343  // FIXME: This algorithm is not Matlab-compatible. See bug #55483.
6344  // Scale the results of Octave's algorithm for better visuals.
6345  ticklen(1) *= (0.76 * std::max (bbox(2), bbox(3)));
6346 
6347  xticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6348  yticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6349  zticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6350 
6351  double offset = get___fontsize_points__ () / 2;
6352 
6353  xtickoffset = (mode2D ? std::max (0., xticklen) : std::abs (xticklen)) +
6354  (xstate == AXE_HORZ_DIR ? offset*1.5 : offset);
6355  ytickoffset = (mode2D ? std::max (0., yticklen) : std::abs (yticklen)) +
6356  (ystate == AXE_HORZ_DIR ? offset*1.5 : offset);
6357  ztickoffset = (mode2D ? std::max (0., zticklen) : std::abs (zticklen)) +
6358  (zstate == AXE_HORZ_DIR ? offset*1.5 : offset);
6359 
6360  update_xlabel_position ();
6361  update_ylabel_position ();
6362  update_zlabel_position ();
6363  update_title_position ();
6364 }
6365 
6366 /*
6367 ## FIXME: A demo can't be called in a C++ file. This should be made a test
6368 ## or moved to a .m file where it can be called.
6369 %!demo
6370 %! clf;
6371 %! subplot (2,1,1);
6372 %! plot (rand (3));
6373 %! xlabel xlabel;
6374 %! ylabel ylabel;
6375 %! title title;
6376 %! subplot (2,1,2);
6377 %! plot (rand (3));
6378 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
6379 %! xlabel xlabel;
6380 %! ylabel ylabel;
6381 %! title title;
6382 */
6383 
6384 static ColumnVector
6386  const text::properties& props,
6387  const graphics_xform& xform,
6388  const Matrix& bbox)
6389 {
6391 
6392  std::string to_units = props.get_units ();
6393 
6394  if (to_units != "data")
6395  {
6396  ColumnVector v = xform.transform (p(0), p(1), p(2));
6397 
6398  retval.resize (3);
6399 
6400  retval(0) = v(0) - bbox(0) + 1;
6401  retval(1) = bbox(1) + bbox(3) - v(1) + 1;
6402  retval(2) = 0;
6403 
6404  retval = convert_position (retval, "pixels", to_units,
6405  bbox.extract_n (0, 2, 1, 2));
6406  }
6407  else
6408  retval = p;
6409 
6410  return retval;
6411 }
6412 
6413 static bool updating_xlabel_position = false;
6414 
6415 void
6417 {
6419  return;
6420 
6421  gh_manager& gh_mgr
6422  = octave::__get_gh_manager__ ("axes::properties::update_xlabel_position");
6423 
6424  graphics_object go = gh_mgr.get_object (get_xlabel ());
6425 
6426  if (! go.valid_object ())
6427  return;
6428 
6429  text::properties& xlabel_props
6430  = reinterpret_cast<text::properties&> (go.get_properties ());
6431 
6432  bool isempty = xlabel_props.get_string ().isempty ();
6433 
6434  octave::unwind_protect frame;
6436  updating_xlabel_position = true;
6437 
6438  if (! isempty)
6439  {
6440  if (xlabel_props.horizontalalignmentmode_is ("auto"))
6441  {
6442  xlabel_props.set_horizontalalignment
6443  (xstate > AXE_DEPTH_DIR ? "center" : (xyzSym ? "left" : "right"));
6444 
6445  xlabel_props.set_horizontalalignmentmode ("auto");
6446  }
6447 
6448  if (xlabel_props.verticalalignmentmode_is ("auto"))
6449  {
6450  xlabel_props.set_verticalalignment
6451  (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
6452 
6453  xlabel_props.set_verticalalignmentmode ("auto");
6454  }
6455  }
6456 
6457  if (xlabel_props.positionmode_is ("auto")
6458  || xlabel_props.rotationmode_is ("auto"))
6459  {
6460  graphics_xform xform = get_transform ();
6461 
6462  Matrix ext (1, 2, 0.0);
6463  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6464  get_xticklabel ().string_vector_value (),
6465  get_xlim ().matrix_value ());
6466 
6467  double margin = 5;
6468  double wmax = ext(0) + margin;
6469  double hmax = ext(1) + margin;
6470  double angle = 0.0;
6471  ColumnVector p
6472  = graphics_xform::xform_vector ((xpTickN + xpTick)/2, ypTick, zpTick);
6473 
6474  bool tick_along_z = nearhoriz || octave::math::isinf (fy);
6475  if (tick_along_z)
6476  p(2) += (octave::math::signum (zpTick - zpTickN) * fz * xtickoffset);
6477  else
6478  p(1) += (octave::math::signum (ypTick - ypTickN) * fy * xtickoffset);
6479 
6480  p = xform.transform (p(0), p(1), p(2), false);
6481 
6482  switch (xstate)
6483  {
6484  case AXE_ANY_DIR:
6485  p(0) += (xyzSym ? wmax : -wmax);
6486  p(1) += hmax;
6487  break;
6488 
6489  case AXE_VERT_DIR:
6490  p(0) -= wmax;
6491  angle = 90;
6492  break;
6493 
6494  case AXE_HORZ_DIR:
6495  p(1) += (x2Dtop ? -hmax : hmax);
6496  break;
6497  }
6498 
6499  if (xlabel_props.positionmode_is ("auto"))
6500  {
6501  p = xform.untransform (p(0), p(1), p(2), true);
6502 
6503  p = convert_label_position (p, xlabel_props, xform,
6504  get_extent (false));
6505 
6506  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
6507  xlabel_props.set_positionmode ("auto");
6508  }
6509 
6510  if (! isempty && xlabel_props.rotationmode_is ("auto"))
6511  {
6512  xlabel_props.set_rotation (angle);
6513  xlabel_props.set_rotationmode ("auto");
6514  }
6515  }
6516 }
6517 
6518 static bool updating_ylabel_position = false;
6519 
6520 void
6522 {
6524  return;
6525 
6526  gh_manager& gh_mgr
6527  = octave::__get_gh_manager__ ("axes::properties::update_ylabel_position");
6528 
6529  graphics_object go = gh_mgr.get_object (get_ylabel ());
6530 
6531  if (! go.valid_object ())
6532  return;
6533 
6534  text::properties& ylabel_props
6535  = reinterpret_cast<text::properties&> (go.get_properties ());
6536 
6537  bool isempty = ylabel_props.get_string ().isempty ();
6538 
6539  octave::unwind_protect frame;
6541  updating_ylabel_position = true;
6542 
6543  if (! isempty)
6544  {
6545  if (ylabel_props.horizontalalignmentmode_is ("auto"))
6546  {
6547  ylabel_props.set_horizontalalignment
6548  (ystate > AXE_DEPTH_DIR ? "center" : (! xyzSym ? "left" : "right"));
6549 
6550  ylabel_props.set_horizontalalignmentmode ("auto");
6551  }
6552 
6553  if (ylabel_props.verticalalignmentmode_is ("auto"))
6554  {
6555  ylabel_props.set_verticalalignment
6556  (ystate == AXE_VERT_DIR && ! y2Dright ? "bottom" : "top");
6557 
6558  ylabel_props.set_verticalalignmentmode ("auto");
6559  }
6560  }
6561 
6562  if (ylabel_props.positionmode_is ("auto")
6563  || ylabel_props.rotationmode_is ("auto"))
6564  {
6565  graphics_xform xform = get_transform ();
6566 
6567  Matrix ext (1, 2, 0.0);
6568 
6569  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
6570  get_yticklabel ().string_vector_value (),
6571  get_ylim ().matrix_value ());
6572  double margin = 5;
6573  double wmax = ext(0) + margin;
6574  double hmax = ext(1) + margin;
6575  double angle = 0.0;
6576  ColumnVector p
6577  = graphics_xform::xform_vector (xpTick, (ypTickN + ypTick)/2, zpTick);
6578 
6579  bool tick_along_z = nearhoriz || octave::math::isinf (fx);
6580  if (tick_along_z)
6581  p(2) += (octave::math::signum (zpTick - zpTickN) * fz * ytickoffset);
6582  else
6583  p(0) += (octave::math::signum (xpTick - xpTickN) * fx * ytickoffset);
6584 
6585  p = xform.transform (p(0), p(1), p(2), false);
6586 
6587  switch (ystate)
6588  {
6589  case AXE_ANY_DIR:
6590  p(0) += (! xyzSym ? wmax : -wmax);
6591  p(1) += hmax;
6592  break;
6593 
6594  case AXE_VERT_DIR:
6595  p(0) += (y2Dright ? wmax : -wmax);
6596  angle = 90;
6597  break;
6598 
6599  case AXE_HORZ_DIR:
6600  p(1) += hmax;
6601  break;
6602  }
6603 
6604  if (ylabel_props.positionmode_is ("auto"))
6605  {
6606  p = xform.untransform (p(0), p(1), p(2), true);
6607 
6608  p = convert_label_position (p, ylabel_props, xform,
6609  get_extent (false));
6610 
6611  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
6612  ylabel_props.set_positionmode ("auto");
6613  }
6614 
6615  if (! isempty && ylabel_props.rotationmode_is ("auto"))
6616  {
6617  ylabel_props.set_rotation (angle);
6618  ylabel_props.set_rotationmode ("auto");
6619  }
6620  }
6621 }
6622 
6623 static bool updating_zlabel_position = false;
6624 
6625 void
6627 {
6629  return;
6630 
6631  gh_manager& gh_mgr
6632  = octave::__get_gh_manager__ ("axes::properties::update_zlabel_position");
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 frame;
6647  updating_zlabel_position = true;
6648 
6649  if (! isempty)
6650  {
6651  if (zlabel_props.horizontalalignmentmode_is ("auto"))
6652  {
6653  zlabel_props.set_horizontalalignment
6654  ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
6655 
6656  zlabel_props.set_horizontalalignmentmode ("auto");
6657  }
6658 
6659  if (zlabel_props.verticalalignmentmode_is ("auto"))
6660  {
6661  zlabel_props.set_verticalalignment
6662  (zstate == AXE_VERT_DIR
6663  ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
6664 
6665  zlabel_props.set_verticalalignmentmode ("auto");
6666  }
6667  }
6668 
6669  if (zlabel_props.positionmode_is ("auto")
6670  || zlabel_props.rotationmode_is ("auto"))
6671  {
6672  graphics_xform xform = get_transform ();
6673 
6674  Matrix ext (1, 2, 0.0);
6675  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
6676  get_zticklabel ().string_vector_value (),
6677  get_zlim ().matrix_value ());
6678 
6679  double margin = 5;
6680  double wmax = ext(0) + margin;
6681  double hmax = ext(1) + margin;
6682  double angle = 0.0;
6683  ColumnVector p;
6684 
6685  if (xySym)
6686  {
6687  p = graphics_xform::xform_vector (xPlaneN, yPlane,
6688  (zpTickN + zpTick)/2);
6689  if (octave::math::isinf (fy))
6690  p(0) += octave::math::signum (xPlaneN - xPlane) * fx * ztickoffset;
6691  else
6692  p(1) += octave::math::signum (yPlane - yPlaneN) * fy * ztickoffset;
6693  }
6694  else
6695  {
6696  p = graphics_xform::xform_vector (xPlane, yPlaneN,
6697  (zpTickN + zpTick)/2);
6698  if (octave::math::isinf (fx))
6699  p(1) += octave::math::signum (yPlaneN - yPlane) * fy * ztickoffset;
6700  else
6701  p(0) += octave::math::signum (xPlane - xPlaneN) * fx * ztickoffset;
6702  }
6703 
6704  p = xform.transform (p(0), p(1), p(2), false);
6705 
6706  switch (zstate)
6707  {
6708  case AXE_ANY_DIR:
6709  if (camAuto)
6710  {
6711  p(0) -= wmax;
6712  angle = 90;
6713  }
6714 
6715  // FIXME: what's the correct offset?
6716  //
6717  // p[0] += (! xySym ? wmax : -wmax);
6718  // p[1] += (zSign ? hmax : -hmax);
6719 
6720  break;
6721 
6722  case AXE_VERT_DIR:
6723  p(0) -= wmax;
6724  angle = 90;
6725  break;
6726 
6727  case AXE_HORZ_DIR:
6728  p(1) += hmax;
6729  break;
6730  }
6731 
6732  if (zlabel_props.positionmode_is ("auto"))
6733  {
6734  p = xform.untransform (p(0), p(1), p(2), true);
6735 
6736  p = convert_label_position (p, zlabel_props, xform,
6737  get_extent (false));
6738 
6739  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
6740  zlabel_props.set_positionmode ("auto");
6741  }
6742 
6743  if (! isempty && zlabel_props.rotationmode_is ("auto"))
6744  {
6745  zlabel_props.set_rotation (angle);
6746  zlabel_props.set_rotationmode ("auto");
6747  }
6748  }
6749 }
6750 
6751 static bool updating_title_position = false;
6752 
6753 void
6755 {
6757  return;
6758 
6759  gh_manager& gh_mgr
6760  = octave::__get_gh_manager__ ("axes::properties::update_title_position");
6761 
6762  graphics_object go = gh_mgr.get_object (get_title ());
6763 
6764  if (! go.valid_object ())
6765  return;
6766 
6767  text::properties& title_props
6768  = reinterpret_cast<text::properties&> (go.get_properties ());
6769 
6770  octave::unwind_protect frame;
6772  updating_title_position = true;
6773 
6774  if (title_props.positionmode_is ("auto"))
6775  {
6776  graphics_xform xform = get_transform ();
6777 
6778  // FIXME: bbox should be stored in axes::properties
6779  Matrix bbox = get_extent (false);
6780 
6781  ColumnVector p
6782  = graphics_xform::xform_vector (bbox(0) + bbox(2)/2, bbox(1) - 10,
6783  (x_zlim(0) + x_zlim(1))/2);
6784 
6785  if (x2Dtop)
6786  {
6787  Matrix ext (1, 2, 0.0);
6788  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6789  get_xticklabel ().string_vector_value (),
6790  get_xlim ().matrix_value ());
6791  p(1) -= ext(1);
6792  }
6793 
6794  p = xform.untransform (p(0), p(1), p(2), true);
6795 
6796  p = convert_label_position (p, title_props, xform, bbox);
6797 
6798  title_props.set_position (p.extract_n (0, 3).transpose ());
6799  title_props.set_positionmode ("auto");
6800  }
6801 }
6802 
6803 void
6804 axes::properties::update_autopos (const std::string& elem_type)
6805 {
6806  if (elem_type == "xlabel")
6807  update_xlabel_position ();
6808  else if (elem_type == "ylabel")
6809  update_ylabel_position ();
6810  else if (elem_type == "zlabel")
6811  update_zlabel_position ();
6812  else if (elem_type == "title")
6813  update_title_position ();
6814  else if (elem_type == "sync")
6815  sync_positions ();
6816 }
6817 
6818 static void
6819 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
6820  double xlength, double ylength, double zlength)
6821 {
6822  double xval = xlength / scalefactors(0);
6823  double yval = ylength / scalefactors(1);
6824  double zval = zlength / scalefactors(2);
6825 
6826  double minval = octave::math::min (octave::math::min (xval, yval), zval);
6827 
6828  aspectratios(0) = xval / minval;
6829  aspectratios(1) = yval / minval;
6830  aspectratios(2) = zval / minval;
6831 }
6832 
6833 static void
6834 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
6835  double pbfactor, double dafactor, char limit_type, bool tight)
6836 {
6837  if (tight)
6838  {
6839  double minval = octave::numeric_limits<double>::Inf ();
6840  double maxval = -octave::numeric_limits<double>::Inf ();
6841  double min_pos = octave::numeric_limits<double>::Inf ();
6842  double max_neg = -octave::numeric_limits<double>::Inf ();
6843  get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
6844  if (octave::math::isfinite (minval) && octave::math::isfinite (maxval))
6845  {
6846  limits(0) = minval;
6847  limits(1) = maxval;
6848  s = octave::math::max (s, (maxval - minval) / (pbfactor * dafactor));
6849  }
6850  }
6851  else
6852  s = octave::math::max (s, (limits(1) - limits(0)) / (pbfactor * dafactor));
6853 }
6854 
6855 static std::set<double> updating_aspectratios;
6856 
6857 void
6859 {
6860  if (updating_aspectratios.find (get___myhandle__ ().value ())
6861  != updating_aspectratios.end ())
6862  return;
6863 
6864  Matrix xlimits = get_xlim ().matrix_value ();
6865  Matrix ylimits = get_ylim ().matrix_value ();
6866  Matrix zlimits = get_zlim ().matrix_value ();
6867 
6868  double dx = (xlimits(1) - xlimits(0));
6869  double dy = (ylimits(1) - ylimits(0));
6870  double dz = (zlimits(1) - zlimits(0));
6871 
6872  Matrix da = get_dataaspectratio ().matrix_value ();
6873  Matrix pba = get_plotboxaspectratio ().matrix_value ();
6874 
6875  if (dataaspectratiomode_is ("auto"))
6876  {
6877  if (plotboxaspectratiomode_is ("auto"))
6878  {
6879  pba = Matrix (1, 3, 1.0);
6880  plotboxaspectratio.set (pba, false);
6881  }
6882 
6883  normalized_aspectratios (da, pba, dx, dy, dz);
6884  dataaspectratio.set (da, false);
6885  }
6886  else if (plotboxaspectratiomode_is ("auto"))
6887  {
6888  normalized_aspectratios (pba, da, dx, dy, dz);
6889  plotboxaspectratio.set (pba, false);
6890  }
6891  else
6892  {
6893  double s = -octave::numeric_limits<double>::Inf ();
6894  bool modified_limits = false;
6895  Matrix kids;
6896 
6897  if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
6898  {
6899  modified_limits = true;
6900  kids = get_children ();
6901  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
6902  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
6903  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
6904  }
6905  else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
6906  {
6907  modified_limits = true;
6908  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
6909  }
6910  else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
6911  {
6912  modified_limits = true;
6913  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
6914  }
6915  else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
6916  {
6917  modified_limits = true;
6918  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
6919  }
6920 
6921  if (modified_limits)
6922  {
6923  octave::unwind_protect frame;
6925 
6926  updating_aspectratios.insert (get___myhandle__ ().value ());
6927 
6928  dx = pba(0) * da(0);
6929  dy = pba(1) * da(1);
6930  dz = pba(2) * da(2);
6931  if (octave::math::isinf (s))
6932  s = 1 / octave::math::min (octave::math::min (dx, dy), dz);
6933 
6934  if (xlimmode_is ("auto"))
6935  {
6936  dx = s * dx;
6937  xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
6938  xlimits(1) = xlimits(0) + dx;
6939  set_xlim (xlimits);
6940  set_xlimmode ("auto");
6941  }
6942 
6943  if (ylimmode_is ("auto"))
6944  {
6945  dy = s * dy;
6946  ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
6947  ylimits(1) = ylimits(0) + dy;
6948  set_ylim (ylimits);
6949  set_ylimmode ("auto");
6950  }
6951 
6952  if (zlimmode_is ("auto"))
6953  {
6954  dz = s * dz;
6955  zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
6956  zlimits(1) = zlimits(0) + dz;
6957  set_zlim (zlimits);
6958  set_zlimmode ("auto");
6959  }
6960  }
6961  else
6962  {
6963  normalized_aspectratios (pba, da, dx, dy, dz);
6964  plotboxaspectratio.set (pba, false);
6965  }
6966  }
6967 }
6968 
6969 void
6970 axes::properties::update_label_color (handle_property label,
6971  color_property col)
6972 {
6973  gh_manager& gh_mgr
6974  = octave::__get_gh_manager__ ("axes::properties::update_label_color");
6975 
6976  gh_mgr.get_object (label.handle_value ()).set ("color", col.get ());
6977 }
6978 
6979 void
6981 {
6982  gh_manager& gh_mgr
6983  = octave::__get_gh_manager__ ("axes::properties::update_font");
6984 
6985  if (! prop.empty ())
6986  {
6987  octave_value val = get (prop);
6988  octave_value tval = val;
6989  if (prop == "fontsize")
6990  {
6991  tval = octave_value (val.double_value () *
6992  get_titlefontsizemultiplier ());
6993  val = octave_value (val.double_value () *
6994  get_labelfontsizemultiplier ());
6995  }
6996  else if (prop == "fontweight")
6997  tval = get ("titlefontweight");
6998 
6999  gh_mgr.get_object (get_xlabel ()).set (prop, val);
7000  gh_mgr.get_object (get_ylabel ()).set (prop, val);
7001  gh_mgr.get_object (get_zlabel ()).set (prop, val);
7002  gh_mgr.get_object (get_title ()).set (prop, tval);
7003  }
7004 
7005  double dpr = device_pixel_ratio (get___myhandle__ ());
7006 
7007  octave::autolock guard (gh_mgr.graphics_lock ());
7008 
7009  txt_renderer.set_font (get ("fontname").string_value (),
7010  get ("fontweight").string_value (),
7011  get ("fontangle").string_value (),
7012  get ("__fontsize_points__").double_value () * dpr);
7013 }
7014 
7015 // The INTERNAL flag defines whether position or outerposition is used.
7016 
7017 Matrix
7019  const Matrix& parent_pix_size) const
7020 {
7021  Matrix pos = (internal ? get_position ().matrix_value ()
7022  : get_outerposition ().matrix_value ());
7023  Matrix parent_size (parent_pix_size);
7024 
7025  if (parent_size.isempty ())
7026  {
7027  gh_manager& gh_mgr
7028  = octave::__get_gh_manager__ ("axes::properties::get_boundingbox");
7029 
7030  graphics_object go = gh_mgr.get_object (get_parent ());
7031 
7032  if (go.valid_object ())
7033  parent_size
7034  = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7035  else
7036  parent_size = default_figure_position ();
7037  }
7038 
7039  pos = convert_position (pos, get_units (), "pixels", parent_size);
7040 
7041  pos(0)--;
7042  pos(1)--;
7043  pos(1) = parent_size(1) - pos(1) - pos(3);
7044 
7045  return pos;
7046 }
7047 
7048 Matrix
7049 axes::properties::get_extent (bool with_text, bool only_text_height) const
7050 {
7051  graphics_xform xform = get_transform ();
7052 
7053  Matrix ext (1, 4, 0.0);
7054  ext(0) = ext(1) = octave::numeric_limits<double>::Inf ();
7055  ext(2) = ext(3) = -octave::numeric_limits<double>::Inf ();
7056  for (int i = 0; i <= 1; i++)
7057  for (int j = 0; j <= 1; j++)
7058  for (int k = 0; k <= 1; k++)
7059  {
7060  ColumnVector p = xform.transform (i ? xPlaneN : xPlane,
7061  j ? yPlaneN : yPlane,
7062  k ? zPlaneN : zPlane, false);
7063  ext(0) = std::min (ext(0), p(0));
7064  ext(1) = std::min (ext(1), p(1));
7065  ext(2) = std::max (ext(2), p(0));
7066  ext(3) = std::max (ext(3), p(1));
7067  }
7068 
7069  if (with_text)
7070  {
7071  for (int i = 0; i < 4; i++)
7072  {
7073  graphics_handle htext;
7074  if (i == 0)
7075  htext = get_title ();
7076  else if (i == 1)
7077  htext = get_xlabel ();
7078  else if (i == 2)
7079  htext = get_ylabel ();
7080  else if (i == 3)
7081  htext = get_zlabel ();
7082 
7083  gh_manager& gh_mgr
7084  = octave::__get_gh_manager__ ("axes::properties::get_extent");
7085 
7086  text::properties& text_props
7087  = reinterpret_cast<text::properties&>
7088  (gh_mgr.get_object (htext).get_properties ());
7089 
7090  Matrix text_pos = text_props.get_data_position ();
7091  text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
7092  if (text_props.get_string ().isempty ())
7093  {
7094  ext(0) = std::min (ext(0), text_pos(0));
7095  ext(1) = std::min (ext(1), text_pos(1));
7096  ext(2) = std::max (ext(2), text_pos(0));
7097  ext(3) = std::max (ext(3), text_pos(1));
7098  }
7099  else
7100  {
7101  Matrix text_ext = text_props.get_extent_matrix ();
7102 
7103  // The text extent is returned in device pixels. Unscale and
7104  // work with logical pixels
7105  double dpr = device_pixel_ratio (get___myhandle__ ());
7106  if (dpr != 1.0)
7107  for (int j = 0; j < 4; j++)
7108  text_ext(j) /= dpr;
7109 
7110  bool ignore_horizontal = false;
7111  bool ignore_vertical = false;
7112  if (only_text_height)
7113  {
7114  double text_rotation = text_props.get_rotation ();
7115  if (text_rotation == 0. || text_rotation == 180.)
7116  ignore_horizontal = true;
7117  else if (text_rotation == 90. || text_rotation == 270.)
7118  ignore_vertical = true;
7119  }
7120 
7121  if (! ignore_horizontal)
7122  {
7123  ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
7124  ext(2) = std::max (ext(2),
7125  text_pos(0)+text_ext(0)+text_ext(2));
7126  }
7127 
7128  if (! ignore_vertical)
7129  {
7130  ext(1) = std::min (ext(1),
7131  text_pos(1)-text_ext(1)-text_ext(3));
7132  ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
7133  }
7134  }
7135  }
7136  }
7137 
7138  ext(2) = ext(2) - ext(0);
7139  ext(3) = ext(3) - ext(1);
7140 
7141  return ext;
7142 }
7143 
7144 static octave_value
7146 {
7147  octave_value retval = val;
7148 
7149  if (val.iscellstr ())
7150  {
7151  // Always return a column vector for Matlab compatibility
7152  if (val.columns () > 1)
7153  retval = val.reshape (dim_vector (val.numel (), 1));
7154  }
7155  else
7156  {
7157  string_vector sv;
7158  if (val.isnumeric ())
7159  {
7160  NDArray data = val.array_value ();
7161  std::ostringstream oss;
7162  oss.precision (5);
7163  for (octave_idx_type i = 0; i < val.numel (); i++)
7164  {
7165  oss.str ("");
7166  oss << data(i);
7167  sv.append (oss.str ());
7168  }
7169  }
7170  else if (val.is_string () && val.rows () == 1)
7171  {
7172  std::string valstr = val.string_value ();
7173  std::istringstream iss (valstr);
7174  std::string tmpstr;
7175 
7176  // Split string with delimiter '|'
7177  while (std::getline (iss, tmpstr, '|'))
7178  sv.append (tmpstr);
7179 
7180  // If string ends with '|' Matlab appends a null string
7181  if (*valstr.rbegin () == '|')
7182  sv.append (std::string (""));
7183  }
7184  else
7185  return retval;
7186 
7187  charMatrix chmat (sv, ' ');
7188 
7189  retval = octave_value (chmat);
7190  }
7191 
7192  return retval;
7193 }
7194 
7195 void
7196 axes::properties::set_xticklabel (const octave_value& val)
7197 {
7198  if (xticklabel.set (convert_ticklabel_string (val), false))
7199  {
7200  set_xticklabelmode ("manual");
7201  xticklabel.run_listeners (GCB_POSTSET);
7202  mark_modified ();
7203  }
7204  else
7205  set_xticklabelmode ("manual");
7206 
7207  sync_positions ();
7208 }
7209 
7210 void
7211 axes::properties::set_yticklabel (const octave_value& val)
7212 {
7213  if (yticklabel.set (convert_ticklabel_string (val), false))
7214  {
7215  set_yticklabelmode ("manual");
7216  yticklabel.run_listeners (GCB_POSTSET);
7217  mark_modified ();
7218  }
7219  else
7220  set_yticklabelmode ("manual");
7221 
7222  sync_positions ();
7223 }
7224 
7225 void
7226 axes::properties::set_zticklabel (const octave_value& val)
7227 {
7228  if (zticklabel.set (convert_ticklabel_string (val), false))
7229  {
7230  set_zticklabelmode ("manual");
7231  zticklabel.run_listeners (GCB_POSTSET);
7232  mark_modified ();
7233  }
7234  else
7235  set_zticklabelmode ("manual");
7236 
7237  sync_positions ();
7238 }
7239 
7240 // Almost identical to convert_ticklabel_string but it only accepts
7241 // cellstr or string, not numeric input.
7242 static octave_value
7244 {
7245  octave_value retval = val;
7246 
7247  if (val.iscellstr ())
7248  {
7249  // Always return a column vector for Matlab Compatibility
7250  if (val.columns () > 1)
7251  retval = val.reshape (dim_vector (val.numel (), 1));
7252  }
7253  else
7254  {
7255  string_vector sv;
7256  if (val.is_string () && val.rows () == 1)
7257  {
7258  std::string valstr = val.string_value ();
7259  std::istringstream iss (valstr);
7260  std::string tmpstr;
7261 
7262  // Split string with delimiter '|'
7263  while (std::getline (iss, tmpstr, '|'))
7264  sv.append (tmpstr);
7265 
7266  // If string ends with '|' Matlab appends a null string
7267  if (*valstr.rbegin () == '|')
7268  sv.append (std::string (""));
7269  }
7270  else
7271  return retval;
7272 
7273  charMatrix chmat (sv, ' ');
7274 
7275  retval = octave_value (chmat);
7276  }
7277 
7278  return retval;
7279 }
7280 
7281 void
7282 axes::properties::set_linestyleorder (const octave_value& val)
7283 {
7284  linestyleorder.set (convert_linestyleorder_string (val), false);
7285 }
7286 
7287 void
7288 axes::properties::set_units (const octave_value& val)
7289 {
7290  caseless_str old_units = get_units ();
7291 
7292  if (units.set (val, true))
7293  {
7294  update_units (old_units);
7295  mark_modified ();
7296  }
7297 }
7298 
7299 void
7301 {
7302  gh_manager& gh_mgr
7303  = octave::__get_gh_manager__ ("axes::properties::update_units");
7304 
7305  graphics_object parent_go = gh_mgr.get_object (get_parent ());
7306 
7307  Matrix parent_bb
7308  = parent_go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7309 
7310  caseless_str new_units = get_units ();
7311  position.set (octave_value (convert_position (get_position ().matrix_value (),
7312  old_units, new_units,
7313  parent_bb)),
7314  false);
7315  outerposition.set (octave_value (convert_position (get_outerposition ().matrix_value (),
7316  old_units, new_units,
7317  parent_bb)),
7318  false);
7319  tightinset.set (octave_value (convert_position (get_tightinset ().matrix_value (),
7320  old_units, new_units,
7321  parent_bb)),
7322  false);
7323  looseinset.set (octave_value (convert_position (get_looseinset ().matrix_value (),
7324  old_units, new_units,
7325  parent_bb)),
7326  false);
7327 }
7328 
7329 void
7330 axes::properties::set_fontunits (const octave_value& val)
7331 {
7332  caseless_str old_fontunits = get_fontunits ();
7333 
7334  if (fontunits.set (val, true))
7335  {
7336  update_fontunits (old_fontunits);
7337  mark_modified ();
7338  }
7339 }
7340 
7341 void
7343 {
7344  caseless_str new_units = get_fontunits ();
7345  double parent_height = get_boundingbox (true).elem (3);
7346  double fontsz = get_fontsize ();
7347 
7348  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
7349 
7350  set_fontsize (octave_value (fontsz));
7351 }
7352 
7353 double
7354 axes::properties::get___fontsize_points__ (double box_pix_height) const
7355 {
7356  double fontsz = get_fontsize ();
7357  double parent_height = box_pix_height;
7358 
7359  if (fontunits_is ("normalized") && parent_height <= 0)
7360  parent_height = get_boundingbox (true).elem (3);
7361 
7362  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
7363 }
7364 
7366 graphics_xform::xform_vector (double x, double y, double z)
7367 {
7368  return ::xform_vector (x, y, z);
7369 }
7370 
7371 Matrix
7373 {
7375 }
7376 
7378 graphics_xform::transform (double x, double y, double z, bool use_scale) const
7379 {
7380  if (use_scale)
7381  {
7382  x = sx.scale (x);
7383  y = sy.scale (y);
7384  z = sz.scale (z);
7385  }
7386 
7387  return ::transform (xform, x, y, z);
7388 }
7389 
7391 graphics_xform::untransform (double x, double y, double z,
7392  bool use_scale) const
7393 {
7394  ColumnVector v = ::transform (xform_inv, x, y, z);
7395 
7396  if (use_scale)
7397  {
7398  v(0) = sx.unscale (v(0));
7399  v(1) = sy.unscale (v(1));
7400  v(2) = sz.unscale (v(2));
7401  }
7402 
7403  return v;
7404 }
7405 
7407 axes::get_default (const caseless_str& pname) const
7408 {
7410 
7411  if (retval.is_undefined ())
7412  {
7413  graphics_handle parent_h = get_parent ();
7414 
7415  gh_manager& gh_mgr
7416  = octave::__get_gh_manager__ ("axes::properties::get_default");
7417 
7418  graphics_object parent_go = gh_mgr.get_object (parent_h);
7419 
7420  retval = parent_go.get_default (pname);
7421  }
7422 
7423  return retval;
7424 }
7425 
7426 // FIXME: remove.
7427 // FIXME: maybe this should go into array_property class?
7428 /*
7429 static void
7430 check_limit_vals (double& min_val, double& max_val,
7431  double& min_pos, double& max_neg,
7432  const array_property& data)
7433 {
7434  double val = data.min_val ();
7435  if (octave::math::isfinite (val) && val < min_val)
7436  min_val = val;
7437  val = data.max_val ();
7438  if (octave::math::isfinite (val) && val > max_val)
7439  max_val = val;
7440  val = data.min_pos ();
7441  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7442  min_pos = val;
7443  val = data.max_neg ();
7444  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7445  max_neg = val;
7446 }
7447 */
7448 
7449 static void
7450 check_limit_vals (double& min_val, double& max_val,
7451  double& min_pos, double& max_neg,
7452  const octave_value& data)
7453 {
7454  Matrix m;
7455 
7456  if (data.is_matrix_type ())
7457  m = data.matrix_value ();
7458 
7459  if (m.numel () != 4)
7460  {
7461  m = Matrix (1, 4, 0.0);
7464  }
7465 
7466  double val;
7467 
7468  val = m(0);
7469  if (octave::math::isfinite (val) && val < min_val)
7470  min_val = val;
7471 
7472  val = m(1);
7473  if (octave::math::isfinite (val) && val > max_val)
7474  max_val = val;
7475 
7476  val = m(2);
7477  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7478  min_pos = val;
7479 
7480  val = m(3);
7481  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7482  max_neg = val;
7483 }
7484 
7485 // magform(x) Returns (a, b),
7486 // where x = a * 10^b, abs (a) >= 1., and b is integer.
7487 
7488 static void
7489 magform (double x, double& a, int& b)
7490 {
7491  if (x == 0)
7492  {
7493  a = 0;
7494  b = 0;
7495  }
7496  else
7497  {
7498  b = static_cast<int> (std::floor (std::log10 (std::abs (x))));
7499  a = x / std::pow (10.0, b);
7500  }
7501 }
7502 
7503 void
7504 axes::properties::update_outerposition (void)
7505 {
7506  set_activepositionproperty ("outerposition");
7507  caseless_str old_units = get_units ();
7508  set_units ("normalized");
7509 
7510  Matrix outerbox = outerposition.get ().matrix_value ();
7511 
7512  double outer_left = outerbox(0);
7513  double outer_bottom = outerbox(1);
7514  double outer_width = outerbox(2);
7515  double outer_height = outerbox(3);
7516 
7517  double outer_right = outer_width + outer_left;
7518  double outer_top = outer_height + outer_bottom;
7519 
7520  Matrix linset = looseinset.get ().matrix_value ();
7521  Matrix tinset = tightinset.get ().matrix_value ();
7522 
7523  double left_margin = std::max (linset(0), tinset(0));
7524  double bottom_margin = std::max (linset(1), tinset(1));
7525  double right_margin = std::max (linset(2), tinset(2));
7526  double top_margin = std::max (linset(3), tinset(3));
7527 
7528  double inner_left = outer_left;
7529  double inner_right = outer_right;
7530 
7531  if ((left_margin + right_margin) < outer_width)
7532  {
7533  inner_left += left_margin;
7534  inner_right -= right_margin;
7535  }
7536 
7537  double inner_bottom = outer_bottom;
7538  double inner_top = outer_top;
7539 
7540  if ((bottom_margin + top_margin) < outer_height)
7541  {
7542  inner_bottom += bottom_margin;
7543  inner_top -= top_margin;
7544  }
7545 
7546  double inner_width = inner_right - inner_left;
7547  double inner_height = inner_top - inner_bottom;
7548 
7549  Matrix innerbox (1, 4);
7550 
7551  innerbox(0) = inner_left;
7552  innerbox(1) = inner_bottom;
7553  innerbox(2) = inner_width;
7554  innerbox(3) = inner_height;
7555 
7556  position = innerbox;
7557 
7558  set_units (old_units);
7559  update_transform ();
7560 }
7561 
7562 void
7563 axes::properties::update_position (void)
7564 {
7565  set_activepositionproperty ("position");
7566  caseless_str old_units = get_units ();
7567  set_units ("normalized");
7568 
7569  Matrix innerbox = position.get ().matrix_value ();
7570 
7571  double inner_left = innerbox(0);
7572  double inner_bottom = innerbox(1);
7573  double inner_width = innerbox(2);
7574  double inner_height = innerbox(3);
7575 
7576  double inner_right = inner_width + inner_left;
7577  double inner_top = inner_height + inner_bottom;
7578 
7579  Matrix linset = looseinset.get ().matrix_value ();
7580  Matrix tinset = tightinset.get ().matrix_value ();
7581 
7582  double left_margin = std::max (linset(0), tinset(0));
7583  double bottom_margin = std::max (linset(1), tinset(1));
7584  double right_margin = std::max (linset(2), tinset(2));
7585  double top_margin = std::max (linset(3), tinset(3));
7586 
7587  // FIXME: do we need to place limits on any of these?
7588 
7589  double outer_left = inner_left - left_margin;
7590  double outer_bottom = inner_bottom - bottom_margin;
7591  double outer_right = inner_right + right_margin;
7592  double outer_top = inner_top + top_margin;
7593 
7594  double outer_width = outer_right - outer_left;
7595  double outer_height = outer_top - outer_bottom;
7596 
7597  Matrix outerbox (1, 4);
7598 
7599  outerbox(0) = outer_left;
7600  outerbox(1) = outer_bottom;
7601  outerbox(2) = outer_width;
7602  outerbox(3) = outer_height;
7603 
7604  outerposition = outerbox;
7605 
7606  set_units (old_units);
7607  update_transform ();
7608 }
7609 
7610 void
7611 axes::properties::update_looseinset (void)
7612 {
7613  caseless_str old_units = get_units ();
7614  set_units ("normalized");
7615 
7616  Matrix linset = looseinset.get ().matrix_value ();
7617  Matrix tinset = tightinset.get ().matrix_value ();
7618 
7619  double left_margin = std::max (linset(0), tinset(0));
7620  double bottom_margin = std::max (linset(1), tinset(1));
7621  double right_margin = std::max (linset(2), tinset(2));
7622  double top_margin = std::max (linset(3), tinset(3));
7623 
7624  if (activepositionproperty.is ("position"))
7625  {
7626  Matrix innerbox = position.get ().matrix_value ();
7627 
7628  double inner_left = innerbox(0);
7629  double inner_bottom = innerbox(1);
7630  double inner_width = innerbox(2);
7631  double inner_height = innerbox(3);
7632 
7633  double inner_right = inner_width + inner_left;
7634  double inner_top = inner_height + inner_bottom;
7635 
7636  // FIXME: do we need to place limits on any of these?
7637 
7638  double outer_left = inner_left - left_margin;
7639  double outer_bottom = inner_bottom - bottom_margin;
7640  double outer_right = inner_right + right_margin;
7641  double outer_top = inner_top + top_margin;
7642 
7643  double outer_width = outer_right - outer_left;
7644  double outer_height = outer_top - outer_bottom;
7645 
7646  Matrix outerbox (1, 4);
7647 
7648  outerbox(0) = outer_left;
7649  outerbox(1) = outer_bottom;
7650  outerbox(2) = outer_width;
7651  outerbox(3) = outer_height;
7652 
7653  outerposition = outerbox;
7654  }
7655  else
7656  {
7657  Matrix outerbox = outerposition.get ().matrix_value ();
7658 
7659  double outer_left = outerbox(0);
7660  double outer_bottom = outerbox(1);
7661  double outer_width = outerbox(2);
7662  double outer_height = outerbox(3);
7663 
7664  double outer_right = outer_width + outer_left;
7665  double outer_top = outer_height + outer_bottom;
7666 
7667  double inner_left = outer_left;
7668  double inner_right = outer_right;
7669 
7670  if ((left_margin + right_margin) < outer_width)
7671  {
7672  inner_left += left_margin;
7673  inner_right -= right_margin;
7674  }
7675 
7676  double inner_bottom = outer_bottom;
7677  double inner_top = outer_top;
7678 
7679  if ((bottom_margin + top_margin) < outer_height)
7680  {
7681  inner_bottom += bottom_margin;
7682  inner_top -= top_margin;
7683  }
7684 
7685  double inner_width = inner_right - inner_left;
7686  double inner_height = inner_top - inner_bottom;
7687 
7688  Matrix innerbox (1, 4);
7689 
7690  innerbox(0) = inner_left;
7691  innerbox(1) = inner_bottom;
7692  innerbox(2) = inner_width;
7693  innerbox(3) = inner_height;
7694 
7695  position = innerbox;
7696  }
7697 
7698  set_units (old_units);
7699  update_transform ();
7700 }
7701 
7702 // A translation from Tom Holoryd's python code at
7703 // http://kurage.nimh.nih.gov/tomh/tics.py
7704 // FIXME: add log ticks
7705 
7706 double
7707 axes::properties::calc_tick_sep (double lo, double hi)
7708 {
7709  int ticint = 5;
7710 
7711  // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and SCALE3 for
7712  // Determination of Scales on Computer Generated Plots", Communications of
7713  // the ACM, 10 (1973), 639-640.
7714  // Also cited as ACM Algorithm 463.
7715 
7716  double a;
7717  int b, x;
7718 
7719  magform ((hi - lo) / ticint, a, b);
7720 
7721  static const double sqrt_2 = sqrt (2.0);
7722  static const double sqrt_10 = sqrt (10.0);
7723  static const double sqrt_50 = sqrt (50.0);
7724 
7725  if (a < sqrt_2)
7726  x = 1;
7727  else if (a < sqrt_10)
7728  x = 2;
7729  else if (a < sqrt_50)
7730  x = 5;
7731  else
7732  x = 10;
7733 
7734  return x * std::pow (10., b);
7735 }
7736 
7737 // Attempt to make "nice" limits from the actual max and min of the data.
7738 // For log plots, we will also use the smallest strictly positive value.
7739 
7740 Matrix
7741 axes::properties::get_axis_limits (double xmin, double xmax,
7742  double min_pos, double max_neg,
7743  const bool logscale)
7744 {
7745  Matrix retval;
7746 
7747  double min_val = xmin;
7748  double max_val = xmax;
7749 
7750  if (octave::math::isinf (min_val) && min_val > 0
7751  && octave::math::isinf (max_val) && max_val < 0)
7752  {
7753  retval = default_lim (logscale);
7754  return retval;
7755  }
7756  else if (! (octave::math::isinf (min_val) || octave::math::isinf (max_val)))
7757  {
7758  if (logscale)
7759  {
7760  if (octave::math::isinf (min_pos) && octave::math::isinf (max_neg))
7761  {
7762  // FIXME: max_neg is needed for "loglog ([0 -Inf])"
7763  // This is the *only* place where max_neg is needed.
7764  // Is there another way?
7765  retval = default_lim (logscale);
7766  return retval;
7767  }
7768  if (min_val <= 0)
7769  {
7770  if (max_val > 0)
7771  {
7772  warning_with_id ("Octave:negative-data-log-axis",
7773  "axis: omitting non-positive data in log plot");
7774  min_val = min_pos;
7775  }
7776  else if (max_val == 0)
7777  max_val = max_neg;
7778  }
7779  // FIXME: maybe this test should also be relative?
7780  if (std::abs (min_val - max_val)
7781  < sqrt (std::numeric_limits<double>::epsilon ()))
7782  {
7783  // Widen range when too small
7784  if (min_val >= 0)
7785  {
7786  min_val *= 0.9;
7787  max_val *= 1.1;
7788  }
7789  else
7790  {
7791  min_val *= 1.1;
7792  max_val *= 0.9;
7793  }
7794  }
7795  if (min_val > 0)
7796  {
7797  // Log plots with all positive data
7798  min_val = std::pow (10, std::floor (log10 (min_val)));
7799  max_val = std::pow (10, std::ceil (log10 (max_val)));
7800  }
7801  else
7802  {
7803  // Log plots with all negative data
7804  min_val = -std::pow (10, std::ceil (log10 (-min_val)));
7805  max_val = -std::pow (10, std::floor (log10 (-max_val)));
7806  }
7807  }
7808  else
7809  {
7810  if (min_val == 0 && max_val == 0)
7811  {
7812  min_val = -1;
7813  max_val = 1;
7814  }
7815  // FIXME: maybe this test should also be relative?
7816  else if (std::abs (min_val - max_val)
7817  < sqrt (std::numeric_limits<double>::epsilon ()))
7818  {
7819  min_val -= 0.1 * std::abs (min_val);
7820  max_val += 0.1 * std::abs (max_val);
7821  }
7822 
7823  double tick_sep = calc_tick_sep (min_val, max_val);
7824  double min_tick = std::floor (min_val / tick_sep);
7825  double max_tick = std::ceil (max_val / tick_sep);
7826  // Prevent round-off from cropping ticks
7827  min_val = std::min (min_val, tick_sep * min_tick);
7828  max_val = std::max (max_val, tick_sep * max_tick);
7829  }
7830  }
7831 
7832  retval.resize (1, 2);
7833 
7834  retval(0) = min_val;
7835  retval(1) = max_val;
7836 
7837  return retval;
7838 }
7839 
7840 void
7841 axes::properties::check_axis_limits (Matrix &limits, const Matrix kids,
7842  const bool logscale, char &update_type)
7843 {
7844  double min_val = octave::numeric_limits<double>::Inf ();
7845  double max_val = -octave::numeric_limits<double>::Inf ();
7846  double min_pos = octave::numeric_limits<double>::Inf ();
7847  double max_neg = -octave::numeric_limits<double>::Inf ();
7848  double eps = std::numeric_limits<double>::epsilon ();
7849  bool do_update = false;
7850  bool have_children_limits = false;
7851 
7852  // check whether we need to get children limits
7853  if (! octave::math::isfinite (limits(0))
7854  || ! octave::math::isfinite (limits(1)))
7855  {
7856  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7857  update_type);
7858  have_children_limits = true;
7859  }
7860  if (! octave::math::isfinite (limits(0)))
7861  {
7862  limits(0) = min_val;
7863  do_update = true;
7864  }
7865  if (! octave::math::isfinite (limits(1)))
7866  {
7867  limits(1) = max_val;
7868  do_update = true;
7869  }
7870  if (limits(0) == 0 && limits(1) == 0)
7871  {
7872  limits = default_lim (logscale);
7873  do_update = true;
7874  }
7875  // FIXME: maybe this test should also be relative?
7876  else if (! logscale && (std::abs (limits(0) - limits(1)) < sqrt (eps)))
7877  {
7878  limits(0) -= 0.1 * std::abs (limits(0));
7879  limits(1) += 0.1 * std::abs (limits(1));
7880  do_update = true;
7881  }
7882  else if (logscale
7883  && (std::abs (std::log10 (limits(0) / limits(1))) < sqrt (eps)))
7884  {
7885  limits(0) = (limits(0) < 0 ? 10.0 * limits(0) : 0.1 * limits(0));
7886  limits(1) = (limits(1) < 0 ? 0.1 * limits(1) : 10.0 * limits(1));
7887  do_update = true;
7888  }
7889 
7890  if (logscale && limits(0)*limits(1) <= 0)
7891  {
7892  if (! have_children_limits)
7893  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7894  update_type);
7895 
7896  if (limits(1) > 0)
7897  {
7898  warning_with_id ("Octave:axis-non-positive-log-limits",
7899  "Non-positive limit for logarithmic axis ignored\n");
7900  if (octave::math::isfinite (min_pos))
7901  limits(0) = min_pos;
7902  else
7903  limits(0) = 0.1 * limits(1);
7904  }
7905  else
7906  {
7907  warning_with_id ("Octave:axis-non-negative-log-limits",
7908  "Non-negative limit for logarithmic axis ignored\n");
7909  if (octave::math::isfinite (max_neg))
7910  limits(1) = max_neg;
7911  else
7912  limits(1) = 0.1 * limits(0);
7913  }
7914  // FIXME: maybe this test should also be relative?
7915  if (std::abs (limits(0) - limits(1)) < sqrt (eps))
7916  {
7917  // Widen range when too small
7918  if (limits(0) > 0)
7919  {
7920  limits(0) *= 0.9;
7921  limits(1) *= 1.1;
7922  }
7923  else
7924  {
7925  limits(0) *= 1.1;
7926  limits(1) *= 0.9;
7927  }
7928  }
7929  do_update = true;
7930  }
7931 
7932  if (! do_update)
7933  update_type = 0;
7934 
7935 }
7936 
7937 /*
7938 ## Test validation of auto and manual axis limits
7939 %!test
7940 %! hf = figure ("visible", "off");
7941 %! unwind_protect
7942 %! hax = axes ("parent", hf);
7943 %! plot (0, pi);
7944 %! assert (get (hax, "xlim"), [-1, 1]);
7945 %! assert (get (hax, "xlimmode"), "auto");
7946 %! assert (get (hax, "ylim"), [2.8, 3.5], 2*eps);
7947 %! assert (get (hax, "ylimmode"), "auto");
7948 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7949 %! assert (get (hax, "xlim"), [0.9, 1.1]);
7950 %! assert (get (hax, "xlimmode"), "manual");
7951 %! assert (get (hax, "ylim"), [0, 1]);
7952 %! assert (get (hax, "ylimmode"), "manual");
7953 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7954 %! ## Matlab does not update the properties
7955 %! assert (get (hax, "xlim"), [0, 1]);
7956 %! assert (get (hax, "ylim"), [0.9, 1.1]*pi, 2*eps);
7957 %! unwind_protect_cleanup
7958 %! delete (hf);
7959 %! end_unwind_protect
7960 
7961 %!test
7962 %! hf = figure ("visible", "off");
7963 %! unwind_protect
7964 %! hax = axes ("parent", hf);
7965 %! plot ([0, exp(1)], [-pi, 3]);
7966 %! assert (get (hax, "xlim"), [0, 3]);
7967 %! assert (get (hax, "xlimmode"), "auto");
7968 %! assert (get (hax, "ylim"), [-4, 3]);
7969 %! assert (get (hax, "ylimmode"), "auto");
7970 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7971 %! ## Matlab does not update the properties but uses tight limits on screen
7972 %! assert (get (hax, "xlim"), [0, exp(1)]);
7973 %! assert (get (hax, "xlimmode"), "manual");
7974 %! assert (get (hax, "ylim"), [-pi, 3]);
7975 %! assert (get (hax, "ylimmode"), "manual");
7976 %! unwind_protect_cleanup
7977 %! delete (hf);
7978 %! end_unwind_protect
7979 
7980 %!test
7981 %! hf = figure ("visible", "off");
7982 %! unwind_protect
7983 %! hax = axes ("parent", hf);
7984 %! loglog (0, pi);
7985 %! assert (get (hax, "xlim"), [0.1, 1.0]);
7986 %! assert (get (hax, "xlimmode"), "auto");
7987 %! assert (get (hax, "ylim"), [1, 10]);
7988 %! assert (get (hax, "ylimmode"), "auto");
7989 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7990 %! assert (get (hax, "xlim"), [0.1, 10]);
7991 %! assert (get (hax, "xlimmode"), "manual");
7992 %! assert (get (hax, "ylim"), [0.1, 1.0]);
7993 %! assert (get (hax, "ylimmode"), "manual");
7994 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7995 %! ## Matlab does not update the properties
7996 %! assert (get (hax, "xlim"), [0.1, 1.0]);
7997 %! assert (get (hax, "ylim"), [0.1, 10]*pi);
7998 %! unwind_protect_cleanup
7999 %! delete (hf);
8000 %! end_unwind_protect
8001 
8002 %!test
8003 %! hf = figure ("visible", "off");
8004 %! unwind_protect
8005 %! hax = axes ("parent", hf);
8006 %! loglog ([0 -1], [0 1]);
8007 %! assert (get (hax, "xlim"), [-10, -0.1]);
8008 %! assert (get (hax, "xlimmode"), "auto");
8009 %! assert (get (hax, "ylim"), [0.1, 10]);
8010 %! assert (get (hax, "ylimmode"), "auto");
8011 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
8012 %! ## Matlab does not update the properties
8013 %! assert (get (hax, "xlim"), [-1.1, -0.9]);
8014 %! assert (get (hax, "ylim"), [0.9, 1.1]);
8015 %! unwind_protect_cleanup
8016 %! delete (hf);
8017 %! end_unwind_protect
8018 
8019 %!test
8020 %! hf = figure ("visible", "off");
8021 %! unwind_protect
8022 %! hax = axes ("parent", hf);
8023 %! loglog ([1 -1], [1 pi]);
8024 %! assert (get (hax, "xlim"), [0.1, 10]);
8025 %! assert (get (hax, "xlimmode"), "auto");
8026 %! assert (get (hax, "ylim"), [1, 10]);
8027 %! assert (get (hax, "ylimmode"), "auto");
8028 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
8029 %! ## Matlab does not update the properties but uses tight limits on screen
8030 %! assert (get (hax, "xlim"), [0.9, 1.1]);
8031 %! assert (get (hax, "ylim"), [1, pi]);
8032 %! unwind_protect_cleanup
8033 %! delete (hf);
8034 %! end_unwind_protect
8035 */
8036 
8037 void
8038 axes::properties::calc_ticks_and_lims (array_property& lims,
8039  array_property& ticks,
8040  array_property& mticks,
8041  bool limmode_is_auto,
8042  bool tickmode_is_auto,
8043  bool is_logscale)
8044 {
8045  if (lims.get ().isempty ())
8046  return;
8047 
8048  double lo = (lims.get ().matrix_value ())(0);
8049  double hi = (lims.get ().matrix_value ())(1);
8050  double lo_lim = lo;
8051  double hi_lim = hi;
8052  bool is_negative = lo < 0 && hi < 0;
8053 
8054  // FIXME: should this be checked for somewhere else? (i.e., set{x,y,z}lim)
8055  if (hi < lo)
8056  std::swap (hi, lo);
8057 
8058  if (is_logscale)
8059  {
8060  if (is_negative)
8061  {
8062  double tmp = hi;
8063  hi = std::log10 (-lo);
8064  lo = std::log10 (-tmp);
8065  }
8066  else
8067  {
8068  hi = std::log10 (hi);
8069  lo = std::log10 (lo);
8070  }
8071  }
8072 
8073  Matrix tmp_ticks;
8074  if (tickmode_is_auto)
8075  {
8076  double tick_sep;
8077 
8078  if (is_logscale)
8079  {
8080  if (! (octave::math::isinf (hi) || octave::math::isinf (lo)))
8081  tick_sep = 1; // Tick is every order of magnitude (bug #39449)
8082  else
8083  tick_sep = 0;
8084  }
8085  else
8086  tick_sep = calc_tick_sep (lo, hi);
8087 
8088  double i1 = std::floor (lo / tick_sep);
8089  double i2 = std::ceil (hi / tick_sep);
8090 
8091  if (limmode_is_auto)
8092  {
8093  // Adjust limits to include min and max ticks
8094  Matrix tmp_lims (1,2);
8095  tmp_lims(0) = std::min (tick_sep * i1, lo);
8096  tmp_lims(1) = std::max (tick_sep * i2, hi);
8097 
8098  if (is_logscale)
8099  {
8100  tmp_lims(0) = std::pow (10., tmp_lims(0));
8101  tmp_lims(1) = std::pow (10., tmp_lims(1));
8102  if (tmp_lims(0) <= 0)
8103  tmp_lims(0) = std::pow (10., lo);
8104  if (is_negative)
8105  {
8106  double tmp = tmp_lims(0);
8107  tmp_lims(0) = -tmp_lims(1);
8108  tmp_lims(1) = -tmp;
8109  }
8110  }
8111  lims = tmp_lims;
8112  }
8113  else
8114  {
8115  // adjust min and max ticks to be within limits
8116  if (i1*tick_sep < lo)
8117  i1++;
8118  if (i2*tick_sep > hi && i2 > i1)
8119  i2--;
8120  }
8121 
8122  tmp_ticks = Matrix (1, i2-i1+1);
8123  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
8124  {
8125  tmp_ticks(i) = tick_sep * (i+i1);
8126  if (is_logscale)
8127  tmp_ticks(i) = std::pow (10., tmp_ticks(i));
8128  }
8129  if (is_logscale && is_negative)
8130  {
8131  Matrix rev_ticks (1, i2-i1+1);
8132  rev_ticks = -tmp_ticks;
8133  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
8134  tmp_ticks(i) = rev_ticks(i2-i1-i);
8135  }
8136 
8137  ticks = tmp_ticks;
8138  }
8139  else
8140  tmp_ticks = ticks.get ().matrix_value ();
8141 
8142  octave_idx_type n_ticks = tmp_ticks.numel ();
8143  if (n_ticks < 2)
8144  return;
8145 
8146  // minor ticks between, above, and below min and max ticks
8147  int n = (is_logscale ? 8 : 4);
8148  double mult_below = (is_logscale ? tmp_ticks(1) / tmp_ticks(0) : 1);
8149  double mult_above = (is_logscale ? tmp_ticks(n_ticks-1) / tmp_ticks(n_ticks-2)
8150  : 1);
8151 
8152  double d_below = (tmp_ticks(1) - tmp_ticks(0)) / mult_below / (n+1);
8153  int n_below = static_cast<int> (std::floor ((tmp_ticks(0)-lo_lim) / d_below));
8154  if (n_below < 0)
8155  n_below = 0;
8156  int n_between = n * (n_ticks - 1);
8157  double d_above = (tmp_ticks(n_ticks-1) - tmp_ticks(n_ticks-2)) * mult_above
8158  / (n+1);
8159  int n_above = static_cast<int> (std::floor ((hi_lim-tmp_ticks(n_ticks-1))
8160  / d_above));
8161  if (n_above < 0)
8162  n_above = 0;
8163 
8164  Matrix tmp_mticks (1, n_below + n_between + n_above);
8165  for (int i = 0; i < n_below; i++)
8166  tmp_mticks(i) = tmp_ticks(0) - (n_below-i) * d_below;
8167  for (int i = 0; i < n_ticks-1; i++)
8168  {
8169  double d = (tmp_ticks(i+1) - tmp_ticks(i)) / (n + 1);
8170  for (int j = 0; j < n; j++)
8171  tmp_mticks(n_below+n*i+j) = tmp_ticks(i) + d * (j+1);
8172  }
8173  for (int i = 0; i < n_above; i++)
8174  tmp_mticks(n_below+n_between+i) = tmp_ticks(n_ticks-1) + (i + 1) * d_above;
8175 
8176  mticks = tmp_mticks;
8177 }
8178 
8179 /*
8180 %!test <*45356>
8181 %! hf = figure ("visible", "off");
8182 %! unwind_protect
8183 %! plot (1:10);
8184 %! xlim ([4.75, 8.5]);
8185 %! tics = get (gca, "xtick");
8186 %! assert (tics, [5 6 7 8]);
8187 %! unwind_protect_cleanup
8188 %! close (hf);
8189 %! end_unwind_protect
8190 */
8191 
8192 void
8193 axes::properties::calc_ticklabels (const array_property& ticks,
8194  any_property& labels, bool logscale,
8195  const bool is_origin,
8196  const int other_axislocation,
8197  const array_property& axis_lims)
8198 {
8199  Matrix values = ticks.get ().matrix_value ();
8200  Matrix lims = axis_lims.get ().matrix_value ();
8201  Cell c (values.dims ());
8202  std::ostringstream os;
8203 
8204  // omit tick labels depending on location of other axis
8206  if (get_is2D () && is_origin)
8207  {
8208  if (other_axislocation == 0)
8209  {
8210  omit_ticks(0) = octave::math::max (octave::math::min (0., lims(1)),
8211  lims(0));
8212  }
8213  else if (other_axislocation == 1)
8214  omit_ticks(0) = lims(1);
8215  else if (other_axislocation == -1)
8216  omit_ticks(0) = lims(0);
8217  if (is_box ())
8218  {
8219  omit_ticks(1) = lims(0);
8220  omit_ticks(2) = lims(1);
8221  }
8222  }
8223 
8224  if (logscale)
8225  {
8226  double significand;
8227  double exponent;
8228  double exp_max = 0.0;
8229  double exp_min = 0.0;
8230 
8231  for (int i = 0; i < values.numel (); i++)
8232  {
8233  double exp = std::log10 (values(i));
8234  exp_min = std::min (exp_min, exp);
8235  exp_max = std::max (exp_max, exp);
8236  }
8237 
8238  for (int i = 0; i < values.numel (); i++)
8239  {
8240  bool omit_tick = false;
8241  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
8242  if (values(i) == omit_ticks(i_omit))
8243  omit_tick = true;
8244  if (omit_tick)
8245  {
8246  c(i) = "";
8247  continue;
8248  }
8249 
8250  if (values(i) < 0.0)
8251  exponent = std::floor (std::log10 (-values(i)));
8252  else
8253  exponent = std::floor (std::log10 (values(i)));
8254  significand = values(i) * std::pow (10., -exponent);
8255 
8256  os.str ("");
8257  if ((std::abs (significand) - 1) >
8258  10*std::numeric_limits<double>::epsilon())
8259  os << significand << 'x';
8260  else if (significand < 0)
8261  os << '-';
8262 
8263  os << "10^{";
8264 
8265  if (exponent < 0.0)
8266  {
8267  os << '-';
8268  exponent = -exponent;
8269  }
8270  if (exponent < 10. && (exp_max > 9 || exp_min < -9))
8271  os << '0';
8272  os << exponent << '}';
8273 
8274  if (ticklabelinterpreter.is ("latex"))
8275  c(i) = "$" + os.str () + "$";
8276  else
8277  c(i) = os.str ();
8278  }
8279  }
8280  else
8281  {
8282  for (int i = 0; i < values.numel (); i++)
8283  {
8284  bool omit_tick = false;
8285  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
8286  if (values(i) == omit_ticks(i_omit))
8287  omit_tick = true;
8288  if (omit_tick)
8289  c(i) = "";
8290  else
8291  {
8292  os.str ("");
8293  os << values(i);
8294  c(i) = os.str ();
8295  }
8296  }
8297  }
8298 
8299  labels = c;
8300 }
8301 
8302 Matrix
8303 axes::properties::get_ticklabel_extents (const Matrix& ticks,
8304  const string_vector& ticklabels,
8305  const Matrix& limits)
8306 {
8307  Matrix ext (1, 2, 0.0);
8308  double wmax, hmax;
8309  double dpr = device_pixel_ratio (get___myhandle__ ());
8310  wmax = hmax = 0.0;
8311  int n = std::min (ticklabels.numel (), ticks.numel ());
8312  for (int i = 0; i < n; i++)
8313  {
8314  double val = ticks(i);
8315  if (limits(0) <= val && val <= limits(1))
8316  {
8317  std::string label (ticklabels(i));
8318  label.erase (0, label.find_first_not_of (' '));
8319  label = label.substr (0, label.find_last_not_of (' ')+1);
8320 
8321  if (txt_renderer.ok ())
8322  {
8323  gh_manager& gh_mgr
8324  = octave::__get_gh_manager__ ("axes::properties::get_ticklabel_extents");
8325 
8326  octave::autolock guard (gh_mgr.graphics_lock ());
8327 
8328  ext = txt_renderer.get_extent (label, 0.0,
8329  get_ticklabelinterpreter ());
8330 
8331  wmax = std::max (wmax, ext(0) / dpr);
8332  hmax = std::max (hmax, ext(1) / dpr);
8333  }
8334  else
8335  {
8336  // FIXME: find a better approximation
8337  double fsize = get ("fontsize").double_value ();
8338  int len = label.length ();
8339 
8340  wmax = std::max (wmax, 0.5*fsize*len);
8341  hmax = fsize;
8342  }
8343  }
8344  }
8345 
8346  ext(0) = wmax;
8347  ext(1) = hmax;
8348  return ext;
8349 }
8350 
8351 void
8352 get_children_limits (double& min_val, double& max_val,
8353  double& min_pos, double& max_neg,
8354  const Matrix& kids, char limit_type)
8355 {
8356  octave_idx_type n = kids.numel ();
8357 
8358  gh_manager& gh_mgr = octave::__get_gh_manager__ ("get_children_limits");
8359 
8360  switch (limit_type)
8361  {
8362  case 'x':
8363  for (octave_idx_type i = 0; i < n; i++)
8364  {
8365  graphics_object go = gh_mgr.get_object (kids(i));
8366 
8367  if (go.is_xliminclude ())
8368  {
8369  octave_value lim = go.get_xlim ();
8370 
8371  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8372  }
8373  }
8374  break;
8375 
8376  case 'y':
8377  for (octave_idx_type i = 0; i < n; i++)
8378  {
8379  graphics_object go = gh_mgr.get_object (kids(i));
8380 
8381  if (go.is_yliminclude ())
8382  {
8383  octave_value lim = go.get_ylim ();
8384 
8385  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8386  }
8387  }
8388  break;
8389 
8390  case 'z':
8391  for (octave_idx_type i = 0; i < n; i++)
8392  {
8393  graphics_object go = gh_mgr.get_object (kids(i));
8394 
8395  if (go.is_zliminclude ())
8396  {
8397  octave_value lim = go.get_zlim ();
8398 
8399  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8400  }
8401  }
8402  break;
8403 
8404  case 'c':
8405  for (octave_idx_type i = 0; i < n; i++)
8406  {
8407  graphics_object go = gh_mgr.get_object (kids(i));
8408 
8409  if (go.is_climinclude ())
8410  {
8411  octave_value lim = go.get_clim ();
8412 
8413  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8414  }
8415  }
8416  break;
8417 
8418  case 'a':
8419  for (octave_idx_type i = 0; i < n; i++)
8420  {
8421  graphics_object go = gh_mgr.get_object (kids(i));
8422 
8423  if (go.is_aliminclude ())
8424  {
8425  octave_value lim = go.get_alim ();
8426 
8427  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8428  }
8429  }
8430  break;
8431 
8432  default:
8433  break;
8434  }
8435 }
8436 
8437 static std::set<double> updating_axis_limits;
8438 
8439 void
8440 axes::update_axis_limits (const std::string& axis_type,
8441  const graphics_handle& h)
8442 {
8443  if (updating_axis_limits.find (get_handle ().value ())
8444  != updating_axis_limits.end ())
8445  return;
8446 
8447  Matrix kids = Matrix (1, 1, h.value ());
8448 
8449  double min_val = octave::numeric_limits<double>::Inf ();
8450  double max_val = -octave::numeric_limits<double>::Inf ();
8451  double min_pos = octave::numeric_limits<double>::Inf ();
8452  double max_neg = -octave::numeric_limits<double>::Inf ();
8453 
8454  char update_type = 0;
8455 
8456  Matrix limits (1, 2);
8457  double val;
8458 
8459 #define FIX_LIMITS \
8460  val = limits(0); \
8461  if (octave::math::isfinite (val)) \
8462  min_val = val; \
8463  val = limits(1); \
8464  if (octave::math::isfinite (val)) \
8465  max_val = val;
8466 
8467  if (axis_type == "xdata" || axis_type == "xscale"
8468  || axis_type == "xlimmode" || axis_type == "xliminclude"
8469  || axis_type == "xlim")
8470  {
8471  limits = xproperties.get_xlim ().matrix_value ();
8472  FIX_LIMITS;
8473 
8474  update_type = 'x';
8475  if (xproperties.xlimmode_is ("auto"))
8476  {
8477  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8478 
8479  limits = xproperties.get_axis_limits (min_val, max_val,
8480  min_pos, max_neg,
8481  xproperties.xscale_is ("log"));
8482  }
8483  else
8484  xproperties.check_axis_limits (limits, kids,
8485  xproperties.xscale_is ("log"),
8486  update_type);
8487  }
8488  else if (axis_type == "ydata" || axis_type == "yscale"
8489  || axis_type == "ylimmode" || axis_type == "yliminclude"
8490  || axis_type == "ylim")
8491  {
8492  limits = xproperties.get_ylim ().matrix_value ();
8493  FIX_LIMITS;
8494 
8495  update_type = 'y';
8496  if (xproperties.ylimmode_is ("auto"))
8497  {
8498  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8499 
8500  limits = xproperties.get_axis_limits (min_val, max_val,
8501  min_pos, max_neg,
8502  xproperties.yscale_is ("log"));
8503  }
8504  else
8505  xproperties.check_axis_limits (limits, kids,
8506  xproperties.yscale_is ("log"),
8507  update_type);
8508  }
8509  else if (axis_type == "zdata" || axis_type == "zscale"
8510  || axis_type == "zlimmode" || axis_type == "zliminclude"
8511  || axis_type == "zlim")
8512  {
8513  limits = xproperties.get_zlim ().matrix_value ();
8514  FIX_LIMITS;
8515 
8516  update_type = 'z';
8517  if (xproperties.zlimmode_is ("auto"))
8518  {
8519  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8520 
8521  xproperties.set_has3Dkids ((max_val - min_val) >
8522  std::numeric_limits<double>::epsilon ());
8523 
8524  limits = xproperties.get_axis_limits (min_val, max_val,
8525  min_pos, max_neg,
8526  xproperties.zscale_is ("log"));
8527  }
8528  else
8529  {
8530  // FIXME: get_children_limits is only needed here in order to know
8531  // if there are 3D children. Is there a way to avoid this call?
8532  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8533 
8534  xproperties.set_has3Dkids ((max_val - min_val) >
8535  std::numeric_limits<double>::epsilon ());
8536 
8537  xproperties.check_axis_limits (limits, kids,
8538  xproperties.zscale_is ("log"),
8539  update_type);
8540  }
8541  }
8542  else if (axis_type == "cdata" || axis_type == "climmode"
8543  || axis_type == "cdatamapping" || axis_type == "climinclude"
8544  || axis_type == "clim")
8545  {
8546  if (xproperties.climmode_is ("auto"))
8547  {
8548  limits = xproperties.get_clim ().matrix_value ();
8549  FIX_LIMITS;
8550 
8551  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8552 
8553  if (min_val > max_val)
8554  {
8555  min_val = min_pos = 0;
8556  max_val = 1;
8557  }
8558  else if (min_val == max_val)
8559  {
8560  max_val = min_val + 1;
8561  min_val -= 1;
8562  }
8563 
8564  limits(0) = min_val;
8565  limits(1) = max_val;
8566 
8567  update_type = 'c';
8568  }
8569  }
8570  else if (axis_type == "alphadata" || axis_type == "alimmode"
8571  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8572  || axis_type == "alim")
8573  {
8574  if (xproperties.alimmode_is ("auto"))
8575  {
8576  limits = xproperties.get_alim ().matrix_value ();
8577  FIX_LIMITS;
8578 
8579  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8580 
8581  if (min_val > max_val)
8582  {
8583  min_val = min_pos = 0;
8584  max_val = 1;
8585  }
8586  else if (min_val == max_val)
8587  max_val = min_val + 1;
8588 
8589  limits(0) = min_val;
8590  limits(1) = max_val;
8591 
8592  update_type = 'a';
8593  }
8594  }
8595 
8596 #undef FIX_LIMITS
8597 
8598  octave::unwind_protect frame;
8600 
8601  updating_axis_limits.insert (get_handle ().value ());
8602  bool is_auto;
8603 
8604  switch (update_type)
8605  {
8606  case 'x':
8607  is_auto = xproperties.xlimmode_is ("auto");
8608  xproperties.set_xlim (limits);
8609  if (is_auto)
8610  xproperties.set_xlimmode ("auto");
8611  xproperties.update_xlim ();
8612  break;
8613 
8614  case 'y':
8615  is_auto = xproperties.ylimmode_is ("auto");
8616  xproperties.set_ylim (limits);
8617  if (is_auto)
8618  xproperties.set_ylimmode ("auto");
8619  xproperties.update_ylim ();
8620  break;
8621 
8622  case 'z':
8623  is_auto = xproperties.zlimmode_is ("auto");
8624  xproperties.set_zlim (limits);
8625  if (is_auto)
8626  xproperties.set_zlimmode ("auto");
8627  xproperties.update_zlim ();
8628  break;
8629 
8630  case 'c':
8631  xproperties.set_clim (limits);
8632  xproperties.set_climmode ("auto");
8633  break;
8634 
8635  case 'a':
8636  xproperties.set_alim (limits);
8637  xproperties.set_alimmode ("auto");
8638  break;
8639 
8640  default:
8641  break;
8642  }
8643 
8644  xproperties.update_transform ();
8645 }
8646 
8647 // FIXME: This function is called repeatedly while the axes are being set up.
8648 // There is probably some way to make this more efficient.
8649 
8650 void
8651 axes::update_axis_limits (const std::string& axis_type)
8652 {
8653  if ((updating_axis_limits.find (get_handle ().value ())
8654  != updating_axis_limits.end ())
8655  || (updating_aspectratios.find (get_handle ().value ())
8656  != updating_aspectratios.end ()))
8657  return;
8658 
8659  Matrix kids = xproperties.get_children ();
8660 
8661  double min_val = octave::numeric_limits<double>::Inf ();
8662  double max_val = -octave::numeric_limits<double>::Inf ();
8663  double min_pos = octave::numeric_limits<double>::Inf ();
8664  double max_neg = -octave::numeric_limits<double>::Inf ();
8665 
8666  char update_type = 0;
8667 
8668  Matrix limits;
8669 
8670  if (axis_type == "xdata" || axis_type == "xscale"
8671  || axis_type == "xlimmode" || axis_type == "xliminclude"
8672  || axis_type == "xlim")
8673  {
8674  update_type = 'x';
8675  if (xproperties.xlimmode_is ("auto"))
8676  {
8677  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8678 
8679  limits = xproperties.get_axis_limits (min_val, max_val,
8680  min_pos, max_neg,
8681  xproperties.xscale_is ("log"));
8682  }
8683  else
8684  {
8685  limits = xproperties.get_xlim ().matrix_value ();
8686  xproperties.check_axis_limits (limits, kids,
8687  xproperties.xscale_is ("log"),
8688  update_type);
8689  }
8690  }
8691  else if (axis_type == "ydata" || axis_type == "yscale"
8692  || axis_type == "ylimmode" || axis_type == "yliminclude"
8693  || axis_type == "ylim")
8694  {
8695  update_type = 'y';
8696  if (xproperties.ylimmode_is ("auto"))
8697  {
8698  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8699 
8700  limits = xproperties.get_axis_limits (min_val, max_val,
8701  min_pos, max_neg,
8702  xproperties.yscale_is ("log"));
8703  }
8704  else
8705  {
8706  limits = xproperties.get_ylim ().matrix_value ();
8707  xproperties.check_axis_limits (limits, kids,
8708  xproperties.yscale_is ("log"),
8709  update_type);
8710  }
8711  }
8712  else if (axis_type == "zdata" || axis_type == "zscale"
8713  || axis_type == "zlimmode" || axis_type == "zliminclude"
8714  || axis_type == "zlim")
8715  {
8716  update_type = 'z';
8717  if (xproperties.zlimmode_is ("auto"))
8718  {
8719  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8720 
8721  xproperties.set_has3Dkids ((max_val - min_val) >
8722  std::numeric_limits<double>::epsilon ());
8723 
8724 
8725  limits = xproperties.get_axis_limits (min_val, max_val,
8726  min_pos, max_neg,
8727  xproperties.zscale_is ("log"));
8728  }
8729  else
8730  {
8731  // FIXME: get_children_limits is only needed here in order to know
8732  // if there are 3D children. Is there a way to avoid this call?
8733  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8734 
8735  xproperties.set_has3Dkids ((max_val - min_val) >
8736  std::numeric_limits<double>::epsilon ());
8737 
8738  limits = xproperties.get_zlim ().matrix_value ();
8739  xproperties.check_axis_limits (limits, kids,
8740  xproperties.zscale_is ("log"),
8741  update_type);
8742  }
8743  }
8744  else if (axis_type == "cdata" || axis_type == "climmode"
8745  || axis_type == "cdatamapping" || axis_type == "climinclude"
8746  || axis_type == "clim")
8747  {
8748  if (xproperties.climmode_is ("auto"))
8749  {
8750  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8751 
8752  if (min_val > max_val)
8753  {
8754  min_val = min_pos = 0;
8755  max_val = 1;
8756  }
8757  else if (min_val == max_val)
8758  {
8759  max_val = min_val + 1;
8760  min_val -= 1;
8761  }
8762 
8763  limits.resize (1, 2);
8764 
8765  limits(0) = min_val;
8766  limits(1) = max_val;
8767 
8768  update_type = 'c';
8769  }
8770 
8771  }
8772  else if (axis_type == "alphadata" || axis_type == "alimmode"
8773  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8774  || axis_type == "alim")
8775  {
8776  if (xproperties.alimmode_is ("auto"))
8777  {
8778  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8779 
8780  if (min_val > max_val)
8781  {
8782  min_val = min_pos = 0;
8783  max_val = 1;
8784  }
8785  else if (min_val == max_val)
8786  max_val = min_val + 1;
8787 
8788  limits.resize (1, 2);
8789 
8790  limits(0) = min_val;
8791  limits(1) = max_val;
8792 
8793  update_type = 'a';
8794  }
8795 
8796  }
8797 
8798  octave::unwind_protect frame;
8800 
8801  updating_axis_limits.insert (get_handle ().value ());
8802  bool is_auto;
8803 
8804  switch (update_type)
8805  {
8806  case 'x':
8807  is_auto = xproperties.xlimmode_is ("auto");
8808  xproperties.set_xlim (limits);
8809  if (is_auto)
8810  xproperties.set_xlimmode ("auto");
8811  xproperties.update_xlim ();
8812  break;
8813 
8814  case 'y':
8815  is_auto = xproperties.ylimmode_is ("auto");
8816  xproperties.set_ylim (limits);
8817  if (is_auto)
8818  xproperties.set_ylimmode ("auto");
8819  xproperties.update_ylim ();
8820  break;
8821 
8822  case 'z':
8823  is_auto = xproperties.zlimmode_is ("auto");
8824  xproperties.set_zlim (limits);
8825  if (is_auto)
8826  xproperties.set_zlimmode ("auto");
8827  xproperties.update_zlim ();
8828  break;
8829 
8830  case 'c':
8831  xproperties.set_clim (limits);
8832  xproperties.set_climmode ("auto");
8833  break;
8834 
8835  case 'a':
8836  xproperties.set_alim (limits);
8837  xproperties.set_alimmode ("auto");
8838  break;
8839 
8840  default:
8841  break;
8842  }
8843 
8844  xproperties.update_transform ();
8845 }
8846 
8847 inline
8848 double force_in_range (double x, double lower, double upper)
8849 {
8850  if (x < lower)
8851  return lower;
8852  else if (x > upper)
8853  return upper;
8854  else
8855  return x;
8856 }
8857 
8858 static Matrix
8859 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
8860 {
8861  Matrix new_lims = lims;
8862 
8863  double lo = lims(0);
8864  double hi = lims(1);
8865 
8866  bool is_negative = lo < 0 && hi < 0;
8867 
8868  if (is_logscale)
8869  {
8870  if (is_negative)
8871  {
8872  double tmp = hi;
8873  hi = std::log10 (-lo);
8874  lo = std::log10 (-tmp);
8875  val = std::log10 (-val);
8876  }
8877  else
8878  {
8879  hi = std::log10 (hi);
8880  lo = std::log10 (lo);
8881  val = std::log10 (val);
8882  }
8883  }
8884 
8885  // Perform the zooming
8886  lo = val + (lo - val) / factor;
8887  hi = val + (hi - val) / factor;
8888 
8889  if (is_logscale)
8890  {
8891  if (is_negative)
8892  {
8893  double tmp = -std::pow (10.0, hi);
8894  hi = -std::pow (10.0, lo);
8895  lo = tmp;
8896  }
8897  else
8898  {
8899  lo = std::pow (10.0, lo);
8900  hi = std::pow (10.0, hi);
8901  }
8902  }
8903 
8904  new_lims(0) = lo;
8905  new_lims(1) = hi;
8906 
8907  return new_lims;
8908 }
8909 
8910 void
8911 axes::properties::zoom_about_point (const std::string& mode,
8912  double x, double y, double factor,
8913  bool push_to_zoom_stack)
8914 {
8915  // FIXME: Do we need error checking here?
8916  Matrix xlims = get_xlim ().matrix_value ();
8917  Matrix ylims = get_ylim ().matrix_value ();
8918 
8919  // Get children axes limits
8920  Matrix kids = get_children ();
8921  double minx = octave::numeric_limits<double>::Inf ();
8922  double maxx = -octave::numeric_limits<double>::Inf ();
8923  double min_pos_x = octave::numeric_limits<double>::Inf ();
8924  double max_neg_x = -octave::numeric_limits<double>::Inf ();
8925  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
8926 
8927  double miny = octave::numeric_limits<double>::Inf ();
8928  double maxy = -octave::numeric_limits<double>::Inf ();
8929  double min_pos_y = octave::numeric_limits<double>::Inf ();
8930  double max_neg_y = -octave::numeric_limits<double>::Inf ();
8931  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
8932 
8933  xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
8934  ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
8935 
8936  zoom (mode, xlims, ylims, push_to_zoom_stack);
8937 }
8938 
8939 void
8940 axes::properties::zoom (const std::string& mode, double factor,
8941  bool push_to_zoom_stack)
8942 {
8943  // FIXME: Do we need error checking here?
8944  Matrix xlims = get_xlim ().matrix_value ();
8945  Matrix ylims = get_ylim ().matrix_value ();
8946 
8947  double x = (xlims(0) + xlims(1)) / 2;
8948  double y = (ylims(0) + ylims(1)) / 2;
8949 
8950  zoom_about_point (mode, x, y, factor, push_to_zoom_stack);
8951 }
8952 
8953 void
8955 {
8956  if (zoom_stack.empty ())
8957  {
8958  zoom_stack.push_front (xlimmode.get ());
8959  zoom_stack.push_front (xlim.get ());
8960  zoom_stack.push_front (ylimmode.get ());
8961  zoom_stack.push_front (ylim.get ());
8962  zoom_stack.push_front (zlimmode.get ());
8963  zoom_stack.push_front (zlim.get ());
8964  zoom_stack.push_front (view.get ());
8965  }
8966 }
8967 
8968 void
8969 axes::properties::zoom (const std::string& mode,
8970  const Matrix& xl, const Matrix& yl,
8971  bool push_to_zoom_stack)
8972 {
8973  if (xl(0) == xl(1) || yl(0) == yl(1))
8974  {
8975  warning ("invalid zoom region");
8976  return;
8977  }
8978 
8979  if (push_to_zoom_stack)
8980  push_zoom_stack ();
8981 
8982  if (mode == "horizontal" || mode == "both")
8983  {
8984  xlim = xl;
8985  xlimmode = "manual";
8986  }
8987 
8988  if (mode == "vertical" || mode == "both")
8989  {
8990  ylim = yl;
8991  ylimmode = "manual";
8992  }
8993 
8994  update_transform ();
8995 
8996  if (mode == "horizontal" || mode == "both")
8997  update_xlim ();
8998 
8999  if (mode == "vertical" || mode == "both")
9000  update_ylim ();
9001 }
9002 
9003 static Matrix
9004 do_translate (double x0, double x1, const Matrix& lims, bool is_logscale)
9005 {
9006  Matrix new_lims = lims;
9007 
9008  double lo = lims(0);
9009  double hi = lims(1);
9010 
9011  bool is_negative = lo < 0 && hi < 0;
9012 
9013  double delta;
9014 
9015  if (is_logscale)
9016  {
9017  if (is_negative)
9018  {
9019  double tmp = hi;
9020  hi = std::log10 (-lo);
9021  lo = std::log10 (-tmp);
9022  x0 = -x0;
9023  x1 = -x1;
9024  }
9025  else
9026  {
9027  hi = std::log10 (hi);
9028  lo = std::log10 (lo);
9029  }
9030 
9031  delta = std::log10 (x0) - std::log10 (x1);
9032  }
9033  else
9034  {
9035  delta = x0 - x1;
9036  }
9037 
9038  // Perform the translation
9039  lo += delta;
9040  hi += delta;
9041 
9042  if (is_logscale)
9043  {
9044  if (is_negative)
9045  {
9046  double tmp = -std::pow (10.0, hi);
9047  hi = -std::pow (10.0, lo);
9048  lo = tmp;
9049  }
9050  else
9051  {
9052  lo = std::pow (10.0, lo);
9053  hi = std::pow (10.0, hi);
9054  }
9055  }
9056 
9057  new_lims(0) = lo;
9058  new_lims(1) = hi;
9059 
9060  return new_lims;
9061 }
9062 
9063 void
9064 axes::properties::translate_view (const std::string& mode,
9065  double x0, double x1, double y0, double y1,
9066  bool push_to_zoom_stack)
9067 {
9068  // FIXME: Do we need error checking here?
9069  Matrix xlims = get_xlim ().matrix_value ();
9070  Matrix ylims = get_ylim ().matrix_value ();
9071 
9072  // Get children axes limits
9073  Matrix kids = get_children ();
9074  double minx = octave::numeric_limits<double>::Inf ();
9075  double maxx = -octave::numeric_limits<double>::Inf ();
9076  double min_pos_x = octave::numeric_limits<double>::Inf ();
9077  double max_neg_x = -octave::numeric_limits<double>::Inf ();
9078  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
9079 
9080  double miny = octave::numeric_limits<double>::Inf ();
9081  double maxy = -octave::numeric_limits<double>::Inf ();
9082  double min_pos_y = octave::numeric_limits<double>::Inf ();
9083  double max_neg_y = -octave::numeric_limits<double>::Inf ();
9084  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
9085 
9086  xlims = do_translate (x0, x1, xlims, xscale_is ("log"));
9087  ylims = do_translate (y0, y1, ylims, yscale_is ("log"));
9088 
9089  zoom (mode, xlims, ylims, push_to_zoom_stack);
9090 }
9091 
9092 void
9093 axes::properties::pan (const std::string& mode, double factor,
9094  bool push_to_zoom_stack)
9095 {
9096  // FIXME: Do we need error checking here?
9097  Matrix xlims = get_xlim ().matrix_value ();
9098  Matrix ylims = get_ylim ().matrix_value ();
9099 
9100  double x0 = (xlims(0) + xlims(1)) / 2;
9101  double y0 = (ylims(0) + ylims(1)) / 2;
9102 
9103  double x1 = x0 + (xlims(1) - xlims(0)) * factor;
9104  double y1 = y0 + (ylims(1) - ylims(0)) * factor;
9105 
9106  translate_view (mode, x0, x1, y0, y1, push_to_zoom_stack);
9107 }
9108 
9109 void
9110 axes::properties::rotate3d (double x0, double x1, double y0, double y1,
9111  bool push_to_zoom_stack)
9112 {
9113  if (push_to_zoom_stack)
9114  push_zoom_stack ();
9115 
9116  Matrix bb = get_boundingbox (true);
9117  Matrix new_view = get_view ().matrix_value ();
9118 
9119  // Compute new view angles
9120  new_view(0) += ((x0 - x1) * (180.0 / bb(2)));
9121  new_view(1) += ((y1 - y0) * (180.0 / bb(3)));
9122 
9123  // Clipping
9124  new_view(1) = std::min (new_view(1), 90.0);
9125  new_view(1) = std::max (new_view(1), -90.0);
9126  if (new_view(0) > 180.0)
9127  new_view(0) -= 360.0;
9128  else if (new_view(0) < -180.0)
9129  new_view(0) += 360.0;
9130 
9131  // Snapping
9132  double snapmargin = 1.0;
9133  for (int a = -90; a <= 90; a += 90)
9134  {
9135  if ((a - snapmargin) < new_view(1) && new_view(1) < (a + snapmargin))
9136  {
9137  new_view(1) = a;
9138  break;
9139  }
9140  }
9141 
9142  for (int a = -180; a <= 180; a += 180)
9143  if ((a - snapmargin) < new_view(0) && new_view(0) < (a + snapmargin))
9144  {
9145  if (a == 180)
9146  new_view(0) = -180;
9147  else
9148  new_view(0) = a;
9149  break;
9150  }
9151 
9152  // Update axes properties
9153  set_view (new_view);
9154 }
9155 
9156 void
9157 axes::properties::rotate_view (double delta_el, double delta_az,
9158  bool push_to_zoom_stack)
9159 {
9160  if (push_to_zoom_stack)
9161  push_zoom_stack ();
9162 
9163  Matrix v = get_view ().matrix_value ();
9164 
9165  v(1) += delta_el;
9166 
9167  if (v(1) > 90)
9168  v(1) = 90;
9169  if (v(1) < -90)
9170  v(1) = -90;
9171 
9172  v(0) = fmod (v(0) - delta_az + 720,360);
9173 
9174  set_view (v);
9175 
9176  update_transform ();
9177 }
9178 
9179 void
9181 {
9182  if (zoom_stack.size () >= 7)
9183  {
9184  view = zoom_stack.front ();
9185  zoom_stack.pop_front ();
9186 
9187  zlim = zoom_stack.front ();
9188  zoom_stack.pop_front ();
9189 
9190  zlimmode = zoom_stack.front ();
9191  zoom_stack.pop_front ();
9192 
9193  ylim = zoom_stack.front ();
9194  zoom_stack.pop_front ();
9195 
9196  ylimmode = zoom_stack.front ();
9197  zoom_stack.pop_front ();
9198 
9199  xlim = zoom_stack.front ();
9200  zoom_stack.pop_front ();
9201 
9202  xlimmode = zoom_stack.front ();
9203  zoom_stack.pop_front ();
9204 
9205  update_transform ();
9206 
9207  update_xlim ();
9208  update_ylim ();
9209  update_zlim ();
9210 
9211  update_view ();
9212  }
9213 }
9214 
9215 void
9217 {
9218  if (! is_handle_visible ())
9219  {
9220  gh_manager& gh_mgr
9221  = octave::__get_gh_manager__ ("axes::properties::update_handlevisibility");
9222 
9223  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9224 
9225  graphics_object fig (go.get_ancestor ("figure"));
9226  octave_value ca = fig.get ("currentaxes");
9227  if (! ca.isempty () && ca.double_value () == __myhandle__)
9228  {
9229  octave::autolock guard (gh_mgr.graphics_lock ());
9230 
9231  octave_value kids = fig.get ("children");
9232  if (kids.isempty ())
9233  fig.set ("currentaxes", Matrix ());
9234  else
9235  {
9236  NDArray kidsarray = kids.array_value ();
9237  fig.set ("currentaxes", kidsarray(0));
9238  }
9239  }
9240  }
9241 
9242  base_properties::update_handlevisibility ();
9243 }
9244 
9245 void
9246 figure::properties::init_toolkit (void)
9247 {
9248  octave::gtk_manager& gtk_mgr
9249  = octave::__get_gtk_manager__ ("figure::properties::init_toolkit");
9250 
9251  toolkit = gtk_mgr.get_toolkit ();
9252 }
9253 
9254 void
9256 {
9257  size_t items_to_leave_on_stack = (do_unzoom ? 7 : 0);
9258 
9259  while (zoom_stack.size () > items_to_leave_on_stack)
9260  zoom_stack.pop_front ();
9261 
9262  if (do_unzoom)
9263  unzoom ();
9264 }
9265 
9266 void
9267 axes::properties::trigger_normals_calc (void)
9268 {
9269  // Find all patch (and surface) objects within axes
9270  std::list<graphics_object> children_list;
9271  std::list<graphics_object>::iterator children_list_iter;
9272  get_children_of_type ("patch", false, true, children_list);
9273  get_children_of_type ("surface", false, true, children_list);
9274 
9275  // trigger normals calculation for these objects
9276  for (children_list_iter = children_list.begin ();
9277  children_list_iter != children_list.end (); children_list_iter++)
9278  {
9279  graphics_object kid = *children_list_iter;
9280  if (kid.isa ("patch"))
9281  {
9282  patch::properties& patch_props
9283  = dynamic_cast<patch::properties&> (kid.get_properties ());
9284  patch_props.update_normals (false);
9285  }
9286  else
9287  {
9288  surface::properties& surface_props
9289  = dynamic_cast<surface::properties&> (kid.get_properties ());
9290  surface_props.update_normals (false);
9291  }
9292  }
9293 }
9294 
9295 void
9297 {
9298  // empty list of local defaults
9300 
9301  // Save warning state of "Octave:deprecated-property"
9302  int state = toggle_warn ("Octave:deprecated-property", false);
9303 
9304  // reset factory defaults
9306  set_defaults ("reset");
9307 
9308  toggle_warn ("Octave:deprecated-property", true, state);
9309 }
9310 
9311 void
9313 {
9315 
9317  xinitialize (xproperties.get_xlabel ());
9318  xinitialize (xproperties.get_ylabel ());
9319  xinitialize (xproperties.get_zlabel ());
9320 
9321  xproperties.sync_positions ();
9322 }
9323 
9324 // ---------------------------------------------------------------------
9325 
9326 Matrix
9327 line::properties::compute_xlim (void) const
9328 {
9329  Matrix m (1, 4);
9330 
9331  m(0) = xdata.min_val ();
9332  m(1) = xdata.max_val ();
9333  m(2) = xdata.min_pos ();
9334  m(3) = xdata.max_neg ();
9335 
9336  return m;
9337 }
9338 
9339 Matrix
9340 line::properties::compute_ylim (void) const
9341 {
9342  Matrix m (1, 4);
9343 
9344  m(0) = ydata.min_val ();
9345  m(1) = ydata.max_val ();
9346  m(2) = ydata.min_pos ();
9347  m(3) = ydata.max_neg ();
9348 
9349  return m;
9350 }
9351 
9352 // ---------------------------------------------------------------------
9353 
9354 Matrix
9355 text::properties::get_data_position (void) const
9356 {
9357  Matrix pos = get_position ().matrix_value ();
9358 
9359  if (! units_is ("data"))
9360  pos = convert_text_position (pos, *this, get_units (), "data");
9361 
9362  return pos;
9363 }
9364 
9365 Matrix
9366 text::properties::get_extent_matrix (void) const
9367 {
9368  // FIXME: Should this function also add the (x,y) base position?
9369  return extent.get ().matrix_value ();
9370 }
9371 
9373 text::properties::get_extent (void) const
9374 {
9375  // FIXME: This doesn't work right for 3D plots.
9376  // (It doesn't in Matlab either, at least not in version 6.5.)
9377  Matrix m = extent.get ().matrix_value ();
9378  Matrix pos = get_position ().matrix_value ();
9379  Matrix p = convert_text_position (pos, *this, get_units (), "pixels");
9380 
9381  m(0) += p(0);
9382  m(1) += p(1);
9383 
9384  Matrix bbox = convert_text_position (m, *this, "pixels", get_units ());
9385 
9386  double dpr = device_pixel_ratio (get___myhandle__ ());
9387 
9388  for (octave_idx_type ii = 0; ii < bbox.numel (); ii++)
9389  bbox(ii) = bbox(ii) / dpr;
9390 
9391  return bbox;
9392 }
9393 
9394 void
9395 text::properties::set_fontunits (const octave_value& val)
9396 {
9397  caseless_str old_fontunits = get_fontunits ();
9398 
9399  if (fontunits.set (val, true))
9400  {
9401  update_fontunits (old_fontunits);
9402  mark_modified ();
9403  }
9404 }
9405 
9406 void
9408 {
9409  caseless_str new_units = get_fontunits ();
9410  double parent_height = 0;
9411  double fontsz = get_fontsize ();
9412 
9413  if (new_units == "normalized" || old_units == "normalized")
9414  {
9415  gh_manager& gh_mgr
9416  = octave::__get_gh_manager__ ("text::properties::update_fontunits");
9417 
9418  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9419 
9420  graphics_object ax (go.get_ancestor ("axes"));
9421 
9422  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
9423  }
9424 
9425  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
9426 
9427  set_fontsize (octave_value (fontsz));
9428 }
9429 
9430 void
9432 {
9433  double dpr = device_pixel_ratio (get___myhandle__ ());
9434 
9435  gh_manager& gh_mgr
9436  = octave::__get_gh_manager__ ("text::properties::update_font");
9437 
9438  octave::autolock guard (gh_mgr.graphics_lock ());
9439 
9440  txt_renderer.set_font (get ("fontname").string_value (),
9441  get ("fontweight").string_value (),
9442  get ("fontangle").string_value (),
9443  get ("__fontsize_points__").double_value () * dpr);
9444 
9445  txt_renderer.set_anti_aliasing (is_fontsmoothing ());
9446 
9447  Matrix c = get_color_rgb ();
9448  if (! c.isempty ())
9449  txt_renderer.set_color (c);
9450 
9451 }
9452 
9453 void
9455 {
9456  int halign = 0;
9457  int valign = 0;
9458 
9459  if (horizontalalignment_is ("center"))
9460  halign = 1;
9461  else if (horizontalalignment_is ("right"))
9462  halign = 2;
9463 
9464  if (verticalalignment_is ("middle"))
9465  valign = 1;
9466  else if (verticalalignment_is ("top"))
9467  valign = 2;
9468  else if (verticalalignment_is ("baseline"))
9469  valign = 3;
9470  else if (verticalalignment_is ("cap"))
9471  valign = 4;
9472 
9473  Matrix bbox;
9474 
9475  // FIXME: string should be parsed only when modified, for efficiency
9476 
9477  octave_value string_prop = get_string ();
9478 
9479  string_vector sv = string_prop.string_vector_value ();
9480 
9481  gh_manager& gh_mgr
9482  = octave::__get_gh_manager__ ("text::properties::update_text_extent");
9483 
9484  octave::autolock guard (gh_mgr.graphics_lock ());
9485 
9486  txt_renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
9487  halign, valign, get_rotation (),
9488  get_interpreter ());
9489  // The bbox is relative to the text's position. We'll leave it that
9490  // way, because get_position does not return valid results when the
9491  // text is first constructed. Conversion to proper coordinates is
9492  // performed in get_extent.
9493  set_extent (bbox);
9494 
9495  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
9496  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
9497  update_autopos ("sync");
9498 }
9499 
9500 void
9501 text::properties::request_autopos (void)
9502 {
9503  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
9504  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
9505  update_autopos (get___autopos_tag__ ());
9506 }
9507 
9508 void
9510 {
9511  if (! units_is ("data"))
9512  {
9513  set_xliminclude ("off");
9514  set_yliminclude ("off");
9515  set_zliminclude ("off");
9516  }
9517 
9518  Matrix pos = get_position ().matrix_value ();
9519 
9520  pos = convert_text_position (pos, *this, cached_units, get_units ());
9521 
9522  // FIXME: if the current axes view is 2D, then one should probably drop
9523  // the z-component of "pos" and leave "zliminclude" to "off".
9524 
9525  bool autopos = positionmode_is ("auto");
9526 
9527  set_position (pos);
9528 
9529  if (autopos)
9530  set_positionmode ("auto");
9531 
9532  if (units_is ("data"))
9533  {
9534  set_xliminclude ("on");
9535  set_yliminclude ("on");
9536  // FIXME: see above
9537  set_zliminclude ("off");
9538  }
9539 
9540  cached_units = get_units ();
9541 }
9542 
9543 double
9544 text::properties::get___fontsize_points__ (double box_pix_height) const
9545 {
9546  double fontsz = get_fontsize ();
9547  double parent_height = box_pix_height;
9548 
9549  gh_manager& gh_mgr
9550  = octave::__get_gh_manager__ ("text::properties::get___fontsize_points__");
9551 
9552  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9553 
9554  if (fontunits_is ("normalized") && parent_height <= 0)
9555  {
9556  graphics_object ax (go.get_ancestor ("axes"));
9557 
9558  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
9559  }
9560 
9561  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
9562 }
9563 
9564 // ---------------------------------------------------------------------
9565 
9568 {
9569  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
9570 }
9571 
9572 // ---------------------------------------------------------------------
9573 
9574 void
9576 {
9578 
9579  // trigger normals calculation for the respective children of this axes object
9580  axes::properties& parent_axes_prop
9581  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
9582  parent_axes_prop.trigger_normals_calc ();
9583 }
9584 
9585 void
9586 light::properties::update_visible (void)
9587 {
9588  gh_manager& gh_mgr
9589  = octave::__get_gh_manager__ ("light::properties::update_visible");
9590 
9591  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
9592 
9593  axes::properties& ax_props = dynamic_cast<axes::properties&>
9594  (go.get_ancestor ("axes").get_properties ());
9595  if (is_visible ())
9596  ax_props.increase_num_lights ();
9597  else
9598  ax_props.decrease_num_lights ();
9599 }
9600 
9601 // ---------------------------------------------------------------------
9602 
9603 bool
9605 {
9606  gh_manager& gh_mgr
9607  = octave::__get_gh_manager__ ("patch::properties::get_do_lighting");
9608 
9609  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
9610 
9611  axes::properties& ax_props = dynamic_cast<axes::properties&>
9612  (go.get_ancestor ("axes").get_properties ());
9613 
9614  return (ax_props.get_num_lights () > 0);
9615 }
9616 
9619 {
9620  octave_value fvc = get_facevertexcdata ();
9621  if (fvc.is_undefined () || fvc.isempty ())
9622  return Matrix ();
9623  else
9624  return convert_cdata (*this, fvc, cdatamapping_is ("scaled"), 2);
9625 }
9626 
9627 static bool updating_patch_data = false;
9628 
9629 void
9630 patch::properties::update_fvc (void)
9631 {
9632  if (updating_patch_data)
9633  return;
9634 
9635  Matrix xd = get_xdata ().matrix_value ();
9636  Matrix yd = get_ydata ().matrix_value ();
9637  Matrix zd = get_zdata ().matrix_value ();
9638  NDArray cd = get_cdata ().array_value ();
9639 
9640  bad_data_msg = "";
9641  if (xd.dims () != yd.dims ()
9642  || (xd.dims () != zd.dims () && ! zd.isempty ()))
9643  {
9644  bad_data_msg = "x/y/zdata must have the same dimensions";
9645  return;
9646  }
9647 
9648  // Faces and Vertices
9649  dim_vector dv;
9650  bool is3D = false;
9651  octave_idx_type nr = xd.rows ();
9652  octave_idx_type nc = xd.columns ();
9653  if (nr == 1 && nc > 1)
9654  {
9655  nr = nc;
9656  nc = 1;
9657  xd = xd.as_column ();
9658  yd = yd.as_column ();
9659  zd = zd.as_column ();
9660  }
9661 
9662  dv(0) = nr * nc;
9663  if (zd.isempty ())
9664  dv(1) = 2;
9665  else
9666  {
9667  dv(1) = 3;
9668  is3D = true;
9669  }
9670 
9671  Matrix vert (dv);
9672  Matrix idx (nc, nr);
9673 
9674  octave_idx_type kk = 0;
9675  for (octave_idx_type jj = 0; jj < nc; jj++)
9676  {
9677  for (octave_idx_type ii = 0; ii < nr; ii++)
9678  {
9679  vert(kk,0) = xd(ii,jj);
9680  vert(kk,1) = yd(ii,jj);
9681  if (is3D)
9682  vert(kk,2) = zd(ii,jj);
9683 
9684  idx(jj,ii) = static_cast<double> (kk+1);
9685 
9686  kk++;
9687  }
9688  }
9689 
9690  // facevertexcdata
9691  Matrix fvc;
9692  if (cd.ndims () == 3)
9693  {
9694  dv(0) = cd.rows () * cd.columns ();
9695  dv(1) = cd.dims ()(2);
9696  fvc = cd.reshape (dv);
9697  }
9698  else
9699  fvc = cd.as_column ();
9700 
9701  // FIXME: shouldn't we update facevertexalphadata here ?
9702 
9703  octave::unwind_protect frame;
9705  updating_patch_data = true;
9706 
9707  faces.set (idx);
9708  vertices.set (vert);
9709  facevertexcdata.set (fvc);
9710 }
9711 
9712 // core coplanar tester
9713 bool is_coplanar (const Matrix &cov)
9714 {
9715  // Accuracy note: this test will also accept single precision input (although
9716  // stored in double precision). This is because the error threshold is
9717  // sqrt(tol) = 1.5e-7.
9718  double tol = 100 * std::numeric_limits<double>::epsilon ();
9719  EIG eig (cov, false, false, true);
9720  ColumnVector ev = real (eig.eigenvalues ());
9721  return ev.min () <= tol * ev.max ();
9722 }
9723 
9724 std::vector<octave_idx_type>
9725 coplanar_partition (const Matrix &vert, const Matrix &idx,
9727 {
9728  std::vector<octave_idx_type> coplanar_ends;
9729 
9730  Matrix plane_pivot = Matrix (1, 3, 0.0);
9731  for (octave_idx_type i = 0; i < 3; i++)
9732  plane_pivot(0,i) = vert(idx(0,jj)-1,i);
9733 
9734  Matrix fc = Matrix (0, 3, 0.0); // face corner vertex coordinates
9735  Matrix fa = Matrix (1, 3, 0.0); // for append face corner
9736  Matrix coor_cov = Matrix (3, 3, 0.0);
9737 
9738  if (nc >= 5)
9739  {
9740  // Coplanar test that involves all points.
9741  // For nc == 4, this initial test is not beneficial at all.
9742  // If the probability of coplanar input is more than half, for
9743  // the best average performance, we should use nc >= 5.
9744  // Higher threshold is meaningful only when input is known to be
9745  // non-coplanar and nc is small.
9746 
9747  fc.resize (nc - 1, 3);
9748  for (octave_idx_type j = 1; j < nc; j++)
9749  for (octave_idx_type i = 0; i < 3; i++)
9750  fc(j-1,i) = vert(idx(j,jj)-1,i) - plane_pivot(i);
9751 
9752  coor_cov = fc.transpose () * fc;
9753  if (is_coplanar (coor_cov))
9754  {
9755  coplanar_ends.push_back (nc - 1);
9756  return coplanar_ends;
9757  }
9758  }
9759 
9760  fc.resize (3, 3);
9761  octave_idx_type i_start = 1;
9762  octave_idx_type i_end = 2;
9763 
9764  // Split the polygon into coplanar segments.
9765  // The first point is common corner of all planes.
9766  while (i_start < nc - 1)
9767  {
9768  i_end = i_start + 2;
9769  if (i_end > nc - 1)
9770  {
9771  coplanar_ends.push_back (nc - 1);
9772  break;
9773  }
9774 
9775  // Algorithm: Start from 3 points, keep adding points until the point set
9776  // is no more in a plane. Record the coplanar point set, then advance
9777  // i_start.
9778 
9779  // Prepare 1+3 points for coplanar test.
9780  // The first point is implicitly included.
9781  for (octave_idx_type j = 0; j < 3; j++)
9782  for (octave_idx_type i = 0; i < 3; i++)
9783  fc(j,i) = vert(idx(j+i_start,jj)-1,i) - plane_pivot(i);
9784 
9785  // covariance matrix between coordinates of vertices
9786  coor_cov = fc.transpose () * fc;
9787 
9788  while (true)
9789  {
9790  // coplanar test
9791  if (! is_coplanar (coor_cov))
9792  break;
9793 
9794  i_end++;
9795  if (i_end > nc - 1)
9796  break;
9797 
9798  // add a point to plane
9799  for (octave_idx_type i = 0; i < 3; i++)
9800  fa(0,i) = vert(idx(i_end,jj)-1,i) - plane_pivot(i);
9801  coor_cov += fa.transpose () * fa;
9802  }
9803 
9804  i_start = i_end - 1;
9805  coplanar_ends.push_back (i_start);
9806  }
9807  return coplanar_ends;
9808 }
9809 
9810 void
9811 patch::properties::update_data (void)
9812 {
9813  if (updating_patch_data)
9814  return;
9815 
9816  Matrix idx = get_faces ().matrix_value ().transpose ();
9817  Matrix vert = get_vertices ().matrix_value ();
9818  NDArray fvc = get_facevertexcdata ().array_value ();
9819 
9820  octave_idx_type nfaces = idx.columns ();
9821  octave_idx_type nvert = vert.rows ();
9822 
9823  // Check all vertices in faces are defined
9824  bad_data_msg = "";
9825  if (static_cast<double> (nvert) < idx.row_max ().max ())
9826  {
9827  bad_data_msg = R"(some vertices in "faces" property are undefined)";
9828  return;
9829  }
9830 
9831  // Replace NaNs
9832  if (idx.any_element_is_inf_or_nan ())
9833  {
9834  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
9835  {
9836  double valid_vert = idx(0,jj);
9837  bool turn_valid = false;
9838  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
9839  {
9840  if (octave::math::isnan (idx(ii,jj)) || turn_valid)
9841  {
9842  idx(ii,jj) = valid_vert;
9843  turn_valid = true;
9844  }
9845  else
9846  valid_vert = idx(ii,jj);
9847  }
9848  }
9849  }
9850 
9851  // check coplanarity for 3D-faces with more than 3 corners
9852  int fcmax = idx.rows ();
9853  if (fcmax > 3 && vert.columns () > 2
9854  && ! (facecolor_is ("none") && edgecolor_is ("none")))
9855  {
9856  coplanar_last_idx.resize (idx.columns ());
9857  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
9858  {
9859  if (octave::math::isnan (idx(3,jj)))
9860  continue;
9861 
9862  // find first element that is NaN to get number of corners
9863  octave_idx_type nc = 3;
9864  while (nc < fcmax && ! octave::math::isnan (idx(nc,jj)))
9865  nc++;
9866 
9867  // If any of the corners is NaN or Inf, skip coplanar test.
9868  // FIXME: Add support for non-coplanar faces with unclosed contour.
9869  bool is_unclosed = false;
9870  for (octave_idx_type j = 0; j < nc; j++)
9871  {
9872  const octave_idx_type k = idx(j, jj) - 1;
9873  if (! (octave::math::isfinite (vert(k, 0))
9874  && octave::math::isfinite (vert(k, 1))
9875  && octave::math::isfinite (vert(k, 2))))
9876  {
9877  is_unclosed = true;
9878  break;
9879  }
9880  }
9881  if (is_unclosed)
9882  continue;
9883 
9884  coplanar_last_idx[jj] = coplanar_partition (vert, idx, nc, jj);
9885  }
9886  }
9887  else
9888  coplanar_last_idx.resize (0);
9889 
9890  // Build cdata
9891  dim_vector dv = dim_vector::alloc (3);
9892  NDArray cd;
9893  bool pervertex = false;
9894 
9895  if (fvc.rows () == nfaces || fvc.rows () == 1)
9896  {
9897  dv(0) = 1;
9898  dv(1) = fvc.rows ();
9899  dv(2) = fvc.columns ();
9900  cd = fvc.reshape (dv);
9901  }
9902  else
9903  {
9904  if (! fvc.isempty ())
9905  {
9906  dv(0) = idx.rows ();
9907  dv(1) = nfaces;
9908  dv(2) = fvc.columns ();
9909  cd.resize (dv);
9910  pervertex = true;
9911  }
9912  }
9913 
9914  // Build x,y,zdata and eventually per vertex cdata
9915  Matrix xd (idx.dims ());
9916  Matrix yd (idx.dims ());
9917  Matrix zd;
9918  bool has_zd = false;
9919  if (vert.columns () > 2)
9920  {
9921  zd = Matrix (idx.dims ());
9922  has_zd = true;
9923  }
9924 
9925  for (octave_idx_type jj = 0; jj < nfaces; jj++)
9926  {
9927  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
9928  {
9929  octave_idx_type row = static_cast<octave_idx_type> (idx(ii,jj)-1);
9930  xd(ii,jj) = vert(row,0);
9931  yd(ii,jj) = vert(row,1);
9932 
9933  if (has_zd)
9934  zd(ii,jj) = vert(row,2);
9935 
9936  if (pervertex)
9937  for (int kk = 0; kk < fvc.columns (); kk++)
9938  cd(ii,jj,kk) = fvc(row,kk);
9939  }
9940  }
9941 
9942  // Update normals
9943  update_normals (true);
9944 
9945  octave::unwind_protect frame;
9947  updating_patch_data = true;
9948 
9949  set_xdata (xd);
9950  set_ydata (yd);
9951  set_zdata (zd);
9952  set_cdata (cd);
9953 }
9954 
9955 inline void
9956 cross_product (double x1, double y1, double z1,
9957  double x2, double y2, double z2,
9958  double& x, double& y, double& z)
9959 {
9960  x += (y1 * z2 - z1 * y2);
9961  y += (z1 * x2 - x1 * z2);
9962  z += (x1 * y2 - y1 * x2);
9963 }
9964 
9965 void
9966 patch::properties::calc_face_normals (Matrix& fn)
9967 {
9968  Matrix v = get_vertices ().matrix_value ();
9969  Matrix f = get_faces ().matrix_value ();
9970 
9971  bool is_3D = (v.columns () == 3); // 2D or 3D patches
9972  octave_idx_type num_f = f.rows (); // number of faces
9973  octave_idx_type max_nc = f.columns (); // max. number of polygon corners
9974 
9975  // In which cases can we skip updating the normals?
9976  if (max_nc < 3)
9977  {
9978  fn = Matrix ();
9979  return;
9980  }
9981 
9982  // Calculate normals for all faces
9983  octave_idx_type i1, i2, i3;
9984  octave_idx_type j1, j2;
9985  for (octave_idx_type i = 0; i < num_f; i++)
9986  {
9987  bool is_coplanar = true;
9988  if (coplanar_last_idx.size () > 0 && coplanar_last_idx[i].size () > 1)
9989  is_coplanar = false;
9990 
9991  // get number of corners
9992  octave_idx_type nc = 3;
9993  if (max_nc > 3)
9994  {
9995  while (nc < max_nc && ! octave::math::isnan (f(i,nc)))
9996  nc++;
9997  }
9998 
9999  RowVector fnc (3, 0.0);
10000  double& nx = fnc(0);
10001  double& ny = fnc(1);
10002  double& nz = fnc(2);
10003 
10004  if (is_coplanar)
10005  {
10006  // fast way for coplanar polygons
10007  i1 = f(i,0) - 1; i2 = f(i,1) - 1; i3 = f(i,nc-1) - 1;
10008 
10009  if (is_3D)
10011  (v(i3,0) - v(i1,0), v(i3,1) - v(i1,1), v(i3,2) - v(i1,2),
10012  v(i2,0) - v(i1,0), v(i2,1) - v(i1,1), v(i2,2) - v(i1,2),
10013  nx, ny, nz);
10014  else
10015  {
10016  nz = (v(i2,0) - v(i1,0)) * (v(i3,1) - v(i1,1)) -
10017  (v(i2,1) - v(i1,1)) * (v(i3,0) - v(i1,0));
10018  // 2-d vertices always point towards +z
10019  nz = (nz < 0) ? -nz : nz;
10020  }
10021  }
10022  else
10023  {
10024  // more general for non-planar polygons
10025 
10026  // calculate face normal with Newell's method
10027  // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method
10028 
10029  j1 = nc - 1; j2 = 0;
10030  i1 = f(i,j1) - 1; i2 = f(i,j2) - 1;
10031 
10032  nx = (v(i2,1) - v(i1,1)) * (v(i1,2) + v(i2,2));
10033  ny = (v(i2,2) - v(i1,2)) * (v(i1,0) + v(i2,0));
10034  nz = (v(i2,0) - v(i1,0)) * (v(i1,1) + v(i2,1));
10035 
10036  for (octave_idx_type j = 1; j < nc; j++)
10037  {
10038  j1 = j-1; j2 = j;
10039  i1 = f(i,j1) - 1; i2 = f(i,j2) - 1;
10040 
10041  nx += (v(i2,1) - v(i1,1)) * (v(i1,2) + v(i2,2));
10042  ny += (v(i2,2) - v(i1,2)) * (v(i1,0) + v(i2,0));
10043  nz += (v(i2,0) - v(i1,0)) * (v(i1,1) + v(i2,1));
10044  }
10045  }
10046 
10047  // normalize normal vector
10048  double n_len = sqrt (nx*nx+ny*ny+nz*nz);
10049 
10050  // assign normal to current face
10051  if ( n_len < std::numeric_limits<double>::epsilon () )
10052  for (octave_idx_type j = 0; j < 3; j++)
10053  fn(i,j) = 0.0;
10054  else
10055  for (octave_idx_type j = 0; j < 3; j++)
10056  fn(i,j) = fnc(j) / n_len;
10057  }
10058 }
10059 
10060 void
10061 patch::properties::update_face_normals (bool reset, bool force)
10062 {
10063  if (updating_patch_data || ! facenormalsmode_is ("auto"))
10064  return;
10065 
10066  if (force || ((facelighting_is ("flat") || edgelighting_is ("flat"))
10067  && get_do_lighting ()))
10068  {
10069  Matrix f = get_faces ().matrix_value ();
10070 
10071  octave_idx_type num_f = f.rows (); // number of faces
10072  Matrix fn (num_f, 3, 0.0);
10073 
10074  calc_face_normals (fn);
10075  facenormals = fn;
10076  }
10077  else if (reset)
10078  facenormals = Matrix ();
10079 }
10080 
10081 void
10082 patch::properties::update_vertex_normals (bool reset, bool force)
10083 {
10084  if (updating_patch_data || ! vertexnormalsmode_is ("auto"))
10085  return;
10086 
10087  if (force || ((facelighting_is ("gouraud") || facelighting_is ("phong")
10088  || edgelighting_is ("gouraud") || edgelighting_is ("phong"))
10089  && get_do_lighting ()))
10090  {
10091  Matrix v = get_vertices ().matrix_value ();
10092  Matrix f = get_faces ().matrix_value ();
10093 
10094  octave_idx_type num_v = v.rows (); // number of vertices
10095  octave_idx_type num_f = f.rows (); // number of faces
10096  octave_idx_type max_nc = f.columns (); // max. number of polygon corners
10097 
10098  // In which cases can we skip updating the normals?
10099  if (max_nc < 3)
10100  return;
10101 
10102  // First step: Calculate the normals for all faces
10103  Matrix fn = get_facenormals ().matrix_value ();
10104  if ( fn.isempty () )
10105  {
10106  // calculate facenormals here
10107  fn = Matrix (num_f, 3, 0.0);
10108  calc_face_normals (fn);
10109  }
10110 
10111  // Second step: assign normals to the respective vertices
10112 
10113  // The following code collects the face normals for all faces adjacent to
10114  // each vertex. For this, a std::vector of length NUM_V (which might be
10115  // very large) is used so that memory is allocated from the heap rather
10116  // than the stack. Each element of this vector corresponds to one vertex
10117  // of the patch. The element itself is a variable length std::vector.
10118  // This second vector contains the face normals (of type RowVector) of
10119  // the adjacent faces.
10120  std::vector<std::vector<RowVector>> vec_vn (num_v);
10121  for (octave_idx_type i = 0; i < num_f; i++)
10122  {
10123  // get number of corners
10124  octave_idx_type nc = 3;
10125  if (max_nc > 3)
10126  {
10127  while (nc < max_nc && ! octave::math::isnan (f(i,nc)))
10128  nc++;
10129  }
10130 
10131  for (octave_idx_type j = 0; j < nc; j++)
10132  vec_vn[static_cast<octave_idx_type> (f(i,j) - 1)].push_back (fn.row (i));
10133  }
10134 
10135  // Third step: Calculate the normal for the vertices taking the average
10136  // of the normals determined from all adjacent faces
10137  Matrix vn (num_v, 3, 0.0);
10138  for (octave_idx_type i = 0; i < num_v; i++)
10139  {
10140  std::vector<RowVector>::iterator it = vec_vn[i].begin ();
10141 
10142  // The normal of unused vertices is NaN.
10143  RowVector vn0 (3, octave_NaN);
10144 
10145  if (it != vec_vn[i].end ())
10146  {
10147  // FIXME: Currently, the first vector also determines the
10148  // direction of the normal. How to determine the inner and outer
10149  // faces of all parts of the patch and point the normals outwards?
10150  // (Necessary for correct lighting with "backfacelighting" set to
10151  // "lit" or "unlit".) Matlab does not seem to do it correctly
10152  // either. So should we bother?
10153 
10154  vn0 = *it;
10155 
10156  for (++it; it != vec_vn[i].end (); ++it)
10157  {
10158  RowVector vn1 = *it;
10159  // Use sign of dot product to point vectors in a similar
10160  // direction before taking the average.
10161  double dir = (vn0(0)*vn1(0) + vn0(1)*vn1(1) + vn0(2)*vn1(2) < 0) ? -1 : 1;
10162  for (octave_idx_type j = 0; j < 3; j++)
10163  vn0(j) += dir * vn1(j);
10164  }
10165 
10166  // normalize normal vector
10167  double n_len = sqrt (vn0(0)*vn0(0)+vn0(1)*vn0(1)+vn0(2)*vn0(2));
10168 
10169  // save normal in matrix
10170  for (octave_idx_type j = 0; j < 3; j++)
10171  vn(i,j) = vn0(j)/n_len;
10172  }
10173  }
10174 
10175  vertexnormals = vn;
10176  }
10177  else if (reset)
10178  vertexnormals = Matrix ();
10179 }
10180 
10181 void
10183 {
10185 
10186  // calculate normals for default data
10187  // This is done because the normals for the default data do not match
10188  // get(0, "DefaultPatchVertexNormals") in Matlab.
10189  xproperties.update_normals (true);
10190 }
10191 
10192 
10193 void
10195 {
10196  // empty list of local defaults
10199 
10200  // calculate normals for default data
10201  // This is done because the normals for the default data do not match
10202  // get(0, "DefaultPatchVertexNormals") in Matlab.
10203  xproperties.update_normals (true);
10204 }
10205 
10206 // ---------------------------------------------------------------------
10207 
10210 {
10211  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
10212 }
10213 
10214 bool
10216 {
10217  gh_manager& gh_mgr
10218  = octave::__get_gh_manager__ ("surface::properties::get_do_lighting");
10219 
10220  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10221 
10222  axes::properties& ax_prop = dynamic_cast<axes::properties&>
10223  (go.get_ancestor ("axes").get_properties ());
10224 
10225  return (ax_prop.get_num_lights () > 0);
10226 }
10227 
10228 void
10229 surface::properties::update_face_normals (bool reset, bool force)
10230 {
10231  if (! facenormalsmode_is ("auto"))
10232  return;
10233 
10234  if (force || ((facelighting_is ("flat") || edgelighting_is ("flat"))
10235  && get_do_lighting ()))
10236  {
10237  Matrix x = get_xdata ().matrix_value ();
10238  Matrix y = get_ydata ().matrix_value ();
10239  Matrix z = get_zdata ().matrix_value ();
10240 
10241  int p = z.columns ();
10242  int q = z.rows ();
10243 
10244  // FIXME: There might be a cleaner way to do this. When data is changed
10245  // the update_xdata, update_ydata, update_zdata routines are called in a
10246  // serial fashion. Until the final call to update_zdata the matrices
10247  // will be of mismatched dimensions which can cause an out-of-bound
10248  // indexing in the code below. This one-liner prevents calculating
10249  // normals until dimensions match.
10250  if (x.columns () != p || y.rows () != q)
10251  return;
10252 
10253  bool x_mat = (x.rows () == q);
10254  bool y_mat = (y.columns () == p);
10255 
10256  NDArray n (dim_vector (q-1, p-1, 3), 1);
10257 
10258  int i1, i2, j1, j2;
10259  i1 = i2 = 0;
10260  j1 = j2 = 0;
10261  double x0, x1, x2, x3, y0, y1, y2, y3, z0, z1, z2, z3;
10262  double x1m0, x2m1, x3m2, x0m3, y1m0, y2m1, y3m2, y0m3;
10263  double x1p0, x2p1, x3p2, x0p3, y1p0, y2p1, y3p2, y0p3;
10264  x3m2 = y0m3 = -1;
10265  x2m1 = x0m3 = y1m0 = y3m2 = 0;
10266  x1m0 = y2m1 = 1;
10267  x0p3 = y1p0 = 0;
10268  x1p0 = x3p2 = y2p1 = y0p3 = 1;
10269  x2p1 = y3p2 = 2;
10270 
10271  for (int i = 0; i < p-1; i++)
10272  {
10273  i1 = i;
10274  i2 = i + 1;
10275 
10276  for (int j = 0; j < q-1; j++)
10277  {
10278  j1 = j;
10279  j2 = j + 1;
10280 
10281  if (x_mat || y_mat)
10282  {
10283  x0 = x(x_mat?j1:0,y_mat?i1:0);
10284  x1 = x(x_mat?j1:0,y_mat?i2:0);
10285  x2 = x(x_mat?j2:0,y_mat?i2:0);
10286  x3 = x(x_mat?j2:0,y_mat?i1:0);
10287  x1m0 = x1 - x0;
10288  x2m1 = x2 - x1;
10289  x3m2 = x3 - x2;
10290  x0m3 = x0 - x3;
10291  x1p0 = x1 + x0;
10292  x2p1 = x2 + x1;
10293  x3p2 = x3 + x2;
10294  x0p3 = x0 + x3;
10295  y0 = y(x_mat?j1:0,y_mat?i1:0);
10296  y1 = y(x_mat?j1:0,y_mat?i2:0);
10297  y2 = y(x_mat?j2:0,y_mat?i2:0);
10298  y3 = y(x_mat?j2:0,y_mat?i1:0);
10299  y1m0 = y1 - y0;
10300  y2m1 = y2 - y1;
10301  y3m2 = y3 - y2;
10302  y0m3 = y0 - y3;
10303  y1p0 = y1 + y0;
10304  y2p1 = y2 + y1;
10305  y3p2 = y3 + y2;
10306  y0p3 = y0 + y3;
10307  }
10308 
10309  double& nx = n(j,i,0);
10310  double& ny = n(j,i,1);
10311  double& nz = n(j,i,2);
10312 
10313  z0 = z(j1,i1);
10314  z1 = z(j1,i2);
10315  z2 = z(j2,i2);
10316  z3 = z(j2,i1);
10317 
10318  // calculate face normal with Newell's method
10319  // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method
10320 
10321  nx = y1m0 * (z1 + z0) + y2m1 * (z2 + z1)
10322  + y3m2 * (z3 + z2) + y0m3 * (z0 + z3);
10323  ny = (z1 - z0) * x1p0 + (z2 - z1) * x2p1
10324  + (z3 - z2) * x3p2 + (z0 - z3) * x0p3;
10325  nz = x1m0 * y1p0 + x2m1 * y2p1 + x3m2 * y3p2 + x0m3 * y0p3;
10326 
10327  double d = std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
10328 
10329  nx /= d;
10330  ny /= d;
10331  nz /= d;
10332  }
10333  }
10334  facenormals = n;
10335  }
10336  else if (reset)
10337  facenormals = Matrix ();
10338 }
10339 
10340 void
10341 surface::properties::update_vertex_normals (bool reset, bool force)
10342 {
10343  if (! vertexnormalsmode_is ("auto"))
10344  return;
10345 
10346  if (force || ((facelighting_is ("gouraud") || facelighting_is ("phong")
10347  || edgelighting_is ("gouraud") || edgelighting_is ("phong"))
10348  && get_do_lighting ()))
10349  {
10350  Matrix x = get_xdata ().matrix_value ();
10351  Matrix y = get_ydata ().matrix_value ();
10352  Matrix z = get_zdata ().matrix_value ();
10353 
10354  int p = z.columns ();
10355  int q = z.rows ();
10356 
10357  // FIXME: There might be a cleaner way to do this. When data is changed
10358  // the update_xdata, update_ydata, update_zdata routines are called in a
10359  // serial fashion. Until the final call to update_zdata the matrices
10360  // will be of mismatched dimensions which can cause an out-of-bound
10361  // indexing in the code below. This one-liner prevents calculating
10362  // normals until dimensions match.
10363  if (x.columns () != p || y.rows () != q)
10364  return;
10365 
10366  NDArray n (dim_vector (q, p, 3), 0.0);
10367 
10368  bool x_mat = (x.rows () == q);
10369  bool y_mat = (y.columns () == p);
10370 
10371  int i1, i2, i3, j1, j2, j3;
10372  i1 = i2 = i3 = 0;
10373  j1 = j2 = j3 = 0;
10374 
10375  for (int i = 0; i < p; i++)
10376  {
10377  if (y_mat)
10378  {
10379  i1 = i - 1;
10380  i2 = i;
10381  i3 = i + 1;
10382  }
10383 
10384  for (int j = 0; j < q; j++)
10385  {
10386  if (x_mat)
10387  {
10388  j1 = j - 1;
10389  j2 = j;
10390  j3 = j + 1;
10391  }
10392 
10393  double& nx = n(j, i, 0);
10394  double& ny = n(j, i, 1);
10395  double& nz = n(j, i, 2);
10396 
10397  if ((j > 0) && (i > 0))
10398  // upper left quadrangle
10400  (x(j1,i-1)-x(j2,i), y(j-1,i1)-y(j,i2), z(j-1,i-1)-z(j,i),
10401  x(j2,i-1)-x(j1,i), y(j,i1)-y(j-1,i2), z(j,i-1)-z(j-1,i),
10402  nx, ny, nz);
10403 
10404  if ((j > 0) && (i < (p -1)))
10405  // upper right quadrangle
10407  (x(j1,i+1)-x(j2,i), y(j-1,i3)-y(j,i2), z(j-1,i+1)-z(j,i),
10408  x(j1,i)-x(j2,i+1), y(j-1,i2)-y(j,i3), z(j-1,i)-z(j,i+1),
10409  nx, ny, nz);
10410 
10411  if ((j < (q - 1)) && (i > 0))
10412  // lower left quadrangle
10414  (x(j2,i-1)-x(j3,i), y(j,i1)-y(j+1,i2), z(j,i-1)-z(j+1,i),
10415  x(j3,i-1)-x(j2,i), y(j+1,i1)-y(j,i2), z(j+1,i-1)-z(j,i),
10416  nx, ny, nz);
10417 
10418  if ((j < (q - 1)) && (i < (p -1)))
10419  // lower right quadrangle
10421  (x(j3,i)-x(j2,i+1), y(j+1,i2)-y(j,i3), z(j+1,i)-z(j,i+1),
10422  x(j3,i+1)-x(j2,i), y(j+1,i3)-y(j,i2), z(j+1,i+1)-z(j,i),
10423  nx, ny, nz);
10424 
10425  double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
10426 
10427  nx /= d;
10428  ny /= d;
10429  nz /= d;
10430  }
10431  }
10432  vertexnormals = n;
10433  }
10434  else if (reset)
10435  vertexnormals = Matrix ();
10436 }
10437 
10438 DEFMETHOD (__update_normals__, interp, args, ,
10439  doc: /* -*- texinfo -*-
10440 @deftypefn {} {} __update_normals__ (@var{h})
10441 Update FaceNormals and VertexNormals of the patch or surface referred to by
10442 @var{h}.
10443 
10444 @end deftypefn */)
10445 {
10446  gh_manager& gh_mgr = interp.get_gh_manager ();
10447 
10448  octave::autolock guard (gh_mgr.graphics_lock ());
10449 
10450  if (args.length () != 1)
10451  print_usage ();
10452 
10453  octave_value val = args(0);
10454 
10455  graphics_object go = gh_mgr.get_object (val);
10456 
10457  if (go.isa ("surface"))
10458  {
10459  surface::properties& props
10460  = dynamic_cast <surface::properties&> (go.get_properties ());
10461  props.update_normals (false, true);
10462  }
10463  else if (go.isa ("patch"))
10464  {
10465  patch::properties& props
10466  = dynamic_cast <patch::properties&> (go.get_properties ());
10467  props.update_normals (false, true);
10468  }
10469  else
10470  error ("__update_normals__: "
10471  "H must be a handle to a valid surface or patch object.");
10472 
10473  return ovl ();
10474 }
10475 
10476 /*
10477 %!test
10478 %! hf = figure ("visible", "off");
10479 %! unwind_protect
10480 %! Z = peaks ();
10481 %! hs = surf (Z, "facelighting", "none");
10482 %! assert (isempty (get (hs, "vertexnormals")));
10483 %! assert (isempty (get (hs, "facenormals")));
10484 %! __update_normals__ (hs);
10485 %! assert (! isempty (get (hs, "vertexnormals")));
10486 %! assert (! isempty (get (hs, "facenormals")));
10487 %! unwind_protect_cleanup
10488 %! close (hf);
10489 %! end_unwind_protect
10490 
10491 %!test
10492 %! hf = figure ("visible", "off");
10493 %! unwind_protect
10494 %! hp = patch ("facelighting", "none");
10495 %! assert (isempty (get (hp, "vertexnormals")));
10496 %! assert (isempty (get (hp, "facenormals")));
10497 %! __update_normals__ (hp);
10498 %! assert (! isempty (get (hp, "vertexnormals")));
10499 %! assert (! isempty (get (hp, "facenormals")));
10500 %! unwind_protect_cleanup
10501 %! close (hf);
10502 %! end_unwind_protect
10503 */
10504 
10505 // ---------------------------------------------------------------------
10506 
10507 void
10509 {
10510  gh_manager& gh_mgr
10511  = octave::__get_gh_manager__ ("hggroup::properties::remove_child");
10512 
10513  graphics_object go = gh_mgr.get_object (h);
10514 
10515  if (! from_root && go.isa ("light") && go.get_properties ().is_visible ())
10516  {
10517  axes::properties& ax_props
10518  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
10519  ax_props.decrease_num_lights ();
10520  }
10521  base_properties::remove_child (h, from_root);
10522  update_limits ();
10523 }
10524 
10525 void
10527 {
10528  gh_manager& gh_mgr
10529  = octave::__get_gh_manager__ ("hggroup::properties::adopt");
10530 
10531  graphics_object go = gh_mgr.get_object (h);
10532 
10533  if (go.isa ("light") && go.get_properties ().is_visible ())
10534  {
10535  axes::properties& ax_props
10536  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
10537  ax_props.increase_num_lights ();
10538  }
10540  update_limits (h);
10541 }
10542 
10543 void
10545 {
10546  gh_manager& gh_mgr
10547  = octave::__get_gh_manager__ ("hggroup::properties::update_limits");
10548 
10549  graphics_object go = gh_mgr.get_object (__myhandle__);
10550 
10551  if (go)
10552  {
10553  go.update_axis_limits ("xlim");
10554  go.update_axis_limits ("ylim");
10555  go.update_axis_limits ("zlim");
10556  go.update_axis_limits ("clim");
10557  go.update_axis_limits ("alim");
10558  }
10559 }
10560 
10561 void
10563 {
10564  gh_manager& gh_mgr
10565  = octave::__get_gh_manager__ ("hggroup::properties::update_limits");
10566 
10567  graphics_object go = gh_mgr.get_object (__myhandle__);
10568 
10569  if (go)
10570  {
10571  go.update_axis_limits ("xlim", h);
10572  go.update_axis_limits ("ylim", h);
10573  go.update_axis_limits ("zlim", h);
10574  go.update_axis_limits ("clim", h);
10575  go.update_axis_limits ("alim", h);
10576  }
10577 }
10578 
10579 static bool updating_hggroup_limits = false;
10580 
10581 void
10582 hggroup::update_axis_limits (const std::string& axis_type,
10583  const graphics_handle& h)
10584 {
10586  return;
10587 
10588  Matrix kids = Matrix (1, 1, h.value ());
10589 
10590  double min_val = octave::numeric_limits<double>::Inf ();
10591  double max_val = -octave::numeric_limits<double>::Inf ();
10592  double min_pos = octave::numeric_limits<double>::Inf ();
10593  double max_neg = -octave::numeric_limits<double>::Inf ();
10594 
10595  Matrix limits;
10596  double val;
10597 
10598  char update_type = 0;
10599 
10600  if (axis_type == "xlim" || axis_type == "xliminclude")
10601  {
10602  limits = xproperties.get_xlim ().matrix_value ();
10603  update_type = 'x';
10604  }
10605  else if (axis_type == "ylim" || axis_type == "yliminclude")
10606  {
10607  limits = xproperties.get_ylim ().matrix_value ();
10608  update_type = 'y';
10609  }
10610  else if (axis_type == "zlim" || axis_type == "zliminclude")
10611  {
10612  limits = xproperties.get_zlim ().matrix_value ();
10613  update_type = 'z';
10614  }
10615  else if (axis_type == "clim" || axis_type == "climinclude")
10616  {
10617  limits = xproperties.get_clim ().matrix_value ();
10618  update_type = 'c';
10619  }
10620  else if (axis_type == "alim" || axis_type == "aliminclude")
10621  {
10622  limits = xproperties.get_alim ().matrix_value ();
10623  update_type = 'a';
10624  }
10625 
10626  if (limits.numel () == 4)
10627  {
10628  val = limits(0);
10629  if (octave::math::isfinite (val))
10630  min_val = val;
10631  val = limits(1);
10632  if (octave::math::isfinite (val))
10633  max_val = val;
10634  val = limits(2);
10635  if (octave::math::isfinite (val))
10636  min_pos = val;
10637  val = limits(3);
10638  if (octave::math::isfinite (val))
10639  max_neg = val;
10640  }
10641  else
10642  {
10643  limits.resize (1, 4);
10644  limits(0) = min_val;
10645  limits(1) = max_val;
10646  limits(2) = min_pos;
10647  limits(3) = max_neg;
10648  }
10649 
10650  get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
10651 
10652  octave::unwind_protect frame;
10654 
10655  updating_hggroup_limits = true;
10656 
10657  if (limits(0) != min_val || limits(1) != max_val
10658  || limits(2) != min_pos || limits(3) != max_neg)
10659  {
10660  limits(0) = min_val;
10661  limits(1) = max_val;
10662  limits(2) = min_pos;
10663  limits(3) = max_neg;
10664 
10665  switch (update_type)
10666  {
10667  case 'x':
10668  xproperties.set_xlim (limits);
10669  break;
10670 
10671  case 'y':
10672  xproperties.set_ylim (limits);
10673  break;
10674 
10675  case 'z':
10676  xproperties.set_zlim (limits);
10677  break;
10678 
10679  case 'c':
10680  xproperties.set_clim (limits);
10681  break;
10682 
10683  case 'a':
10684  xproperties.set_alim (limits);
10685  break;
10686 
10687  default:
10688  break;
10689  }
10690 
10691  graphics_handle hg = xproperties.get___myhandle__ ();
10693  }
10694 }
10695 
10696 void
10697 hggroup::update_axis_limits (const std::string& axis_type)
10698 {
10700  return;
10701 
10702  Matrix kids = xproperties.get_children ();
10703 
10704  double min_val = octave::numeric_limits<double>::Inf ();
10705  double max_val = -octave::numeric_limits<double>::Inf ();
10706  double min_pos = octave::numeric_limits<double>::Inf ();
10707  double max_neg = -octave::numeric_limits<double>::Inf ();
10708 
10709  char update_type = 0;
10710 
10711  if (axis_type == "xlim" || axis_type == "xliminclude")
10712  {
10713  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
10714 
10715  update_type = 'x';
10716  }
10717  else if (axis_type == "ylim" || axis_type == "yliminclude")
10718  {
10719  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
10720 
10721  update_type = 'y';
10722  }
10723  else if (axis_type == "zlim" || axis_type == "zliminclude")
10724  {
10725  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
10726 
10727  update_type = 'z';
10728  }
10729  else if (axis_type == "clim" || axis_type == "climinclude")
10730  {
10731  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
10732 
10733  update_type = 'c';
10734  }
10735  else if (axis_type == "alim" || axis_type == "aliminclude")
10736  {
10737  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
10738 
10739  update_type = 'a';
10740  }
10741 
10742  octave::unwind_protect frame;
10744 
10745  updating_hggroup_limits = true;
10746 
10747  Matrix limits (1, 4);
10748 
10749  limits(0) = min_val;
10750  limits(1) = max_val;
10751  limits(2) = min_pos;
10752  limits(3) = max_neg;
10753 
10754  switch (update_type)
10755  {
10756  case 'x':
10757  xproperties.set_xlim (limits);
10758  break;
10759 
10760  case 'y':
10761  xproperties.set_ylim (limits);
10762  break;
10763 
10764  case 'z':
10765  xproperties.set_zlim (limits);
10766  break;
10767 
10768  case 'c':
10769  xproperties.set_clim (limits);
10770  break;
10771 
10772  case 'a':
10773  xproperties.set_alim (limits);
10774  break;
10775 
10776  default:
10777  break;
10778  }
10779 
10781 }
10782 
10783 // ---------------------------------------------------------------------
10784 
10785 void
10787 {
10788  // Clear the uicontextmenu property of dependent objects
10789  if (beingdeleted.is ("on"))
10790  {
10791  gh_manager& gh_mgr
10792  = octave::__get_gh_manager__ ("uicontextmenu::properties::update_beingdeleted");
10793 
10794  std::list<graphics_handle> lst = get_dependent_obj_list ();
10795 
10796  for (auto& hobj : lst)
10797  {
10798  graphics_object go = gh_mgr.get_object (hobj);
10799 
10800  if (go.valid_object ()
10801  && go.get ("uicontextmenu") == get___myhandle__ ())
10802  go.set ("uicontextmenu", Matrix ());
10803  }
10804  }
10805 }
10806 
10807 /*
10808 ## Test deletion/reset of uicontextmenu
10809 %!test
10810 %! hf = figure ("visible", "off");
10811 %! hax = axes ("parent", hf);
10812 %! unwind_protect
10813 %! hctx1 = uicontextmenu ("parent", hf);
10814 %! hctx2 = uicontextmenu ("parent", hf);
10815 %! set (hf, "uicontextmenu", hctx2);
10816 %! set (hax, "uicontextmenu", hctx2);
10817 %! assert (get (hf, "uicontextmenu"), hctx2);
10818 %! assert (get (hax, "uicontextmenu"), hctx2);
10819 %! assert (get (hf, "children"), [hctx2; hctx1; hax]);
10820 %! delete (hctx2);
10821 %! assert (get (hf, "uicontextmenu"), []);
10822 %! assert (get (hax, "uicontextmenu"), []);
10823 %! assert (get (hf, "children"), [hctx1; hax]);
10824 %! set (hf, "uicontextmenu", hctx1);
10825 %! assert (get (hf, "uicontextmenu"), hctx1);
10826 %! set (hf, "uicontextmenu", []);
10827 %! assert (get (hf, "uicontextmenu"), []);
10828 %! assert (get (hf, "children"), [hctx1; hax]);
10829 %! unwind_protect_cleanup
10830 %! close (hf);
10831 %! end_unwind_protect;
10832 */
10833 
10834 // ---------------------------------------------------------------------
10835 
10837 uicontrol::properties::get_extent (void) const
10838 {
10839  Matrix m = extent.get ().matrix_value ();
10840 
10841  gh_manager& gh_mgr
10842  = octave::__get_gh_manager__ ("uicontrol::properties::get_extent");
10843 
10844  graphics_object parent_go = gh_mgr.get_object (get_parent ());
10845 
10846  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
10847  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
10848 
10849  return convert_position (m, "pixels", get_units (), parent_size);
10850 }
10851 
10852 void
10853 uicontrol::properties::update_text_extent (void)
10854 {
10855  // FIXME: support multiline text
10856 
10857  gh_manager& gh_mgr
10858  = octave::__get_gh_manager__ ("uicontrol::properties::update_text_extent");
10859 
10860  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10861 
10862  set_extent (go.get_toolkit ().get_text_extent (go));
10863 }
10864 
10865 void
10867 {
10868  Matrix pos = get_position ().matrix_value ();
10869 
10870  gh_manager& gh_mgr
10871  = octave::__get_gh_manager__ ("uicontrol::properties::update_units");
10872 
10873  graphics_object parent_go = gh_mgr.get_object (get_parent ());
10874 
10875  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
10876  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
10877 
10878  pos = convert_position (pos, cached_units, get_units (), parent_size);
10879  set_position (pos);
10880 
10881  cached_units = get_units ();
10882 }
10883 
10884 void
10885 uicontrol::properties::set_style (const octave_value& st)
10886 {
10887  gh_manager& gh_mgr
10888  = octave::__get_gh_manager__ ("uicontrol::properties::set_style");
10889 
10890  graphics_object go_parent = gh_mgr.get_object (get_parent ());
10891  if (go_parent.valid_object () && go_parent.isa ("uibuttongroup"))
10892  {
10893  bool was_button = style_is ("radiobutton") || style_is ("togglebutton");
10894  style = st;
10895  bool now_button = style_is ("radiobutton") || style_is ("togglebutton");
10896  uibuttongroup::properties& props =
10897  dynamic_cast<uibuttongroup::properties&> (go_parent.get_properties ());
10898  // update selectedobject
10899  if (! was_button && now_button && ! props.get_selectedobject ().ok ())
10900  {
10901  props.set_selectedobject (get___myhandle__ ().value ());
10902  value.set (octave_value (1));
10903  }
10904  else if (was_button && ! now_button
10905  && (props.get_selectedobject ().value ()
10906  == get___myhandle__ ().value ()))
10907  props.set_selectedobject (Matrix ());
10908  }
10909 
10910  // Don't notify the style change until the "value" property is fixed
10911  bool modified = style.set (st, true, false);
10912 
10913  // Override "value" property for listbox and popupmenu.
10914  if (modified)
10915  {
10916  if (style_is ("listbox") || style_is ("popupmenu"))
10917  {
10918  Matrix v = value.get ().matrix_value ();
10919  if (v.numel () == 1 && v(0) == 0)
10920  value.set (octave_value (1), true, false);
10921  }
10922 
10923  // Notify toolkit
10924 
10925  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10926 
10927  if (go)
10928  go.update (style.get_id ());
10929  }
10930 }
10931 
10932 Matrix
10934  const Matrix& parent_pix_size) const
10935 {
10936  Matrix pos = get_position ().matrix_value ();
10937  Matrix parent_size (parent_pix_size);
10938 
10939  if (parent_size.isempty ())
10940  {
10941  gh_manager& gh_mgr
10942  = octave::__get_gh_manager__ ("uicontrol::properties::get_boundingbox");
10943 
10944  graphics_object go = gh_mgr.get_object (get_parent ());
10945 
10946  if (go.valid_object ())
10947  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
10948  else
10949  parent_size = default_figure_position ();
10950  }
10951 
10952  pos = convert_position (pos, get_units (), "pixels", parent_size);
10953 
10954  pos(0)--;
10955  pos(1)--;
10956  pos(1) = parent_size(1) - pos(1) - pos(3);
10957 
10958  return pos;
10959 }
10960 
10961 void
10962 uicontrol::properties::set_fontunits (const octave_value& val)
10963 {
10964  caseless_str old_fontunits = get_fontunits ();
10965 
10966  if (fontunits.set (val, true))
10967  {
10968  update_fontunits (old_fontunits);
10969  mark_modified ();
10970  }
10971 }
10972 
10973 void
10975 {
10976  caseless_str new_units = get_fontunits ();
10977  double parent_height = get_boundingbox (false).elem (3);
10978  double fontsz = get_fontsize ();
10979 
10980  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
10981 
10982  fontsize.set (octave_value (fontsz), true);
10983 }
10984 
10985 double
10987 {
10988  double fontsz = get_fontsize ();
10989  double parent_height = box_pix_height;
10990 
10991  if (fontunits_is ("normalized") && parent_height <= 0)
10992  parent_height = get_boundingbox (false).elem (3);
10993 
10994  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
10995 }
10996 
10997 // ---------------------------------------------------------------------
10998 
10999 Matrix
11001  const Matrix& parent_pix_size) const
11002 {
11003  Matrix pos = get_position ().matrix_value ();
11004  Matrix parent_size (parent_pix_size);
11005 
11006  if (parent_size.isempty ())
11007  {
11008  gh_manager& gh_mgr
11009  = octave::__get_gh_manager__ ("uibuttongroup::properties::get_boundingbox");
11010 
11011  graphics_object go = gh_mgr.get_object (get_parent ());
11012 
11013  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11014  }
11015 
11016  pos = convert_position (pos, get_units (), "pixels", parent_size);
11017 
11018  pos(0)--;
11019  pos(1)--;
11020  pos(1) = parent_size(1) - pos(1) - pos(3);
11021 
11022  if (internal)
11023  {
11024  double outer_height = pos(3);
11025 
11026  pos(0) = pos(1) = 0;
11027 
11028  if (! bordertype_is ("none"))
11029  {
11030  double bw = get_borderwidth ();
11031  double mul = 1.0;
11032 
11033  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
11034  mul = 2.0;
11035 
11036  pos(0) += mul * bw;
11037  pos(1) += mul * bw;
11038  pos(2) -= 2 * mul * bw;
11039  pos(3) -= 2 * mul * bw;
11040  }
11041 
11042  if (! get_title ().empty ())
11043  {
11044  double fontsz = get_fontsize ();
11045 
11046  if (! fontunits_is ("pixels"))
11047  {
11048  double res = xget (0, "screenpixelsperinch").double_value ();
11049 
11050  if (fontunits_is ("points"))
11051  fontsz *= (res / 72.0);
11052  else if (fontunits_is ("inches"))
11053  fontsz *= res;
11054  else if (fontunits_is ("centimeters"))
11055  fontsz *= (res / 2.54);
11056  else if (fontunits_is ("normalized"))
11057  fontsz *= outer_height;
11058  }
11059 
11060  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
11061  || titleposition_is ("righttop"))
11062  pos(1) += (fontsz / 2);
11063  pos(3) -= (fontsz / 2);
11064  }
11065  }
11066 
11067  return pos;
11068 }
11069 
11070 void
11071 uibuttongroup::properties::set_position (const octave_value& v)
11072 {
11073  Matrix old_bb, new_bb;
11074  bool modified = false;
11075 
11076  old_bb = get_boundingbox (true);
11077  modified = position.set (v, false);
11078  new_bb = get_boundingbox (true);
11079 
11080  if (old_bb != new_bb)
11081  {
11082  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
11083  {
11084  gh_manager& gh_mgr
11085  = octave::__get_gh_manager__ ("uibuttongroup::properties::set_position");
11086 
11087  if (! get_resizefcn ().isempty ())
11088  gh_mgr.post_callback (__myhandle__, "resizefcn");
11089 
11090  if (! get_sizechangedfcn ().isempty ())
11091  gh_mgr.post_callback (__myhandle__, "sizechangedfcn");
11092 
11093  update_boundingbox ();
11094  }
11095  }
11096 
11097  if (modified)
11098  {
11099  position.run_listeners (GCB_POSTSET);
11100  mark_modified ();
11101  }
11102 }
11103 
11104 void
11105 uibuttongroup::properties::set_units (const octave_value& val)
11106 {
11107  caseless_str old_units = get_units ();
11108 
11109  if (units.set (val, true))
11110  {
11111  update_units (old_units);
11112  mark_modified ();
11113  }
11114 }
11115 
11116 void
11117 uibuttongroup::properties::update_units (const caseless_str& old_units)
11118 {
11119  Matrix pos = get_position ().matrix_value ();
11120 
11121  gh_manager& gh_mgr
11122  = octave::__get_gh_manager__ ("uibuttongroup::properties::update_units");
11123 
11124  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11125 
11126  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11127  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11128 
11129  pos = convert_position (pos, old_units, get_units (), parent_size);
11130  set_position (pos);
11131 }
11132 
11133 void
11134 uibuttongroup::properties::set_fontunits (const octave_value& val)
11135 {
11136  caseless_str old_fontunits = get_fontunits ();
11137 
11138  if (fontunits.set (val, true))
11139  {
11140  update_fontunits (old_fontunits);
11141  mark_modified ();
11142  }
11143 }
11144 
11145 void
11146 uibuttongroup::properties::update_fontunits (const caseless_str& old_units)
11147 {
11148  caseless_str new_units = get_fontunits ();
11149  double parent_height = get_boundingbox (false).elem (3);
11150  double fontsz = get_fontsize ();
11151 
11152  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11153 
11154  set_fontsize (octave_value (fontsz));
11155 }
11156 
11157 double
11159 {
11160  double fontsz = get_fontsize ();
11161  double parent_height = box_pix_height;
11162 
11163  if (fontunits_is ("normalized") && parent_height <= 0)
11164  parent_height = get_boundingbox (false).elem (3);
11165 
11166  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11167 }
11168 
11169 void
11170 uibuttongroup::properties::set_selectedobject (const octave_value& v)
11171 {
11172  graphics_handle current_selectedobject = get_selectedobject();
11173  selectedobject = current_selectedobject;
11174  if (v.isempty ())
11175  {
11176  if (current_selectedobject.ok ())
11177  {
11178  selectedobject = graphics_handle ();
11179  mark_modified ();
11180  }
11181  return;
11182  }
11183 
11184  graphics_handle val (v);
11185  if (val.ok ())
11186  {
11187  gh_manager& gh_mgr
11188  = octave::__get_gh_manager__ ("uibuttongroup::properties::set_selectedobject");
11189 
11190  graphics_object go (gh_mgr.get_object (val));
11191 
11192  base_properties& gop = go.get_properties ();
11193 
11194  if (go.valid_object ()
11195  && gop.get_parent () == get___myhandle__ ()
11196  && go.isa ("uicontrol"))
11197  {
11199  = dynamic_cast<uicontrol::properties&> (go.get_properties ());
11200  const caseless_str& style = cop.get_style ();
11201  if (style.compare ("radiobutton") || style.compare ("togglebutton"))
11202  {
11203  selectedobject = val;
11204  mark_modified ();
11205  return;
11206  }
11207  }
11208  }
11209  err_set_invalid ("selectedobject");
11210 }
11211 
11212 void
11214  bool from_root)
11215 {
11216  graphics_handle current_selected = get_selectedobject ();
11217  if (h.value () == current_selected.value ())
11218  set_selectedobject (Matrix ());
11219 
11220  base_properties::remove_child (h, from_root);
11221 }
11222 
11223 void
11225 {
11227 
11228  graphics_handle current_selected = get_selectedobject ();
11229  bool has_selected = current_selected.ok ();
11230 
11231  gh_manager& gh_mgr
11232  = octave::__get_gh_manager__ ("uibuttongroup::properties::adopt");
11233 
11234  graphics_object go = gh_mgr.get_object (h);
11235 
11236  if (! has_selected && go.valid_object () && go.isa ("uicontrol"))
11237  {
11238  const uicontrol::properties& props =
11239  dynamic_cast<const uicontrol::properties&> (go.get_properties ());
11240  if (props.style_is ("radiobutton") || props.style_is ("togglebutton"))
11241  set_selectedobject (h.value ());
11242  }
11243 }
11244 
11245 // ---------------------------------------------------------------------
11246 
11247 Matrix
11249  const Matrix& parent_pix_size) const
11250 {
11251  Matrix pos = get_position ().matrix_value ();
11252  Matrix parent_size (parent_pix_size);
11253 
11254  if (parent_size.isempty ())
11255  {
11256  gh_manager& gh_mgr
11257  = octave::__get_gh_manager__ ("uipanel::properties::get_boundingbox");
11258 
11259  graphics_object go = gh_mgr.get_object (get_parent ());
11260 
11261  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11262  }
11263 
11264  pos = convert_position (pos, get_units (), "pixels", parent_size);
11265 
11266  pos(0)--;
11267  pos(1)--;
11268  pos(1) = parent_size(1) - pos(1) - pos(3);
11269 
11270  if (internal)
11271  {
11272  double outer_height = pos(3);
11273 
11274  pos(0) = pos(1) = 0;
11275 
11276  if (! bordertype_is ("none"))
11277  {
11278  double bw = get_borderwidth ();
11279  double mul = 1.0;
11280 
11281  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
11282  mul = 2.0;
11283 
11284  pos(0) += mul * bw;
11285  pos(1) += mul * bw;
11286  pos(2) -= 2 * mul * bw;
11287  pos(3) -= 2 * mul * bw;
11288  }
11289 
11290  if (! get_title ().empty ())
11291  {
11292  double fontsz = get_fontsize ();
11293 
11294  if (! fontunits_is ("pixels"))
11295  {
11296  double res = xget (0, "screenpixelsperinch").double_value ();
11297 
11298  if (fontunits_is ("points"))
11299  fontsz *= (res / 72.0);
11300  else if (fontunits_is ("inches"))
11301  fontsz *= res;
11302  else if (fontunits_is ("centimeters"))
11303  fontsz *= (res / 2.54);
11304  else if (fontunits_is ("normalized"))
11305  fontsz *= outer_height;
11306  }
11307 
11308  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
11309  || titleposition_is ("righttop"))
11310  pos(1) += (fontsz / 2);
11311  pos(3) -= (fontsz / 2);
11312  }
11313  }
11314 
11315  return pos;
11316 }
11317 
11318 void
11319 uipanel::properties::set_position (const octave_value& v)
11320 {
11321  Matrix old_bb, new_bb;
11322  bool modified = false;
11323 
11324  old_bb = get_boundingbox (true);
11325  modified = position.set (v, false);
11326  new_bb = get_boundingbox (true);
11327 
11328  if (old_bb != new_bb)
11329  {
11330  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
11331  {
11332  gh_manager& gh_mgr
11333  = octave::__get_gh_manager__ ("uipanel::properties::set_position");
11334 
11335  if (! get_resizefcn ().isempty ())
11336  gh_mgr.post_callback (__myhandle__, "resizefcn");
11337 
11338  if (! get_sizechangedfcn ().isempty ())
11339  gh_mgr.post_callback (__myhandle__, "sizechangedfcn");
11340 
11341  update_boundingbox ();
11342  }
11343  }
11344 
11345  if (modified)
11346  {
11347  position.run_listeners (GCB_POSTSET);
11348  mark_modified ();
11349  }
11350 }
11351 
11352 
11353 void
11354 uipanel::properties::set_units (const octave_value& val)
11355 {
11356  caseless_str old_units = get_units ();
11357 
11358  if (units.set (val, true))
11359  {
11360  update_units (old_units);
11361  mark_modified ();
11362  }
11363 }
11364 
11365 void
11366 uipanel::properties::update_units (const caseless_str& old_units)
11367 {
11368  Matrix pos = get_position ().matrix_value ();
11369 
11370  gh_manager& gh_mgr
11371  = octave::__get_gh_manager__ ("uipanel::properties::update_units");
11372 
11373  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11374 
11375  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11376  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11377 
11378  pos = convert_position (pos, old_units, get_units (), parent_size);
11379  set_position (pos);
11380 }
11381 
11382 void
11383 uipanel::properties::set_fontunits (const octave_value& val)
11384 {
11385  caseless_str old_fontunits = get_fontunits ();
11386 
11387  if (fontunits.set (val, true))
11388  {
11389  update_fontunits (old_fontunits);
11390  mark_modified ();
11391  }
11392 }
11393 
11394 void
11395 uipanel::properties::update_fontunits (const caseless_str& old_units)
11396 {
11397  caseless_str new_units = get_fontunits ();
11398  double parent_height = get_boundingbox (false).elem (3);
11399  double fontsz = get_fontsize ();
11400 
11401  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11402 
11403  set_fontsize (octave_value (fontsz));
11404 }
11405 
11406 double
11408 {
11409  double fontsz = get_fontsize ();
11410  double parent_height = box_pix_height;
11411 
11412  if (fontunits_is ("normalized") && parent_height <= 0)
11413  parent_height = get_boundingbox (false).elem (3);
11414 
11415  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11416 }
11417 
11418 // ---------------------------------------------------------------------
11419 
11420 Matrix
11422  const Matrix& parent_pix_size) const
11423 {
11424  Matrix pos = get_position ().matrix_value ();
11425  Matrix parent_size (parent_pix_size);
11426 
11427  if (parent_size.isempty ())
11428  {
11429  gh_manager& gh_mgr
11430  = octave::__get_gh_manager__ ("uitable::properties::get_boundingbox");
11431 
11432  graphics_object go = gh_mgr.get_object (get_parent ());
11433 
11434  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11435  }
11436 
11437  pos = convert_position (pos, get_units (), "pixels", parent_size);
11438 
11439  pos(0)--;
11440  pos(1)--;
11441  pos(1) = parent_size(1) - pos(1) - pos(3);
11442 
11443  return pos;
11444 }
11445 
11446 void
11447 uitable::properties::set_columnformat (const octave_value& val)
11448 {
11449  /* Matlab only allows certain values for ColumnFormat. Here we only check the
11450  * structure of the argument. Values will be checked in Table.cc */
11451 
11452  if (val.iscellstr ())
11453  {
11454  if (columnformat.set (val, true))
11455  mark_modified ();
11456  }
11457  else if (val.iscell ())
11458  {
11459  Cell cell_value = val.cell_value ();
11460 
11461  for (int i = 0; i < cell_value.numel (); i++)
11462  {
11463  octave_value v = cell_value(i);
11464  if (v.iscell ())
11465  {
11466  /* We are in a pop-up menu selection.
11467  * Matlab only allows non-empty strings here. */
11468  Cell popup = v.cell_value ();
11469  for (int j = 0; j < popup.numel (); j++)
11470  {
11471  octave_value p = popup(j);
11472  if (! p.is_string () || p.isempty ())
11473  error ("set: pop-up menu definitions must be non-empty strings.");
11474  }
11475  }
11476  else if (! (v.is_string () || v.isempty ()))
11477  {
11478  error ("set: columnformat definintions must be a cellstr of "
11479  "either 'char', 'short [e|g|eng]?', 'long [e|g|eng]?', "
11480  "'numeric', 'bank', '+', 'rat', 'logical', "
11481  "or a cellstr of non-empty pop-up menu definitions.");
11482  }
11483  }
11484 
11485  if (columnformat.set (val, true))
11486  mark_modified ();
11487  }
11488  else if (val.isempty ())
11489  {
11490  if (columnformat.set (Cell (), true))
11491  mark_modified ();
11492  }
11493  else
11494  {
11495  error ("set: expecting cell of strings.");
11496  }
11497 }
11498 
11499 void
11500 uitable::properties::set_columnwidth (const octave_value& val)
11501 {
11502  bool error_exists = false;
11503 
11504  if (val.is_string () && val.string_value (false) == "auto")
11505  error_exists = false;
11506  else if (val.iscell ())
11507  {
11508  Cell cell_value = val.cell_value ();
11509  for (int i = 0; i < cell_value.numel (); i++)
11510  {
11511  octave_value v = cell_value(i);
11512  if (v.is_string ())
11513  {
11514  if (v.string_value (false) != "auto")
11515  error_exists = true;
11516  }
11517  else if (v.iscell ())
11518  {
11519  error_exists = true;
11520  }
11521  else if (! v.is_scalar_type ())
11522  {
11523  error_exists = true;
11524  }
11525  }
11526  }
11527  else
11528  error_exists = true;
11529 
11530  if (error_exists)
11531  error ("set: expecting either 'auto' or a cell of pixel values or auto.");
11532  else
11533  {
11534  if (columnwidth.set (val, true))
11535  mark_modified ();
11536  }
11537 }
11538 
11539 void
11540 uitable::properties::set_units (const octave_value& val)
11541 {
11542  caseless_str old_units = get_units ();
11543 
11544  if (units.set (val, true))
11545  {
11546  update_units (old_units);
11547  mark_modified ();
11548  }
11549 }
11550 
11551 void
11552 uitable::properties::update_units (const caseless_str& old_units)
11553 {
11554  Matrix pos = get_position ().matrix_value ();
11555 
11556  gh_manager& gh_mgr
11557  = octave::__get_gh_manager__ ("uitable::properties::update_units");
11558 
11559  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11560 
11561  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11562  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11563 
11564  pos = convert_position (pos, old_units, get_units (), parent_size);
11565  set_position (pos);
11566 }
11567 
11568 void
11569 uitable::properties::set_fontunits (const octave_value& val)
11570 {
11571  caseless_str old_fontunits = get_fontunits ();
11572 
11573  if (fontunits.set (val, true))
11574  {
11575  update_fontunits (old_fontunits);
11576  mark_modified ();
11577  }
11578 }
11579 
11580 void
11581 uitable::properties::update_fontunits (const caseless_str& old_units)
11582 {
11583  caseless_str new_units = get_fontunits ();
11584  double parent_height = get_boundingbox (false).elem (3);
11585  double fontsz = get_fontsize ();
11586 
11587  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11588 
11589  set_fontsize (octave_value (fontsz));
11590 }
11591 
11592 double
11594 {
11595  double fontsz = get_fontsize ();
11596  double parent_height = box_pix_height;
11597 
11598  if (fontunits_is ("normalized") && parent_height <= 0)
11599  parent_height = get_boundingbox (false).elem (3);
11600 
11601  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11602 }
11603 
11604 double
11605 uitable::properties::get_fontsize_pixels (double box_pix_height) const
11606 {
11607  double fontsz = get_fontsize ();
11608  double parent_height = box_pix_height;
11609 
11610  if (fontunits_is ("normalized") && parent_height <= 0)
11611  parent_height = get_boundingbox (false).elem (3);
11612 
11613  return convert_font_size (fontsz, get_fontunits (), "pixels", parent_height);
11614 }
11615 
11616 Matrix
11617 uitable::properties::get_backgroundcolor_rgb (void)
11618 {
11619  Matrix bg = backgroundcolor.get ().matrix_value ();
11620  return bg.row (0);
11621 }
11622 
11623 Matrix
11624 uitable::properties::get_alternatebackgroundcolor_rgb (void)
11625 {
11626  int i = 0;
11627  Matrix bg = backgroundcolor.get ().matrix_value ();
11628  if (bg.rows () > 1)
11629  i = 1;
11630 
11631  return bg.row (i);
11632 }
11633 
11634 Matrix
11635 uitable::properties::get_extent_matrix (void) const
11636 {
11637  return extent.get ().matrix_value ();
11638 }
11639 
11641 uitable::properties::get_extent (void) const
11642 {
11643  // FIXME: Is it really acceptable to just let the toolkit update the extent?
11644  Matrix m = extent.get ().matrix_value ();
11645 
11646  gh_manager& gh_mgr
11647  = octave::__get_gh_manager__ ("uitable::properties::get_extent");
11648 
11649  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11650 
11651  if (parent_go)
11652  {
11653  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11654  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11655 
11656  return convert_position (m, "pixels", get_units (), parent_size);
11657  }
11658 
11659  return m;
11660 }
11661 
11662 // ---------------------------------------------------------------------
11663 
11666 {
11668 
11669  if (retval.is_undefined ())
11670  {
11671  graphics_handle parent_h = get_parent ();
11672 
11673  gh_manager& gh_mgr
11674  = octave::__get_gh_manager__ ("uitoolbar::get_default");
11675 
11676  graphics_object parent_go = gh_mgr.get_object (parent_h);
11677 
11678  retval = parent_go.get_default (pname);
11679  }
11680 
11681  return retval;
11682 }
11683 
11684 void
11686 {
11687  // empty list of local defaults
11689 
11692 }
11693 
11694 // ---------------------------------------------------------------------
11695 
11698 {
11699  graphics_handle parent_h = get_parent ();
11700 
11701  gh_manager& gh_mgr
11702  = octave::__get_gh_manager__ ("base_graphics_object::get_default");
11703 
11704  graphics_object parent_go = gh_mgr.get_object (parent_h);
11705 
11706  return parent_go.get_default (type () + pname);
11707 }
11708 
11711 {
11712  gh_manager& gh_mgr
11713  = octave::__get_gh_manager__ ("base_graphics_object::get_factory_default");
11714 
11715  graphics_object parent_go = gh_mgr.get_object (0);
11716 
11717  return parent_go.get_factory_default (type () + name);
11718 }
11719 
11720 // We use a random value for the handle to avoid issues with plots and
11721 // scalar values for the first argument.
11723  : m_interpreter (interp), m_handle_map (), m_handle_free_list (),
11724  m_next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
11725  m_figure_list (), m_graphics_lock (), m_event_queue (),
11726  m_callback_objects (), m_event_processing (0)
11727 {
11729 
11730  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ("gh_manager");
11731 
11732  // Make sure the default graphics toolkit is registered.
11733  gtk_mgr.default_toolkit ();
11734 }
11735 
11737 gh_manager::make_graphics_handle (const std::string& go_name,
11738  const graphics_handle& p,
11739  bool integer_figure_handle,
11740  bool call_createfcn, bool notify_toolkit)
11741 {
11742  graphics_handle h = get_handle (integer_figure_handle);
11743 
11745 
11746  if (! bgo)
11747  error ("gh_manager::make_graphics_handle: invalid object type '%s'",
11748  go_name.c_str ());
11749 
11750  graphics_object go (bgo);
11751 
11752  m_handle_map[h] = go;
11753 
11754  // Overriding defaults will work now because the handle is valid
11755  // and we can find parent objects (not just handles).
11756  go.override_defaults ();
11757 
11758  if (go_name == "axes")
11759  {
11760  // Handle defaults for labels since overriding defaults for
11761  // them can't work before the axes object is fully
11762  // constructed.
11763 
11764  axes::properties& props
11765  = dynamic_cast<axes::properties&> (go.get_properties ());
11766 
11767  graphics_object tgo;
11768 
11769  tgo = get_object (props.get_xlabel ());
11770  tgo.override_defaults ();
11771 
11772  tgo = get_object (props.get_ylabel ());
11773  tgo.override_defaults ();
11774 
11775  tgo = get_object (props.get_zlabel ());
11776  tgo.override_defaults ();
11777 
11778  tgo = get_object (props.get_title ());
11779  tgo.override_defaults ();
11780  }
11781 
11782  if (call_createfcn)
11783  bgo->get_properties ().execute_createfcn ();
11784 
11785  // Notify graphics toolkit.
11786  if (notify_toolkit)
11787  go.initialize ();
11788 
11789  return h;
11790 }
11791 
11793 gh_manager::make_figure_handle (double val, bool notify_toolkit)
11794 {
11795  graphics_handle h = val;
11796 
11797  base_graphics_object *bgo = new figure (h, 0);
11798  graphics_object go (bgo);
11799 
11800  m_handle_map[h] = go;
11801 
11802  // Notify graphics toolkit.
11803  if (notify_toolkit)
11804  go.initialize ();
11805 
11806  go.override_defaults ();
11807 
11808  return h;
11809 }
11810 
11811 void
11813 {
11814  pop_figure (h);
11815 
11816  m_figure_list.push_front (h);
11817 }
11818 
11819 void
11821 {
11822  for (auto it = m_figure_list.begin (); it != m_figure_list.end (); it++)
11823  {
11824  if (*it == h)
11825  {
11826  m_figure_list.erase (it);
11827  break;
11828  }
11829  }
11830 }
11831 
11832 class
11834 {
11835 public:
11836  callback_event (const graphics_handle& h, const std::string& name,
11837  const octave_value& data = Matrix (),
11838  int busyaction = base_graphics_event::QUEUE)
11839  : base_graphics_event (busyaction), handle (h), callback_name (name),
11840  callback (), callback_data (data) { }
11841 
11843  const octave_value& data = Matrix (),
11844  int busyaction = base_graphics_event::QUEUE)
11845  : base_graphics_event (busyaction), handle (h), callback_name (),
11846  callback (cb), callback_data (data) { }
11847 
11848  void execute (void)
11849  {
11850  gh_manager& gh_mgr
11851  = octave::__get_gh_manager__ ("callback_event::execute");
11852 
11853  if (callback.is_defined ())
11854  gh_mgr.execute_callback (handle, callback, callback_data);
11855  else
11856  gh_mgr.execute_callback (handle, callback_name, callback_data);
11857  }
11858 
11859 private:
11861  : base_graphics_event (), handle (), callback_name (), callback_data ()
11862  { }
11863 
11864 private:
11866  std::string callback_name;
11869 };
11870 
11871 class
11873 {
11874 public:
11875  mcode_event (const graphics_handle& h, const std::string& cmd,
11876  int busyaction = base_graphics_event::QUEUE)
11877  : base_graphics_event (busyaction), handle (h), mcode (cmd)
11878  { }
11879 
11880  void execute (void)
11881  {
11882  if (! mcode.empty ())
11883  {
11884  gh_manager& gh_mgr
11885  = octave::__get_gh_manager__ ("mcode_event::execute");
11886 
11887  graphics_object go = gh_mgr.get_object (handle);
11888 
11889  if (go.valid_object ())
11890  {
11891  octave_value cb (mcode);
11892  gh_mgr.execute_callback (handle, cb);
11893  }
11894  }
11895  }
11896 
11897 private:
11899  : base_graphics_event (), handle (), mcode ()
11900  { }
11901 
11902 private:
11904  std::string mcode;
11905 };
11906 
11907 class
11909 {
11910 public:
11911 
11912  // function_event objects must be created with at least a function.
11913 
11914  function_event (void) = delete;
11915 
11916  function_event (graphics_event::event_fcn fcn, void *data = nullptr)
11917  : base_graphics_event (), function (fcn), function_data (data)
11918  { }
11919 
11920  // No copying!
11921 
11922  function_event (const function_event&) = delete;
11923 
11924  function_event & operator = (const function_event&) = delete;
11925 
11926  void execute (void)
11927  {
11928  function (function_data);
11929  }
11930 
11931 private:
11932 
11934 
11936 };
11937 
11938 class
11940 {
11941 public:
11942  set_event (const graphics_handle& h, const std::string& name,
11943  const octave_value& value, bool do_notify_toolkit = true,
11944  bool redraw_figure = false)
11945  : base_graphics_event (), handle (h), property_name (name),
11946  property_value (value), notify_toolkit (do_notify_toolkit),
11947  m_redraw_figure (redraw_figure)
11948  { }
11949 
11950  void execute (void)
11951  {
11952  gh_manager& gh_mgr = octave::__get_gh_manager__ ("set_event::execute");
11953 
11954  octave::autolock guard (gh_mgr.graphics_lock ());
11955 
11956  graphics_object go = gh_mgr.get_object (handle);
11957 
11958  if (go)
11959  {
11960  property p = go.get_properties ().get_property (property_name);
11961 
11962  if (p.ok ())
11963  {
11964  // FIXME: figure position and outerposition properties set_xxx have
11965  // a signature that allows passing the notify_toolkit argument.
11966  // Should we change all set_xxx signatures and allow
11967  // base_properties::set to accept this also? This would allow for
11968  // the use of high level set_xxx instead of directly changing the
11969  // property value.
11970  if (go.isa ("figure") && property_name == "position")
11971  {
11972  figure::properties& fprops
11973  = dynamic_cast<figure::properties&> (go.get_properties ());
11974  fprops.set_position (property_value, notify_toolkit);
11975  }
11976  else if (go.isa ("figure") && property_name == "outerposition")
11977  {
11978  figure::properties& fprops
11979  = dynamic_cast<figure::properties&> (go.get_properties ());
11980  fprops.set_outerposition (property_value, notify_toolkit);
11981  }
11982  else
11983  p.set (property_value, true, notify_toolkit);
11984 
11985  if (m_redraw_figure)
11986  {
11987  if (! go.isa ("figure"))
11988  go = go.get_ancestor ("figure");
11989 
11990  if (go.valid_object ())
11991  {
11992  figure::properties& fprops
11993  = dynamic_cast<figure::properties&> (go.get_properties ());
11994  fprops.get_toolkit ().redraw_figure (go);
11995  }
11996  }
11997  }
11998  }
11999  }
12000 
12001 private:
12002  set_event (void)
12003  : base_graphics_event (), handle (), property_name (), property_value ()
12004  { }
12005 
12006 private:
12008  std::string property_name;
12012 };
12013 
12016  const std::string& name,
12017  const octave_value& data,
12018  int busyaction)
12019 {
12020  return graphics_event (new callback_event (h, name, data, busyaction));
12021 }
12022 
12025  const octave_value& cb,
12026  const octave_value& data,
12027  int busyaction)
12028 {
12029  return graphics_event (new callback_event (h, cb, data, busyaction));
12030 }
12031 
12034  const std::string& cmd,
12035  int busyaction)
12036 {
12037  return graphics_event (new mcode_event (h, cmd, busyaction));
12038 }
12039 
12042  void *data)
12043 {
12044  return graphics_event (new function_event (fcn, data));
12045 }
12046 
12049  const std::string& name,
12050  const octave_value& data,
12051  bool notify_toolkit, bool redraw_figure)
12052 {
12053  return graphics_event (new set_event (h, name, data, notify_toolkit,
12054  redraw_figure));
12055 }
12056 
12057 static void
12059 {
12060  gh_manager& gh_mgr = octave::__get_gh_manager__ ("xset_gcbo");
12061 
12062  graphics_object go = gh_mgr.get_object (0);
12063 
12065  = dynamic_cast<root_figure::properties&> (go.get_properties ());
12066 
12067  props.set_callbackobject (h.as_octave_value ());
12068 }
12069 
12070 void
12072 {
12074 
12075  m_callback_objects.pop_front ();
12076 
12077  xset_gcbo (m_callback_objects.empty ()
12078  ? graphics_handle () : m_callback_objects.front ().get_handle ());
12079 }
12080 
12081 void
12083 {
12085  execute_callback (h, l, octave_value ());
12086  else
12087  {
12089 
12091  }
12092 }
12093 
12094 void
12096  const octave_value& cb_arg,
12097  const octave_value& data)
12098 {
12099  if (cb_arg.is_defined () && ! cb_arg.isempty ())
12100  {
12101  octave_value_list args;
12102  octave_value ov_fcn;
12103  octave_function *fcn = nullptr;
12104 
12105  args(0) = h.as_octave_value ();
12106  if (data.is_defined ())
12107  args(1) = data;
12108  else
12109  args(1) = Matrix ();
12110 
12112 
12113  frame.add_method (this, &gh_manager::restore_gcbo);
12114 
12115  graphics_object go (get_object (h));
12116  if (go)
12117  {
12118  // FIXME: Is the lock necessary when we're only calling a
12119  // const "get" method?
12121  m_callback_objects.push_front (go);
12122  xset_gcbo (h);
12123  }
12124 
12125  // Copy CB because "function_value" method is non-const.
12126  octave_value cb = cb_arg;
12127 
12128  if (cb.is_function ())
12129  fcn = cb.function_value ();
12130  else if (cb.is_function_handle ())
12131  ov_fcn = cb;
12132  else if (cb.is_string ())
12133  {
12134  int status;
12135  std::string s = cb.string_value ();
12136 
12137  try
12138  {
12139  m_interpreter.eval_string (s, false, status, 0);
12140  }
12141  catch (octave::execution_exception& e)
12142  {
12144  }
12145  }
12146  else if (cb.iscell () && cb.length () > 0
12147  && (cb.rows () == 1 || cb.columns () == 1)
12148  && (cb.cell_value ()(0).is_function ()
12149  || cb.cell_value ()(0).is_function_handle ()))
12150  {
12151  Cell c = cb.cell_value ();
12152 
12153  ov_fcn = c(0);
12154 
12155  for (int i = 1; i < c.numel () ; i++)
12156  args(1+i) = c(i);
12157  }
12158  else
12159  {
12160  std::string nm = cb.class_name ();
12161  error ("trying to execute non-executable object (class = %s)",
12162  nm.c_str ());
12163  }
12164 
12165  if (fcn || ov_fcn.is_defined ())
12166  try
12167  {
12168  if (ov_fcn.is_defined ())
12169  octave::feval (ov_fcn, args);
12170  else
12171  octave::feval (fcn, args);
12172  }
12173  catch (octave::execution_exception& e)
12174  {
12176  }
12177 
12178  // Redraw after interacting with a user-interface (ui*) object.
12179  if (Vdrawnow_requested)
12180  {
12181  if (go)
12182  {
12183  std::string go_name
12185 
12186  if (go_name.length () > 1
12187  && go_name[0] == 'u' && go_name[1] == 'i')
12188  {
12190  Vdrawnow_requested = false;
12191  }
12192  }
12193  }
12194  }
12195 }
12196 
12197 static int
12199 {
12200  gh_manager& gh_mgr = octave::__get_gh_manager__ ("process_graphics_events");
12201 
12202  return gh_mgr.process_events ();
12203 }
12204 
12205 void
12207 {
12208  m_event_queue.push_back (e);
12209 
12211 }
12212 
12213 void
12214 gh_manager::post_callback (const graphics_handle& h, const std::string& name,
12215  const octave_value& data)
12216 {
12218 
12219  graphics_object go = get_object (h);
12220 
12221  if (go.valid_object ())
12222  {
12223  caseless_str cname (name);
12224  int busyaction = base_graphics_event::QUEUE;
12225 
12226  if (cname == "deletefcn" || cname == "createfcn"
12227  || cname == "closerequestfcn"
12228  || ((go.isa ("figure") || go.isa ("uipanel")
12229  || go.isa ("uibuttongroup"))
12230  && (cname == "resizefcn" || cname == "sizechangedfcn")))
12231  busyaction = base_graphics_event::INTERRUPT;
12232  else if (go.get_properties ().get_busyaction () == "cancel")
12233  busyaction = base_graphics_event::CANCEL;
12234 
12235  // The "closerequestfcn" callback must be executed once the figure has
12236  // been made current. Let "close" do the job.
12237  if (cname == "closerequestfcn")
12238  {
12239  std::string cmd ("close (gcbf ());");
12240  post_event (graphics_event::create_mcode_event (h, cmd, busyaction));
12241  }
12242  else
12244  busyaction));
12245  }
12246 }
12247 
12248 void
12250 {
12252 
12254 }
12255 
12256 void
12257 gh_manager::post_set (const graphics_handle& h, const std::string& name,
12258  const octave_value& value, bool notify_toolkit,
12259  bool redraw_figure)
12260 {
12262 
12263  post_event (graphics_event::create_set_event (h, name, value, notify_toolkit,
12264  redraw_figure));
12265 }
12266 
12267 int
12269 {
12270  graphics_event e;
12271  bool old_Vdrawnow_requested = Vdrawnow_requested;
12272  bool events_executed = false;
12273 
12274  do
12275  {
12276  e = graphics_event ();
12277 
12278  {
12280 
12281  if (! m_event_queue.empty ())
12282  {
12283  if (m_callback_objects.empty () || force)
12284  {
12285  e = m_event_queue.front ();
12286 
12287  m_event_queue.pop_front ();
12288  }
12289  else
12290  {
12291  const graphics_object& go = m_callback_objects.front ();
12292 
12293  if (go.get_properties ().is_interruptible ())
12294  {
12295  e = m_event_queue.front ();
12296 
12297  m_event_queue.pop_front ();
12298  }
12299  else
12300  {
12301  std::list<graphics_event>::iterator p = m_event_queue.begin ();
12302 
12303  while (p != m_event_queue.end ())
12304  if (p->get_busyaction () == base_graphics_event::CANCEL)
12305  {
12306  p = m_event_queue.erase (p);
12307  }
12308  else if (p->get_busyaction ()
12310  {
12311  e = (*p);
12312  m_event_queue.erase (p);
12313  break;
12314  }
12315  else
12316  p++;
12317  }
12318  }
12319  }
12320  }
12321 
12322  if (e.ok ())
12323  {
12324  e.execute ();
12325  events_executed = true;
12326  }
12327  }
12328  while (e.ok ());
12329 
12330  {
12332 
12333  if (m_event_queue.empty () && m_event_processing == 0)
12335  }
12336 
12337  if (events_executed)
12339 
12340  if (Vdrawnow_requested && ! old_Vdrawnow_requested)
12341  {
12343 
12344  Vdrawnow_requested = false;
12345  }
12346 
12347  return 0;
12348 }
12349 
12350 
12351 /*
12352 ## Test interruptible/busyaction properties
12353 %!function cb (h)
12354 %! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]);
12355 %! drawnow ();
12356 %! setappdata (gcbf (), "cb_exec", [getappdata(gcbf (), "cb_exec") h]);
12357 %!endfunction
12358 %!
12359 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
12360 %! hf = figure ("visible", "off", "resizefcn", @cb);
12361 %! graphics_toolkit (hf, "qt");
12362 %! unwind_protect
12363 %! ## Default
12364 %! hui1 = uicontrol ("parent", hf, "interruptible", "on", "callback", @cb);
12365 %! hui2 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb);
12366 %! hui3 = uicontrol ("parent", hf, "busyaction", "queue", "callback", @cb);
12367 %! __go_post_callback__ (hui1, "callback");
12368 %! __go_post_callback__ (hui2, "callback");
12369 %! __go_post_callback__ (hui3, "callback");
12370 %!
12371 %! assert (getappdata (hf, "cb_exec"), []);
12372 %! drawnow ();
12373 %! assert (getappdata (hf, "cb_exec"), [hui1 hui2 hui3 hui3 hui2 hui1]);
12374 %!
12375 %! ## Interruptible off
12376 %! setappdata (hf, "cb_exec", []);
12377 %! set (hui1, "interruptible", "off");
12378 %! __go_post_callback__ (hui1, "callback");
12379 %! __go_post_callback__ (hui2, "callback");
12380 %! __go_post_callback__ (hui3, "callback");
12381 %! drawnow ();
12382 %! assert (getappdata (hf, "cb_exec"), [hui1 hui1 hui2 hui3 hui3 hui2]);
12383 %!
12384 %! ## "resizefcn" callback interrupts regardless of interruptible property
12385 %! setappdata (hf, "cb_exec", []);
12386 %! __go_post_callback__ (hui1, "callback");
12387 %! __go_post_callback__ (hf, "resizefcn");
12388 %! drawnow ();
12389 %! assert (getappdata (hf, "cb_exec"), [hui1 hf hf hui1]);
12390 %!
12391 %! ## test "busyaction" "cancel"
12392 %! setappdata (hf, "cb_exec", []);
12393 %! set (hui2, "busyaction", "cancel");
12394 %! __go_post_callback__ (hui1, "callback");
12395 %! __go_post_callback__ (hui2, "callback");
12396 %! __go_post_callback__ (hui3, "callback");
12397 %! __go_post_callback__ (hf, "resizefcn");
12398 %! drawnow ();
12399 %! assert (getappdata (hf, "cb_exec"), [hui1 hf hui3 hui3 hf hui1]);
12400 %! unwind_protect_cleanup
12401 %! close (hf)
12402 %! end_unwind_protect
12403 */
12404 
12405 void
12407 {
12409 
12410  if (enable)
12411  {
12413 
12415  }
12416  else
12417  {
12419 
12420  if (m_event_queue.empty () && m_event_processing == 0)
12422  }
12423 }
12424 
12427 {
12429 
12430  plist_map["figure"] = figure::properties::factory_defaults ();
12431  plist_map["axes"] = axes::properties::factory_defaults ();
12432  plist_map["line"] = line::properties::factory_defaults ();
12433  plist_map["text"] = text::properties::factory_defaults ();
12434  plist_map["image"] = image::properties::factory_defaults ();
12435  plist_map["patch"] = patch::properties::factory_defaults ();
12436  plist_map["surface"] = surface::properties::factory_defaults ();
12437  plist_map["light"] = light::properties::factory_defaults ();
12438  plist_map["hggroup"] = hggroup::properties::factory_defaults ();
12439  plist_map["uimenu"] = uimenu::properties::factory_defaults ();
12440  plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
12441  plist_map["uibuttongroup"] = uibuttongroup::properties::factory_defaults ();
12442  plist_map["uipanel"] = uipanel::properties::factory_defaults ();
12443  plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
12444  plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
12445  plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
12446  plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
12447 
12448  return plist_map;
12449 }
12450 
12451 // ---------------------------------------------------------------------
12452 
12453 DEFMETHOD (ishghandle, interp, args, ,
12454  doc: /* -*- texinfo -*-
12455 @deftypefn {} {} ishghandle (@var{h})
12456 Return true if @var{h} is a graphics handle and false otherwise.
12457 
12458 @var{h} may also be a matrix of handles in which case a logical array is
12459 returned that is true where the elements of @var{h} are graphics handles and
12460 false where they are not.
12461 @seealso{isgraphics, isaxes, isfigure, ishandle}
12462 @end deftypefn */)
12463 {
12464  gh_manager& gh_mgr = interp.get_gh_manager ();
12465 
12466  octave::autolock guard (gh_mgr.graphics_lock ());
12467 
12468  if (args.length () != 1)
12469  print_usage ();
12470 
12471  return ovl (ishghandle (args(0)));
12472 }
12473 
12474 /*
12475 %!test
12476 %! hf = figure ("visible", "off");
12477 %! unwind_protect
12478 %! assert (ishghandle (hf));
12479 %! assert (! ishghandle (-hf));
12480 %! ax = gca;
12481 %! l = line;
12482 %! assert (ishghandle (ax));
12483 %! assert (! ishghandle (-ax));
12484 %! assert (ishghandle ([l, -1, ax, hf]), logical ([1, 0, 1, 1]));
12485 %! assert (ishghandle ([l, -1, ax, hf]'), logical ([1, 0, 1, 1]'));
12486 %! unwind_protect_cleanup
12487 %! close (hf);
12488 %! end_unwind_protect
12489 
12490 %!assert (ishghandle ([-1 0]), [false true])
12491 */
12492 
12493 static bool
12495 {
12496  gh_manager& gh_mgr = octave::__get_gh_manager__ ("is_handle_visible");
12497 
12498  return h.ok () && gh_mgr.is_handle_visible (h);
12499 }
12500 
12501 static bool
12502 is_handle_visible (double val)
12503 {
12504  gh_manager& gh_mgr = octave::__get_gh_manager__ ("is_handle_visible");
12505 
12506  return is_handle_visible (gh_mgr.lookup (val));
12507 }
12508 
12509 static octave_value
12511 {
12512  octave_value retval = false;
12513 
12514  if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
12515  retval = true;
12516  else if (val.isnumeric () && val.isreal ())
12517  {
12518  const NDArray handles = val.array_value ();
12519 
12520  boolNDArray result (handles.dims ());
12521 
12522  for (octave_idx_type i = 0; i < handles.numel (); i++)
12523  result.xelem (i) = is_handle_visible (handles(i));
12524 
12525  retval = result;
12526  }
12527 
12528  return retval;
12529 }
12530 
12531 DEFUN (__is_handle_visible__, args, ,
12532  doc: /* -*- texinfo -*-
12533 @deftypefn {} {} __is_handle_visible__ (@var{h})
12534 Undocumented internal function.
12535 @end deftypefn */)
12536 {
12537  if (args.length () != 1)
12538  print_usage ();
12539 
12540  return ovl (is_handle_visible (args(0)));
12541 }
12542 
12543 DEFMETHOD (reset, interp, args, ,
12544  doc: /* -*- texinfo -*-
12545 @deftypefn {} {} reset (@var{h})
12546 Reset the properties of the graphic object @var{h} to their default values.
12547 
12548 For figures, the properties @qcode{"position"}, @qcode{"units"},
12549 @qcode{"windowstyle"}, and @qcode{"paperunits"} are not affected.
12550 For axes, the properties @qcode{"position"} and @qcode{"units"} are
12551 not affected.
12552 
12553 The input @var{h} may also be a vector of graphic handles in which case
12554 each individual object will be reset.
12555 @seealso{cla, clf, newplot}
12556 @end deftypefn */)
12557 {
12558  if (args.length () != 1)
12559  print_usage ();
12560 
12561  // get vector of graphics handles
12562  ColumnVector hcv = args(0).xvector_value ("reset: H must be a graphics handle");
12563 
12564  gh_manager& gh_mgr = interp.get_gh_manager ();
12565 
12566  // loop over graphics objects
12567  for (octave_idx_type n = 0; n < hcv.numel (); n++)
12568  gh_mgr.get_object (hcv(n)).reset_default_properties ();
12569 
12570  Vdrawnow_requested = true;
12571 
12572  return ovl ();
12573 }
12574 
12575 /*
12576 
12577 %!test # line object
12578 %! hf = figure ("visible", "off");
12579 %! unwind_protect
12580 %! tol = 20 * eps;
12581 %! hax = axes ("defaultlinelinewidth", 3);
12582 %!
12583 %! hli = line (1:10, 1:10, 1:10, "marker", "o",
12584 %! "markerfacecolor", "b", "linestyle", ":");
12585 %!
12586 %! reset (hli);
12587 %! assert (get (hli, "marker"), get (0, "defaultlinemarker"));
12588 %! assert (get (hli, "markerfacecolor"),
12589 %! get (0, "defaultlinemarkerfacecolor"));
12590 %! assert (get (hli, "linestyle"), get (0, "defaultlinelinestyle"));
12591 %! assert (get (hli, "linewidth"), 3, tol); # parent axes defaults
12592 %!
12593 %! unwind_protect_cleanup
12594 %! close (hf);
12595 %! end_unwind_protect
12596 
12597 %!test # patch object
12598 %! hf = figure ("visible", "off");
12599 %! unwind_protect
12600 %! tol = 20 * eps;
12601 %! t1 = (1/16:1/8:1)' * 2*pi;
12602 %! t2 = ((1/16:1/16:1)' + 1/32) * 2*pi;
12603 %! x1 = sin (t1) - 0.8;
12604 %! y1 = cos (t1);
12605 %! x2 = sin (t2) + 0.8;
12606 %! y2 = cos (t2);
12607 %! vert = [x1, y1; x2, y2];
12608 %! fac = [1:8,NaN(1,8);9:24];
12609 %! hpa = patch ("Faces",fac, "Vertices",vert, "FaceColor","r");
12610 %!
12611 %! reset (hpa);
12612 %! assert (get (hpa, "faces"), get (0, "defaultpatchfaces"), tol);
12613 %! assert (get (hpa, "vertices"), get (0, "defaultpatchvertices"), tol);
12614 %! assert (get (hpa, "facevertexcdata"),
12615 %! get (0, "defaultpatchfacevertexcdata"), tol);
12616 %! unwind_protect_cleanup
12617 %! close (hf);
12618 %! end_unwind_protect
12619 
12620 %!test # surface object
12621 %! hf = figure ("visible", "off");
12622 %! unwind_protect
12623 %! tol = 20 * eps;
12624 %! hsu = surface (peaks, "edgecolor", "none");
12625 %!
12626 %! reset (hsu);
12627 %! assert (get (hsu, "xdata"), get (0, "defaultsurfacexdata"), tol);
12628 %! assert (get (hsu, "ydata"), get (0, "defaultsurfaceydata"), tol);
12629 %! assert (get (hsu, "zdata"), get (0, "defaultsurfacezdata"), tol);
12630 %! assert (get (hsu, "edgecolor"), get (0, "defaultsurfaceedgecolor"), tol);
12631 %! unwind_protect_cleanup
12632 %! close (hf);
12633 %! end_unwind_protect
12634 
12635 %!test # image object
12636 %! hf = figure ("visible", "off");
12637 %! unwind_protect
12638 %! tol = 20 * eps;
12639 %! him = image (rand (10,10), "cdatamapping", "scaled");
12640 %!
12641 %! reset (him);
12642 %! assert (get (him, "cdata"), get (0, "defaultimagecdata"), tol);
12643 %! assert (get (him, "cdatamapping"),
12644 %! get (0, "defaultimagecdatamapping"), tol);
12645 %! unwind_protect_cleanup
12646 %! close (hf);
12647 %! end_unwind_protect
12648 
12649 %!test # text object
12650 %! hf = figure ("visible", "off");
12651 %! unwind_protect
12652 %! tol = 20 * eps;
12653 %! hte = text (5, 5, "Hi!", "fontsize", 20 ,"color", "r");
12654 %!
12655 %! reset (hte);
12656 %! assert (get (hte, "position"), get (0, "defaulttextposition"), tol);
12657 %! assert (get (hte, "fontsize"), get (0, "defaulttextfontsize"), tol);
12658 %! assert (get (hte, "color"), get (0, "defaulttextcolor"), tol);
12659 %! unwind_protect_cleanup
12660 %! close (hf);
12661 %! end_unwind_protect
12662 
12663 %!test # axes object
12664 %! hf = figure ("visible", "off");
12665 %! unwind_protect
12666 %! tol = 20 * eps;
12667 %! pos = get (0, "defaultaxesposition") * .5;
12668 %! hax = axes ("linewidth", 2, "position", pos);
12669 %! title ("Reset me, please!");
12670 %!
12671 %! reset (hax);
12672 %! assert (get (hax, "linewidth"), get (0, "defaultaxeslinewidth"), tol);
12673 %! assert (get (hax, "position"), pos, tol); # axes position is unchanged
12674 %! assert (get (hax, "default"), struct ()); # no more axes' defaults
12675 %! assert (get (get (hax, "title"), "string"), "");
12676 %! unwind_protect_cleanup
12677 %! close (hf);
12678 %! end_unwind_protect
12679 
12680 %!test # root object
12681 %! set (0, "defaultfigurevisible", "off");
12682 %! hf = figure ("visible", "off", "paperunits", "centimeters",
12683 %! "papertype", "a4");
12684 %! unwind_protect
12685 %! reset (hf);
12686 %! assert (get (hf, "papertype"), get (0, "defaultfigurepapertype"));
12687 %! assert (get (hf, "paperunits"), "centimeters"); # paperunits is unchanged
12688 %! assert (get (hf, "visible"), get (0, "defaultfigurevisible"));
12689 %! unwind_protect_cleanup
12690 %! close (hf);
12691 %! set (0, "defaultfigurevisible", "remove");
12692 %! end_unwind_protect
12693 
12694 */
12695 
12696 DEFMETHOD (set, interp, args, nargout,
12697  doc: /* -*- texinfo -*-
12698 @deftypefn {} {} set (@var{h}, @var{property}, @var{value}, @dots{})
12699 @deftypefnx {} {} set (@var{h}, @var{properties}, @var{values})
12700 @deftypefnx {} {} set (@var{h}, @var{pv})
12701 @deftypefnx {} {@var{value_list} =} set (@var{h}, @var{property})
12702 @deftypefnx {} {@var{all_value_list} =} set (@var{h})
12703 Set named property values for the graphics handle (or vector of graphics
12704 handles) @var{h}.
12705 
12706 There are three ways to give the property names and values:
12707 
12708 @itemize
12709 @item as a comma separated list of @var{property}, @var{value} pairs
12710 
12711 Here, each @var{property} is a string containing the property name, each
12712 @var{value} is a value of the appropriate type for the property.
12713 
12714 @item as a cell array of strings @var{properties} containing property names
12715 and a cell array @var{values} containing property values.
12716 
12717 In this case, the number of columns of @var{values} must match the number of
12718 elements in @var{properties}. The first column of @var{values} contains
12719 values for the first entry in @var{properties}, etc. The number of rows of
12720 @var{values} must be 1 or match the number of elements of @var{h}. In the
12721 first case, each handle in @var{h} will be assigned the same values. In the
12722 latter case, the first handle in @var{h} will be assigned the values from
12723 the first row of @var{values} and so on.
12724 
12725 @item as a structure array @var{pv}
12726 
12727 Here, the field names of @var{pv} represent the property names, and the
12728 field values give the property values. In contrast to the previous case,
12729 all elements of @var{pv} will be set in all handles in @var{h} independent
12730 of the dimensions of @var{pv}.
12731 @end itemize
12732 
12733 @code{set} is also used to query the list of values a named property will
12734 take. @code{@var{clist} = set (@var{h}, "property")} will return the list
12735 of possible values for @qcode{"property"} in the cell list @var{clist}.
12736 If no output variable is used then the list is formatted and printed to the
12737 screen.
12738 
12739 If no property is specified (@code{@var{slist} = set (@var{h})}) then a
12740 structure @var{slist} is returned where the fieldnames are the properties of
12741 the object @var{h} and the fields are the list of possible values for each
12742 property. If no output variable is used then the list is formatted and
12743 printed to the screen.
12744 
12745 For example,
12746 
12747 @example
12748 @group
12749 hf = figure ();
12750 set (hf, "paperorientation")
12751 @result{} [ landscape | @{portrait@} ]
12752 @end group
12753 @end example
12754 
12755 @noindent
12756 shows the paperorientation property can take two values with the default
12757 being @qcode{"portrait"}.
12758 @seealso{get}
12759 @end deftypefn */)
12760 {
12761  gh_manager& gh_mgr = interp.get_gh_manager ();
12762 
12763  octave::autolock guard (gh_mgr.graphics_lock ());
12764 
12765  int nargin = args.length ();
12766 
12767  if (nargin == 0)
12768  print_usage ();
12769 
12771 
12772  // get vector of graphics handles
12773  ColumnVector hcv = args(0).xvector_value ("set: H must be a graphics handle");
12774 
12775  bool request_drawnow = false;
12776 
12777  // loop over graphics objects
12778  for (octave_idx_type n = 0; n < hcv.numel (); n++)
12779  {
12780  graphics_object go = gh_mgr.get_object (hcv(n));
12781 
12782  if (! go)
12783  error ("set: invalid handle (= %g)", hcv(n));
12784 
12785  if (nargin == 3 && args(1).iscellstr () && args(2).iscell ())
12786  {
12787  if (args(2).cell_value ().rows () == 1)
12788  go.set (args(1).cellstr_value (), args(2).cell_value (), 0);
12789  else if (hcv.numel () == args(2).cell_value ().rows ())
12790  go.set (args(1).cellstr_value (), args(2).cell_value (), n);
12791  else
12792  error ("set: number of graphics handles must match number of "
12793  "value rows (%" OCTAVE_IDX_TYPE_FORMAT " != "
12794  "%" OCTAVE_IDX_TYPE_FORMAT ")",
12795  hcv.numel (), args(2).cell_value ().rows ());
12796  }
12797  else if (nargin == 2 && args(1).isstruct ())
12798  go.set (args(1).map_value ());
12799  else if (nargin == 2 && args(1).is_string ())
12800  {
12801  std::string property = args(1).string_value ();
12802  std::transform (property.begin (), property.end (),
12803  property.begin (), tolower);
12804 
12805  octave_map pmap = go.values_as_struct ();
12806 
12808  if (nargout != 0)
12809  retval = Matrix ();
12810  else
12811  octave_stdout << "set: " << property
12812  <<" is read-only" << std::endl;
12813  else if (pmap.isfield (property))
12814  {
12815  if (nargout != 0)
12816  retval = pmap.getfield (property)(0);
12817  else
12818  {
12819  std::string s = go.value_as_string (property);
12820 
12821  octave_stdout << s;
12822  }
12823  }
12824  else
12825  error ("set: unknown property");
12826  }
12827  else if (nargin == 1)
12828  {
12829  if (nargout != 0)
12830  retval = go.values_as_struct ();
12831  else
12832  {
12833  std::string s = go.values_as_string ();
12834 
12835  octave_stdout << s;
12836  }
12837  }
12838  else
12839  {
12840  go.set (args.splice (0, 1));
12841  }
12842 
12843  request_drawnow = true;
12844  }
12845 
12846  if (request_drawnow)
12847  Vdrawnow_requested = true;
12848 
12849  return retval;
12850 }
12851 
12852 static std::string
12854 {
12855  std::string retval;
12856 
12857  gh_manager& gh_mgr = octave::__get_gh_manager__ ("get_graphics_object_type");
12858 
12859  graphics_object go = gh_mgr.get_object (val);
12860 
12861  if (! go)
12862  error ("get: invalid handle (= %g)", val);
12863 
12864  return go.type ();
12865 }
12866 
12867 DEFMETHOD (get, interp, args, ,
12868  doc: /* -*- texinfo -*-
12869 @deftypefn {} {@var{val} =} get (@var{h})
12870 @deftypefnx {} {@var{val} =} get (@var{h}, @var{p})
12871 Return the value of the named property @var{p} from the graphics handle
12872 @var{h}.
12873 
12874 If @var{p} is omitted, return the complete property list for @var{h}.
12875 
12876 If @var{h} is a vector, return a cell array including the property values or
12877 lists respectively.
12878 @seealso{set}
12879 @end deftypefn */)
12880 {
12881  gh_manager& gh_mgr = interp.get_gh_manager ();
12882 
12883  octave::autolock guard (gh_mgr.graphics_lock ());
12884 
12885  int nargin = args.length ();
12886 
12887  if (nargin < 1 || nargin > 2)
12888  print_usage ();
12889 
12890  if (args(0).isempty ())
12891  return ovl (Matrix ());
12892 
12893  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
12894 
12895  octave_idx_type hcv_len = hcv.numel ();
12896 
12897  if (nargin == 1 && hcv_len > 1)
12898  {
12899  std::string typ0 = get_graphics_object_type (hcv(0));
12900 
12901  for (octave_idx_type n = 1; n < hcv_len; n++)
12902  {
12903  std::string typ = get_graphics_object_type (hcv(n));
12904 
12905  if (typ != typ0)
12906  error ("get: vector of handles must all have the same type");
12907  }
12908  }
12909 
12911  Cell vals;
12912  bool use_cell_format = false;
12913 
12914  if (nargin > 1 && args(1).iscellstr ())
12915  {
12916  Array<std::string> plist = args(1).cellstr_value ();
12917 
12918  octave_idx_type plen = plist.numel ();
12919 
12920  use_cell_format = true;
12921 
12922  vals.resize (dim_vector (hcv_len, plen));
12923 
12924  for (octave_idx_type n = 0; n < hcv_len; n++)
12925  {
12926  graphics_object go = gh_mgr.get_object (hcv(n));
12927 
12928  if (! go)
12929  error ("get: invalid handle (= %g)", hcv(n));
12930 
12931  for (octave_idx_type m = 0; m < plen; m++)
12932  {
12933  caseless_str property = plist(m);
12934 
12935  vals(n, m) = go.get (property);
12936  }
12937  }
12938  }
12939  else
12940  {
12942 
12943  if (nargin > 1)
12944  property = args(1).xstring_value ("get: second argument must be property name or cell array of property names");
12945 
12946  vals.resize (dim_vector (hcv_len, 1));
12947 
12948  for (octave_idx_type n = 0; n < hcv_len; n++)
12949  {
12950  graphics_object go = gh_mgr.get_object (hcv(n));
12951 
12952  if (! go)
12953  error ("get: invalid handle (= %g)", hcv(n));
12954 
12955  if (nargin == 1)
12956  vals(n) = go.get ();
12957  else
12958  vals(n) = go.get (property);
12959  }
12960  }
12961 
12962  if (use_cell_format)
12963  retval = vals;
12964  else
12965  {
12966  octave_idx_type vals_len = vals.numel ();
12967 
12968  if (vals_len == 0)
12969  retval = Matrix ();
12970  else if (vals_len == 1)
12971  retval = vals(0);
12972  else if (vals_len > 1 && nargin == 1)
12973  {
12974  OCTAVE_LOCAL_BUFFER (octave_scalar_map, tmp, vals_len);
12975 
12976  for (octave_idx_type n = 0; n < vals_len; n++)
12977  tmp[n] = vals(n).scalar_map_value ();
12978 
12979  retval = octave_map::cat (0, vals_len, tmp);
12980  }
12981  else
12982  retval = vals;
12983  }
12984 
12985  return retval;
12986 }
12987 
12988 /*
12989 %!assert (get (findobj (0, "Tag", "nonexistenttag"), "nonexistentproperty"), [])
12990 */
12991 
12992 // Return all properties from the graphics handle @var{h}.
12993 // If @var{h} is a vector, return a cell array including the
12994 // property values or lists respectively.
12995 
12996 DEFMETHOD (__get__, interp, args, ,
12997  doc: /* -*- texinfo -*-
12998 @deftypefn {} {} __get__ (@var{h})
12999 Undocumented internal function.
13000 @end deftypefn */)
13001 {
13002  gh_manager& gh_mgr = interp.get_gh_manager ();
13003 
13004  octave::autolock guard (gh_mgr.graphics_lock ());
13005 
13006  if (args.length () != 1)
13007  print_usage ();
13008 
13009  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
13010 
13011  octave_idx_type hcv_len = hcv.numel ();
13012 
13013  Cell vals (dim_vector (hcv_len, 1));
13014 
13015 // vals.resize (dim_vector (hcv_len, 1));
13016 
13017  for (octave_idx_type n = 0; n < hcv_len; n++)
13018  {
13019  graphics_object go = gh_mgr.get_object (hcv(n));
13020 
13021  if (! go)
13022  error ("get: invalid handle (= %g)", hcv(n));
13023 
13024  // Disable "Octave:deprecated-property" warnings
13025  int state = toggle_warn ("Octave:deprecated-property", false);
13026 
13027  vals(n) = go.get (true);
13028 
13029  toggle_warn ("Octave:deprecated-property", true, state);
13030  }
13031 
13032  octave_idx_type vals_len = vals.numel ();
13033 
13034  if (vals_len > 1)
13035  return ovl (vals);
13036  else if (vals_len == 1)
13037  return ovl (vals(0));
13038  else
13039  return ovl ();
13040 }
13041 
13042 static octave_value
13043 make_graphics_object (const std::string& go_name,
13044  bool integer_figure_handle,
13045  const octave_value_list& args)
13046 {
13048 
13049  double val = octave::numeric_limits<double>::NaN ();
13050 
13051  octave_value_list xargs = args.splice (0, 1);
13052 
13053  caseless_str p ("parent");
13054 
13055  // Remove all "parent" property overrides of the first argument to function
13056  // and accept only the last one (bug #55322).
13057  for (int i = 0; i < xargs.length (); i += 2)
13058  {
13059  if (xargs(i).is_string () && p.compare (xargs(i).string_value ()))
13060  {
13061  if (i >= (xargs.length () - 1))
13062  error ("__go_%s__: missing value for parent property",
13063  go_name.c_str ());
13064 
13065  val = xargs(i+1).double_value ();
13066 
13067  xargs = xargs.splice (i, 2);
13068  i -= 2;
13069  }
13070  }
13071 
13072  if (octave::math::isnan (val))
13073  val = args(0).xdouble_value ("__go_%s__: invalid parent", go_name.c_str ());
13074 
13075  gh_manager& gh_mgr = octave::__get_gh_manager__ ("make_graphics_object");
13076 
13077  graphics_handle parent = gh_mgr.lookup (val);
13078 
13079  if (! parent.ok ())
13080  error ("__go_%s__: invalid parent", go_name.c_str ());
13081 
13082  graphics_handle h;
13083 
13084  try
13085  {
13086  h = gh_mgr.make_graphics_handle (go_name, parent,
13087  integer_figure_handle, false, false);
13088  }
13089  catch (octave::execution_exception& e)
13090  {
13091  error (e, "__go_%s__: %s, unable to create graphics handle",
13092  go_name.c_str (), e.message ().c_str ());
13093  }
13094 
13095  try
13096  {
13097  xset (h, xargs);
13098  }
13099  catch (octave::execution_exception& e)
13100  {
13102  error (e, "__go_%s__: %s, unable to create graphics handle",
13103  go_name.c_str (), e.message ().c_str ());
13104  }
13105 
13106  adopt (parent, h);
13107 
13108  xcreatefcn (h);
13109  xinitialize (h);
13110 
13111  retval = h.value ();
13112 
13113  Vdrawnow_requested = true;
13114 
13115  return retval;
13116 }
13117 
13118 DEFMETHOD (__go_figure__, interp, args, ,
13119  doc: /* -*- texinfo -*-
13120 @deftypefn {} {} __go_figure__ (@var{fignum})
13121 Undocumented internal function.
13122 @end deftypefn */)
13123 {
13124  gh_manager& gh_mgr = interp.get_gh_manager ();
13125 
13126  octave::autolock guard (gh_mgr.graphics_lock ());
13127 
13128  if (args.length () == 0)
13129  print_usage ();
13130 
13131  double val = args(0).xdouble_value ("__go_figure__: figure number must be a double value");
13132 
13134 
13135  if (isfigure (val))
13136  {
13137  graphics_handle h = gh_mgr.lookup (val);
13138 
13139  xset (h, args.splice (0, 1));
13140 
13141  retval = h.value ();
13142  }
13143  else
13144  {
13145  bool int_fig_handle = true;
13146 
13147  octave_value_list xargs = args.splice (0, 1);
13148 
13150 
13151  if (octave::math::isnan (val))
13152  {
13153  caseless_str pname ("integerhandle");
13154 
13155  for (int i = 0; i < xargs.length (); i++)
13156  {
13157  if (xargs(i).is_string ()
13158  && pname.compare (xargs(i).string_value ()))
13159  {
13160  if (i < (xargs.length () - 1))
13161  {
13162  std::string pval = xargs(i+1).string_value ();
13163 
13164  caseless_str on ("on");
13165  int_fig_handle = on.compare (pval);
13166  xargs = xargs.splice (i, 2);
13167 
13168  break;
13169  }
13170  }
13171  }
13172 
13173  h = gh_mgr.make_graphics_handle ("figure", 0, int_fig_handle,
13174  false, false);
13175 
13176  if (! int_fig_handle)
13177  {
13178  // We need to initialize the integerhandle property
13179  // without calling the set_integerhandle method,
13180  // because doing that will generate a new handle value...
13181  graphics_object go = gh_mgr.get_object (h);
13182  go.get_properties ().init_integerhandle ("off");
13183  }
13184  }
13185  else if (val > 0 && octave::math::x_nint (val) == val)
13186  h = gh_mgr.make_figure_handle (val, false);
13187 
13188  if (! h.ok ())
13189  error ("__go_figure__: failed to create figure handle");
13190 
13191  try
13192  {
13193  xset (h, xargs);
13194  }
13195  catch (octave::execution_exception& e)
13196  {
13198  error (e, "__go_figure__: unable to create figure handle");
13199  }
13200 
13201  adopt (0, h);
13202 
13203  gh_mgr.push_figure (h);
13204 
13205  xcreatefcn (h);
13206  xinitialize (h);
13207 
13208  retval = h.value ();
13209  }
13210 
13211  return retval;
13212 }
13213 
13214 #define GO_BODY(TYPE) \
13215  gh_manager& gh_mgr = interp.get_gh_manager (); \
13216  \
13217  octave::autolock guard (gh_mgr.graphics_lock ()); \
13218  \
13219  if (args.length () == 0) \
13220  print_usage (); \
13221  \
13222  return octave_value (make_graphics_object (#TYPE, false, args)); \
13223 
13224 int
13226 {
13227  int nd = 2;
13228 
13229  if (go.isa ("surface"))
13230  nd = 3;
13231  else if ((go.isa ("line") || go.isa ("patch"))
13232  && ! go.get ("zdata").isempty ())
13233  nd = 3;
13234  else
13235  {
13236  Matrix kids = go.get_properties ().get_children ();
13237 
13238  gh_manager& gh_mgr = octave::__get_gh_manager__ ("calc_dimensions");
13239 
13240  for (octave_idx_type i = 0; i < kids.numel (); i++)
13241  {
13242  graphics_handle hkid = gh_mgr.lookup (kids(i));
13243 
13244  if (hkid.ok ())
13245  {
13246  const graphics_object& kid = gh_mgr.get_object (hkid);
13247 
13248  if (kid.valid_object ())
13249  nd = calc_dimensions (kid);
13250 
13251  if (nd == 3)
13252  break;
13253  }
13254  }
13255  }
13256 
13257  return nd;
13258 }
13259 
13260 DEFMETHOD (__calc_dimensions__, interp, args, ,
13261  doc: /* -*- texinfo -*-
13262 @deftypefn {} {} __calc_dimensions__ (@var{axes})
13263 Internal function.
13264 
13265 Determine the number of dimensions in a graphics object, either 2 or 3.
13266 @end deftypefn */)
13267 {
13268  gh_manager& gh_mgr = interp.get_gh_manager ();
13269 
13270  octave::autolock guard (gh_mgr.graphics_lock ());
13271 
13272  if (args.length () != 1)
13273  print_usage ();
13274 
13275  double h = args(0).xdouble_value ("__calc_dimensions__: first argument must be a graphics handle");
13276 
13277  return ovl (calc_dimensions (gh_mgr.get_object (h)));
13278 }
13279 
13280 DEFMETHOD (__go_axes__, interp, args, ,
13281  doc: /* -*- texinfo -*-
13282 @deftypefn {} {} __go_axes__ (@var{parent})
13283 Undocumented internal function.
13284 @end deftypefn */)
13285 {
13286  GO_BODY (axes);
13287 }
13288 
13289 DEFMETHOD (__go_line__, interp, args, ,
13290  doc: /* -*- texinfo -*-
13291 @deftypefn {} {} __go_line__ (@var{parent})
13292 Undocumented internal function.
13293 @end deftypefn */)
13294 {
13295  GO_BODY (line);
13296 }
13297 
13298 DEFMETHOD (__go_text__, interp, args, ,
13299  doc: /* -*- texinfo -*-
13300 @deftypefn {} {} __go_text__ (@var{parent})
13301 Undocumented internal function.
13302 @end deftypefn */)
13303 {
13304  GO_BODY (text);
13305 }
13306 
13307 DEFMETHOD (__go_image__, interp, args, ,
13308  doc: /* -*- texinfo -*-
13309 @deftypefn {} {} __go_image__ (@var{parent})
13310 Undocumented internal function.
13311 @end deftypefn */)
13312 {
13313  GO_BODY (image);
13314 }
13315 
13316 DEFMETHOD (__go_surface__, interp, args, ,
13317  doc: /* -*- texinfo -*-
13318 @deftypefn {} {} __go_surface__ (@var{parent})
13319 Undocumented internal function.
13320 @end deftypefn */)
13321 {
13322  GO_BODY (surface);
13323 }
13324 
13325 DEFMETHOD (__go_patch__, interp, args, ,
13326  doc: /* -*- texinfo -*-
13327 @deftypefn {} {} __go_patch__ (@var{parent})
13328 Undocumented internal function.
13329 @end deftypefn */)
13330 {
13331  GO_BODY (patch);
13332 }
13333 
13334 DEFMETHOD (__go_light__, interp, args, ,
13335  doc: /* -*- texinfo -*-
13336 @deftypefn {} {} __go_light__ (@var{parent})
13337 Undocumented internal function.
13338 @end deftypefn */)
13339 {
13340  GO_BODY (light);
13341 }
13342 
13343 DEFMETHOD (__go_hggroup__, interp, args, ,
13344  doc: /* -*- texinfo -*-
13345 @deftypefn {} {} __go_hggroup__ (@var{parent})
13346 Undocumented internal function.
13347 @end deftypefn */)
13348 {
13349  GO_BODY (hggroup);
13350 }
13351 
13352 DEFMETHOD (__go_uimenu__, interp, args, ,
13353  doc: /* -*- texinfo -*-
13354 @deftypefn {} {} __go_uimenu__ (@var{parent})
13355 Undocumented internal function.
13356 @end deftypefn */)
13357 {
13358  GO_BODY (uimenu);
13359 }
13360 
13361 DEFMETHOD (__go_uicontrol__, interp, args, ,
13362  doc: /* -*- texinfo -*-
13363 @deftypefn {} {} __go_uicontrol__ (@var{parent})
13364 Undocumented internal function.
13365 @end deftypefn */)
13366 {
13367  GO_BODY (uicontrol);
13368 }
13369 
13370 DEFMETHOD (__go_uibuttongroup__, interp, args, ,
13371  doc: /* -*- texinfo -*-
13372 @deftypefn {} {} __go_uibuttongroup__ (@var{parent})
13373 Undocumented internal function.
13374 @end deftypefn */)
13375 {
13377 }
13378 
13379 DEFMETHOD (__go_uipanel__, interp, args, ,
13380  doc: /* -*- texinfo -*-
13381 @deftypefn {} {} __go_uipanel__ (@var{parent})
13382 Undocumented internal function.
13383 @end deftypefn */)
13384 {
13385  GO_BODY (uipanel);
13386 }
13387 
13388 DEFMETHOD (__go_uicontextmenu__, interp, args, ,
13389  doc: /* -*- texinfo -*-
13390 @deftypefn {} {} __go_uicontextmenu__ (@var{parent})
13391 Undocumented internal function.
13392 @end deftypefn */)
13393 {
13395 }
13396 
13397 DEFMETHOD (__go_uitable__, interp, args, ,
13398  doc: /* -*- texinfo -*-
13399 @deftypefn {} {} __go_uitable__ (@var{parent})
13400 Undocumented internal function.
13401 @end deftypefn */)
13402 {
13403  GO_BODY (uitable);
13404 }
13405 
13406 DEFMETHOD (__go_uitoolbar__, interp, args, ,
13407  doc: /* -*- texinfo -*-
13408 @deftypefn {} {} __go_uitoolbar__ (@var{parent})
13409 Undocumented internal function.
13410 @end deftypefn */)
13411 {
13412  GO_BODY (uitoolbar);
13413 }
13414 
13415 DEFMETHOD (__go_uipushtool__, interp, args, ,
13416  doc: /* -*- texinfo -*-
13417 @deftypefn {} {} __go_uipushtool__ (@var{parent})
13418 Undocumented internal function.
13419 @end deftypefn */)
13420 {
13421  GO_BODY (uipushtool);
13422 }
13423 
13424 DEFMETHOD (__go_uitoggletool__, interp, args, ,
13425  doc: /* -*- texinfo -*-
13426 @deftypefn {} {} __go_uitoggletool__ (@var{parent})
13427 Undocumented internal function.
13428 @end deftypefn */)
13429 {
13431 }
13432 
13433 DEFMETHOD (__go_delete__, interp, args, ,
13434  doc: /* -*- texinfo -*-
13435 @deftypefn {} {} __go_delete__ (@var{h})
13436 Undocumented internal function.
13437 @end deftypefn */)
13438 {
13439  gh_manager& gh_mgr = interp.get_gh_manager ();
13440 
13441  octave::autolock guard (gh_mgr.graphics_lock ());
13442 
13443  if (args.length () != 1)
13444  print_usage ();
13445 
13447 
13448  const NDArray vals = args(0).xarray_value ("delete: invalid graphics object");
13449 
13450  // Check all the handles to delete are valid first,
13451  // as callbacks might delete one of the handles we later want to delete.
13452  for (octave_idx_type i = 0; i < vals.numel (); i++)
13453  {
13454  h = gh_mgr.lookup (vals(i));
13455 
13456  if (! h.ok ())
13457  error ("delete: invalid graphics object (= %g)", vals(i));
13458  }
13459 
13460  delete_graphics_objects (vals);
13461 
13462  return ovl ();
13463 }
13464 
13465 DEFMETHOD (__go_handles__, interp, args, ,
13466  doc: /* -*- texinfo -*-
13467 @deftypefn {} {} __go_handles__ (@var{show_hidden})
13468 Undocumented internal function.
13469 @end deftypefn */)
13470 {
13471  gh_manager& gh_mgr = interp.get_gh_manager ();
13472 
13473  octave::autolock guard (gh_mgr.graphics_lock ());
13474 
13475  bool show_hidden = false;
13476 
13477  if (args.length () > 0)
13478  show_hidden = args(0).bool_value ();
13479 
13480  return ovl (gh_mgr.handle_list (show_hidden));
13481 }
13482 
13483 DEFMETHOD (__go_figure_handles__, interp, args, ,
13484  doc: /* -*- texinfo -*-
13485 @deftypefn {} {} __go_figure_handles__ (@var{show_hidden})
13486 Undocumented internal function.
13487 @end deftypefn */)
13488 {
13489  gh_manager& gh_mgr = interp.get_gh_manager ();
13490 
13491  octave::autolock guard (gh_mgr.graphics_lock ());
13492 
13493  bool show_hidden = false;
13494 
13495  if (args.length () > 0)
13496  show_hidden = args(0).bool_value ();
13497 
13498  return ovl (gh_mgr.figure_handle_list (show_hidden));
13499 }
13500 
13501 DEFMETHOD (__go_execute_callback__, interp, args, ,
13502  doc: /* -*- texinfo -*-
13503 @deftypefn {} {} __go_execute_callback__ (@var{h}, @var{name})
13504 @deftypefnx {} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})
13505 Undocumented internal function.
13506 @end deftypefn */)
13507 {
13508  int nargin = args.length ();
13509 
13510  if (nargin < 2 || nargin > 3)
13511  print_usage ();
13512 
13513  const NDArray vals = args(0).xarray_value ("__go_execute_callback__: invalid graphics object");
13514 
13515  std::string name = args(1).xstring_value ("__go_execute_callback__: invalid callback name");
13516 
13517  gh_manager& gh_mgr = interp.get_gh_manager ();
13518 
13519  for (octave_idx_type i = 0; i < vals.numel (); i++)
13520  {
13521  double val = vals(i);
13522 
13523  graphics_handle h = gh_mgr.lookup (val);
13524 
13525  if (! h.ok ())
13526  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
13527 
13528  if (nargin == 2)
13529  gh_mgr.execute_callback (h, name);
13530  else
13531  gh_mgr.execute_callback (h, name, args(2));
13532  }
13533 
13534  return ovl ();
13535 }
13536 
13537 DEFMETHOD (__go_post_callback__, interp, args, ,
13538  doc: /* -*- texinfo -*-
13539 @deftypefn {} {} __go_post_callback__ (@var{h}, @var{name})
13540 @deftypefnx {} {} __go_post_callback__ (@var{h}, @var{name}, @var{param})
13541 Undocumented internal function.
13542 @end deftypefn */)
13543 {
13544  int nargin = args.length ();
13545 
13546  if (nargin < 2 || nargin > 3)
13547  print_usage ();
13548 
13549  const NDArray vals = args(0).xarray_value ("__go_post_callback__: invalid graphics object");
13550 
13551  std::string name = args(1).xstring_value ("__go_post_callback__: invalid callback name");
13552 
13553  gh_manager& gh_mgr = interp.get_gh_manager ();
13554 
13555  for (octave_idx_type i = 0; i < vals.numel (); i++)
13556  {
13557  double val = vals(i);
13558 
13559  graphics_handle h = gh_mgr.lookup (val);
13560 
13561  if (! h.ok ())
13562  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
13563 
13564  if (nargin == 2)
13565  gh_mgr.post_callback (h, name);
13566  else
13567  gh_mgr.post_callback (h, name, args(2));
13568  }
13569 
13570  return ovl ();
13571 }
13572 
13573 DEFMETHOD (__image_pixel_size__, interp, args, ,
13574  doc: /* -*- texinfo -*-
13575 @deftypefn {} {@var{sz} =} __image_pixel_size__ (@var{h})
13576 Internal function: returns the pixel size of the image in normalized units.
13577 @end deftypefn */)
13578 {
13579  if (args.length () != 1)
13580  print_usage ();
13581 
13582  gh_manager& gh_mgr = interp.get_gh_manager ();
13583 
13584  double h = args(0).xdouble_value ("__image_pixel_size__: argument is not a handle");
13585 
13586  graphics_object go = gh_mgr.get_object (h);
13587 
13588  if (! go || ! go.isa ("image"))
13589  error ("__image_pixel_size__: object is not an image");
13590 
13591  image::properties& ip
13592  = dynamic_cast<image::properties&> (go.get_properties ());
13593 
13594  Matrix dp = Matrix (1, 2);
13595  dp(0) = ip.pixel_xsize ();
13596  dp(1) = ip.pixel_ysize ();
13597  return ovl (dp);
13598 }
13599 
13600 DEFMETHOD (available_graphics_toolkits, interp, , ,
13601  doc: /* -*- texinfo -*-
13602 @deftypefn {} {} available_graphics_toolkits ()
13603 Return a cell array of registered graphics toolkits.
13604 @seealso{graphics_toolkit, register_graphics_toolkit}
13605 @end deftypefn */)
13606 {
13607  gh_manager& gh_mgr = interp.get_gh_manager ();
13608 
13609  octave::autolock guard (gh_mgr.graphics_lock ());
13610 
13611  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13612 
13613  return ovl (gtk_mgr.available_toolkits_list ());
13614 }
13615 
13616 DEFMETHOD (register_graphics_toolkit, interp, args, ,
13617  doc: /* -*- texinfo -*-
13618 @deftypefn {} {} register_graphics_toolkit (@var{toolkit})
13619 List @var{toolkit} as an available graphics toolkit.
13620 @seealso{available_graphics_toolkits}
13621 @end deftypefn */)
13622 {
13623  gh_manager& gh_mgr = interp.get_gh_manager ();
13624 
13625  octave::autolock guard (gh_mgr.graphics_lock ());
13626 
13627  if (args.length () != 1)
13628  print_usage ();
13629 
13630  std::string name = args(0).xstring_value ("register_graphics_toolkit: TOOLKIT must be a string");
13631 
13632  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13633 
13634  gtk_mgr.register_toolkit (name);
13635 
13636  return ovl ();
13637 }
13638 
13639 DEFMETHOD (loaded_graphics_toolkits, interp, , ,
13640  doc: /* -*- texinfo -*-
13641 @deftypefn {} {} loaded_graphics_toolkits ()
13642 Return a cell array of the currently loaded graphics toolkits.
13643 @seealso{available_graphics_toolkits}
13644 @end deftypefn */)
13645 {
13646  gh_manager& gh_mgr = interp.get_gh_manager ();
13647 
13648  octave::autolock guard (gh_mgr.graphics_lock ());
13649 
13650  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13651 
13652  return ovl (gtk_mgr.loaded_toolkits_list ());
13653 }
13654 
13655 DEFMETHOD (__show_figure__, interp, args, ,
13656  doc: /* -*- texinfo -*-
13657 @deftypefn {} {} __show_figure__ (@var{n})
13658 Undocumented internal function.
13659 @end deftypefn */)
13660 {
13661  if (args.length () != 1)
13662  print_usage ();
13663 
13664  gh_manager& gh_mgr = interp.get_gh_manager ();
13665 
13666  double h = args(0).xdouble_value ("__show_figure__: invalid handle H");
13667 
13668  graphics_handle gh = gh_mgr.lookup (h);
13669 
13670  if (! gh.ok ())
13671  error ("__show_figure__: invalid graphics object (= %g)", h);
13672 
13673  graphics_object go = gh_mgr.get_object (gh);
13674 
13675  figure::properties& fprops
13676  = dynamic_cast<figure::properties&> (go.get_properties ());
13677 
13678  fprops.get_toolkit ().show_figure (go);
13679 
13680  return ovl ();
13681 }
13682 
13683 DEFMETHOD (drawnow, interp, args, ,
13684  doc: /* -*- texinfo -*-
13685 @deftypefn {} {} drawnow ()
13686 @deftypefnx {} {} drawnow ("expose")
13687 @deftypefnx {} {} drawnow (@var{term}, @var{file}, @var{debug_file})
13688 Update figure windows and their children.
13689 
13690 The event queue is flushed and any callbacks generated are executed.
13691 
13692 With the optional argument @qcode{"expose"}, only graphic objects are
13693 updated and no other events or callbacks are processed.
13694 
13695 The third calling form of @code{drawnow} is for debugging and is
13696 undocumented.
13697 @seealso{refresh}
13698 @end deftypefn */)
13699 {
13700  if (args.length () > 3)
13701  print_usage ();
13702 
13703  octave::unwind_protect frame;
13704 
13705  frame.protect_var (Vdrawnow_requested, false);
13706 
13707  // Redraw unless we are in the middle of a deletion.
13708 
13709  if (! delete_executing)
13710  {
13711  gh_manager& gh_mgr = interp.get_gh_manager ();
13712 
13713  octave::autolock guard (gh_mgr.graphics_lock ());
13714 
13715  if (args.length () <= 1)
13716  {
13717  // First process events so that the redraw happens when all
13718  // objects are in their definite state.
13719  bool do_events = true;
13720 
13721  if (args.length () == 1)
13722  {
13723  caseless_str val (args(0).xstring_value ("drawnow: first argument must be a string"));
13724 
13725  if (val.compare ("expose"))
13726  do_events = false;
13727  else
13728  error ("drawnow: invalid argument, 'expose' is only valid option");
13729  }
13730 
13731  if (do_events)
13732  {
13733  gh_mgr.unlock ();
13734 
13735  gh_mgr.process_events ();
13736 
13737  gh_mgr.lock ();
13738  }
13739 
13740  Matrix hlist = gh_mgr.figure_handle_list (true);
13741 
13742  // Redraw modified figures
13743  for (int i = 0; i < hlist.numel (); i++)
13744  {
13745  graphics_handle h = gh_mgr.lookup (hlist(i));
13746 
13747  if (h.ok () && h != 0)
13748  {
13749  graphics_object go = gh_mgr.get_object (h);
13750  figure::properties& fprops
13751  = dynamic_cast<figure::properties&> (go.get_properties ());
13752 
13753  if (fprops.is_modified ())
13754  {
13755  if (fprops.is_visible ())
13756  {
13757  gh_mgr.unlock ();
13758 
13759  fprops.get_toolkit ().redraw_figure (go);
13760 
13761  gh_mgr.lock ();
13762  }
13763 
13764  fprops.set_modified (false);
13765  }
13766  }
13767 
13768  }
13769  }
13770  else if (args.length () >= 2 && args.length () <= 3)
13771  {
13772  std::string term, file, debug_file;
13773 
13774  term = args(0).xstring_value ("drawnow: TERM must be a string");
13775 
13776  file = args(1).xstring_value ("drawnow: FILE must be a string");
13777 
13778  if (file.empty ())
13779  error ("drawnow: empty output ''");
13780  else if (file.length () == 1 && file[0] == '|')
13781  error ("drawnow: empty pipe '|'");
13782  else if (file[0] != '|')
13783  {
13784  size_t pos = file.find_last_of (octave::sys::file_ops::dir_sep_chars ());
13785 
13786  if (pos != std::string::npos)
13787  {
13788  std::string dirname = file.substr (0, pos+1);
13789 
13791 
13792  if (! fs || ! fs.is_dir ())
13793  error ("drawnow: nonexistent directory '%s'",
13794  dirname.c_str ());
13795 
13796  }
13797  }
13798 
13799  debug_file = (args.length () > 2 ? args(2).xstring_value ("drawnow: DEBUG_FILE must be a string") : "");
13800 
13801  graphics_handle h = gcf ();
13802 
13803  if (! h.ok ())
13804  error ("drawnow: nothing to draw");
13805 
13806  graphics_object go = gh_mgr.get_object (h);
13807 
13808  gh_mgr.unlock ();
13809 
13810  go.get_toolkit ().print_figure (go, term, file, debug_file);
13811 
13812  gh_mgr.lock ();
13813  }
13814  }
13815 
13816  return ovl ();
13817 }
13818 
13819 DEFMETHOD (addlistener, interp, args, ,
13820  doc: /* -*- texinfo -*-
13821 @deftypefn {} {} addlistener (@var{h}, @var{prop}, @var{fcn})
13822 Register @var{fcn} as listener for the property @var{prop} of the graphics
13823 object @var{h}.
13824 
13825 Property listeners are executed (in order of registration) when the property
13826 is set. The new value is already available when the listeners are executed.
13827 
13828 @var{prop} must be a string naming a valid property in @var{h}.
13829 
13830 @var{fcn} can be a function handle, a string or a cell array whose first
13831 element is a function handle. If @var{fcn} is a function handle, the
13832 corresponding function should accept at least 2 arguments, that will be
13833 set to the object handle and the empty matrix respectively. If @var{fcn}
13834 is a string, it must be any valid octave expression. If @var{fcn} is a cell
13835 array, the first element must be a function handle with the same signature
13836 as described above. The next elements of the cell array are passed
13837 as additional arguments to the function.
13838 
13839 Example:
13840 
13841 @example
13842 @group
13843 function my_listener (h, dummy, p1)
13844  fprintf ("my_listener called with p1=%s\n", p1);
13845 endfunction
13846 
13847 addlistener (gcf, "position", @{@@my_listener, "my string"@})
13848 @end group
13849 @end example
13850 
13851 @seealso{dellistener, addproperty, hggroup}
13852 @end deftypefn */)
13853 {
13854  gh_manager& gh_mgr = interp.get_gh_manager ();
13855 
13856  octave::autolock guard (gh_mgr.graphics_lock ());
13857 
13858  int nargin = args.length ();
13859 
13860  if (nargin < 3 || nargin > 4)
13861  print_usage ();
13862 
13863  double h = args(0).xdouble_value ("addlistener: invalid handle H");
13864 
13865  std::string pname = args(1).xstring_value ("addlistener: PROP must be a string");
13866 
13867  graphics_handle gh = gh_mgr.lookup (h);
13868 
13869  if (! gh.ok ())
13870  error ("addlistener: invalid graphics object (= %g)", h);
13871 
13872  graphics_object go = gh_mgr.get_object (gh);
13873 
13874  go.add_property_listener (pname, args(2), GCB_POSTSET);
13875 
13876  if (args.length () == 4)
13877  {
13878  caseless_str persistent = args(3).string_value ();
13879  if (persistent.compare ("persistent"))
13880  go.add_property_listener (pname, args(2), GCB_PERSISTENT);
13881  }
13882 
13883  return ovl ();
13884 }
13885 
13886 DEFMETHOD (dellistener, interp, args, ,
13887  doc: /* -*- texinfo -*-
13888 @deftypefn {} {} dellistener (@var{h}, @var{prop}, @var{fcn})
13889 Remove the registration of @var{fcn} as a listener for the property
13890 @var{prop} of the graphics object @var{h}.
13891 
13892 The function @var{fcn} must be the same variable (not just the same value),
13893 as was passed to the original call to @code{addlistener}.
13894 
13895 If @var{fcn} is not defined then all listener functions of @var{prop}
13896 are removed.
13897 
13898 Example:
13899 
13900 @example
13901 @group
13902 function my_listener (h, dummy, p1)
13903  fprintf ("my_listener called with p1=%s\n", p1);
13904 endfunction
13905 
13906 c = @{@@my_listener, "my string"@};
13907 addlistener (gcf, "position", c);
13908 dellistener (gcf, "position", c);
13909 @end group
13910 @end example
13911 
13912 @seealso{addlistener}
13913 @end deftypefn */)
13914 {
13915  gh_manager& gh_mgr = interp.get_gh_manager ();
13916 
13917  octave::autolock guard (gh_mgr.graphics_lock ());
13918 
13919  if (args.length () < 2 || args.length () > 3)
13920  print_usage ();
13921 
13922  double h = args(0).xdouble_value ("dellistener: invalid handle");
13923 
13924  std::string pname = args(1).xstring_value ("dellistener: PROP must be a string");
13925 
13926  graphics_handle gh = gh_mgr.lookup (h);
13927 
13928  if (! gh.ok ())
13929  error ("dellistener: invalid graphics object (= %g)", h);
13930 
13931  graphics_object go = gh_mgr.get_object (gh);
13932 
13933  if (args.length () == 2)
13935  else
13936  {
13937  if (args(2).is_string ()
13938  && args(2).string_value () == "persistent")
13939  {
13940  go.delete_property_listener (pname, octave_value (),
13941  GCB_PERSISTENT);
13942  go.delete_property_listener (pname, octave_value (),
13943  GCB_POSTSET);
13944  }
13945  else
13946  go.delete_property_listener (pname, args(2), GCB_POSTSET);
13947  }
13948 
13949  return ovl ();
13950 }
13951 
13952 DEFMETHOD (addproperty, interp, args, ,
13953  doc: /* -*- texinfo -*-
13954 @deftypefn {} {} addproperty (@var{name}, @var{h}, @var{type})
13955 @deftypefnx {} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})
13956 Create a new property named @var{name} in graphics object @var{h}.
13957 
13958 @var{type} determines the type of the property to create. @var{args}
13959 usually contains the default value of the property, but additional
13960 arguments might be given, depending on the type of the property.
13961 
13962 The supported property types are:
13963 
13964 @table @code
13965 @item string
13966 A string property. @var{arg} contains the default string value.
13967 
13968 @item any
13969 An @nospell{un-typed} property. This kind of property can hold any octave
13970 value. @var{args} contains the default value.
13971 
13972 @item radio
13973 A string property with a limited set of accepted values. The first
13974 argument must be a string with all accepted values separated by
13975 a vertical bar ('|'). The default value can be marked by enclosing
13976 it with a '@{' '@}' pair. The default value may also be given as
13977 an optional second string argument.
13978 
13979 @item boolean
13980 A boolean property. This property type is equivalent to a radio
13981 property with "on|off" as accepted values. @var{arg} contains
13982 the default property value.
13983 
13984 @item double
13985 A scalar double property. @var{arg} contains the default value.
13986 
13987 @item handle
13988 A handle property. This kind of property holds the handle of a
13989 graphics object. @var{arg} contains the default handle value.
13990 When no default value is given, the property is initialized to
13991 the empty matrix.
13992 
13993 @item data
13994 A data (matrix) property. @var{arg} contains the default data
13995 value. When no default value is given, the data is initialized to
13996 the empty matrix.
13997 
13998 @item color
13999 A color property. @var{arg} contains the default color value.
14000 When no default color is given, the property is set to black.
14001 An optional second string argument may be given to specify an
14002 additional set of accepted string values (like a radio property).
14003 @end table
14004 
14005 @var{type} may also be the concatenation of a core object type and
14006 a valid property name for that object type. The property created
14007 then has the same characteristics as the referenced property (type,
14008 possible values, hidden state@dots{}). This allows one to clone an
14009 existing property into the graphics object @var{h}.
14010 
14011 Examples:
14012 
14013 @example
14014 @group
14015 addproperty ("my_property", gcf, "string", "a string value");
14016 addproperty ("my_radio", gcf, "radio", "val_1|val_2|@{val_3@}");
14017 addproperty ("my_style", gcf, "linelinestyle", "--");
14018 @end group
14019 @end example
14020 
14021 @seealso{addlistener, hggroup}
14022 @end deftypefn */)
14023 {
14024  gh_manager& gh_mgr = interp.get_gh_manager ();
14025 
14026  octave::autolock guard (gh_mgr.graphics_lock ());
14027 
14028  if (args.length () < 3)
14029  print_usage ();
14030 
14031  std::string name = args(0).xstring_value ("addproperty: NAME must be a string");
14032 
14033  double h = args(1).xdouble_value ("addproperty: invalid handle H");
14034 
14035  graphics_handle gh = gh_mgr.lookup (h);
14036 
14037  if (! gh.ok ())
14038  error ("addproperty: invalid graphics object (= %g)", h);
14039 
14040  graphics_object go = gh_mgr.get_object (gh);
14041 
14042  std::string type = args(2).xstring_value ("addproperty: TYPE must be a string");
14043 
14044  if (go.get_properties ().has_property (name))
14045  error ("addproperty: a '%s' property already exists in the graphics object",
14046  name.c_str ());
14047 
14048  property p = property::create (name, gh, type, args.splice (0, 3));
14049 
14050  go.get_properties ().insert_property (name, p);
14051 
14052  return ovl ();
14053 }
14054 
14056 get_property_from_handle (double handle, const std::string& property,
14057  const std::string& func)
14058 {
14059  gh_manager& gh_mgr = octave::__get_gh_manager__ ("get_property_from_handle");
14060 
14061  octave::autolock guard (gh_mgr.graphics_lock ());
14062 
14063  graphics_object go = gh_mgr.get_object (handle);
14064 
14065  if (! go)
14066  error ("%s: invalid handle (= %g)", func.c_str (), handle);
14067 
14068  return go.get (caseless_str (property));
14069 }
14070 
14071 bool
14072 set_property_in_handle (double handle, const std::string& property,
14073  const octave_value& arg, const std::string& func)
14074 {
14075  gh_manager& gh_mgr = octave::__get_gh_manager__ ("set_property_in_handle");
14076 
14077  octave::autolock guard (gh_mgr.graphics_lock ());
14078 
14079  graphics_object go = gh_mgr.get_object (handle);
14080 
14081  if (! go)
14082  error ("%s: invalid handle (= %g)", func.c_str (), handle);
14083 
14084  go.set (caseless_str (property), arg);
14085 
14086  return true;
14087 }
14088 
14089 static bool
14091 {
14092  octave_value_list args(2);
14093 
14094  args(0) = ov1;
14095  args(1) = ov2;
14096 
14097  octave_value_list result = octave::feval ("isequal", args, 1);
14098 
14099  if (result.length () > 0)
14100  return result(0).bool_value ();
14101 
14102  return false;
14103 }
14104 
14105 static std::map<uint32_t, bool> waitfor_results;
14106 
14107 static void
14108 cleanup_waitfor_id (uint32_t id)
14109 {
14110  waitfor_results.erase (id);
14111 }
14112 
14113 static void
14115  listener_mode mode = GCB_POSTSET)
14116 {
14117  Cell c = listener.cell_value ();
14118 
14119  if (c.numel () >= 4)
14120  {
14121  double h = c(2).double_value ();
14122 
14123  caseless_str pname = c(3).string_value ();
14124 
14125  gh_manager& gh_mgr
14126  = octave::__get_gh_manager__ ("do_cleanup_waitfor_listener");
14127 
14128  octave::autolock guard (gh_mgr.graphics_lock ());
14129 
14130  graphics_handle gh = gh_mgr.lookup (h);
14131 
14132  if (gh.ok ())
14133  {
14134  graphics_object go = gh_mgr.get_object (gh);
14135 
14136  if (go.get_properties ().has_property (pname))
14137  {
14138  go.get_properties ().delete_listener (pname, listener, mode);
14139 
14140  if (mode == GCB_POSTSET)
14141  go.get_properties ().delete_listener (pname, listener,
14142  GCB_PERSISTENT);
14143  }
14144  }
14145  }
14146 }
14147 
14148 static void
14151 
14152 static void
14155 
14157 {
14158  if (args.length () > 3)
14159  {
14160  uint32_t id = args(2).uint32_scalar_value ().value ();
14161 
14162  if (args.length () > 5)
14163  {
14164  double h = args(0).double_value ();
14165 
14166  caseless_str pname = args(4).string_value ();
14167 
14168  gh_manager& gh_mgr = octave::__get_gh_manager__ ("waitfor_listener");
14169 
14170  octave::autolock guard (gh_mgr.graphics_lock ());
14171 
14172  graphics_handle gh = gh_mgr.lookup (h);
14173 
14174  if (gh.ok ())
14175  {
14176  graphics_object go = gh_mgr.get_object (gh);
14177  octave_value pvalue = go.get (pname);
14178 
14179  if (compare_property_values (pvalue, args(5)))
14180  waitfor_results[id] = true;
14181  }
14182  }
14183  else
14184  waitfor_results[id] = true;
14185  }
14186 
14187  return ovl ();
14188 }
14189 
14191 {
14192  if (args.length () > 2)
14193  {
14194  uint32_t id = args(2).uint32_scalar_value ().value ();
14195 
14196  waitfor_results[id] = true;
14197  }
14198 
14199  return ovl ();
14200 }
14201 
14202 DEFMETHOD (waitfor, interp, args, ,
14203  doc: /* -*- texinfo -*-
14204 @deftypefn {} {} waitfor (@var{h})
14205 @deftypefnx {} {} waitfor (@var{h}, @var{prop})
14206 @deftypefnx {} {} waitfor (@var{h}, @var{prop}, @var{value})
14207 @deftypefnx {} {} waitfor (@dots{}, "timeout", @var{timeout})
14208 Suspend the execution of the current program until a condition is
14209 satisfied on the graphics handle @var{h}.
14210 
14211 While the program is suspended graphics events are still processed normally,
14212 allowing callbacks to modify the state of graphics objects. This function
14213 is reentrant and can be called from a callback, while another @code{waitfor}
14214 call is pending at the top-level.
14215 
14216 In the first form, program execution is suspended until the graphics object
14217 @var{h} is destroyed. If the graphics handle is invalid or if @var{h} is
14218 the root graphics handle and no property @var{prop} was provided, the function
14219 returns immediately.
14220 
14221 In the second form, execution is suspended until the graphics object is
14222 destroyed or the property named @var{prop} is modified. If the graphics
14223 handle is invalid or the property does not exist, the function returns
14224 immediately.
14225 
14226 In the third form, execution is suspended until the graphics object is
14227 destroyed or the property named @var{prop} is set to @var{value}. The
14228 function @code{isequal} is used to compare property values. If the graphics
14229 handle is invalid, the property does not exist or the property is already
14230 set to @var{value}, the function returns immediately.
14231 
14232 An optional timeout can be specified using the property @qcode{"timeout"}.
14233 This timeout value is the number of seconds to wait for the condition to be
14234 true. @var{timeout} must be at least 1. If a smaller value is specified, a
14235 warning is issued and a value of 1 is used instead. If the timeout value is
14236 not an integer, it is truncated towards 0.
14237 
14238 To define a condition on a property named @qcode{"timeout"}, use the string
14239 @qcode{'\timeout'} instead.
14240 
14241 In all cases, typing CTRL-C stops program execution immediately.
14242 @seealso{waitforbuttonpress, isequal}
14243 @end deftypefn */)
14244 {
14245  if (args.length () == 0)
14246  print_usage ();
14247 
14248  // return immediately if the graphics handle is invalid
14249  if (args(0).isempty ())
14250  return ovl ();
14251 
14252  double h = args(0).xdouble_value ("waitfor: invalid handle value");
14253 
14254  if (! ishghandle (h) || (h == 0 && args.length () == 1))
14255  return ovl ();
14256 
14257  caseless_str pname;
14258 
14259  octave::unwind_protect frame;
14260 
14261  static uint32_t id_counter = 0;
14262  uint32_t id = 0;
14263 
14264  int max_arg_index = 0;
14265  int timeout_index = -1;
14266 
14267  double timeout = 0;
14268 
14269  gh_manager& gh_mgr = interp.get_gh_manager ();
14270 
14271  if (args.length () > 1)
14272  {
14273  pname = args(1).xstring_value ("waitfor: PROP must be a string");
14274 
14275  if (pname.empty ())
14276  error ("waitfor: PROP must be a non-empty string");
14277 
14278  if (pname != "timeout")
14279  {
14280  if (pname.compare (R"(\timeout)"))
14281  pname = "timeout";
14282 
14283  static octave_value wf_listener;
14284 
14285  if (! wf_listener.is_defined ())
14286  wf_listener
14288  "waitfor_listener"));
14289 
14290  max_arg_index++;
14291  if (args.length () > 2)
14292  {
14293  if (args(2).is_string ())
14294  {
14295  caseless_str s = args(2).string_value ();
14296 
14297  if (s.compare ("timeout"))
14298  timeout_index = 2;
14299  else
14300  max_arg_index++;
14301  }
14302  else
14303  max_arg_index++;
14304  }
14305 
14306  Cell listener (1, max_arg_index >= 2 ? 5 : 4);
14307 
14308  id = id_counter++;
14309  frame.add_fcn (cleanup_waitfor_id, id);
14310  waitfor_results[id] = false;
14311 
14312  listener(0) = wf_listener;
14313  listener(1) = octave_uint32 (id);
14314  listener(2) = h;
14315  listener(3) = pname;
14316 
14317  if (max_arg_index >= 2)
14318  listener(4) = args(2);
14319 
14320  octave_value ov_listener (listener);
14321 
14322  octave::autolock guard (gh_mgr.graphics_lock ());
14323 
14324  graphics_handle gh = gh_mgr.lookup (h);
14325 
14326  if (gh.ok ())
14327  {
14328  graphics_object go = gh_mgr.get_object (gh);
14329 
14330  if (max_arg_index >= 2
14331  && compare_property_values (go.get (pname), args(2)))
14332  waitfor_results[id] = true;
14333  else
14334  {
14335 
14336  frame.add_fcn (cleanup_waitfor_postset_listener, ov_listener);
14337  go.add_property_listener (pname, ov_listener, GCB_POSTSET);
14338  go.add_property_listener (pname, ov_listener, GCB_PERSISTENT);
14339 
14340  if (go.get_properties ().has_dynamic_property (pname))
14341  {
14342  static octave_value wf_del_listener;
14343 
14344  if (! wf_del_listener.is_defined ())
14345  wf_del_listener
14348  "waitfor_del_listener"));
14349 
14350  Cell del_listener (1, 4);
14351 
14352  del_listener(0) = wf_del_listener;
14353  del_listener(1) = octave_uint32 (id);
14354  del_listener(2) = h;
14355  del_listener(3) = pname;
14356 
14357  octave_value ov_del_listener (del_listener);
14358 
14360  ov_del_listener);
14361  go.add_property_listener (pname, ov_del_listener,
14362  GCB_PREDELETE);
14363  }
14364  }
14365  }
14366  }
14367  }
14368 
14369  if (timeout_index < 0 && args.length () > (max_arg_index + 1))
14370  {
14371  caseless_str s = args(max_arg_index + 1).xstring_value ("waitfor: invalid parameter, expected 'timeout'");
14372 
14373  if (! s.compare ("timeout"))
14374  error ("waitfor: invalid parameter '%s'", s.c_str ());
14375 
14376  timeout_index = max_arg_index + 1;
14377  }
14378 
14379  if (timeout_index >= 0)
14380  {
14381  if (args.length () <= (timeout_index + 1))
14382  error ("waitfor: missing TIMEOUT value");
14383 
14384  timeout = args(timeout_index + 1).xscalar_value ("waitfor: TIMEOUT must be a scalar >= 1");
14385 
14386  if (timeout < 1)
14387  {
14388  warning ("waitfor: TIMEOUT value must be >= 1, using 1 instead");
14389  timeout = 1;
14390  }
14391  }
14392 
14393  // FIXME: There is still a "hole" in the following loop. The code
14394  // assumes that an object handle is unique, which is a fair
14395  // assumption, except for figures. If a figure is destroyed
14396  // then recreated with the same figure ID, within the same
14397  // run of event hooks, then the figure destruction won't be
14398  // caught and the loop will not stop. This is an unlikely
14399  // possibility in practice, though.
14400  //
14401  // Using deletefcn callback is also unreliable as it could be
14402  // modified during a callback execution and the waitfor loop
14403  // would not stop.
14404  //
14405  // The only "good" implementation would require object
14406  // listeners, similar to property listeners.
14407 
14408  octave::sys::time start;
14409 
14410  if (timeout > 0)
14411  start.stamp ();
14412 
14413  while (true)
14414  {
14415  if (true)
14416  {
14417  octave::autolock guard (gh_mgr.graphics_lock ());
14418 
14419  graphics_handle gh = gh_mgr.lookup (h);
14420 
14421  if (gh.ok ())
14422  {
14423  if (! pname.empty () && waitfor_results[id])
14424  break;
14425  }
14426  else
14427  break;
14428  }
14429 
14430  octave::sleep (0.1); // FIXME: really needed?
14431 
14432  octave_quit ();
14433 
14435 
14436  if (timeout > 0)
14437  {
14438  octave::sys::time now;
14439 
14440  if (start + timeout < now)
14441  break;
14442  }
14443  }
14444 
14445  return ovl ();
14446 }
14447 
14448 DEFMETHOD (__zoom__, interp, args, ,
14449  doc: /* -*- texinfo -*-
14450 @deftypefn {} {} __zoom__ (@var{axes}, @var{mode}, @var{factor})
14451 @deftypefnx {} {} __zoom__ (@var{axes}, "out")
14452 @deftypefnx {} {} __zoom__ (@var{axes}, "reset")
14453 Undocumented internal function.
14454 @end deftypefn */)
14455 {
14456  int nargin = args.length ();
14457 
14458  if (nargin != 2 && nargin != 3)
14459  print_usage ();
14460 
14461  double h = args(0).double_value ();
14462 
14463  gh_manager& gh_mgr = interp.get_gh_manager ();
14464 
14465  octave::autolock guard (gh_mgr.graphics_lock ());
14466 
14467  graphics_handle handle = gh_mgr.lookup (h);
14468 
14469  if (! handle.ok ())
14470  error ("__zoom__: invalid handle");
14471 
14472  graphics_object ax = gh_mgr.get_object (handle);
14473 
14474  axes::properties& ax_props
14475  = dynamic_cast<axes::properties&> (ax.get_properties ());
14476 
14477  if (nargin == 2)
14478  {
14479  std::string opt = args(1).string_value ();
14480 
14481  if (opt == "out" || opt == "reset")
14482  {
14483  if (opt == "out")
14484  {
14485  ax_props.clear_zoom_stack ();
14486  Vdrawnow_requested = true;
14487  }
14488  else
14489  ax_props.clear_zoom_stack (false);
14490  }
14491  }
14492  else
14493  {
14494  std::string mode = args(1).string_value ();
14495  double factor = args(2).scalar_value ();
14496 
14497  ax_props.zoom (mode, factor);
14498  Vdrawnow_requested = true;
14499  }
14500 
14501  return ovl ();
14502 }
14503 
14504 DEFMETHOD (__get_frame__, interp, args, ,
14505  doc: /* -*- texinfo -*-
14506 @deftypefn {} {@var{cdata} =} __get_frame__ (@var{hfig})
14507 Internal function.
14508 
14509 Return the pixel cdata of figure hfig in the form of a height-by-width-by-3
14510 uint8 array.
14511 @end deftypefn */)
14512 {
14513  if (args.length () != 1)
14514  print_usage ();
14515 
14516  double h = args(0).xdouble_value ("__get_frame__: HFIG is not a handle");
14517 
14518  gh_manager& gh_mgr = interp.get_gh_manager ();
14519 
14520  graphics_object go = gh_mgr.get_object (h);
14521 
14522  if (! go || ! go.isa ("figure"))
14523  error ("__get_frame__: HFIG is not a figure");
14524 
14525  // For Matlab compatibility, getframe must flush the event queue.
14526  gh_mgr.process_events ();
14527 
14528  return ovl (go.get_toolkit ().get_pixels (go));
14529 }
14530 
14531 DEFUN (__get_system_fonts__, args, ,
14532  doc: /* -*- texinfo -*-
14533 @deftypefn {} {@var{font_struct} =} __get_system_fonts__ ()
14534 Internal function.
14535 @end deftypefn */)
14536 {
14537  if (args.length () != 0)
14538  print_usage ();
14539 
14540  octave::text_renderer txt_renderer;
14541 
14542  return ovl (txt_renderer.get_system_fonts ());
14543 }
#define Inf
Definition: Faddeeva.cc:247
#define NaN
Definition: Faddeeva.cc:248
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
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:128
Array< T > as_column(void) const
Return the array as a column vector.
Definition: Array.h:381
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
void assign(const idx_vector &i, const Array< T > &rhs, const T &rfv)
Indexed assignment (always with resize & fill).
Definition: Array.cc:1116
octave_idx_type columns(void) const
Definition: Array.h:424
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:469
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:499
const T * data(void) const
Size of the specified dimension.
Definition: Array.h:581
octave_idx_type rows(void) const
Definition: Array.h:415
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:453
int ndims(void) const
Size of the specified dimension.
Definition: Array.h:589
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
bool isempty(void) const
Size of the specified dimension.
Definition: Array.h:572
Definition: Cell.h:43
double max(void) const
Definition: dColVector.cc:261
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:109
RowVector transpose(void) const
Definition: dColVector.cc:125
double min(void) const
Definition: dColVector.cc:245
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:93
Definition: dMatrix.h:42
ColumnVector row_max(void) const
Definition: dMatrix.cc:2468
RowVector row(octave_idx_type i) const
Definition: dMatrix.cc:416
Matrix extract_n(octave_idx_type r1, octave_idx_type c1, octave_idx_type nr, octave_idx_type nc) const
Definition: dMatrix.cc:407
ColumnVector row_min(void) const
Definition: dMatrix.cc:2413
Matrix transpose(void) const
Definition: dMatrix.h:135
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
bool any_element_is_inf_or_nan(void) const
Definition: dNDArray.cc:324
finite_type finite_constraint
Definition: graphics.in.h:1496
std::pair< double, bool > minval
Definition: graphics.in.h:1497
octave_value data
Definition: graphics.in.h:1489
std::pair< double, bool > maxval
Definition: graphics.in.h:1497
OCTINTERP_API void get_data_limits(void)
Definition: graphics.cc:1691
OCTINTERP_API bool is_equal(const octave_value &v) const
Definition: graphics.cc:1632
OCTINTERP_API bool validate(const octave_value &v)
Definition: graphics.cc:1525
octave_value get(void) const
Definition: graphics.in.h:1404
std::list< dim_vector > size_constraints
Definition: graphics.in.h:1495
std::set< std::string > type_constraints
Definition: graphics.in.h:1494
void update_camera(void)
Definition: graphics.cc:5948
void rotate_view(double delta_az, double delta_el, bool push_to_zoom_stack=true)
Definition: graphics.cc:9157
void zoom(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8940
void rotate3d(double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:9110
void update_aspectratios(void)
Definition: graphics.cc:6858
void update_title_position(void)
Definition: graphics.cc:6754
graphics_xform get_transform(void) const
Definition: graphics.in.h:3480
void pan(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:9093
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:5710
unsigned int get_num_lights(void) const
Definition: graphics.in.h:3569
void update_font(std::string prop="")
Definition: graphics.cc:6980
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:7018
void update_fontunits(const caseless_str &old_fontunits)
Definition: graphics.cc:7342
Matrix get_extent(bool with_text=false, bool only_text_height=false) const
Definition: graphics.cc:7049
void sync_positions(void)
Definition: graphics.cc:5267
void set_defaults(base_graphics_object &obj, const std::string &mode)
Definition: graphics.cc:5450
void update_zlabel_position(void)
Definition: graphics.cc:6626
void push_zoom_stack(void)
Definition: graphics.cc:8954
void set_text_child(handle_property &h, const std::string &who, const octave_value &v)
Definition: graphics.cc:5357
void update_autopos(const std::string &elem_type)
Definition: graphics.cc:6804
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:7354
void update_xlabel_position(void)
Definition: graphics.cc:6416
void adopt(const graphics_handle &h)
Definition: graphics.cc:5747
void update_units(const caseless_str &old_units)
Definition: graphics.cc:7300
void update_axes_layout(void)
Definition: graphics.cc:6164
void update_handlevisibility(void)
Definition: graphics.cc:9216
void zoom_about_point(const std::string &mode, double x, double y, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8911
void clear_zoom_stack(bool do_unzoom=true)
Definition: graphics.cc:9255
void delete_text_child(handle_property &h, bool from_root=false)
Definition: graphics.cc:5679
void unzoom(void)
Definition: graphics.cc:9180
void increase_num_lights(void)
Definition: graphics.in.h:3567
void update_ylabel_position(void)
Definition: graphics.cc:6521
void translate_view(const std::string &mode, double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:9064
void decrease_num_lights(void)
Definition: graphics.in.h:3568
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:7407
void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:8651
void reset_default_properties(void)
Definition: graphics.cc:9296
void initialize(const graphics_object &go)
Definition: graphics.cc:9312
virtual std::string value_as_string(const std::string &prop)
Definition: graphics.cc:3820
virtual graphics_handle get_parent(void) const
Definition: graphics.in.h:2555
virtual void adopt(const graphics_handle &h)
Definition: graphics.in.h:2579
virtual void defaults(void) const
Definition: graphics.in.h:2595
virtual std::string type(void) const
Definition: graphics.in.h:2627
virtual std::string values_as_string(void)
Definition: graphics.cc:3784
virtual octave_value get_default(const caseless_str &) const
Definition: graphics.cc:11697
virtual void reset_default_properties(void)
Definition: graphics.cc:3768
virtual octave_value get_factory_default(const caseless_str &) const
Definition: graphics.cc:11710
virtual octave_scalar_map values_as_struct(void)
Definition: graphics.cc:3850
virtual void remove_all_listeners(void)
Definition: graphics.cc:3708
graphics_handle get_handle(void) const
Definition: graphics.in.h:2563
virtual void initialize(const graphics_object &go)
Definition: graphics.in.h:2667
virtual void reparent(const graphics_handle &np)
Definition: graphics.in.h:2587
friend class graphics_object
Definition: graphics.in.h:2441
virtual base_properties & get_properties(void)
Definition: graphics.in.h:2604
virtual void set(const caseless_str &pname, const octave_value &pval)
Definition: graphics.in.h:2479
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2633
octave_value get(bool all=false) const
Definition: graphics.in.h:2495
virtual void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:3677
void build_user_defaults_map(property_list::pval_map_type &def, const std::string go_name) const
Definition: graphics.cc:3742
virtual void set_defaults(const std::string &)
Definition: graphics.in.h:2487
virtual void mark_modified(void)
Definition: graphics.in.h:2453
void set_from_list(base_graphics_object &obj, property_list &defaults)
Definition: graphics.cc:3269
void mark_modified(void)
Definition: graphics.cc:3432
Matrix get_children(void) const
Definition: graphics.in.h:2292
void override_defaults(base_graphics_object &obj)
Definition: graphics.cc:3449
bool is_modified(void) const
Definition: graphics.in.h:2247
virtual octave_value get_ylim(void) const
Definition: graphics.in.h:2352
virtual void update_uicontextmenu(void) const
Definition: graphics.cc:3486
virtual void update_autopos(const std::string &elem_type)
Definition: graphics.cc:3543
virtual octave::graphics_toolkit get_toolkit(void) const
Definition: graphics.cc:3512
bool is_handle_visible(void) const
Definition: graphics.cc:3505
bool has_dynamic_property(const std::string &pname) const
Definition: graphics.cc:3344
std::set< std::string > dynamic_property_names(void) const
Definition: graphics.cc:3338
virtual octave_value get_xlim(void) const
Definition: graphics.in.h:2351
virtual void update_boundingbox(void)
Definition: graphics.cc:3526
void get_children_of_type(const caseless_str &type, bool get_invisible, bool traverse, std::list< graphics_object > &children_list) const
Definition: graphics.cc:3643
virtual void update_axis_limits(const std::string &axis_type) const
Definition: graphics.cc:3461
octave_value get_dynamic(const caseless_str &pname) const
Definition: graphics.cc:3313
Matrix get_all_children(void) const
Definition: graphics.in.h:2297
static property_list::pval_map_type factory_defaults(void)
virtual void add_listener(const caseless_str &, const octave_value &, listener_mode=GCB_POSTSET)
Definition: graphics.cc:3621
virtual void set(const caseless_str &, const octave_value &)
void insert_property(const std::string &name, property p)
Definition: graphics.in.h:2213
property get_property_dynamic(const caseless_str &pname) const
Definition: graphics.cc:3371
virtual bool has_property(const caseless_str &) const
Definition: graphics.in.h:2241
void set_modified(const octave_value &val)
Definition: graphics.in.h:2311
virtual octave_value get_alim(void) const
Definition: graphics.in.h:2349
void set_parent(const octave_value &val)
Definition: graphics.cc:3383
virtual void adopt(const graphics_handle &h)
Definition: graphics.in.h:2258
virtual octave_value get_zlim(void) const
Definition: graphics.in.h:2353
virtual octave_value get_clim(void) const
Definition: graphics.in.h:2350
void set_beingdeleted(const octave_value &val)
Definition: graphics.in.h:2282
virtual void init_integerhandle(const octave_value &)
Definition: graphics.in.h:2203
void set_dynamic(const caseless_str &pname, const octave_value &val)
Definition: graphics.cc:3355
virtual void delete_children(bool clear=false, bool from_root=false)
Definition: graphics.in.h:2327
virtual void remove_child(const graphics_handle &h, bool=false)
Definition: graphics.in.h:2249
virtual void delete_listener(const caseless_str &, const octave_value &, listener_mode=GCB_POSTSET)
Definition: graphics.cc:3632
virtual property get_property(const caseless_str &pname)
virtual std::string graphics_object_name(void) const
Definition: graphics.in.h:2197
void renumber_child(graphics_handle old_gh, graphics_handle new_gh)
Definition: graphics.in.h:2332
virtual Matrix get_boundingbox(bool=false, const Matrix &=Matrix()) const
Definition: graphics.in.h:2268
void renumber_parent(graphics_handle new_gh)
Definition: graphics.in.h:2337
graphics_handle parent
Definition: graphics.in.h:426
std::string get_name(void) const
Definition: graphics.in.h:303
virtual bool do_set(const octave_value &)
Definition: graphics.in.h:410
OCTINTERP_API void run_listeners(listener_mode mode=GCB_POSTSET)
Definition: graphics.cc:1283
listener_map listeners
Definition: graphics.in.h:428
graphics_handle get_parent(void) const
Definition: graphics.in.h:307
OCTINTERP_API bool set(const octave_value &v, bool do_run=true, bool do_notify_toolkit=true)
Definition: graphics.cc:1257
std::string callback_name
Definition: graphics.cc:11866
callback_event(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix(), int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:11836
void execute(void)
Definition: graphics.cc:11848
octave_value callback
Definition: graphics.cc:11867
octave_value callback_data
Definition: graphics.cc:11868
callback_event(void)
Definition: graphics.cc:11860
callback_event(const graphics_handle &h, const octave_value &cb, const octave_value &data=Matrix(), int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:11842
graphics_handle handle
Definition: graphics.cc:11865
OCTINTERP_API void execute(const octave_value &data=octave_value()) const
Definition: graphics.cc:1941
OCTINTERP_API bool validate(const octave_value &v) const
Definition: graphics.cc:1878
octave_value callback
Definition: graphics.in.h:1945
~callback_props(void)=default
callback_props(const callback_props &)=delete
bool empty(void) const
Definition: graphics.cc:1912
callback_props(void)
Definition: graphics.cc:1904
void erase(const callback_property *ptr)
Definition: graphics.cc:1919
callback_props & operator=(const callback_props &)=delete
std::set< intptr_t > m_set
Definition: graphics.cc:1931
bool contains(const callback_property *ptr) const
Definition: graphics.cc:1924
void insert(const callback_property *ptr)
Definition: graphics.cc:1914
bool compare(const std::string &s, size_t limit=std::string::npos) const
Definition: caseless-str.h:78
void do_delete_children(bool clear, bool from_root)
Definition: graphics.cc:1845
Matrix do_get_children(bool return_hidden) const
Definition: graphics.cc:1805
std::list< double > children_list
Definition: graphics.in.h:1793
radio_values radio_val
Definition: graphics.in.h:1162
OCTINTERP_API bool do_set(const octave_value &newval)
Definition: graphics.cc:1412
std::string current_val
Definition: graphics.in.h:1163
color_values color_val
Definition: graphics.in.h:1161
enum color_property::current_enum current_type
octave_value get(void) const
Definition: graphics.in.h:1110
OCTINTERP_API bool str2rgb(const std::string &str)
Definition: graphics.cc:1370
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
void resize(int n, int fill_value=0)
Definition: dim-vector.h:349
static dim_vector alloc(int n)
Definition: dim-vector.h:281
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:334
enum double_radio_property::current_enum current_type
OCTINTERP_API bool do_set(const octave_value &v)
Definition: graphics.cc:1483
std::string current_val
Definition: graphics.in.h:1364
radio_values radio_val
Definition: graphics.in.h:1363
void update_paperunits(const caseless_str &old_paperunits)
Definition: graphics.cc:4681
void set___graphics_toolkit__(const octave_value &val)
Definition: graphics.cc:4185
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:4254
array_property Matrix() callback_property closerequestfcn
void set_visible(const octave_value &val)
Definition: graphics.cc:4243
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:4139
Matrix bbox2position(const Matrix &bbox) const
Definition: graphics.cc:4271
Matrix map_to_boundingbox(double x, double y) const
Definition: graphics.cc:4314
std::string get_title(void) const
Definition: graphics.cc:5004
octave::graphics_toolkit get_toolkit(void) const
Definition: graphics.cc:4179
Matrix map_from_boundingbox(double x, double y) const
Definition: graphics.cc:4297
void adopt(const graphics_handle &h)
Definition: graphics.cc:4208
void set_boundingbox(const Matrix &bb, bool internal=false, bool do_notify_toolkit=true)
Definition: graphics.cc:4284
void set_toolkit(const octave::graphics_toolkit &b)
Definition: graphics.cc:2129
void set_position(const octave_value &val, bool do_notify_toolkit=true)
Definition: graphics.cc:4331
void set_outerposition(const octave_value &val, bool do_notify_toolkit=true)
Definition: graphics.cc:4369
void update_units(const caseless_str &old_units)
Definition: graphics.cc:4979
property_list get_defaults_list(void) const
Definition: graphics.in.h:3315
base_properties & get_properties(void)
Definition: graphics.in.h:3320
octave_value get(const caseless_str &name) const
Definition: graphics.in.h:3296
void reset_default_properties(void)
Definition: graphics.cc:5049
properties xproperties
Definition: graphics.in.h:3262
property_list default_properties
Definition: graphics.in.h:3337
void override_defaults(base_graphics_object &obj)
Definition: graphics.in.h:3271
void set(const caseless_str &name, const octave_value &value)
Definition: graphics.in.h:3285
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:5030
bool valid_object(void) const
Definition: graphics.in.h:3324
void execute(void)
Definition: graphics.cc:11926
function_event(graphics_event::event_fcn fcn, void *data=nullptr)
Definition: graphics.cc:11916
function_event(void)=delete
void * function_data
Definition: graphics.cc:11935
function_event(const function_event &)=delete
void unlock(void)
Definition: graphics.in.h:6320
void post_set(const graphics_handle &h, const std::string &name, const octave_value &value, bool notify_toolkit=true, bool redraw_figure=false)
Definition: graphics.cc:12257
int m_event_processing
Definition: graphics.in.h:6435
Matrix handle_list(bool show_hidden=false)
Definition: graphics.in.h:6298
graphics_object get_object(double val) const
Definition: graphics.in.h:6260
octave::interpreter & m_interpreter
Definition: graphics.in.h:6410
void renumber_figure(const graphics_handle &old_gh, const graphics_handle &new_gh)
Definition: graphics.cc:2906
void close_all_figures(void)
Definition: graphics.cc:3089
void execute_listener(const graphics_handle &h, const octave_value &l)
Definition: graphics.cc:12082
gh_manager(octave::interpreter &interp)
Definition: graphics.cc:11722
void push_figure(const graphics_handle &h)
Definition: graphics.cc:11812
bool is_handle_visible(const graphics_handle &h) const
Definition: graphics.in.h:6375
std::list< graphics_handle > m_figure_list
Definition: graphics.in.h:6423
std::list< graphics_object > m_callback_objects
Definition: graphics.in.h:6432
void post_event(const graphics_event &e)
Definition: graphics.cc:12206
Matrix figure_handle_list(bool show_hidden=false)
Definition: graphics.in.h:6322
graphics_handle lookup(double val) const
Definition: graphics.in.h:6246
std::map< graphics_handle, graphics_object > m_handle_map
Definition: graphics.in.h:6413
void enable_event_processing(bool enable=true)
Definition: graphics.cc:12406
std::list< graphics_event > m_event_queue
Definition: graphics.in.h:6429
void post_function(graphics_event::event_fcn fcn, void *fcn_data=nullptr)
Definition: graphics.cc:12249
void free(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:2843
octave::mutex m_graphics_lock
Definition: graphics.in.h:6426
void pop_figure(const graphics_handle &h)
Definition: graphics.cc:11820
void post_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.cc:12214
graphics_handle make_graphics_handle(const std::string &go_name, const graphics_handle &p, bool integer_figure_handle=false, bool call_createfcn=true, bool notify_toolkit=true)
Definition: graphics.cc:11737
void execute_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.in.h:6340
int process_events(bool force=false)
Definition: graphics.cc:12268
graphics_handle current_figure(void) const
Definition: graphics.in.h:6285
octave::mutex graphics_lock(void)
Definition: graphics.in.h:6393
graphics_handle make_figure_handle(double val, bool notify_toolkit=true)
Definition: graphics.cc:11793
void restore_gcbo(void)
Definition: graphics.cc:12071
graphics_handle get_handle(bool integer_figure_handle)
Definition: graphics.cc:2792
void lock(void)
Definition: graphics.in.h:6316
graphics_event(void)=default
bool ok(void) const
Definition: graphics.in.h:6200
static graphics_event create_mcode_event(const graphics_handle &h, const std::string &cmd, int busyaction)
Definition: graphics.cc:12033
void execute(void)
Definition: graphics.in.h:6194
void(* event_fcn)(void *)
Definition: graphics.in.h:6174
static graphics_event create_function_event(event_fcn fcn, void *data=nullptr)
Definition: graphics.cc:12041
static graphics_event create_set_event(const graphics_handle &h, const std::string &name, const octave_value &value, bool notify_toolkit=true, bool redraw_figure=false)
Definition: graphics.cc:12048
static graphics_event create_callback_event(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix(), int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:12015
void update(int id)
Definition: graphics.in.h:2907
octave_value get_zlim(void) const
Definition: graphics.in.h:2870
std::string value_as_string(const std::string &prop)
Definition: graphics.in.h:2804
octave_map values_as_struct(void)
Definition: graphics.in.h:2811
octave_value get_ylim(void) const
Definition: graphics.in.h:2867
void override_defaults(base_graphics_object &obj)
Definition: graphics.in.h:2711
bool has_readonly_property(const caseless_str &pname) const
Definition: graphics.in.h:2793
octave_value get(bool all=false) const
Definition: graphics.in.h:2746
bool is_aliminclude(void) const
Definition: graphics.in.h:2873
void set(const caseless_str &name, const octave_value &val)
Definition: graphics.in.h:2729
graphics_object get_ancestor(const std::string &type) const
Definition: graphics.cc:3905
octave_value get_alim(void) const
Definition: graphics.in.h:2858
bool is_yliminclude(void) const
Definition: graphics.in.h:2882
void reset_default_properties(void)
Definition: graphics.in.h:2909
void update_axis_limits(const std::string &axis_type)
Definition: graphics.in.h:2836
void delete_property_listener(const std::string &nm, const octave_value &v, listener_mode mode=GCB_POSTSET)
Definition: graphics.in.h:2897
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2827
base_properties & get_properties(void)
Definition: graphics.in.h:2829
void finalize(void)
Definition: graphics.in.h:2905
std::string type(void) const
Definition: graphics.in.h:2849
void set_value_or_default(const caseless_str &name, const octave_value &val)
Definition: graphics.cc:2729
octave_value get_factory_default(const caseless_str &name) const
Definition: graphics.in.h:2771
void reparent(const graphics_handle &h)
Definition: graphics.in.h:2823
graphics_handle get_handle(void) const
Definition: graphics.in.h:2815
bool is_xliminclude(void) const
Definition: graphics.in.h:2879
octave_value get_default(const caseless_str &name) const
Definition: graphics.in.h:2766
octave::graphics_toolkit get_toolkit(void) const
Definition: graphics.in.h:2891
void initialize(void)
Definition: graphics.in.h:2903
void build_user_defaults_map(property_list::pval_map_type &def, const std::string go_name) const
Definition: graphics.in.h:2721
bool is_climinclude(void) const
Definition: graphics.in.h:2876
octave_value get_xlim(void) const
Definition: graphics.in.h:2864
bool is_zliminclude(void) const
Definition: graphics.in.h:2885
graphics_handle get_parent(void) const
Definition: graphics.in.h:2813
property_list get_factory_defaults_list(void) const
Definition: graphics.in.h:2788
void remove_child(const graphics_handle &h)
Definition: graphics.in.h:2819
std::string values_as_string(void)
Definition: graphics.in.h:2800
bool valid_object(void) const
Definition: graphics.in.h:2847
void add_property_listener(const std::string &nm, const octave_value &v, listener_mode mode=GCB_POSTSET)
Definition: graphics.in.h:2893
void adopt(const graphics_handle &h)
Definition: graphics.in.h:2821
void mark_modified(void)
Definition: graphics.in.h:2709
octave_value get_clim(void) const
Definition: graphics.in.h:2861
ColumnVector untransform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:7391
static ColumnVector xform_vector(double x, double y, double z)
Definition: graphics.cc:7366
static Matrix xform_eye(void)
Definition: graphics.cc:7372
ColumnVector transform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:7378
std::set< std::string > type_constraints
Definition: graphics.in.h:1670
graphics_handle current_val
Definition: graphics.in.h:1673
octave_value get(void) const
Definition: graphics.in.h:1644
OCTINTERP_API bool do_set(const octave_value &v)
Definition: graphics.cc:1731
graphics_handle handle_value(void) const
Definition: graphics.in.h:1646
void adopt(const graphics_handle &h)
Definition: graphics.cc:10526
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:10508
void update_limits(void) const
Definition: graphics.cc:10544
void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:10697
octave_value get_color_data(void) const
Definition: graphics.cc:9567
void initialize(const graphics_object &go)
Definition: graphics.cc:9575
void execute(void)
Definition: graphics.cc:11880
std::string mcode
Definition: graphics.cc:11904
mcode_event(void)
Definition: graphics.cc:11898
graphics_handle handle
Definition: graphics.cc:11903
mcode_event(const graphics_handle &h, const std::string &cmd, int busyaction=base_graphics_event::QUEUE)
Definition: graphics.cc:11875
void add_method(T *obj, void(T::*method)(Params...), Args &&... args)
void add_fcn(void(*fcn)(Params...), Args &&... args)
static void remove_event_hook(event_hook_fcn f)
Definition: cmd-edit.cc:1560
static void run_event_hooks(void)
Definition: cmd-edit.cc:1572
static void add_event_hook(event_hook_fcn f)
Definition: cmd-edit.cc:1552
double x_dpi(void) const
Definition: display.h:58
int depth(void) const
Definition: display.h:66
int height(void) const
Definition: display.h:62
int width(void) const
Definition: display.h:64
double y_dpi(void) const
Definition: display.h:60
Matrix get_text_extent(const graphics_object &go) const
void redraw_figure(const graphics_object &go) const
std::string get_name(void) const
uint8NDArray get_pixels(const graphics_object &go) const
void print_figure(const graphics_object &go, const std::string &term, const std::string &file, const std::string &debug_file="") const
void show_figure(const graphics_object &go) const
graphics_toolkit find_toolkit(const std::string &name) const
Definition: gtk-manager.h:67
void register_toolkit(const std::string &name)
Definition: gtk-manager.cc:74
Cell loaded_toolkits_list(void) const
Definition: gtk-manager.h:88
Cell available_toolkits_list(void) const
Definition: gtk-manager.h:77
graphics_toolkit get_toolkit(void) const
Definition: gtk-manager.cc:39
std::string default_toolkit(void) const
Definition: gtk-manager.h:115
octave_value_list eval_string(const std::string &eval_str, bool silent, int &parse_status, int nargout)
void recover_from_exception(void)
void handle_exception(const execution_exception &e)
bool is_dir(void) const
Definition: file-stat.cc:65
void stamp(void)
Definition: oct-time.cc:109
octave_map get_system_fonts(void)
static bool is_thread(void)
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:690
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:190
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:730
octave_idx_type length(void) const
int32NDArray int32_array_value(void) const
Definition: ov.h:909
bool iscellstr(void) const
Definition: ov.h:563
uint16NDArray uint16_array_value(void) const
Definition: ov.h:918
bool iscell(void) const
Definition: ov.h:560
bool isreal(void) const
Definition: ov.h:691
bool is_uint16_type(void) const
Definition: ov.h:674
bool is_bool_scalar(void) const
Definition: ov.h:578
bool is_int8_type(void) const
Definition: ov.h:659
octave_idx_type rows(void) const
Definition: ov.h:504
bool isnumeric(void) const
Definition: ov.h:703
octave_idx_type numel(void) const
Definition: ov.h:518
bool is_scalar_type(void) const
Definition: ov.h:697
bool is_string(void) const
Definition: ov.h:593
double xdouble_value(const char *fmt,...) const
bool is_defined(void) const
Definition: ov.h:551
bool isinteger(void) const
Definition: ov.h:683
bool is_double_type(void) const
Definition: ov.h:648
Cell cell_value(void) const
bool is_function_handle(void) const
Definition: ov.h:721
std::string class_name(void) const
Definition: ov.h:1256
bool is_uint32_type(void) const
Definition: ov.h:677
octave_idx_type columns(void) const
Definition: ov.h:506
int8NDArray int8_array_value(void) const
Definition: ov.h:903
bool is_int64_type(void) const
Definition: ov.h:668
int64NDArray int64_array_value(void) const
Definition: ov.h:912
octave_function * function_value(bool silent=false) const
uint8NDArray uint8_array_value(void) const
Definition: ov.h:915
octave_value reshape(const dim_vector &dv) const
Definition: ov.h:530
std::string string_value(bool force=false) const
Definition: ov.h:927
bool is_matrix_type(void) const
Definition: ov.h:700
octave_scalar_map scalar_map_value(void) const
bool is_int32_type(void) const
Definition: ov.h:665
bool is_uint64_type(void) const
Definition: ov.h:680
bool is_int16_type(void) const
Definition: ov.h:662
uint64NDArray uint64_array_value(void) const
Definition: ov.h:924
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:930
bool isempty(void) const
Definition: ov.h:557
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:812
bool is_single_type(void) const
Definition: ov.h:651
uint32NDArray uint32_array_value(void) const
Definition: ov.h:921
octave_map map_value(void) const
bool is_real_scalar(void) const
Definition: ov.h:566
bool is_undefined(void) const
Definition: ov.h:554
bool is_uint8_type(void) const
Definition: ov.h:671
int16NDArray int16_array_value(void) const
Definition: ov.h:906
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
bool iscomplex(void) const
Definition: ov.h:694
double double_value(bool frc_str_conv=false) const
Definition: ov.h:794
std::string type_name(void) const
Definition: ov.h:1254
bool islogical(void) const
Definition: ov.h:688
dim_vector dims(void) const
Definition: ov.h:500
octave_value get_color_data(void) const
Definition: graphics.cc:9618
bool get_do_lighting(void) const
Definition: graphics.cc:9604
void reset_default_properties(void)
Definition: graphics.cc:10194
void initialize(const graphics_object &go)
Definition: graphics.cc:10182
octave_scalar_map as_struct(const std::string &prefix_arg) const
Definition: graphics.cc:2505
plist_map_iterator find(const std::string &go_name)
Definition: graphics.in.h:2167
plist_map_iterator end(void)
Definition: graphics.in.h:2164
void set(const caseless_str &name, const octave_value &val)
Definition: graphics.cc:2271
plist_map_type::const_iterator plist_map_const_iterator
Definition: graphics.in.h:2150
octave_value lookup(const caseless_str &name) const
Definition: graphics.cc:2411
pval_map_type::const_iterator pval_map_const_iterator
Definition: graphics.in.h:2147
std::map< std::string, pval_map_type > plist_map_type
Definition: graphics.in.h:2144
void delete_listener(const octave_value &v=octave_value(), listener_mode mode=GCB_POSTSET)
Definition: graphics.in.h:2033
property(void)
Definition: graphics.in.h:1953
static OCTINTERP_API property create(const std::string &name, const graphics_handle &parent, const caseless_str &type, const octave_value_list &args)
Definition: graphics.cc:1969
void erase(const std::string pname)
Definition: graphics.in.h:2126
const_iterator find(const std::string pname) const
Definition: graphics.in.h:2079
octave_idx_type nelem(void) const
Definition: graphics.in.h:915
bool contains(const std::string &val, std::string &match)
Definition: graphics.in.h:872
Cell values_as_cell(void) const
Definition: graphics.cc:1358
OCTINTERP_API radio_values(const std::string &opt_string="")
Definition: graphics.cc:1294
std::string values_as_string(void) const
Definition: graphics.cc:1329
std::set< caseless_str > possible_vals
Definition: graphics.in.h:920
std::string default_value(void) const
Definition: graphics.in.h:860
std::string default_val
Definition: graphics.in.h:919
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:4064
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:4100
static property_list::plist_map_type init_factory_properties(void)
Definition: graphics.cc:12426
void reset_default_properties(void)
Definition: graphics.cc:4115
graphics_handle handle
Definition: graphics.cc:12007
octave_value property_value
Definition: graphics.cc:12009
void execute(void)
Definition: graphics.cc:11950
bool m_redraw_figure
Definition: graphics.cc:12011
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:11942
bool notify_toolkit
Definition: graphics.cc:12010
set_event(void)
Definition: graphics.cc:12002
std::string property_name
Definition: graphics.cc:12008
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
octave_value get_color_data(void) const
Definition: graphics.cc:10209
bool get_do_lighting(void) const
Definition: graphics.cc:10215
void update_font(void)
Definition: graphics.cc:9431
void update_text_extent(void)
Definition: graphics.cc:9454
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:9544
void update_units(void)
Definition: graphics.cc:9509
void set_position(const octave_value &val)
Definition: graphics.in.h:4353
void update_fontunits(const caseless_str &old_fontunits)
Definition: graphics.cc:9407
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:11158
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11000
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:11213
void adopt(const graphics_handle &h)
Definition: graphics.cc:11224
void add_dependent_obj(graphics_handle gh)
Definition: graphics.in.h:5440
void update_beingdeleted(void)
Definition: graphics.cc:10786
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:10974
void update_units(void)
Definition: graphics.cc:10866
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:10933
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:10986
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11248
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:11407
double get_fontsize_pixels(double box_pix_height=0) const
Definition: graphics.cc:11605
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:11593
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11421
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:11665
void reset_default_properties(void)
Definition: graphics.cc:11685
static octave_idx_type find(octave_idx_type i, octave_idx_type *pp)
Definition: colamd.cc:104
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
T eps(const T &x)
Definition: data.cc:4578
#define DECLARE_STATIC_FUNX(name, args_name, nargout_name)
Definition: defun-int.h:115
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:138
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning(const char *fmt,...)
Definition: error.cc:1050
void interpreter_try(octave::unwind_protect &frame)
Definition: error.cc:2182
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1065
void error(const char *fmt,...)
Definition: error.cc:968
octave_value_list set_warning_state(const std::string &id, const std::string &state)
Definition: error.cc:1780
void disable_warning(const std::string &id)
Definition: error.cc:1803
int warning_enabled(const std::string &id)
Definition: error.cc:1036
octave_handle graphics_handle
octave::graphics_toolkit graphics_toolkit
static double default_screendepth(void)
Definition: graphics.cc:223
static Matrix default_patch_vertices(void)
Definition: graphics.cc:404
static void xset(const graphics_handle &h, const caseless_str &pname, const octave_value &val)
Definition: graphics.cc:2935
static Matrix default_surface_ydata(void)
Definition: graphics.cc:363
static Matrix default_light_position(void)
Definition: graphics.cc:583
static bool compare_property_values(const octave_value &ov1, const octave_value &ov2)
Definition: graphics.cc:14090
static Matrix default_patch_ydata(void)
Definition: graphics.cc:426
static double make_handle_fraction(void)
Definition: graphics.cc:2784
void cross_product(double x1, double y1, double z1, double x2, double y2, double z2, double &x, double &y, double &z)
Definition: graphics.cc:9956
Matrix unit_cube(void)
Definition: graphics.cc:5911
double dot(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5887
static bool updating_zlabel_position
Definition: graphics.cc:6623
static Matrix default_image_cdata(void)
Definition: graphics.cc:335
void translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5852
static std::set< double > updating_aspectratios
Definition: graphics.cc:6855
OCTAVE_EXPORT octave_value_list Fdrawnow(octave::interpreter &interp, const octave_value_list &args, int)
Definition: graphics.cc:13698
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:85
static Matrix default_data(void)
Definition: graphics.cc:311
#define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL)
static bool updating_hggroup_limits
Definition: graphics.cc:10579
ColumnVector cross(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5899
static void delete_graphics_objects(const NDArray vals, bool from_root=false)
Definition: graphics.cc:3055
static void finalize_r(const graphics_handle &h)
Definition: graphics.cc:2093
static Matrix default_figure_paperposition(void)
Definition: graphics.cc:523
static Matrix default_figure_papersize(void)
Definition: graphics.cc:512
graphics_handle gca(void)
Definition: graphics.cc:3012
static Matrix default_table_backgroundcolor(void)
Definition: graphics.cc:608
static octave_value convert_ticklabel_string(const octave_value &val)
Definition: graphics.cc:7145
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:8352
#define FIX_LIMITS
static Matrix screen_size_pixels(void)
Definition: graphics.cc:929
double norm(const ColumnVector &v)
Definition: graphics.cc:5893
static void xreset_default_properties(graphics_handle h, property_list::pval_map_type factory_pval)
Definition: graphics.cc:3222
static bool updating_patch_data
Definition: graphics.cc:9627
void xform(ColumnVector &v, const Matrix &m)
Definition: graphics.cc:5858
static Matrix default_axes_position(void)
Definition: graphics.cc:436
static Matrix default_screensize(void)
Definition: graphics.cc:232
void normalize(ColumnVector &v)
Definition: graphics.cc:5880
static Matrix default_axes_view(void)
Definition: graphics.cc:462
static Matrix default_surface_xdata(void)
Definition: graphics.cc:351
static void close_figure(const graphics_handle &h)
Definition: graphics.cc:3067
Matrix xform_scale(double x, double y, double z)
Definition: graphics.cc:5820
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:621
static void normalized_aspectratios(Matrix &aspectratios, const Matrix &scalefactors, double xlength, double ylength, double zlength)
Definition: graphics.cc:6819
static std::map< caseless_str, graphics_object > dprop_obj_map
Definition: graphics.cc:1966
static Matrix default_control_position(void)
Definition: graphics.cc:546
static Matrix do_translate(double x0, double x1, const Matrix &lims, bool is_logscale)
Definition: graphics.cc:9004
static Matrix default_axes_tick(void)
Definition: graphics.cc:473
std::vector< octave_idx_type > coplanar_partition(const Matrix &vert, const Matrix &idx, octave_idx_type nc, octave_idx_type jj)
Definition: graphics.cc:9725
static void update_text_pos(graphics_handle h)
Definition: graphics.cc:2232
static callback_props executing_callbacks
Definition: graphics.cc:1938
static double device_pixel_ratio(graphics_handle h)
Definition: graphics.cc:942
static Matrix default_control_sliderstep(void)
Definition: graphics.cc:559
ColumnVector cam2xform(const Array< double > &m)
Definition: graphics.cc:5932
static void delete_graphics_object(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:3021
static OCTAVE_NORETURN void err_set_invalid(const std::string &pname)
Definition: graphics.cc:75
int calc_dimensions(const graphics_object &go)
Definition: graphics.cc:13225
static bool updating_ylabel_position
Definition: graphics.cc:6518
static bool ishghandle(const graphics_handle &h)
Definition: graphics.cc:3143
RowVector xform2cam(const ColumnVector &v)
Definition: graphics.cc:5942
static bool delete_executing
Definition: graphics.cc:3052
static void magform(double x, double &a, int &b)
Definition: graphics.cc:7489
static Matrix papersize_from_type(const caseless_str punits, const caseless_str ptype)
Definition: graphics.cc:4410
static std::map< uint32_t, bool > waitfor_results
Definition: graphics.cc:14105
static bool updating_axes_layout
Definition: graphics.cc:6161
static Matrix default_patch_xdata(void)
Definition: graphics.cc:416
static void check_limit_vals(double &min_val, double &max_val, double &min_pos, double &max_neg, const octave_value &data)
Definition: graphics.cc:7450
static bool lookup_object_name(const caseless_str &name, caseless_str &go_name, caseless_str &rest)
Definition: graphics.cc:1130
static Matrix default_lim(bool logscale=false)
Definition: graphics.cc:292
static void force_close_figure(const graphics_handle &h)
Definition: graphics.cc:3077
static Matrix default_table_position(void)
Definition: graphics.cc:595
static int process_graphics_events(void)
Definition: graphics.cc:12198
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:634
static Matrix viridis_colormap(void)
Definition: graphics.cc:139
static void get_array_limits(const Array< T > &m, double &emin, double &emax, double &eminp, double &emaxp)
Definition: graphics.cc:1101
#define GO_BODY(TYPE)
Definition: graphics.cc:13214
static void cleanup_waitfor_predelete_listener(const octave_value &listener)
Definition: graphics.cc:14153
static octave_value_list waitfor_listener(const octave_value_list &args, int)
Definition: graphics.cc:14156
static std::set< double > updating_axis_limits
Definition: graphics.cc:8437
bool set_property_in_handle(double handle, const std::string &property, const octave_value &arg, const std::string &func)
Definition: graphics.cc:14072
static void xset_gcbo(const graphics_handle &h)
Definition: graphics.cc:12058
static ColumnVector convert_label_position(const ColumnVector &p, const text::properties &props, const graphics_xform &xform, const Matrix &bbox)
Definition: graphics.cc:6385
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5846
static octave_value convert_linestyleorder_string(const octave_value &val)
Definition: graphics.cc:7243
Matrix xform_translate(double x, double y, double z)
Definition: graphics.cc:5833
static std::string default_graphics_toolkit(void)
Definition: graphics.cc:537
static void initialize_r(const graphics_handle &h)
Definition: graphics.cc:2111
static void cleanup_waitfor_id(uint32_t id)
Definition: graphics.cc:14108
static std::string get_graphics_object_type(double val)
Definition: graphics.cc:12853
octave_value get_property_from_handle(double handle, const std::string &property, const std::string &func)
Definition: graphics.cc:14056
Matrix xform_matrix(void)
Definition: graphics.cc:5781
static double default_screenpixelsperinch(void)
Definition: graphics.cc:248
static bool updating_xlabel_position
Definition: graphics.cc:6413
ColumnVector xform_vector(void)
Definition: graphics.cc:5792
static Matrix default_surface_cdata(void)
Definition: graphics.cc:386
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5814
graphics_handle gcf(void)
Definition: graphics.cc:3002
static void do_cleanup_waitfor_listener(const octave_value &listener, listener_mode mode=GCB_POSTSET)
Definition: graphics.cc:14114
static void xinitialize(const graphics_handle &h)
Definition: graphics.cc:3191
static octave_value xget(const graphics_handle &h, const caseless_str &name)
Definition: graphics.cc:2959
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:957
static Matrix default_data_lim(void)
Definition: graphics.cc:322
#define CHECK_ARRAY_EQUAL(T, F, A)
static Matrix default_panel_position(void)
Definition: graphics.cc:570
static octave_value_list waitfor_del_listener(const octave_value_list &args, int)
Definition: graphics.cc:14190
bool is_coplanar(const Matrix &cov)
Definition: graphics.cc:9713
static Matrix do_zoom(double val, double factor, const Matrix &lims, bool is_logscale)
Definition: graphics.cc:8859
static bool updating_title_position
Definition: graphics.cc:6751
static bool is_handle_visible(const graphics_handle &h)
Definition: graphics.cc:12494
static Matrix default_colororder(void)
Definition: graphics.cc:257
static octave_value convert_cdata(const base_properties &props, const octave_value &cdata, bool is_scaled, int cdim)
Definition: graphics.cc:999
static void adopt(const graphics_handle &parent_h, const graphics_handle &h)
Definition: graphics.cc:3133
static Matrix default_axes_ticklength(void)
Definition: graphics.cc:488
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:1209
double force_in_range(double x, double lower, double upper)
Definition: graphics.cc:8848
static Matrix default_surface_zdata(void)
Definition: graphics.cc:375
static void max_axes_scale(double &s, Matrix &limits, const Matrix &kids, double pbfactor, double dafactor, char limit_type, bool tight)
Definition: graphics.cc:6834
static void xcreatefcn(const graphics_handle &h)
Definition: graphics.cc:3181
static Matrix default_axes_outerposition(void)
Definition: graphics.cc:449
static Matrix convert_position(const Matrix &pos, const caseless_str &from_units, const caseless_str &to_units, const Matrix &parent_dim)
Definition: graphics.cc:692
static Matrix default_patch_faces(void)
Definition: graphics.cc:392
static octave_value make_graphics_object(const std::string &go_name, bool integer_figure_handle, const octave_value_list &args)
Definition: graphics.cc:13043
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:837
static int toggle_warn(std::string id, bool on, int state=-1)
Definition: graphics.cc:3204
static bool isfigure(double val)
Definition: graphics.cc:2833
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:989
static Matrix default_figure_position(void)
Definition: graphics.cc:499
static void cleanup_waitfor_postset_listener(const octave_value &listener)
Definition: graphics.cc:14149
#define OCTAVE_DEFAULT_FONTNAME
Definition: graphics.in.h:58
@ NOT_INF
Definition: graphics.in.h:1173
@ NOT_NAN
Definition: graphics.in.h:1172
@ NO_CHECK
Definition: graphics.in.h:1170
@ FINITE
Definition: graphics.in.h:1171
@ AXE_ANY_DIR
Definition: graphics.in.h:3424
@ AXE_DEPTH_DIR
Definition: graphics.in.h:3425
@ AXE_HORZ_DIR
Definition: graphics.in.h:3426
@ AXE_VERT_DIR
Definition: graphics.in.h:3427
listener_mode
Definition: graphics.in.h:278
@ GCB_PREDELETE
Definition: graphics.in.h:278
@ GCB_POSTSET
Definition: graphics.in.h:278
@ GCB_PERSISTENT
Definition: graphics.in.h:278
QString name
bool Vdrawnow_requested
Definition: input.cc:90
#define octave_NaN
Definition: lo-ieee.h:44
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
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
double fix(double x)
Definition: lo-mappers.h:118
Complex atan(const Complex &x)
Definition: lo-mappers.h:71
double signum(double x)
Definition: lo-mappers.h:222
T max(T x, T y)
Definition: lo-mappers.h:361
T x_nint(T x)
Definition: lo-mappers.h:262
bool isfinite(double x)
Definition: lo-mappers.h:192
bool isnan(bool)
Definition: lo-mappers.h:178
bool isinf(double x)
Definition: lo-mappers.h:203
double round(double x)
Definition: lo-mappers.h:136
std::complex< T > ceil(const std::complex< T > &x)
Definition: lo-mappers.h:103
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
T min(T x, T y)
Definition: lo-mappers.h:354
std::string dirname(const std::string &path)
Definition: file-ops.cc:363
std::string dir_sep_chars(void)
Definition: file-ops.cc:252
gh_manager & __get_gh_manager__(const std::string &who)
static uint32_t state[624]
Definition: randmtzig.cc:190
void flush_stdout(void)
Definition: pager.cc:260
interpreter & __get_interpreter__(const std::string &who)
void sleep(double seconds, bool do_graphics_events)
Definition: utils.cc:1415
gtk_manager & __get_gtk_manager__(const std::string &who)
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:9580
display_info & __get_display_info__(const std::string &who)
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
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
static octave_value box(JNIEnv *jni_env, void *jobj, void *jcls_arg=nullptr)
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
#define octave_stdout
Definition: pager.h:313
static T abs(T x)
Definition: pr-output.cc:1678
F77_RET_T len
Definition: xerbla.cc:61