GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
graphics.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-2024 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 "lo-sysdep.h"
48 #include "oct-locbuf.h"
49 #include "oct-time.h"
50 
51 #include "builtin-defun-decls.h"
52 #include "defun.h"
53 #include "display.h"
54 #include "error.h"
55 #include "gh-manager.h"
56 #include "graphics-utils.h"
57 #include "graphics.h"
58 #include "input.h"
59 #include "interpreter-private.h"
60 #include "interpreter.h"
61 #include "ov.h"
62 #include "ovl.h"
63 #include "oct-map.h"
64 #include "ov-fcn-handle.h"
65 #include "pager.h"
66 #include "text-engine.h"
67 #include "text-renderer.h"
68 #include "unwind-prot.h"
69 #include "utils.h"
70 #include "octave-default-image.h"
71 
73 
74 OCTAVE_NORETURN static
75 void
76 err_set_invalid (const std::string& pname)
77 {
78  error ("set: invalid value for %s property", pname.c_str ());
79 }
80 
81 // Check to see that PNAME matches just one of PNAMES uniquely.
82 // Return the full name of the match, or an empty caseless_str object
83 // if there is no match, or the match is ambiguous.
84 
85 static caseless_str
86 validate_property_name (const std::string& who, const std::string& what,
87  const std::set<std::string>& pnames,
88  const caseless_str& pname)
89 {
90  std::size_t len = pname.length ();
91  std::set<std::string> matches;
92 
93  // Find exact or partial matches to property name
94  for (const auto& propnm : pnames)
95  {
96  if (pname.compare (propnm, len))
97  {
98  if (len == propnm.length ())
99  return pname; // Exact match.
100 
101  matches.insert (propnm);
102  }
103  }
104 
105  std::size_t num_matches = matches.size ();
106 
107  if (num_matches == 0)
108  error ("%s: unknown %s property %s",
109  who.c_str (), what.c_str (), pname.c_str ());
110  else if (num_matches > 1)
111  {
112  string_vector sv (matches);
113 
114  std::ostringstream os;
115 
116  sv.list_in_columns (os);
117 
118  std::string match_list = os.str ();
119 
120  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
121  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
122  }
123  else // num_matches == 1
124  {
125  // Exact match was handled above.
126  std::string possible_match = *(matches.begin ());
127 
128  warning_with_id ("Octave:abbreviated-property-match",
129  "%s: allowing %s to match %s property %s",
130  who.c_str (), pname.c_str (), what.c_str (),
131  possible_match.c_str ());
132 
133  return possible_match;
134  }
135 
136  return caseless_str ();
137 }
138 
139 static Matrix
140 viridis_colormap ()
141 {
142  // The values below have been produced by viridis (64)(:)
143  // It would be nice to be able to feval the
144  // viridis function but since there is a static property object that includes
145  // a colormap_property object, we need to initialize this before main is
146  // even called, so calling an interpreted function is not possible.
147 
148  const double cmapv[] =
149  {
150  2.67004010000000e-01, 2.72651720952381e-01, 2.77106307619048e-01,
151  2.80356151428571e-01, 2.82390045238095e-01, 2.83204606666667e-01,
152  2.82809341428571e-01, 2.81230763333333e-01, 2.78516153333333e-01,
153  2.74735528571429e-01, 2.69981791904762e-01, 2.64368580952381e-01,
154  2.58026184285714e-01, 2.51098684761905e-01, 2.43732853333333e-01,
155  2.36073294285714e-01, 2.28263191428571e-01, 2.20424955714286e-01,
156  2.12666598571429e-01, 2.05079113809524e-01, 1.97721880952381e-01,
157  1.90631350000000e-01, 1.83819438571429e-01, 1.77272360952381e-01,
158  1.70957518571429e-01, 1.64832915714286e-01, 1.58845368095238e-01,
159  1.52951235714286e-01, 1.47131626666667e-01, 1.41402210952381e-01,
160  1.35832975714286e-01, 1.30582113809524e-01, 1.25898377619048e-01,
161  1.22163105714286e-01, 1.19872409523810e-01, 1.19626570000000e-01,
162  1.22045948571429e-01, 1.27667691904762e-01, 1.36834947142857e-01,
163  1.49643331428571e-01, 1.65967274285714e-01, 1.85538397142857e-01,
164  2.08030450000000e-01, 2.33127309523809e-01, 2.60531475238095e-01,
165  2.90000730000000e-01, 3.21329971428571e-01, 3.54355250000000e-01,
166  3.88930322857143e-01, 4.24933143333333e-01, 4.62246770476190e-01,
167  5.00753620000000e-01, 5.40336957142857e-01, 5.80861172380952e-01,
168  6.22170772857143e-01, 6.64087320476191e-01, 7.06403823333333e-01,
169  7.48885251428571e-01, 7.91273132857143e-01, 8.33302102380952e-01,
170  8.74717527142857e-01, 9.15296319047619e-01, 9.54839555238095e-01,
171  9.93247890000000e-01, 4.87433000000000e-03, 2.58456800000000e-02,
172  5.09139004761905e-02, 7.42014957142857e-02, 9.59536042857143e-02,
173  1.16893314761905e-01, 1.37350195714286e-01, 1.57479940000000e-01,
174  1.77347967619048e-01, 1.96969168571429e-01, 2.16330337619048e-01,
175  2.35404660952381e-01, 2.54161735714286e-01, 2.72573219047619e-01,
176  2.90619516666667e-01, 3.08291041428571e-01, 3.25586450952381e-01,
177  3.42517215238095e-01, 3.59102207142857e-01, 3.75366067142857e-01,
178  3.91340913333333e-01, 4.07061480000000e-01, 4.22563764285714e-01,
179  4.37885543809524e-01, 4.53062984285714e-01, 4.68129543809524e-01,
180  4.83117059523810e-01, 4.98052961428571e-01, 5.12959473333333e-01,
181  5.27854311428571e-01, 5.42750087142857e-01, 5.57652481904762e-01,
182  5.72563073333333e-01, 5.87476284285714e-01, 6.02382410952381e-01,
183  6.17265840000000e-01, 6.32106955714286e-01, 6.46881817142857e-01,
184  6.61562926190476e-01, 6.76119717142857e-01, 6.90518987142857e-01,
185  7.04725181904762e-01, 7.18700950000000e-01, 7.32406441904762e-01,
186  7.45802021904762e-01, 7.58846480000000e-01, 7.71497934761905e-01,
187  7.83714033809524e-01, 7.95453081428571e-01, 8.06673890000000e-01,
188  8.17337565714286e-01, 8.27409135714286e-01, 8.36858167619048e-01,
189  8.45663399523809e-01, 8.53815582857143e-01, 8.61321019047619e-01,
190  8.68206316666667e-01, 8.74522215714286e-01, 8.80346158571429e-01,
191  8.85780083333333e-01, 8.90945338571429e-01, 8.95973498571429e-01,
192  9.01005800000000e-01, 9.06156570000000e-01, 3.29415190000000e-01,
193  3.53367293333333e-01, 3.76236064761905e-01, 3.97901482857143e-01,
194  4.18250757142857e-01, 4.37178920000000e-01, 4.54595888571429e-01,
195  4.70433883333333e-01, 4.84653865714286e-01, 4.97250492857143e-01,
196  5.08254501428571e-01, 5.17731949047619e-01, 5.25780221428571e-01,
197  5.32522206190476e-01, 5.38097133333333e-01, 5.42651800000000e-01,
198  5.46335411904762e-01, 5.49287148571429e-01, 5.51635008571429e-01,
199  5.53493173333333e-01, 5.54953478571429e-01, 5.56089070000000e-01,
200  5.56952166666667e-01, 5.57576145714286e-01, 5.57974025714286e-01,
201  5.58142745238095e-01, 5.58058673809524e-01, 5.57684744285714e-01,
202  5.56973310000000e-01, 5.55864478571429e-01, 5.54288677142857e-01,
203  5.52175699047619e-01, 5.49445382857143e-01, 5.46023368571429e-01,
204  5.41830633809524e-01, 5.36795616666667e-01, 5.30847985714286e-01,
205  5.23924198571429e-01, 5.15966779523810e-01, 5.06924262857143e-01,
206  4.96751861428571e-01, 4.85412122857143e-01, 4.72873300000000e-01,
207  4.59105875238095e-01, 4.44095883333333e-01, 4.27825852857143e-01,
208  4.10292713809524e-01, 3.91487632857143e-01, 3.71420688571429e-01,
209  3.50098750000000e-01, 3.27544678571429e-01, 3.03798967142857e-01,
210  2.78916748571429e-01, 2.53000856190476e-01, 2.26223670000000e-01,
211  1.98879439523810e-01, 1.71494930000000e-01, 1.45037631428572e-01,
212  1.21291048571429e-01, 1.03326155238095e-01, 9.53507900000000e-02,
213  1.00469958095238e-01, 1.17876387142857e-01, 1.43936200000000e-01
214  };
215 
216  // It would be nice if Matrix had a ctor allowing to do the
217  // following without a copy
218  Matrix cmap (64, 3, 0.0);
219  std::copy (cmapv, cmapv + (64*3), cmap.fortran_vec ());
220  return cmap;
221 }
222 
223 static double
224 default_screendepth ()
225 {
226  octave::display_info& dpy_info = octave::__get_display_info__ ();
227 
228  return dpy_info.depth ();
229 }
230 
231 static Matrix
232 default_screensize ()
233 {
234  Matrix retval (1, 4);
235 
236  octave::display_info& dpy_info = octave::__get_display_info__ ();
237 
238  retval(0) = 1.0;
239  retval(1) = 1.0;
240  retval(2) = dpy_info.width ();
241  retval(3) = dpy_info.height ();
242 
243  return retval;
244 }
245 
246 static double
247 default_screenpixelsperinch ()
248 {
249  octave::display_info& dpy_info = octave::__get_display_info__ ();
250 
251  return (dpy_info.x_dpi () + dpy_info.y_dpi ()) / 2;
252 }
253 
254 static Matrix
255 default_colororder ()
256 {
257  Matrix retval (7, 3, 0.0);
258 
259  retval(0, 1) = 0.447;
260  retval(0, 2) = 0.741;
261 
262  retval(1, 0) = 0.850;
263  retval(1, 1) = 0.325;
264  retval(1, 2) = 0.098;
265 
266  retval(2, 0) = 0.929;
267  retval(2, 1) = 0.694;
268  retval(2, 2) = 0.125;
269 
270  retval(3, 0) = 0.494;
271  retval(3, 1) = 0.184;
272  retval(3, 2) = 0.556;
273 
274  retval(4, 0) = 0.466;
275  retval(4, 1) = 0.674;
276  retval(4, 2) = 0.188;
277 
278  retval(5, 0) = 0.301;
279  retval(5, 1) = 0.745;
280  retval(5, 2) = 0.933;
281 
282  retval(6, 0) = 0.635;
283  retval(6, 1) = 0.078;
284  retval(6, 2) = 0.184;
285 
286  return retval;
287 }
288 
289 static Matrix
290 default_lim (bool logscale = false)
291 {
292  Matrix m (1, 2);
293 
294  if (logscale)
295  {
296  m(0) = 0.1;
297  m(1) = 1.0;
298  }
299  else
300  {
301  m(0) = 0.0;
302  m(1) = 1.0;
303  }
304 
305  return m;
306 }
307 
308 static Matrix
309 default_data ()
310 {
311  Matrix retval (1, 2);
312 
313  retval(0) = 0;
314  retval(1) = 1;
315 
316  return retval;
317 }
318 
319 static Matrix
320 default_data_lim ()
321 {
322  Matrix retval (1, 4);
323 
324  retval(0) = 0;
325  retval(1) = 1;
326  retval(2) = 1; // minimum positive
327  retval(3) = -octave::numeric_limits<double>::Inf (); // maximum negative
328 
329  return retval;
330 }
331 
332 static Matrix
333 default_image_cdata ()
334 {
335  Matrix m (64, 64);
336 
337  int i = 0;
338  for (int col = 0; col < 64; col++)
339  for (int row = 0; row < 64; row++)
340  {
341  m(col, row) = static_cast<double> (default_im_data[i]);
342  i++;
343  }
344 
345  return m;
346 }
347 
348 static Matrix
349 default_surface_xdata ()
350 {
351  Matrix m (3, 3);
352 
353  for (int col = 0; col < 3; col++)
354  for (int row = 0; row < 3; row++)
355  m(row, col) = col+1;
356 
357  return m;
358 }
359 
360 static Matrix
361 default_surface_ydata ()
362 {
363  Matrix m (3, 3);
364 
365  for (int row = 0; row < 3; row++)
366  for (int col = 0; col < 3; col++)
367  m(row, col) = row+1;
368 
369  return m;
370 }
371 
372 static Matrix
373 default_surface_zdata ()
374 {
375  Matrix m (3, 3, 0.0);
376 
377  for (int row = 0; row < 3; row++)
378  m(row, row) = 1.0;
379 
380  return m;
381 }
382 
383 static Matrix
384 default_surface_cdata ()
385 {
386  return default_surface_zdata ();
387 }
388 
389 static Matrix
390 default_patch_faces ()
391 {
392  Matrix m (1, 3);
393 
394  m(0) = 1.0;
395  m(1) = 2.0;
396  m(2) = 3.0;
397 
398  return m;
399 }
400 
401 static Matrix
402 default_patch_vertices ()
403 {
404  Matrix m (3, 2, 0.0);
405 
406  m(1) = 1.0;
407  m(3) = 1.0;
408  m(4) = 1.0;
409 
410  return m;
411 }
412 
413 static Matrix
414 default_patch_xdata ()
415 {
416  Matrix m (3, 1, 0.0);
417 
418  m(1) = 1.0;
419 
420  return m;
421 }
422 
423 static Matrix
424 default_patch_ydata ()
425 {
426  Matrix m (3, 1, 1.0);
427 
428  m(2) = 0.0;
429 
430  return m;
431 }
432 
433 static Matrix
434 default_axes_position ()
435 {
436  Matrix m (1, 4);
437 
438  m(0) = 0.13;
439  m(1) = 0.11;
440  m(2) = 0.775;
441  m(3) = 0.815;
442 
443  return m;
444 }
445 
446 static Matrix
447 default_axes_outerposition ()
448 {
449  Matrix m (1, 4);
450 
451  m(0) = 0.0;
452  m(1) = 0.0;
453  m(2) = 1.0;
454  m(3) = 1.0;
455 
456  return m;
457 }
458 
459 static Matrix
460 default_axes_view ()
461 {
462  Matrix m (1, 2);
463 
464  m(0) = 0.0;
465  m(1) = 90.0;
466 
467  return m;
468 }
469 
470 static Matrix
471 default_axes_tick ()
472 {
473  Matrix m (1, 6);
474 
475  m(0) = 0.0;
476  m(1) = 0.2;
477  m(2) = 0.4;
478  m(3) = 0.6;
479  m(4) = 0.8;
480  m(5) = 1.0;
481 
482  return m;
483 }
484 
485 static Matrix
486 default_axes_ticklength ()
487 {
488  Matrix m (1, 2);
489 
490  m(0) = 0.01;
491  m(1) = 0.025;
492 
493  return m;
494 }
495 
496 static Matrix
497 default_figure_position ()
498 {
499  Matrix m (1, 4);
500 
501  m(0) = 300;
502  m(1) = 200;
503  m(2) = 560;
504  m(3) = 420;
505 
506  return m;
507 }
508 
509 static Matrix
510 default_figure_papersize ()
511 {
512  Matrix m (1, 2);
513 
514  m(0) = 8.5;
515  m(1) = 11.0;
516 
517  return m;
518 }
519 
520 static Matrix
521 default_figure_paperposition ()
522 {
523  Matrix m (1, 4);
524 
525  // Update if default_figure_position or default_figure_papersize change
526  m(0) = 1.3421852580027660;
527  m(1) = 3.3191389435020748;
528  m(2) = 5.8156294839944680;
529  m(3) = 4.3617221129958503;
530 
531  return m;
532 }
533 
534 static std::string
535 default_graphics_toolkit ()
536 {
537  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
538 
539  return gtk_mgr.default_toolkit ();
540 }
541 
542 static Matrix
543 default_control_position ()
544 {
545  Matrix retval (1, 4);
546 
547  retval(0) = 0;
548  retval(1) = 0;
549  retval(2) = 80;
550  retval(3) = 30;
551 
552  return retval;
553 }
554 
555 static Matrix
556 default_control_sliderstep ()
557 {
558  Matrix retval (1, 2);
559 
560  retval(0) = 0.01;
561  retval(1) = 0.1;
562 
563  return retval;
564 }
565 
566 static Matrix
567 default_panel_position ()
568 {
569  Matrix retval (1, 4);
570 
571  retval(0) = 0;
572  retval(1) = 0;
573  retval(2) = 1;
574  retval(3) = 1;
575 
576  return retval;
577 }
578 
579 static Matrix
580 default_light_position ()
581 {
582  Matrix m (1, 3);
583 
584  m(0) = 1.0;
585  m(1) = 0.0;
586  m(2) = 1.0;
587 
588  return m;
589 }
590 
591 static Matrix
592 default_table_position ()
593 {
594  Matrix retval (1, 4);
595 
596  retval(0) = 20;
597  retval(1) = 20;
598  retval(2) = 300;
599  retval(3) = 300;
600 
601  return retval;
602 }
603 
604 static Matrix
605 default_table_backgroundcolor ()
606 {
607  Matrix retval (2, 3);
608  retval(0, 0) = 1;
609  retval(0, 1) = 1;
610  retval(0, 2) = 1;
611  retval(1, 0) = 0.94;
612  retval(1, 1) = 0.94;
613  retval(1, 2) = 0.94;
614  return retval;
615 }
616 
617 static graphics_handle
618 make_graphics_handle (const std::string& go_name,
619  const graphics_handle& parent,
620  bool integer_figure_handle = false,
621  bool call_createfcn = true,
622  bool notify_toolkit = true)
623 {
625 
626  return gh_mgr.make_graphics_handle (go_name, parent, integer_figure_handle,
627  call_createfcn, notify_toolkit);
628 }
629 
630 static double
631 convert_font_size (double font_size, const caseless_str& from_units,
632  const caseless_str& to_units, double parent_height = 0)
633 {
634  // Simple case where from_units == to_units
635 
636  if (from_units.compare (to_units))
637  return font_size;
638 
639  // Converts the given fontsize using the following transformation:
640  // <old_font_size> => points => <new_font_size>
641 
642  double points_size = 0;
643  double res = 0;
644 
645  if (from_units.compare ("points"))
646  points_size = font_size;
647  else
648  {
649  res = xget (0, "screenpixelsperinch").double_value ();
650 
651  if (from_units.compare ("pixels"))
652  points_size = font_size * 72.0 / res;
653  else if (from_units.compare ("inches"))
654  points_size = font_size * 72.0;
655  else if (from_units.compare ("centimeters"))
656  points_size = font_size * 72.0 / 2.54;
657  else if (from_units.compare ("normalized"))
658  points_size = font_size * parent_height * 72.0 / res;
659  }
660 
661  double new_font_size = 0;
662 
663  if (to_units.compare ("points"))
664  new_font_size = points_size;
665  else
666  {
667  if (res <= 0)
668  res = xget (0, "screenpixelsperinch").double_value ();
669 
670  if (to_units.compare ("pixels"))
671  new_font_size = points_size * res / 72.0;
672  else if (to_units.compare ("inches"))
673  new_font_size = points_size / 72.0;
674  else if (to_units.compare ("centimeters"))
675  new_font_size = points_size * 2.54 / 72.0;
676  else if (to_units.compare ("normalized"))
677  {
678  // Avoid setting font size to (0/0) = NaN
679 
680  if (parent_height > 0)
681  new_font_size = points_size * res / (parent_height * 72.0);
682  }
683  }
684 
685  return new_font_size;
686 }
687 
688 static Matrix
689 convert_position (const Matrix& pos, const caseless_str& from_units,
690  const caseless_str& to_units, const Matrix& parent_dim)
691 {
692  Matrix retval (1, pos.numel (), 0.0);
693  double res = 0;
694  bool is_rectangle = (pos.numel () == 4);
695  bool is_2D = (pos.numel () == 2);
696 
697  if (from_units.compare ("pixels"))
698  retval = pos;
699  else if (from_units.compare ("normalized"))
700  {
701  retval(0) = pos(0) * parent_dim(0) + 1;
702  retval(1) = pos(1) * parent_dim(1) + 1;
703  if (is_rectangle)
704  {
705  retval(2) = pos(2) * parent_dim(0);
706  retval(3) = pos(3) * parent_dim(1);
707  }
708  else if (! is_2D)
709  retval(2) = 0;
710  }
711  else if (from_units.compare ("characters"))
712  {
713  if (res <= 0)
714  res = xget (0, "screenpixelsperinch").double_value ();
715 
716  double f = 0.0;
717 
718  // FIXME: this assumes the system font is Helvetica 10pt
719  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
720  f = 12.0 * res / 74.951;
721 
722  if (f > 0)
723  {
724  retval(0) = 0.5 * pos(0) * f;
725  retval(1) = pos(1) * f;
726  if (is_rectangle)
727  {
728  retval(2) = 0.5 * pos(2) * f;
729  retval(3) = pos(3) * f;
730  }
731  else if (! is_2D)
732  retval(2) = 0;
733  }
734  }
735  else
736  {
737  if (res <= 0)
738  res = xget (0, "screenpixelsperinch").double_value ();
739 
740  double f = 0.0;
741 
742  if (from_units.compare ("points"))
743  f = res / 72.0;
744  else if (from_units.compare ("inches"))
745  f = res;
746  else if (from_units.compare ("centimeters"))
747  f = res / 2.54;
748 
749  if (f > 0)
750  {
751  retval(0) = pos(0) * f + 1;
752  retval(1) = pos(1) * f + 1;
753  if (is_rectangle)
754  {
755  retval(2) = pos(2) * f;
756  retval(3) = pos(3) * f;
757  }
758  else if (! is_2D)
759  retval(2) = 0;
760  }
761  }
762 
763  if (! to_units.compare ("pixels"))
764  {
765  if (to_units.compare ("normalized"))
766  {
767  retval(0) = (retval(0) - 1) / parent_dim(0);
768  retval(1) = (retval(1) - 1) / parent_dim(1);
769  if (is_rectangle)
770  {
771  retval(2) /= parent_dim(0);
772  retval(3) /= parent_dim(1);
773  }
774  else if (! is_2D)
775  retval(2) = 0;
776  }
777  else if (to_units.compare ("characters"))
778  {
779  if (res <= 0)
780  res = xget (0, "screenpixelsperinch").double_value ();
781 
782  double f = 0.0;
783 
784  f = 12.0 * res / 74.951;
785 
786  if (f > 0)
787  {
788  retval(0) = 2 * retval(0) / f;
789  retval(1) = retval(1) / f;
790  if (is_rectangle)
791  {
792  retval(2) = 2 * retval(2) / f;
793  retval(3) = retval(3) / f;
794  }
795  else if (! is_2D)
796  retval(2) = 0;
797  }
798  }
799  else
800  {
801  if (res <= 0)
802  res = xget (0, "screenpixelsperinch").double_value ();
803 
804  double f = 0.0;
805 
806  if (to_units.compare ("points"))
807  f = res / 72.0;
808  else if (to_units.compare ("inches"))
809  f = res;
810  else if (to_units.compare ("centimeters"))
811  f = res / 2.54;
812 
813  if (f > 0)
814  {
815  retval(0) = (retval(0) - 1) / f;
816  retval(1) = (retval(1) - 1) / f;
817  if (is_rectangle)
818  {
819  retval(2) /= f;
820  retval(3) /= f;
821  }
822  else if (! is_2D)
823  retval(2) = 0;
824  }
825  }
826  }
827 
828  return retval;
829 }
830 
831 static Matrix
832 convert_text_position (const Matrix& pos, const text::properties& props,
833  const caseless_str& from_units,
834  const caseless_str& to_units)
835 {
837 
838  graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
839 
840  graphics_object ax = go.get_ancestor ("axes");
841 
842  Matrix retval;
843 
844  if (ax.valid_object ())
845  {
846  const axes::properties& ax_props
847  = dynamic_cast<const axes::properties&> (ax.get_properties ());
848  graphics_xform ax_xform = ax_props.get_transform ();
849  bool is_rectangle = (pos.numel () == 4);
850  Matrix ax_bbox = ax_props.get_boundingbox (true),
851  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
852 
853  if (from_units.compare ("data"))
854  {
855  if (is_rectangle)
856  {
857  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
858  v2 = ax_xform.transform (pos(0) + pos(2),
859  pos(1) + pos(3), 0);
860 
861  retval.resize (1, 4);
862 
863  retval(0) = v1(0) - ax_bbox(0) + 1;
864  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
865  retval(2) = v2(0) - v1(0);
866  retval(3) = v1(1) - v2(1);
867  }
868  else
869  {
870  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
871 
872  retval.resize (1, 3);
873 
874  retval(0) = v(0) - ax_bbox(0) + 1;
875  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
876  retval(2) = 0;
877  }
878  }
879  else
880  retval = convert_position (pos, from_units, "pixels", ax_size);
881 
882  if (! to_units.compare ("pixels"))
883  {
884  if (to_units.compare ("data"))
885  {
886  if (is_rectangle)
887  {
888  ColumnVector v1, v2;
889  v1 = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
890  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
891  v2 = ax_xform.untransform (retval(0) + retval(2) + ax_bbox(0) - 1,
892  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
893 
894  retval.resize (1, 4);
895 
896  retval(0) = v1(0);
897  retval(1) = v1(1);
898  retval(2) = v2(0) - v1(0);
899  retval(3) = v2(1) - v1(1);
900  }
901  else
902  {
903  ColumnVector v;
904  v = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
905  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
906 
907  retval.resize (1, 3);
908 
909  retval(0) = v(0);
910  retval(1) = v(1);
911  retval(2) = v(2);
912  }
913  }
914  else
915  retval = convert_position (retval, "pixels", to_units, ax_size);
916  }
917  }
918 
919  return retval;
920 }
921 
922 // This function always returns the screensize in pixels
923 static Matrix
924 screen_size_pixels ()
925 {
927 
928  graphics_object obj = gh_mgr.get_object (0);
929 
930  Matrix sz = obj.get ("screensize").matrix_value ();
931 
932  return convert_position (sz, obj.get ("units").string_value (), "pixels",
933  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
934 }
935 
936 static double
937 device_pixel_ratio (graphics_handle h)
938 {
939  double retval = 1.0;
940 
942 
943  graphics_object fig = gh_mgr.get_object (h).get_ancestor ("figure");
944 
945  if (fig.valid_object ())
946  retval = fig.get ("__device_pixel_ratio__").double_value ();
947 
948  return retval;
949 }
950 
951 static void
952 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
953  const double *cmapv, double x, octave_idx_type lda,
954  octave_idx_type nc, octave_idx_type i, double *av)
955 {
956  if (is_scaled)
957  x = octave::math::fix (nc * (x - clim_0) / (clim_1 - clim_0));
958  else if (is_real)
959  x = octave::math::fix (x - 1);
960 
961  if (octave::math::isnan (x))
962  {
963  av[i] = x;
964  av[i+lda] = x;
965  av[i+2*lda] = x;
966  }
967  else
968  {
969  if (x < 0)
970  x = 0;
971  else if (x >= nc)
972  x = (nc - 1);
973 
974  octave_idx_type idx = static_cast<octave_idx_type> (x);
975 
976  av[i] = cmapv[idx];
977  av[i+lda] = cmapv[idx+nc];
978  av[i+2*lda] = cmapv[idx+2*nc];
979  }
980 }
981 
982 template <typename T>
983 void
984 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
985  const double *cmapv, const T *cv, octave_idx_type lda,
986  octave_idx_type nc, double *av)
987 {
988  for (octave_idx_type i = 0; i < lda; i++)
989  convert_cdata_2 (is_scaled, is_real,
990  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
991 }
992 
993 static octave_value
994 convert_cdata (const base_properties& props, const octave_value& cdata,
995  bool is_scaled, int cdim)
996 {
997  dim_vector dv (cdata.dims ());
998 
999  // TrueColor data doesn't require conversion
1000  if (dv.ndims () == cdim && dv(cdim-1) == 3)
1001  return cdata;
1002 
1003  Matrix cmap (1, 3, 0.0);
1004  Matrix clim (1, 2, 0.0);
1005 
1007 
1008  graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
1009  graphics_object ax = go.get_ancestor ("axes");
1010 
1011  if (ax.valid_object ())
1012  {
1013  Matrix _cmap = ax.get (caseless_str ("colormap")).matrix_value ();
1014 
1015  cmap = _cmap;
1016 
1017  if (is_scaled)
1018  {
1019  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
1020 
1021  clim = _clim;
1022  }
1023  }
1024 
1025  dv.resize (cdim);
1026  dv(cdim-1) = 3;
1027 
1028  NDArray a (dv);
1029 
1030  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
1031  octave_idx_type nc = cmap.rows ();
1032 
1033  double *av = a.fortran_vec ();
1034  const double *cmapv = cmap.data ();
1035 
1036  double clim_0 = clim(0);
1037  double clim_1 = clim(1);
1038 
1039  // FIXME: There is a lot of processing time spent just on data conversion
1040  // both here in graphics.cc and again in gl-render.cc. There must
1041  // be room for improvement! Here a macro expands to a templated
1042  // function which in turn calls another function (covert_cdata_2).
1043  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
1044  // is supported anyways so there is another double for loop across
1045  // height and width to convert all of the input data to GLfloat.
1046 
1047 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
1048  do \
1049  { \
1050  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
1051  \
1052  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
1053  tmp.data (), lda, nc, av); \
1054  } \
1055  while (0)
1056 
1057  if (cdata.is_int8_type ())
1058  CONVERT_CDATA_1 (int8NDArray, int8_, false);
1059  else if (cdata.is_int16_type ())
1060  CONVERT_CDATA_1 (int16NDArray, int16_, false);
1061  else if (cdata.is_int32_type ())
1062  CONVERT_CDATA_1 (int32NDArray, int32_, false);
1063  else if (cdata.is_int64_type ())
1064  CONVERT_CDATA_1 (int64NDArray, int64_, false);
1065  else if (cdata.is_uint8_type ())
1066  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
1067  else if (cdata.is_uint16_type ())
1068  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
1069  else if (cdata.is_uint32_type ())
1070  CONVERT_CDATA_1 (uint32NDArray, uint32_, false);
1071  else if (cdata.is_uint64_type ())
1072  CONVERT_CDATA_1 (uint64NDArray, uint64_, false);
1073  else if (cdata.is_double_type ())
1074  CONVERT_CDATA_1 (NDArray,, true);
1075  else if (cdata.is_single_type ())
1076  CONVERT_CDATA_1 (FloatNDArray, float_, true);
1077  else if (cdata.islogical ())
1078  CONVERT_CDATA_1 (boolNDArray, bool_, false);
1079  else
1080  {
1081  // Don't throw an error; leads to an incomplete FLTK object (bug #46933).
1082  warning ("unsupported type for cdata (= %s). "
1083  "Valid types are int8, int16, int32, int64, uint8, uint16, "
1084  "uint32, uint64, double, single, and bool.",
1085  cdata.type_name ().c_str ());
1086  a = NDArray (dv, 0); // return 0 instead
1087  }
1088 
1089 #undef CONVERT_CDATA_1
1090 
1091  return octave_value (a);
1092 }
1093 
1094 template <typename T>
1095 static void
1096 get_array_limits (const Array<T>& m, double& emin, double& emax,
1097  double& eminp, double& emaxp)
1098 {
1099  const T *data = m.data ();
1100  octave_idx_type n = m.numel ();
1101 
1102  for (octave_idx_type i = 0; i < n; i++)
1103  {
1104  double e = double (data[i]);
1105 
1106  // Don't need to test for NaN here as NaN>x and NaN<x is always false
1107  if (! octave::math::isinf (e))
1108  {
1109  if (e < emin)
1110  emin = e;
1111 
1112  if (e > emax)
1113  emax = e;
1114 
1115  if (e > 0 && e < eminp)
1116  eminp = e;
1117 
1118  if (e < 0 && e > emaxp)
1119  emaxp = e;
1120  }
1121  }
1122 }
1123 
1124 static bool
1125 lookup_object_name (const caseless_str& name, caseless_str& go_name,
1126  caseless_str& rest)
1127 {
1128  int len = name.length ();
1129  int offset = 0;
1130  bool result = false;
1131 
1132  if (len >= 4)
1133  {
1134  caseless_str pfx = name.substr (0, 4);
1135 
1136  if (pfx.compare ("axes") || pfx.compare ("line")
1137  || pfx.compare ("text"))
1138  offset = 4;
1139  else if (len >= 5)
1140  {
1141  pfx = name.substr (0, 5);
1142 
1143  if (pfx.compare ("image") || pfx.compare ("patch"))
1144  offset = 5;
1145  else if (len >= 6)
1146  {
1147  pfx = name.substr (0, 6);
1148 
1149  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1150  offset = 6;
1151  else if (len >= 7)
1152  {
1153  pfx = name.substr (0, 7);
1154 
1155  if (pfx.compare ("surface") || pfx.compare ("scatter")
1156  || pfx.compare ("hggroup") || pfx.compare ("uipanel")
1157  || pfx.compare ("uitable"))
1158  offset = 7;
1159  else if (len >= 9)
1160  {
1161  pfx = name.substr (0, 9);
1162 
1163  if (pfx.compare ("uicontrol")
1164  || pfx.compare ("uitoolbar"))
1165  offset = 9;
1166  else if (len >= 10)
1167  {
1168  pfx = name.substr (0, 10);
1169 
1170  if (pfx.compare ("uipushtool"))
1171  offset = 10;
1172  else if (len >= 12)
1173  {
1174  pfx = name.substr (0, 12);
1175 
1176  if (pfx.compare ("uitoggletool"))
1177  offset = 12;
1178  else if (len >= 13)
1179  {
1180  pfx = name.substr (0, 13);
1181 
1182  if (pfx.compare ("uicontextmenu")
1183  || pfx.compare ("uibuttongroup"))
1184  offset = 13;
1185  }
1186  }
1187  }
1188  }
1189  }
1190  }
1191  }
1192 
1193  if (offset > 0)
1194  {
1195  go_name = pfx;
1196  rest = name.substr (offset);
1197  result = true;
1198  }
1199  }
1200 
1201  return result;
1202 }
1203 
1206  const graphics_handle& h,
1207  const graphics_handle& p)
1208 {
1209  base_graphics_object *go = nullptr;
1210 
1211  if (type.compare ("figure"))
1212  go = new figure (h, p);
1213  else if (type.compare ("axes"))
1214  go = new axes (h, p);
1215  else if (type.compare ("line"))
1216  go = new line (h, p);
1217  else if (type.compare ("text"))
1218  go = new text (h, p);
1219  else if (type.compare ("image"))
1220  go = new image (h, p);
1221  else if (type.compare ("light"))
1222  go = new light (h, p);
1223  else if (type.compare ("patch"))
1224  go = new patch (h, p);
1225  else if (type.compare ("scatter"))
1226  go = new scatter (h, p);
1227  else if (type.compare ("surface"))
1228  go = new surface (h, p);
1229  else if (type.compare ("hggroup"))
1230  go = new hggroup (h, p);
1231  else if (type.compare ("uimenu"))
1232  go = new uimenu (h, p);
1233  else if (type.compare ("uicontrol"))
1234  go = new uicontrol (h, p);
1235  else if (type.compare ("uipanel"))
1236  go = new uipanel (h, p);
1237  else if (type.compare ("uibuttongroup"))
1238  go = new uibuttongroup (h, p);
1239  else if (type.compare ("uicontextmenu"))
1240  go = new uicontextmenu (h, p);
1241  else if (type.compare ("uitable"))
1242  go = new uitable (h, p);
1243  else if (type.compare ("uitoolbar"))
1244  go = new uitoolbar (h, p);
1245  else if (type.compare ("uipushtool"))
1246  go = new uipushtool (h, p);
1247  else if (type.compare ("uitoggletool"))
1248  go = new uitoggletool (h, p);
1249  return go;
1250 }
1251 
1252 // ---------------------------------------------------------------------
1253 
1254 bool
1255 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1256 {
1257  if (do_set (v))
1258  {
1259  // Notify graphics toolkit.
1260  if (m_id >= 0 && do_notify_toolkit)
1261  {
1263 
1264  graphics_object go = gh_mgr.get_object (m_parent);
1265  if (go)
1266  go.update (m_id);
1267  }
1268 
1269  // run listeners
1270  if (do_run)
1272 
1273  return true;
1274  }
1275 
1276  return false;
1277 }
1278 
1279 void
1281 {
1282  const octave_value_list& l = m_listeners[mode];
1283 
1285 
1286  for (int i = 0; i < l.length (); i++)
1287  gh_mgr.execute_listener (m_parent, l(i));
1288 }
1289 
1290 radio_values::radio_values (const std::string& opt_string)
1291  : m_default_val (), m_possible_vals ()
1292 {
1293  std::size_t beg = 0;
1294  std::size_t len = opt_string.length ();
1295  bool done = len == 0;
1296 
1297  while (! done)
1298  {
1299  std::size_t end = opt_string.find ('|', beg);
1300 
1301  if (end == std::string::npos)
1302  {
1303  end = len;
1304  done = true;
1305  }
1306 
1307  std::string t = opt_string.substr (beg, end-beg);
1308 
1309  // Special case for '|' symbol itself
1310  if (t.empty () && opt_string[beg] == '|')
1311  {
1312  t = '|';
1313  end++;
1314  }
1315 
1316  // Might want more error checking on parsing default value...
1317  if (t[0] == '{')
1318  {
1319  t = t.substr (1, t.length () - 2);
1320  m_default_val = t;
1321  }
1322  else if (beg == 0) // ensure default value
1323  m_default_val = t;
1324 
1325  m_possible_vals.insert (t);
1326 
1327  beg = end + 1;
1328  }
1329 }
1330 
1331 std::string
1333 {
1334  std::string retval;
1335 
1336  for (const auto& val : m_possible_vals)
1337  {
1338  if (retval.empty ())
1339  {
1340  if (val == default_value ())
1341  retval = '{' + val + '}';
1342  else
1343  retval = val;
1344  }
1345  else
1346  {
1347  if (val == default_value ())
1348  retval += " | {" + val + '}';
1349  else
1350  retval += " | " + val;
1351  }
1352  }
1353 
1354  if (! retval.empty ())
1355  retval = "[ " + retval + " ]";
1356 
1357  return retval;
1358 }
1359 
1360 Cell
1362 {
1363  octave_idx_type i = 0;
1364  Cell retval (nelem (), 1);
1365 
1366  for (const auto& val : m_possible_vals)
1367  retval(i++) = std::string (val);
1368 
1369  return retval;
1370 }
1371 
1372 bool
1373 color_values::str2rgb (const std::string& str_arg)
1374 {
1375  bool retval = true;
1376 
1377  double tmp_rgb[3] = {0, 0, 0};
1378 
1379  std::string str = str_arg;
1380  unsigned int len = str.length ();
1381 
1382  std::transform (str.begin (), str.end (), str.begin (), tolower);
1383 
1384  // "blue" must precede black for Matlab compatibility
1385  if (str.compare (0, len, "blue", 0, len) == 0)
1386  tmp_rgb[2] = 1;
1387  else if (str.compare (0, len, "black", 0, len) == 0
1388  || str.compare (0, len, "k", 0, len) == 0)
1389  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1390  else if (str.compare (0, len, "red", 0, len) == 0)
1391  tmp_rgb[0] = 1;
1392  else if (str.compare (0, len, "green", 0, len) == 0)
1393  tmp_rgb[1] = 1;
1394  else if (str.compare (0, len, "yellow", 0, len) == 0)
1395  tmp_rgb[0] = tmp_rgb[1] = 1;
1396  else if (str.compare (0, len, "magenta", 0, len) == 0)
1397  tmp_rgb[0] = tmp_rgb[2] = 1;
1398  else if (str.compare (0, len, "cyan", 0, len) == 0)
1399  tmp_rgb[1] = tmp_rgb[2] = 1;
1400  else if (str.compare (0, len, "white", 0, len) == 0
1401  || str.compare (0, len, "w", 0, len) == 0)
1402  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1403  else if (str[0] == '#' && len == 7)
1404  {
1405  try
1406  {
1407  tmp_rgb[0] = static_cast<double> (stoi (str.substr (1, 2), nullptr, 16))
1408  / 255.0;
1409  tmp_rgb[1] = static_cast<double> (stoi (str.substr (3, 2), nullptr, 16))
1410  / 255.0;
1411  tmp_rgb[2] = static_cast<double> (stoi (str.substr (5, 2), nullptr, 16))
1412  / 255.0;
1413  }
1414  catch (const octave::execution_exception&)
1415  {
1416  retval = false;
1417  }
1418  catch (const std::invalid_argument&)
1419  {
1420  retval = false;
1421  }
1422 
1423  }
1424  else if (str[0] == '#' && len == 4)
1425  {
1426  try
1427  {
1428  tmp_rgb[0] = static_cast<double> (stoi (str.substr (1, 1), nullptr, 16))
1429  / 15.0;
1430  tmp_rgb[1] = static_cast<double> (stoi (str.substr (2, 1), nullptr, 16))
1431  / 15.0;
1432  tmp_rgb[2] = static_cast<double> (stoi (str.substr (3, 1), nullptr, 16))
1433  / 15.0;
1434  }
1435  catch (const octave::execution_exception&)
1436  {
1437  retval = false;
1438  }
1439  catch (const std::invalid_argument&)
1440  {
1441  retval = false;
1442  }
1443  }
1444  else
1445  retval = false;
1446 
1447  if (retval)
1448  {
1449  for (int i = 0; i < 3; i++)
1450  m_rgb(i) = tmp_rgb[i];
1451  }
1452 
1453  return retval;
1454 }
1455 
1456 bool
1458 {
1459  if (val.is_string ())
1460  {
1461  std::string s = val.string_value ();
1462 
1463  if (s.empty ())
1464  error (R"(invalid value for color property "%s")",
1465  get_name ().c_str ());
1466 
1467  std::string match;
1468 
1469  if (m_radio_val.contains (s, match))
1470  {
1471  if (m_current_type != radio_t || match != m_current_val)
1472  {
1473  if (s.length () != match.length ())
1474  warning_with_id ("Octave:abbreviated-property-match",
1475  "%s: allowing %s to match %s value %s",
1476  "set", s.c_str (), get_name ().c_str (),
1477  match.c_str ());
1478  m_current_val = match;
1479  m_current_type = radio_t;
1480  return true;
1481  }
1482  }
1483  else
1484  {
1485  try
1486  {
1487  color_values col (s);
1488 
1489  if (m_current_type != color_t || col != m_color_val)
1490  {
1491  m_color_val = col;
1492  m_current_type = color_t;
1493  return true;
1494  }
1495  }
1496  catch (octave::execution_exception& ee)
1497  {
1498  error (ee, R"(invalid value for color property "%s" (value = %s))",
1499  get_name ().c_str (), s.c_str ());
1500  }
1501  }
1502  }
1503  else if (val.isnumeric ())
1504  {
1505  Matrix m = val.matrix_value ();
1506 
1507  if (m.numel () != 3)
1508  error (R"(invalid value for color property "%s")",
1509  get_name ().c_str ());
1510 
1511  color_values col (m(0), m(1), m(2));
1512 
1513  if (m_current_type != color_t || col != m_color_val)
1514  {
1515  m_color_val = col;
1516  m_current_type = color_t;
1517  return true;
1518  }
1519  }
1520  else
1521  error (R"(invalid value for color property "%s")",
1522  get_name ().c_str ());
1523 
1524  return false;
1525 }
1526 
1527 bool
1529 {
1530  if (val.is_string ())
1531  {
1532  std::string s = val.string_value ();
1533  std::string match;
1534 
1535  if (s.empty () || ! m_radio_val.contains (s, match))
1536  error (R"(invalid value for double_radio property "%s")",
1537  get_name ().c_str ());
1538 
1539  if (m_current_type != radio_t || match != m_current_val)
1540  {
1541  if (s.length () != match.length ())
1542  warning_with_id ("Octave:abbreviated-property-match",
1543  "%s: allowing %s to match %s value %s",
1544  "set", s.c_str (), get_name ().c_str (),
1545  match.c_str ());
1546  m_current_val = match;
1547  m_current_type = radio_t;
1548  return true;
1549  }
1550  }
1551  else if (val.is_scalar_type () && val.isreal ())
1552  {
1553  double new_dval = val.double_value ();
1554 
1555  if (m_current_type != double_t || new_dval != m_dval)
1556  {
1557  m_dval = new_dval;
1558  m_current_type = double_t;
1559  return true;
1560  }
1561  }
1562  else
1563  error (R"(invalid value for double_radio property "%s")",
1564  get_name ().c_str ());
1565 
1566  return false;
1567 }
1568 
1569 bool
1570 array_property::validate (const octave_value& v)
1571 {
1572  bool xok = false;
1573 
1574  // check value type
1575  if (m_type_constraints.size () > 0)
1576  {
1577  if (m_type_constraints.find (v.class_name ()) != m_type_constraints.end ())
1578  xok = true;
1579 
1580  // check if complex is allowed (it's also of class "double", so
1581  // checking that alone is not enough to ensure real type)
1582  if (m_type_constraints.find ("real") != m_type_constraints.end ()
1583  && v.iscomplex ())
1584  xok = false;
1585  }
1586  else
1587  xok = v.isnumeric () || v.is_bool_scalar ();
1588 
1589  if (xok && m_size_constraints.size () > 0)
1590  {
1591  dim_vector vdims = v.dims ();
1592  int vlen = vdims.ndims ();
1593 
1594  xok = false;
1595 
1596  // check dimensional size constraints until a match is found
1597  for (auto it = m_size_constraints.cbegin ();
1598  ! xok && it != m_size_constraints.cend ();
1599  ++it)
1600  {
1601  dim_vector itdims = (*it);
1602 
1603  if (itdims.ndims () == vlen)
1604  {
1605  xok = true;
1606 
1607  for (int i = 0; xok && i < vlen; i++)
1608  {
1609  if (itdims(i) > 0)
1610  {
1611  if (itdims(i) != vdims(i))
1612  xok = false;
1613  }
1614  else if (itdims(i) == 0)
1615  {
1616  if (! v.isempty ())
1617  xok = false;
1618  break;
1619  }
1620  }
1621  }
1622  }
1623  }
1624 
1625  if (xok)
1626  {
1627  NDArray v_mat = v.array_value ();
1628  // Check min and max
1629  if (! octave::math::isnan (m_minval.first))
1630  {
1631  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1632  if (m_minval.second && m_minval.first > v_mat(i))
1633  error (R"(set: "%s" must be greater than or equal to %g)",
1634  get_name ().c_str (), m_minval.first);
1635  else if (! m_minval.second && m_minval.first >= v_mat(i))
1636  error (R"(set: "%s" must be greater than %g)",
1637  get_name ().c_str (), m_minval.first);
1638  }
1639 
1640  if (! octave::math::isnan (m_maxval.first))
1641  {
1642  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1643  if (m_maxval.second && m_maxval.first < v_mat(i))
1644  error (R"(set: "%s" must be less than or equal to %g)",
1645  get_name ().c_str (), m_maxval.first);
1646  else if (! m_maxval.second && m_maxval.first <= v_mat(i))
1647  error (R"(set: "%s" must be less than %g)",
1648  get_name ().c_str (), m_maxval.first);
1649  }
1650 
1651  if (m_finite_constraint == NO_CHECK) { /* do nothing */ }
1652  else if (m_finite_constraint == FINITE)
1653  {
1654  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1655  if (! octave::math::isfinite (v_mat(i)))
1656  error (R"(set: "%s" must be finite)", get_name ().c_str ());
1657  }
1658  else if (m_finite_constraint == NOT_NAN)
1659  {
1660  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1661  if (octave::math::isnan (v_mat(i)))
1662  error (R"(set: "%s" must not be nan)", get_name ().c_str ());
1663  }
1664  else if (m_finite_constraint == NOT_INF)
1665  {
1666  for (octave_idx_type i = 0; i < v_mat.numel (); i++)
1667  if (octave::math::isinf (v_mat(i)))
1668  error (R"(set: "%s" must not be infinite)", get_name ().c_str ());
1669  }
1670 
1671  }
1672 
1673  return xok;
1674 }
1675 
1676 bool
1677 array_property::is_equal (const octave_value& v) const
1678 {
1679  if (m_data.type_name () == v.type_name ())
1680  {
1681  if (m_data.dims () == v.dims ())
1682  {
1683 
1684 #define CHECK_ARRAY_EQUAL(T, F, A) \
1685  { \
1686  if (m_data.numel () == 1) \
1687  return m_data.F ## scalar_value () == \
1688  v.F ## scalar_value (); \
1689  else \
1690  { \
1691  /* Keep copy of array_value to allow */ \
1692  /* sparse/bool arrays that are converted, to */ \
1693  /* not be deallocated early */ \
1694  const A m1 = m_data.F ## array_value (); \
1695  const T *d1 = m1.data (); \
1696  const A m2 = v.F ## array_value (); \
1697  const T *d2 = m2.data (); \
1698  \
1699  bool flag = true; \
1700  \
1701  for (int i = 0; flag && i < m_data.numel (); i++) \
1702  if (d1[i] != d2[i]) \
1703  flag = false; \
1704  \
1705  return flag; \
1706  } \
1707  }
1708 
1709  if (m_data.is_double_type () || m_data.islogical ())
1710  CHECK_ARRAY_EQUAL (double,, NDArray)
1711  else if (m_data.is_single_type ())
1712  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1713  else if (m_data.is_int8_type ())
1715  else if (m_data.is_int16_type ())
1717  else if (m_data.is_int32_type ())
1719  else if (m_data.is_int64_type ())
1721  else if (m_data.is_uint8_type ())
1723  else if (m_data.is_uint16_type ())
1725  else if (m_data.is_uint32_type ())
1727  else if (m_data.is_uint64_type ())
1729  }
1730  }
1731 
1732  return false;
1733 }
1734 
1735 void
1736 array_property::get_data_limits ()
1737 {
1740 
1741  if (! m_data.isempty ())
1742  {
1743  if (m_data.isinteger ())
1744  {
1745  if (m_data.is_int8_type ())
1746  get_array_limits (m_data.int8_array_value (),
1748  else if (m_data.is_uint8_type ())
1749  get_array_limits (m_data.uint8_array_value (),
1751  else if (m_data.is_int16_type ())
1752  get_array_limits (m_data.int16_array_value (),
1754  else if (m_data.is_uint16_type ())
1755  get_array_limits (m_data.uint16_array_value (),
1757  else if (m_data.is_int32_type ())
1758  get_array_limits (m_data.int32_array_value (),
1760  else if (m_data.is_uint32_type ())
1761  get_array_limits (m_data.uint32_array_value (),
1763  else if (m_data.is_int64_type ())
1764  get_array_limits (m_data.int64_array_value (),
1766  else if (m_data.is_uint64_type ())
1767  get_array_limits (m_data.uint64_array_value (),
1769  }
1770  else
1771  get_array_limits (m_data.array_value (),
1773  }
1774 }
1775 
1776 bool
1778 {
1779  // Users may want to use empty matrix to reset a handle property
1780  if (v.isempty ())
1781  {
1782  if (! get ().isempty ())
1783  {
1784  m_current_val = graphics_handle ();
1785  return true;
1786  }
1787  else
1788  return false;
1789  }
1790 
1791  double dv = v.xdouble_value (R"(set: invalid graphics handle for property "%s")",
1792  get_name ().c_str ());
1793 
1795 
1796  graphics_handle gh = gh_mgr.lookup (dv);
1797 
1798  // Check the object type if necessary
1799  bool type_ok = true;
1800  if (gh.ok () && ! m_type_constraints.empty ())
1801  {
1802  type_ok = false;
1803  graphics_object obj = gh_mgr.get_object (gh);
1804 
1805  for (const auto& type : m_type_constraints)
1806  if (obj.isa (type))
1807  {
1808  type_ok = true;
1809  break;
1810  }
1811  }
1812 
1813  if (! octave::math::isnan (gh.value ()) && ! (gh.ok () && type_ok))
1814  {
1815  if (type_ok)
1816  error (R"(set: invalid graphics handle (= %g) for property "%s")",
1817  dv, get_name ().c_str ());
1818  else
1819  error (R"(set: invalid graphics object type for property "%s")",
1820  get_name ().c_str ());
1821  }
1822 
1823  if (m_current_val != gh)
1824  {
1825  m_current_val = gh;
1826  return true;
1827  }
1828 
1829  return false;
1830 }
1831 
1832 /*
1833 ## Test validation of contextmenu property
1834 %!test
1835 %! hf = figure ("visible", "off");
1836 %! unwind_protect
1837 %! hax = axes ("parent", hf);
1838 %! hpa = patch ("parent", hax);
1839 %! try
1840 %! set (hax, "contextmenu", hpa);
1841 %! catch
1842 %! err = lasterr ();
1843 %! end_try_catch
1844 %! assert (err, 'set: invalid graphics object type for property "contextmenu"');
1845 %! unwind_protect_cleanup
1846 %! delete (hf);
1847 %! end_unwind_protect
1848 */
1849 
1850 Matrix
1851 children_property::do_get_children (bool return_hidden) const
1852 {
1853  Matrix retval (m_children_list.size (), 1);
1854  octave_idx_type k = 0;
1855 
1857 
1858  graphics_object go = gh_mgr.get_object (0);
1859 
1861  = dynamic_cast<root_figure::properties&> (go.get_properties ());
1862 
1863  if (! props.is_showhiddenhandles ())
1864  {
1865  for (const auto& hchild : m_children_list)
1866  {
1867  graphics_handle kid = hchild;
1868 
1869  if (gh_mgr.is_handle_visible (kid))
1870  {
1871  if (! return_hidden)
1872  retval(k++) = hchild;
1873  }
1874  else if (return_hidden)
1875  retval(k++) = hchild;
1876  }
1877 
1878  retval.resize (k, 1);
1879  }
1880  else
1881  {
1882  for (const auto& hchild : m_children_list)
1883  retval(k++) = hchild;
1884  }
1885 
1886  return retval;
1887 }
1888 
1889 void
1890 children_property::do_delete_children (bool clear, bool from_root)
1891 {
1893 
1894  if (from_root)
1895  {
1896  for (graphics_handle hchild : m_children_list)
1897  {
1898  graphics_object go = gh_mgr.get_object (hchild);
1899  if (go.valid_object ()
1900  && ! go.get_properties ().is_beingdeleted ())
1901  gh_mgr.free (hchild, from_root);
1902  }
1903  m_children_list.clear ();
1904  }
1905  else
1906  while (! m_children_list.empty ())
1907  {
1908  // gh_mgr.free removes hchild from children_list
1909  graphics_handle hchild = m_children_list.front ();
1910  graphics_object go = gh_mgr.get_object (hchild);
1911  if (go.valid_object ()
1912  && ! go.get_properties ().is_beingdeleted ())
1913  gh_mgr.free (hchild, from_root);
1914  }
1915 
1916  // FIXME: children_list should be clear anyway at this point.
1917  if (clear)
1918  m_children_list.clear ();
1919 }
1920 
1921 bool
1922 callback_property::validate (const octave_value& v) const
1923 {
1924  // case 1: empty matrix
1925  // case 2: function handle
1926  // case 3: string corresponding to known function name
1927  // case 4: string that can be eval()'ed
1928  // case 5: cell array with first element being a function handle
1929 
1930  if (v.isempty ())
1931  return true;
1932  else if (v.is_function_handle ())
1933  return true;
1934  else if (v.is_string ())
1935  // complete validation will be done at execution-time
1936  return true;
1937  else if (v.iscell () && (v.rows () == 1 || v.columns () == 1)
1938  && v.cell_value ()(0).is_function_handle ())
1939  return true;
1940 
1941  return false;
1942 }
1943 
1944 class callback_props
1945 {
1946 public:
1947 
1948  callback_props () : m_set () { }
1949 
1950  OCTAVE_DISABLE_COPY_MOVE (callback_props)
1951 
1952  ~callback_props () = default;
1953 
1954  bool empty () const { return m_set.empty (); }
1955 
1956  void insert (const callback_property *ptr)
1957  {
1958  m_set.insert (reinterpret_cast<intptr_t> (ptr));
1959  }
1960 
1961  void erase (const callback_property *ptr)
1962  {
1963  m_set.erase (reinterpret_cast<intptr_t> (ptr));
1964  }
1965 
1966  bool contains (const callback_property *ptr) const
1967  {
1968  return m_set.find (reinterpret_cast<intptr_t> (ptr)) != m_set.end ();
1969  }
1970 
1971 private:
1972 
1973  std::set<intptr_t> m_set;
1974 };
1975 
1976 // Elements of this set are pointers to currently executing
1977 // callback_property objects. Used to determine handle visibility
1978 // inside callback functions.
1979 
1980 static callback_props executing_callbacks;
1981 
1982 void
1984 {
1985  // We are executing a callback function, so allow handles that have
1986  // their handlevisibility property set to "callback" to be visible.
1987 
1988  octave::unwind_action executing_callbacks_cleanup
1989  ([=] () { executing_callbacks.erase (this); });
1990 
1991  if (! executing_callbacks.contains (this))
1992  {
1993  executing_callbacks.insert (this);
1994 
1995  if (m_callback.is_defined () && ! m_callback.isempty ())
1996  {
1998 
1999  gh_mgr.execute_callback (get_parent (), m_callback, data);
2000  }
2001  }
2002 }
2003 
2004 // Used to cache dummy graphics objects from which dynamic properties can be
2005 // cloned.
2006 static std::map<caseless_str, graphics_object> dprop_obj_map;
2007 
2008 property
2009 property::create (const std::string& name, const graphics_handle& h,
2010  const caseless_str& type, const octave_value_list& args)
2011 {
2012  property retval;
2013 
2014  if (type.compare ("string"))
2015  {
2016  std::string sv = (args.length () > 0 ? args(0).string_value () : "");
2017 
2018  retval = property (new string_property (name, h, sv));
2019  }
2020  else if (type.compare ("any"))
2021  {
2022  octave_value ov = (args.length () > 0 ? args(0)
2023  : octave_value (Matrix ()));
2024 
2025  retval = property (new any_property (name, h, ov));
2026  }
2027  else if (type.compare ("radio"))
2028  {
2029  if (args.length () < 1)
2030  error ("addproperty: missing possible values for radio property");
2031 
2032  std::string sv = args(0).xstring_value ("addproperty: argument for radio property must be a string");
2033 
2034  retval = property (new radio_property (name, h, sv));
2035 
2036  if (args.length () > 1)
2037  retval.set (args(1));
2038  }
2039  else if (type.compare ("double"))
2040  {
2041  double dv = (args.length () > 0 ? args(0).double_value () : 0.0);
2042 
2043  retval = property (new double_property (name, h, dv));
2044  }
2045  else if (type.compare ("handle"))
2046  {
2047  double hv = (args.length () > 0 ? args(0).double_value ()
2049 
2050  graphics_handle gh (hv);
2051 
2052  retval = property (new handle_property (name, h, gh));
2053  }
2054  else if (type.compare ("boolean"))
2055  {
2056  retval = property (new bool_property (name, h, false));
2057 
2058  if (args.length () > 0)
2059  retval.set (args(0));
2060  }
2061  else if (type.compare ("data"))
2062  {
2063  retval = property (new array_property (name, h, Matrix ()));
2064 
2065  if (args.length () > 0)
2066  {
2067  retval.set (args(0));
2068  // FIXME: additional argument could define constraints,
2069  // but is this really useful?
2070  }
2071  }
2072  else if (type.compare ("color"))
2073  {
2074  color_values cv (0, 0, 0);
2075  radio_values rv;
2076 
2077  if (args.length () > 1)
2078  rv = radio_values (args(1).string_value ());
2079 
2080  retval = property (new color_property (name, h, cv, rv));
2081 
2082  if (args.length () > 0 && ! args(0).isempty ())
2083  retval.set (args(0));
2084  else
2085  retval.set (rv.default_value ());
2086  }
2087  else
2088  {
2089  caseless_str go_name, go_rest;
2090 
2091  if (! lookup_object_name (type, go_name, go_rest))
2092  error ("addproperty: unsupported type for dynamic property (= %s)",
2093  type.c_str ());
2094 
2095  graphics_object go;
2096 
2097  std::map<caseless_str, graphics_object>::const_iterator it
2098  = dprop_obj_map.find (go_name);
2099 
2100  if (it == dprop_obj_map.end ())
2101  {
2103 
2104  if (bgo)
2105  {
2106  go = graphics_object (bgo);
2107 
2108  dprop_obj_map[go_name] = go;
2109  }
2110  }
2111  else
2112  go = it->second;
2113 
2114  if (! go.valid_object ())
2115  error ("addproperty: invalid object type (= %s)",
2116  go_name.c_str ());
2117 
2118  property prop = go.get_properties ().get_property (go_rest);
2119 
2120  retval = prop.clone ();
2121 
2122  retval.set_parent (h);
2123  retval.set_name (name);
2124 
2125  if (args.length () > 0)
2126  retval.set (args(0));
2127  }
2128 
2129  return retval;
2130 }
2131 
2132 static void
2133 finalize_r (const graphics_handle& h)
2134 {
2136 
2137  graphics_object go = gh_mgr.get_object (h);
2138 
2139  if (go)
2140  {
2141  Matrix children = go.get_properties ().get_all_children ();
2142 
2143  for (int k = 0; k < children.numel (); k++)
2144  finalize_r (children(k));
2145 
2146  go.finalize ();
2147  }
2148 }
2149 
2150 static void
2151 initialize_r (const graphics_handle& h)
2152 {
2154 
2155  graphics_object go = gh_mgr.get_object (h);
2156 
2157  if (go)
2158  {
2159  Matrix children = go.get_properties ().get_all_children ();
2160 
2161  go.initialize ();
2162 
2163  for (int k = 0; k < children.numel (); k++)
2164  initialize_r (children(k));
2165  }
2166 }
2167 
2168 void
2169 figure::properties::set_toolkit (const octave::graphics_toolkit& b)
2170 {
2171  if (m_toolkit)
2172  finalize_r (get___myhandle__ ());
2173 
2174  m_toolkit = b;
2175  m___graphics_toolkit__ = b.get_name ();
2176  m___plot_stream__ = Matrix ();
2177 
2178  if (m_toolkit)
2179  initialize_r (get___myhandle__ ());
2180 
2181  mark_modified ();
2182 }
2183 
2184 void
2186 {
2187  std::string direction = "in";
2188 
2189  octave_value val = val_arg;
2190 
2191  if (val.is_string ())
2192  {
2193  std::string modestr = val.string_value ();
2194 
2195  if (modestr == "zoom in")
2196  {
2197  val = modestr = "zoom";
2198  direction = "in";
2199  }
2200  else if (modestr == "zoom out")
2201  {
2202  val = modestr = "zoom";
2203  direction = "out";
2204  }
2205 
2206  if (m___mouse_mode__.set (val, true))
2207  {
2208  std::string mode = m___mouse_mode__.current_value ();
2209 
2210  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
2211  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
2212  set___pan_mode__ (pm);
2213 
2214  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
2215  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
2216  set___rotate_mode__ (rm);
2217 
2218  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2219  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
2220  zm.setfield ("Direction", direction);
2221  set___zoom_mode__ (zm);
2222 
2223  mark_modified ();
2224  }
2225  else if (modestr == "zoom")
2226  {
2227  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
2228  std::string curr_direction
2229  = zm.getfield ("Direction").string_value ();
2230 
2231  if (direction != curr_direction)
2232  {
2233  zm.setfield ("Direction", direction);
2234  set___zoom_mode__ (zm);
2235 
2236  mark_modified ();
2237  }
2238  }
2239  }
2240 }
2241 
2242 void
2243 figure::properties::update_handlevisibility ()
2244 {
2245  if (! is_handle_visible ())
2246  {
2248 
2249  octave_value cf = gh_mgr.get_object (0).get ("currentfigure");
2250 
2251  if (! cf.isempty () && cf.double_value () == m___myhandle__)
2252  {
2253  octave::autolock guard (gh_mgr.graphics_lock ());
2254 
2255  octave_value kids = gh_mgr.get_object (0).get ("children");
2256 
2257  if (kids.isempty ())
2258  gh_mgr.get_object (0).set ("currentfigure", Matrix ());
2259  else
2260  {
2261  NDArray kidsarray = kids.array_value ();
2262  gh_mgr.get_object (0).set ("currentfigure", kidsarray(0));
2263  }
2264  }
2265  }
2266 
2268 }
2269 
2270 static void
2271 update_text_pos (graphics_handle h)
2272 {
2274 
2275  graphics_object go = gh_mgr.get_object (h);
2276 
2277  if (go.isa ("text"))
2278  {
2279  text::properties& tp
2280  = dynamic_cast<text::properties&> (go.get_properties ());
2281  tp.update_font ();
2282  tp.update_text_extent ();
2283  }
2284  else if (go.isa ("figure") || go.isa ("uipanel") || go.isa ("axes")
2285  || go.isa ("hggroup"))
2286  {
2287  Matrix ch = go.get_properties ().get_all_children ();
2288  for (octave_idx_type ii = 0; ii < ch.numel (); ii++)
2289  update_text_pos (graphics_handle (ch(ii)));
2290 
2291  if (go.isa ("axes"))
2292  {
2293  axes::properties& ap
2294  = dynamic_cast<axes::properties&> (go.get_properties ());
2295  ap.update_font ();
2296  ap.sync_positions ();
2297  }
2298  }
2299 }
2300 
2301 void
2303 {
2304  update_text_pos (get___myhandle__ ());
2305 }
2306 
2307 // ---------------------------------------------------------------------
2308 
2309 void
2311 {
2312  std::size_t offset = 0;
2313 
2314  std::size_t len = name.length ();
2315 
2316  if (len > 4)
2317  {
2318  caseless_str pfx = name.substr (0, 4);
2319 
2320  if (pfx.compare ("axes") || pfx.compare ("line")
2321  || pfx.compare ("text"))
2322  offset = 4;
2323  else if (len > 5)
2324  {
2325  pfx = name.substr (0, 5);
2326 
2327  if (pfx.compare ("image") || pfx.compare ("patch"))
2328  offset = 5;
2329  else if (len > 6)
2330  {
2331  pfx = name.substr (0, 6);
2332 
2333  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2334  offset = 6;
2335  else if (len > 7)
2336  {
2337  pfx = name.substr (0, 7);
2338 
2339  if (pfx.compare ("surface") || pfx.compare ("scatter")
2340  || pfx.compare ("hggroup")|| pfx.compare ("uipanel")
2341  || pfx.compare ("uitable"))
2342  offset = 7;
2343  else if (len > 9)
2344  {
2345  pfx = name.substr (0, 9);
2346 
2347  if (pfx.compare ("uicontrol")
2348  || pfx.compare ("uitoolbar"))
2349  offset = 9;
2350  else if (len > 10)
2351  {
2352  pfx = name.substr (0, 10);
2353 
2354  if (pfx.compare ("uipushtool"))
2355  offset = 10;
2356  else if (len > 12)
2357  {
2358  pfx = name.substr (0, 12);
2359 
2360  if (pfx.compare ("uitoogletool"))
2361  offset = 12;
2362  else if (len > 13)
2363  {
2364  pfx = name.substr (0, 13);
2365 
2366  if (pfx.compare ("uicontextmenu")
2367  || pfx.compare ("uibuttongroup"))
2368  offset = 13;
2369  }
2370  }
2371  }
2372  }
2373  }
2374  }
2375  }
2376 
2377  if (offset > 0)
2378  {
2379  // FIXME: should we validate property names and values here?
2380 
2381  std::string pname = name.substr (offset);
2382 
2383  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2384  std::transform (pname.begin (), pname.end (), pname.begin (),
2385  tolower);
2386 
2387  bool has_property = false;
2388  if (pfx == "axes")
2389  has_property = axes::properties::has_core_property (pname);
2390  else if (pfx == "figure")
2391  has_property = figure::properties::has_core_property (pname);
2392  else if (pfx == "line")
2393  has_property = line::properties::has_core_property (pname);
2394  else if (pfx == "text")
2395  has_property = text::properties::has_core_property (pname);
2396  else if (pfx == "image")
2397  has_property = image::properties::has_core_property (pname);
2398  else if (pfx == "patch")
2399  has_property = patch::properties::has_core_property (pname);
2400  else if (pfx == "scatter")
2401  has_property = scatter::properties::has_core_property (pname);
2402  else if (pfx == "surface")
2403  has_property = surface::properties::has_core_property (pname);
2404  else if (pfx == "hggroup")
2405  has_property = hggroup::properties::has_core_property (pname);
2406  else if (pfx == "uimenu")
2407  has_property = uimenu::properties::has_core_property (pname);
2408  else if (pfx == "uicontrol")
2409  has_property = uicontrol::properties::has_core_property (pname);
2410  else if (pfx == "uibuttongroup")
2411  has_property = uibuttongroup::properties::has_core_property (pname);
2412  else if (pfx == "uipanel")
2413  has_property = uipanel::properties::has_core_property (pname);
2414  else if (pfx == "uicontextmenu")
2415  has_property = uicontextmenu::properties::has_core_property (pname);
2416  else if (pfx == "uitable")
2417  has_property = uitable::properties::has_core_property (pname);
2418  else if (pfx == "uitoolbar")
2419  has_property = uitoolbar::properties::has_core_property (pname);
2420  else if (pfx == "uipushtool")
2421  has_property = uipushtool::properties::has_core_property (pname);
2422 
2423  if (! has_property)
2424  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2425 
2426  bool remove = false;
2427  if (val.is_string ())
2428  {
2429  std::string sval = val.string_value ();
2430 
2431  remove = (sval == "remove");
2432  }
2433 
2434  pval_map_type& pval_map = m_plist_map[pfx];
2435 
2436  if (remove)
2437  {
2438  auto p = pval_map.find (pname);
2439 
2440  if (p != pval_map.end ())
2441  pval_map.erase (p);
2442  }
2443  else
2444  pval_map[pname] = val;
2445  }
2446  }
2447 
2448  if (offset == 0)
2449  error ("invalid default property specification");
2450 }
2451 
2454 {
2455  octave_value retval;
2456 
2457  std::size_t offset = 0;
2458 
2459  std::size_t len = name.length ();
2460 
2461  if (len > 4)
2462  {
2463  caseless_str pfx = name.substr (0, 4);
2464 
2465  if (pfx.compare ("axes") || pfx.compare ("line")
2466  || pfx.compare ("text"))
2467  offset = 4;
2468  else if (len > 5)
2469  {
2470  pfx = name.substr (0, 5);
2471 
2472  if (pfx.compare ("image") || pfx.compare ("patch"))
2473  offset = 5;
2474  else if (len > 6)
2475  {
2476  pfx = name.substr (0, 6);
2477 
2478  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2479  offset = 6;
2480  else if (len > 7)
2481  {
2482  pfx = name.substr (0, 7);
2483 
2484  if (pfx.compare ("surface") || pfx.compare ("scatter")
2485  || pfx.compare ("hggroup") || pfx.compare ("uipanel")
2486  || pfx.compare ("uitable"))
2487  offset = 7;
2488  else if (len > 9)
2489  {
2490  pfx = name.substr (0, 9);
2491 
2492  if (pfx.compare ("uicontrol")
2493  || pfx.compare ("uitoolbar"))
2494  offset = 9;
2495  else if (len > 10)
2496  {
2497  pfx = name.substr (0, 10);
2498 
2499  if (pfx.compare ("uipushtool"))
2500  offset = 10;
2501  else if (len > 12)
2502  {
2503  pfx = name.substr (0, 12);
2504 
2505  if (pfx.compare ("uitoggletool"))
2506  offset = 12;
2507  else if (len > 13)
2508  {
2509  pfx = name.substr (0, 13);
2510 
2511  if (pfx.compare ("uicontextmenu")
2512  || pfx.compare ("uibuttongroup"))
2513  offset = 13;
2514  }
2515  }
2516  }
2517  }
2518  }
2519  }
2520  }
2521 
2522  if (offset > 0)
2523  {
2524  std::string pname = name.substr (offset);
2525 
2526  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2527  std::transform (pname.begin (), pname.end (), pname.begin (),
2528  tolower);
2529 
2530  plist_map_const_iterator p = find (pfx);
2531 
2532  if (p != end ())
2533  {
2534  const pval_map_type& pval_map = p->second;
2535 
2536  pval_map_const_iterator q = pval_map.find (pname);
2537 
2538  if (q != pval_map.end ())
2539  retval = q->second;
2540  }
2541  }
2542  }
2543 
2544  return retval;
2545 }
2546 
2548 property_list::as_struct (const std::string& prefix_arg) const
2549 {
2551 
2552  for (const auto& p : *this)
2553  {
2554  std::string prefix = prefix_arg + p.first;
2555 
2556  for (const auto& prop_val : p.second)
2557  m.assign (prefix + prop_val.first, prop_val.second);
2558  }
2559 
2560  return m;
2561 }
2562 
2563 // Set property given as either cs-list of name/value pairs or a struct.
2564 
2565 void
2567 {
2568  int nargin = args.length ();
2569 
2570  if (nargin == 0)
2571  error ("graphics_object::set: Nothing to set");
2572 
2573  for (int i = 0; i < nargin; )
2574  {
2575  if (args(i).isstruct ())
2576  {
2577  set (args(i).map_value ());
2578  i++;
2579  }
2580  else if (i < nargin - 1)
2581  {
2582  caseless_str pname = args(i).xstring_value ("set: argument %d must be a property name", i);
2583  octave_value val = args(i+1);
2584  set_value_or_default (pname, val);
2585  i += 2;
2586  }
2587  else
2588  error ("set: invalid number of arguments");
2589  }
2590 }
2591 
2592 /*
2593 ## test set with name, value pairs
2594 %!test
2595 %! hf = figure ("visible", "off");
2596 %! h = plot (1:10, 10:-1:1);
2597 %! set (h, "linewidth", 10, "marker", "x");
2598 %! lw = get (h, "linewidth");
2599 %! mk = get (h, "marker");
2600 %! close (hf);
2601 %! assert (lw, 10);
2602 %! assert (mk, "x");
2603 */
2604 
2605 // Set properties given in two cell arrays containing names and values.
2606 void
2608  const Cell& values, octave_idx_type row)
2609 {
2610  if (pnames.numel () != values.columns ())
2611  error ("set: number of names must match number of value columns "
2612  "(%" OCTAVE_IDX_TYPE_FORMAT " != %" OCTAVE_IDX_TYPE_FORMAT ")",
2613  pnames.numel (), values.columns ());
2614 
2615  octave_idx_type k = pnames.columns ();
2616 
2617  for (octave_idx_type column = 0; column < k; column++)
2618  {
2619  caseless_str pname = pnames(column);
2620  octave_value val = values(row, column);
2621 
2622  set_value_or_default (pname, val);
2623  }
2624 }
2625 
2626 /*
2627 ## test set with cell array arguments
2628 %!test
2629 %! hf = figure ("visible", "off");
2630 %! h = plot (1:10, 10:-1:1);
2631 %! set (h, {"linewidth", "marker"}, {10, "x"});
2632 %! lw = get (h, "linewidth");
2633 %! mk = get (h, "marker");
2634 %! close (hf);
2635 %! assert (lw, 10);
2636 %! assert (mk, "x");
2637 
2638 ## test set with multiple handles and cell array arguments
2639 %!test
2640 %! hf = figure ("visible", "off");
2641 %! unwind_protect
2642 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2643 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2644 %! assert (get (h, "linewidth"), {10; 5});
2645 %! assert (get (h, "marker"), {"x"; "o"});
2646 %! set (h, {"linewidth", "marker"}, {10, "x"});
2647 %! assert (get (h, "linewidth"), {10; 10});
2648 %! assert (get (h, "marker"), {"x"; "x"});
2649 %! unwind_protect_cleanup
2650 %! close (hf);
2651 %! end_unwind_protect
2652 
2653 %!error <set: number of graphics handles must match number of value rows>
2654 %! hf = figure ("visible", "off");
2655 %! unwind_protect
2656 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2657 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2658 %! unwind_protect_cleanup
2659 %! close (hf);
2660 %! end_unwind_protect
2661 
2662 %!error <set: number of names must match number of value columns>
2663 %! hf = figure ("visible", "off");
2664 %! unwind_protect
2665 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2666 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2667 %! unwind_protect_cleanup
2668 %! close (hf);
2669 %! end_unwind_protect
2670 */
2671 
2672 // Set properties given in a struct array
2673 void
2675 {
2676  for (octave_idx_type p = 0; p < m.nfields (); p++)
2677  {
2678  // FIXME: Would it be better to extract all the keys at once rather than
2679  // repeatedly call keys() inside a for loop?
2680  caseless_str pname = m.keys ()[p];
2681 
2682  octave_value val = octave_value (m.contents (pname).elem (m.numel () - 1));
2683 
2684  set_value_or_default (pname, val);
2685  }
2686 }
2687 
2688 /*
2689 ## test set with struct arguments
2690 %!test
2691 %! hf = figure ("visible", "off");
2692 %! unwind_protect
2693 %! h = plot (1:10, 10:-1:1);
2694 %! set (h, struct ("linewidth", 10, "marker", "x"));
2695 %! assert (get (h, "linewidth"), 10);
2696 %! assert (get (h, "marker"), "x");
2697 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2698 %! set (h, struct ("linewidth", {5, 10}));
2699 %! assert (get (h, "linewidth"), {10; 10});
2700 %! unwind_protect_cleanup
2701 %! close (hf);
2702 %! end_unwind_protect
2703 
2704 ## test ordering
2705 %!test
2706 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2707 %! hf = figure ("visible", "off");
2708 %! unwind_protect
2709 %! h = line ();
2710 %! set (h, "userdata", {});
2711 %! addlistener (h, "color", {markchanged, "color"});
2712 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2713 %! ## "linewidth" first
2714 %! props.linewidth = 2;
2715 %! props.color = "r";
2716 %! set (h, props);
2717 %! assert (get (h, "userdata"), fieldnames (props));
2718 %! clear props;
2719 %! clf ();
2720 %! h = line ();
2721 %! set (h, "userdata", {});
2722 %! addlistener (h, "color", {markchanged, "color"});
2723 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2724 %! ## "color" first
2725 %! props.color = "r";
2726 %! props.linewidth = 2;
2727 %! set (h, props);
2728 %! assert (get (h, "userdata"), fieldnames (props));
2729 %! unwind_protect_cleanup
2730 %! close (hf);
2731 %! end_unwind_protect
2732 */
2733 
2734 // Set a property to a value or to its (factory) default value.
2735 
2736 void
2738  const octave_value& val)
2739 {
2740  if (val.is_string () && val.rows () == 1)
2741  {
2742  std::string sval = val.string_value ();
2743 
2744  octave_value default_val;
2745 
2746  if (sval == "default")
2747  {
2748  default_val = get_default (pname);
2749 
2750  m_rep->set (pname, default_val);
2751  }
2752  else if (sval == "factory")
2753  {
2754  default_val = get_factory_default (pname);
2755 
2756  m_rep->set (pname, default_val);
2757  }
2758  else
2759  {
2760  // Matlab specifically uses "\default" to escape string setting
2761  if (sval == R"(\default)")
2762  m_rep->set (pname, "default");
2763  else if (sval == R"(\factory)")
2764  m_rep->set (pname, "factory");
2765  else
2766  m_rep->set (pname, val);
2767  }
2768  }
2769  else
2770  m_rep->set (pname, val);
2771 }
2772 
2773 /*
2774 ## test setting of default values
2775 %!test
2776 %! old_lw = get (0, "defaultlinelinewidth");
2777 %! unwind_protect
2778 %! hf = figure ("visible", "off");
2779 %! h = plot (1:10, 10:-1:1);
2780 %! set (0, "defaultlinelinewidth", 20);
2781 %! set (h, "linewidth", "default");
2782 %! assert (get (h, "linewidth"), 20);
2783 %! set (h, "linewidth", "factory");
2784 %! assert (get (h, "linewidth"), 0.5);
2785 %! unwind_protect_cleanup
2786 %! close (hf);
2787 %! set (0, "defaultlinelinewidth", old_lw);
2788 %! end_unwind_protect
2789 */
2790 
2791 // This function is NOT equivalent to the scripting language function gcf.
2794 {
2795  octave_value val = xget (0, "currentfigure");
2796 
2797  return val.isempty () ? octave::numeric_limits<double>::NaN ()
2798  : val.double_value ();
2799 }
2800 
2801 // This function is NOT equivalent to the scripting language function gca.
2804 {
2805  octave_value val = xget (gcf (), "currentaxes");
2806 
2807  return val.isempty () ? octave::numeric_limits<double>::NaN ()
2808  : val.double_value ();
2809 }
2810 
2811 static void
2812 adopt (const graphics_handle& parent_h, const graphics_handle& h)
2813 {
2815 
2816  graphics_object parent_go = gh_mgr.get_object (parent_h);
2817 
2818  parent_go.adopt (h);
2819 }
2820 
2821 static bool
2822 ishghandle (const graphics_handle& h)
2823 {
2824  return h.ok ();
2825 }
2826 
2827 static bool
2828 ishghandle (double val)
2829 {
2831 
2832  graphics_handle h = gh_mgr.lookup (val);
2833 
2834  return h.ok ();
2835 }
2836 
2837 static octave_value
2838 ishghandle (const octave_value& val)
2839 {
2840  octave_value retval = false;
2841 
2842  if (val.is_real_scalar () && ishghandle (val.double_value ()))
2843  retval = true;
2844  else if (val.isnumeric () && val.isreal ())
2845  {
2846  const NDArray handles = val.array_value ();
2847 
2848  boolNDArray result (handles.dims ());
2849 
2850  for (octave_idx_type i = 0; i < handles.numel (); i++)
2851  result.xelem (i) = ishghandle (handles(i));
2852 
2853  retval = result;
2854  }
2855 
2856  return retval;
2857 }
2858 
2859 static void
2860 xcreatefcn (const graphics_handle& h)
2861 {
2863 
2864  graphics_object go = gh_mgr.get_object (h);
2865 
2867 }
2868 
2869 static void
2870 xinitialize (const graphics_handle& h)
2871 {
2873 
2874  graphics_object go = gh_mgr.get_object (h);
2875 
2876  if (go)
2877  go.initialize ();
2878 }
2879 
2880 // ---------------------------------------------------------------------
2881 
2882 static int
2883 toggle_warn (std::string id, bool on, int state = -1)
2884 {
2885  if (! on)
2886  {
2887  state = octave::warning_enabled (id);
2889  }
2890  else
2891  {
2892  if (state == 1)
2893  octave::set_warning_state (id, "on");
2894  else if (state == 2)
2895  octave::set_warning_state (id, "error");
2896  }
2897  return state;
2898 }
2899 
2900 static void
2901 xreset_default_properties (graphics_handle h,
2902  property_list::pval_map_type factory_pval)
2903 {
2905 
2906  graphics_object go = gh_mgr.get_object (h);
2907 
2908  // Replace factory defaults by user defined ones
2909  std::string go_name = go.get_properties ().graphics_object_name ();
2911  go.build_user_defaults_map (pval, go_name);
2912 
2913  for (const auto& p : pval)
2914  factory_pval[p.first] = p.second;
2915 
2916  // Save warning state of "Octave:deprecated-property"
2917  int state = toggle_warn ("Octave:deprecated-property", false);
2918 
2919  // Reset defaults
2920  for (const auto& p : factory_pval)
2921  {
2922  std::string pname = p.first;
2923 
2924  // Don't reset internal properties and handle_properties
2925  if (! go.has_readonly_property (pname)
2926  && pname.find ("__") != 0 && pname.find ("current") != 0
2927  && pname != "uicontextmenu" && pname != "parent")
2928  {
2929  // Store *mode prop/val in order to set them last
2930  if (pname.find ("mode") == (pname.length () - 4))
2931  pval[pname] = p.second;
2932  else
2933  go.set (pname, p.second);
2934  }
2935  }
2936 
2937  // set *mode properties
2938  for (const auto& p : pval)
2939  go.set (p.first, p.second);
2940 
2941  toggle_warn ("Octave:deprecated-property", true, state);
2942 }
2943 
2944 // ---------------------------------------------------------------------
2945 
2946 void
2949 {
2950  std::string go_name = graphics_object_name ();
2951 
2952  property_list::plist_map_const_iterator plist = defaults.find (go_name);
2953 
2954  if (plist != defaults.end ())
2955  {
2956  const property_list::pval_map_type pval_map = plist->second;
2957 
2958  for (const auto& prop_val : pval_map)
2959  {
2960  std::string pname = prop_val.first;
2961 
2962  try
2963  {
2964  bgo.set (pname, prop_val.second);
2965  }
2966  catch (octave::execution_exception& ee)
2967  {
2968  error (ee, "error setting default property %s", pname.c_str ());
2969  }
2970  }
2971  }
2972 }
2973 
2974 /*
2975 ## test defaults are set in the order they were stored
2976 %!test
2977 %! set (0, "defaultfigureunits", "normalized");
2978 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
2979 %! hf = figure ("visible", "off");
2980 %! tol = 20 * eps;
2981 %! unwind_protect
2982 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
2983 %! unwind_protect_cleanup
2984 %! close (hf);
2985 %! set (0, "defaultfigureunits", "remove");
2986 %! set (0, "defaultfigureposition", "remove");
2987 %! end_unwind_protect
2988 */
2989 
2992 {
2993  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it
2994  = m_all_props.find (pname);
2995 
2996  if (it == m_all_props.end ())
2997  error (R"(get: unknown property "%s")", pname.c_str ());
2998 
2999  return it->second.get ();
3000 }
3001 
3004 {
3006 
3007  for (const auto& it : m_all_props)
3008  if (all || ! it.second.is_hidden ())
3009  m.assign (it.second.get_name (), it.second.get ());
3010 
3011  return m;
3012 }
3013 
3014 std::set<std::string>
3016 {
3017  return m_dynamic_properties;
3018 }
3019 
3020 bool
3021 base_properties::has_dynamic_property (const std::string& pname) const
3022 {
3023  const std::set<std::string>& dynprops = dynamic_property_names ();
3024 
3025  if (dynprops.find (pname) != dynprops.end ())
3026  return true;
3027  else
3028  return m_all_props.find (pname) != m_all_props.end ();
3029 }
3030 
3031 void
3033  const octave_value& val)
3034 {
3035  auto it = m_all_props.find (pname);
3036 
3037  if (it == m_all_props.end ())
3038  error (R"(set: unknown property "%s")", pname.c_str ());
3039 
3040  it->second.set (val);
3041 
3042  m_dynamic_properties.insert (pname);
3043 
3044  mark_modified ();
3045 }
3046 
3047 property
3049 {
3050  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it
3051  = m_all_props.find (pname);
3052 
3053  if (it == m_all_props.end ())
3054  error (R"(get_property: unknown property "%s")", pname.c_str ());
3055 
3056  return it->second;
3057 }
3058 
3059 void
3061 {
3062  double hp = val.xdouble_value ("set: parent must be a graphics handle");
3063  if (hp == m___myhandle__)
3064  error ("set: can not set object parent to be object itself");
3065 
3067 
3068  graphics_handle new_parent = gh_mgr.lookup (hp);
3069  if (! new_parent.ok ())
3070  error ("set: invalid graphics handle (= %g) for parent", hp);
3071 
3072  // Remove child from current parent
3073  graphics_object old_parent_go;
3074  old_parent_go = gh_mgr.get_object (get_parent ());
3075 
3076  if (old_parent_go.get_handle () != hp)
3077  old_parent_go.remove_child (m___myhandle__);
3078  else
3079  return; // Do nothing more
3080 
3081  // Check new parent's parent is not this child to avoid recursion
3082  graphics_object new_parent_go;
3083  new_parent_go = gh_mgr.get_object (new_parent);
3084  if (new_parent_go.get_parent () == m___myhandle__)
3085  {
3086  // new parent's parent gets child's original parent
3087  new_parent_go.get_properties ().set_parent (get_parent ().as_octave_value ());
3088  }
3089 
3090  // Set parent property to new_parent and do adoption
3091  m_parent = new_parent.as_octave_value ();
3092  octave::adopt (m_parent.handle_value (), m___myhandle__);
3093 }
3094 
3095 /*
3096 %!test
3097 %! hf = figure ("visible", "off");
3098 %! unwind_protect
3099 %! hax = gca ();
3100 %! set (hax, "parent", gcf ());
3101 %! assert (gca (), hax);
3102 %! unwind_protect_cleanup
3103 %! close (hf);
3104 %! end_unwind_protect
3105 */
3106 
3107 void
3109 {
3110  // Mark existing object as modified
3111  m___modified__ = "on";
3112 
3113  // Attempt to mark parent object as modified if it exists
3114 
3116 
3117  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3118 
3119  if (parent_go)
3120  parent_go.mark_modified ();
3121 }
3122 
3123 void
3125 {
3127 
3128  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3129 
3130  if (parent_go)
3131  parent_go.override_defaults (obj);
3132 }
3133 
3134 void
3135 base_properties::update_axis_limits (const std::string& axis_type) const
3136 {
3138 
3139  graphics_object go = gh_mgr.get_object (m___myhandle__);
3140 
3141  if (go)
3142  go.update_axis_limits (axis_type);
3143 }
3144 
3145 void
3146 base_properties::update_axis_limits (const std::string& axis_type,
3147  const graphics_handle& h) const
3148 {
3150 
3151  graphics_object go = gh_mgr.get_object (m___myhandle__);
3152 
3153  if (go)
3154  go.update_axis_limits (axis_type, h);
3155 }
3156 
3157 void
3159 {
3160  if (m_contextmenu.get ().isempty ())
3161  return;
3162 
3164 
3165  graphics_object go = gh_mgr.get_object (m_contextmenu.get ());
3166 
3167  if (go && go.isa ("uicontextmenu"))
3168  {
3170  = reinterpret_cast<uicontextmenu::properties&> (go.get_properties ());
3171  props.add_dependent_obj (m___myhandle__);
3172  }
3173 }
3174 
3175 bool
3177 {
3178  return (m_handlevisibility.is ("on")
3179  || (! executing_callbacks.empty () && ! m_handlevisibility.is ("off")));
3180 }
3181 
3182 octave::graphics_toolkit
3184 {
3186 
3187  graphics_object go = gh_mgr.get_object (get_parent ());
3188 
3189  if (go)
3190  return go.get_toolkit ();
3191  else
3192  return octave::graphics_toolkit ();
3193 }
3194 
3195 void
3197 {
3198  Matrix kids = get_children ();
3199 
3201 
3202  for (int i = 0; i < kids.numel (); i++)
3203  {
3204  graphics_object go = gh_mgr.get_object (kids(i));
3205 
3206  if (go.valid_object ())
3208  }
3209 }
3210 
3211 void
3212 base_properties::update_autopos (const std::string& elem_type)
3213 {
3215 
3216  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3217 
3218  if (parent_go.valid_object ())
3219  parent_go.get_properties ().update_autopos (elem_type);
3220 }
3221 
3222 void
3224 {
3225  if (is_handle_visible ())
3226  return;
3227 
3228  // This object should not be the figure "currentobject"
3229 
3231 
3232  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
3233 
3234  graphics_object fig (go.get_ancestor ("figure"));
3235 
3236  if (fig.valid_object ())
3237  {
3238  octave_value co = fig.get ("currentobject");
3239  if (! co.isempty () && co.double_value () == m___myhandle__)
3240  {
3241  octave::autolock guard (gh_mgr.graphics_lock ());
3242 
3243  auto& fig_props = dynamic_cast<figure::properties&> (fig.get_properties ());
3244  fig_props.set_currentobject (Matrix ());
3245  }
3246  }
3247 }
3248 
3249 /*
3250 ## test current figure and current axes have visible handles
3251 %!test
3252 %! hf1 = figure ("visible", "off");
3253 %! hf2 = figure ("visible", "off");
3254 %! hax1 = axes ();
3255 %! hax2 = axes ();
3256 %! unwind_protect
3257 %! assert (get (0, "currentfigure"), hf2);
3258 %! assert (get (hf2, "currentaxes"), hax2);
3259 %! set (hf2, "handlevisibility", "off");
3260 %! assert (get (0, "currentfigure"), hf1);
3261 %! set (hax2, "handlevisibility", "off");
3262 %! assert (get (hf2, "currentaxes"), hax1);
3263 %! assert (get (hf2, "currentobject"), []);
3264 %! unwind_protect_cleanup
3265 %! close ([hf1, hf2]);
3266 %! end_unwind_protect
3267 */
3268 
3269 /*
3270 ## test current callback object have visible handle
3271 %!test
3272 %! hf = figure ("handlevisibility", "off", "visible", "off");
3273 %! hax = axes ("parent", hf, "handlevisibility", "off");
3274 %! unwind_protect
3275 %! fcn = @(h, ~) setappdata (h, "testdata", gcbo ());
3276 %! addlistener (hf, "color", fcn);
3277 %! addlistener (hax, "color", fcn);
3278 %! set (hf, "color", "b");
3279 %! set (hax, "color", "b");
3280 %! assert (getappdata (hf, "testdata"), hf)
3281 %! assert (getappdata (hax, "testdata"), hax)
3282 %! unwind_protect_cleanup
3283 %! close (hf);
3284 %! end_unwind_protect
3285 */
3286 
3287 void
3289  const octave_value& val,
3290  listener_mode mode)
3291 {
3292  property p = get_property (pname);
3293 
3294  if (p.ok ())
3295  p.add_listener (val, mode);
3296 }
3297 
3298 void
3300  const octave_value& val,
3301  listener_mode mode)
3302 {
3303  property p = get_property (pname);
3304 
3305  if (p.ok ())
3306  p.delete_listener (val, mode);
3307 }
3308 
3309 void
3311  bool get_invisible,
3312  bool traverse,
3313  std::list<graphics_object>& children_list) const
3314 {
3316 
3317  Matrix ch = get_children ();
3318 
3319  for (octave_idx_type i = 0; i < ch.numel (); i++)
3320  {
3321  graphics_handle hkid = gh_mgr.lookup (ch(i));
3322 
3323  if (hkid.ok ())
3324  {
3325  graphics_object go = gh_mgr.get_object (hkid);
3326  if ( get_invisible || go.get_properties ().is_visible () )
3327  {
3328  if (go.isa (chtype))
3329  children_list.push_back (go);
3330  else if (traverse && go.isa ("hggroup"))
3331  go.get_properties ().get_children_of_type (chtype,
3332  get_invisible,
3333  traverse,
3334  children_list);
3335  }
3336  }
3337  }
3338 }
3339 
3340 // ---------------------------------------------------------------------
3341 
3342 void
3343 base_graphics_object::update_axis_limits (const std::string& axis_type)
3344 {
3345  if (! valid_object ())
3346  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3347 
3349 
3350  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3351 
3352  if (parent_go)
3353  parent_go.update_axis_limits (axis_type);
3354 }
3355 
3356 void
3357 base_graphics_object::update_axis_limits (const std::string& axis_type,
3358  const graphics_handle& h)
3359 {
3360  if (! valid_object ())
3361  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3362 
3364 
3365  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3366 
3367  if (parent_go)
3368  parent_go.update_axis_limits (axis_type, h);
3369 }
3370 
3371 void
3373 {
3374  int state = toggle_warn ("Octave:deprecated-property", false);
3375  octave_map m = get (true).map_value ();
3376  toggle_warn ("Octave:deprecated-property", true, state);
3377 
3378  for (const auto& pm : m)
3379  {
3380  // FIXME: there has to be a better way. I think we want to
3381  // ask whether it is OK to delete the listener for the given
3382  // property. How can we know in advance that it will be OK?
3383 
3384  octave::unwind_protect frame;
3385 
3386  octave::interpreter_try (frame);
3387 
3388  try
3389  {
3390  property p = get_properties ().get_property (pm.first);
3391 
3392  if (p.ok ())
3393  p.delete_listener ();
3394  }
3395  catch (const octave::execution_exception&)
3396  {
3397  octave::interpreter& interp = octave::__get_interpreter__ ();
3398 
3399  interp.recover_from_exception ();
3400  }
3401  }
3402 }
3403 
3404 void
3406  const std::string go_name) const
3407 {
3408  property_list local_defaults = get_defaults_list ();
3409  const auto it = local_defaults.find (go_name);
3410 
3411  if (it != local_defaults.end ())
3412  {
3413  property_list::pval_map_type pval_lst = it->second;
3414  for (const auto& prop_val : pval_lst)
3415  {
3416  std::string pname = prop_val.first;
3417  if (def.find (pname) == def.end ())
3418  def[pname] = prop_val.second;
3419  }
3420  }
3421 
3423 
3424  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3425 
3426  if (parent_go)
3427  parent_go.build_user_defaults_map (def, go_name);
3428 }
3429 
3430 void
3432 {
3433  if (valid_object ())
3434  {
3436 
3437  property_list::pval_map_type factory_pval
3438  = gh_mgr.get_object (0).get_factory_defaults_list ().find (type ())->second;
3439 
3441  xreset_default_properties (get_handle (), factory_pval);
3442  }
3443 }
3444 
3445 std::string
3447 {
3448  if (! valid_object ())
3449  error ("base_graphics_object::values_as_string: invalid graphics object");
3450 
3451  std::string retval;
3452  octave_map m = get ().map_value ();
3453 
3455 
3456  graphics_object go = gh_mgr.get_object (get_handle ());
3457 
3458  for (const auto& pm : m)
3459  {
3460  const auto& pname = pm.first;
3461  if (pname != "children" && ! go.has_readonly_property (pname))
3462  {
3463  property p = get_properties ().get_property (pname);
3464 
3465  if (p.ok () && ! p.is_hidden ())
3466  {
3467  retval += "\n\t" + std::string (pname) + ": ";
3468  if (p.is_radio ())
3469  retval += p.values_as_string ();
3470  }
3471  }
3472  }
3473 
3474  if (! retval.empty ())
3475  retval += '\n';
3476 
3477  return retval;
3478 }
3479 
3480 std::string
3481 base_graphics_object::value_as_string (const std::string& prop)
3482 {
3483  std::string retval;
3484 
3485  if (! valid_object ())
3486  error ("base_graphics_object::value_as_string: invalid graphics object");
3487 
3489 
3490  graphics_object go = gh_mgr.get_object (get_handle ());
3491 
3492  if (prop != "children" && ! go.has_readonly_property (prop))
3493  {
3494  property p = get_properties ().get_property (prop);
3495 
3496  if (p.ok () && ! p.is_hidden ())
3497  {
3498  if (p.is_radio ())
3499  retval += p.values_as_string ();
3500  }
3501  }
3502 
3503  if (! retval.empty ())
3504  retval += '\n';
3505 
3506  return retval;
3507 }
3508 
3511 {
3512  octave_scalar_map retval;
3513 
3514  if (! valid_object ())
3515  error ("base_graphics_object::values_as_struct: invalid graphics object");
3516 
3518 
3520 
3521  graphics_object go = gh_mgr.get_object (get_handle ());
3522 
3523  for (const auto& pm : m)
3524  {
3525  const auto& pname = pm.first;
3526  if (pname != "children" && ! go.has_readonly_property (pname))
3527  {
3528  property p = get_properties ().get_property (pname);
3529 
3530  if (p.ok () && ! p.is_hidden ())
3531  {
3532  if (p.is_radio ())
3533  retval.assign (p.get_name (), p.values_as_cell ());
3534  else
3535  retval.assign (p.get_name (), Cell ());
3536  }
3537  }
3538  }
3539 
3540  return retval;
3541 }
3542 
3543 /*
3544 %!test
3545 %! hfig = figure ("visible", "off");
3546 %! unwind_protect
3547 %! hax = axes ();
3548 %! ret = set (hax, "tightinset");
3549 %! assert (isempty (ret));
3550 %! ret = set (hax, "type");
3551 %! assert (isempty (ret));
3552 %! ret = set (hfig, "tag");
3553 %! assert (isempty (ret));
3554 %! ret = set (0, "commandwindowsize");
3555 %! assert (isempty (ret));
3556 %! ret = set (0);
3557 %! assert (! isfield (ret, "commandwindowsize"));
3558 %! unwind_protect_cleanup
3559 %! close (hfig);
3560 %! end_unwind_protect
3561 */
3562 
3564 graphics_object::get_ancestor (const std::string& obj_type) const
3565 {
3566  if (valid_object ())
3567  {
3568  if (isa (obj_type))
3569  return *this;
3570  else
3571  {
3573 
3574  return gh_mgr.get_object (get_parent ()).get_ancestor (obj_type);
3575  }
3576  }
3577  else
3578  return graphics_object ();
3579 }
3580 
3581 // ---------------------------------------------------------------------
3582 
3583 #include "graphics-props.cc"
3584 
3585 // ---------------------------------------------------------------------
3586 
3587 void
3589 {
3590  graphics_handle val (v);
3591 
3592  if (octave::math::isnan (val.value ()))
3593  m_callbackobject = graphics_handle ();
3594  else if (ishghandle (val))
3595  m_callbackobject = val;
3596  else
3597  err_set_invalid ("callbackobject");
3598 }
3599 
3600 void
3602 {
3603  graphics_handle val (v);
3604 
3605  if (octave::math::isnan (val.value ()) || ishghandle (val))
3606  {
3607  m_currentfigure = val;
3608 
3609  if (val.ok ())
3610  {
3612 
3613  gh_mgr.push_figure (val);
3614  }
3615  }
3616  else
3617  err_set_invalid ("currentfigure");
3618 }
3619 
3620 void
3622 {
3623  if (m_integerhandle.set (val, true))
3624  {
3625  bool int_fig_handle = m_integerhandle.is_on ();
3626 
3628 
3629  graphics_object this_go = gh_mgr.get_object (m___myhandle__);
3630 
3631  graphics_handle old_myhandle = m___myhandle__;
3632 
3633  m___myhandle__ = gh_mgr.get_handle (int_fig_handle);
3634 
3635  gh_mgr.renumber_figure (old_myhandle, m___myhandle__);
3636 
3637  graphics_object parent_go = gh_mgr.get_object (get_parent ());
3638 
3639  base_properties& props = parent_go.get_properties ();
3640 
3641  props.renumber_child (old_myhandle, m___myhandle__);
3642 
3643  Matrix kids = get_children ();
3644 
3645  for (octave_idx_type i = 0; i < kids.numel (); i++)
3646  {
3647  graphics_object kid = gh_mgr.get_object (kids(i));
3648 
3649  kid.get_properties ().renumber_parent (m___myhandle__);
3650  }
3651 
3652  graphics_handle cf = gh_mgr.current_figure ();
3653 
3654  if (m___myhandle__ == cf)
3655  xset (0, "currentfigure", m___myhandle__.value ());
3656 
3657  this_go.update (m_integerhandle.get_id ());
3658 
3659  mark_modified ();
3660  }
3661 }
3662 
3663 // FIXME: This should update monitorpositions and pointerlocation, but as these
3664 // properties aren't yet used, it doesn't matter that they aren't set either.
3665 void
3667 {
3668  std::string xunits = get_units ();
3669 
3670  Matrix scrn_sz = default_screensize ();
3671 
3672  double dpi = get_screenpixelsperinch ();
3673 
3674  if (xunits == "pixels")
3675  {
3676  // Most common case (default).
3677  // Don't need to convert anything, but short-circuit if/else tree.
3678  }
3679  else if (xunits == "normalized")
3680  {
3681  scrn_sz = Matrix (1, 4, 1.0);
3682  scrn_sz(0) = 0;
3683  scrn_sz(1) = 0;
3684  }
3685  else if (xunits == "inches")
3686  {
3687  scrn_sz(0) = 0;
3688  scrn_sz(1) = 0;
3689  scrn_sz(2) /= dpi;
3690  scrn_sz(3) /= dpi;
3691  }
3692  else if (xunits == "centimeters")
3693  {
3694  scrn_sz(0) = 0;
3695  scrn_sz(1) = 0;
3696  scrn_sz(2) *= 2.54 / dpi;
3697  scrn_sz(3) *= 2.54 / dpi;
3698  }
3699  else if (xunits == "points")
3700  {
3701  scrn_sz(0) = 0;
3702  scrn_sz(1) = 0;
3703  scrn_sz(2) *= 72 / dpi;
3704  scrn_sz(3) *= 72 / dpi;
3705  }
3706  else if (xunits == "characters")
3707  {
3708  scrn_sz(0) = 0;
3709  scrn_sz(1) = 0;
3710  // FIXME: this assumes the system font is Helvetica 10pt
3711  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
3712  scrn_sz(2) *= 74.951 / 12.0 / dpi;
3713  scrn_sz(3) *= 74.951 / 12.0 / dpi;
3714  }
3715 
3716  set_screensize (scrn_sz);
3717 }
3718 
3719 Matrix
3721 {
3722  Matrix screen_size = screen_size_pixels ();
3723  Matrix pos = Matrix (1, 4, 0.0);
3724 
3725  pos(2) = screen_size(0);
3726  pos(3) = screen_size(1);
3727 
3728  return pos;
3729 }
3730 
3731 /*
3732 %!test
3733 %! old_units = get (0, "units");
3734 %! unwind_protect
3735 %! set (0, "units", "pixels");
3736 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3737 %! dpi = get (0, "screenpixelsperinch");
3738 %! set (0, "units", "inches");
3739 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3740 %! set (0, "units", "centimeters");
3741 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3742 %! set (0, "units", "points");
3743 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3744 %! set (0, "units", "normalized");
3745 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3746 %! set (0, "units", "pixels");
3747 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3748 %! set (0, "units", "characters");
3749 %! assert (get (0, "screensize"),
3750 %! sz / dpi * (74.951 / 12.0), 0.5 / dpi * (74.951 / 12.0));
3751 %! unwind_protect_cleanup
3752 %! set (0, "units", old_units);
3753 %! end_unwind_protect
3754 */
3755 
3756 void
3758 {
3760 
3761  gh_mgr.pop_figure (h);
3762 
3763  graphics_handle cf = gh_mgr.current_figure ();
3764 
3765  xset (0, "currentfigure", cf.value ());
3766 
3768 }
3769 
3770 void
3772 {
3773  // empty list of local defaults
3774  m_default_properties = property_list ();
3775 
3777  xreset_default_properties (get_handle (),
3778  m_properties.factory_defaults ());
3779 }
3780 
3781 // ---------------------------------------------------------------------
3782 
3783 void
3785 {
3786  graphics_handle hax (val);
3787 
3788  if (octave::math::isnan (hax.value ()) || ishghandle (hax))
3789  m_currentaxes = hax;
3790  else
3791  err_set_invalid ("currentaxes");
3792 }
3793 
3794 void
3796 {
3797  base_properties::remove_child (h, from_root);
3798 
3799  if (h == m_currentaxes.handle_value ())
3800  {
3801  graphics_handle new_currentaxes;
3802 
3803  Matrix kids = get_children ();
3804 
3806 
3807  for (octave_idx_type i = 0; i < kids.numel (); i++)
3808  {
3809  graphics_handle kid = kids(i);
3810 
3811  graphics_object go = gh_mgr.get_object (kid);
3812 
3813  if (go.isa ("axes"))
3814  {
3815  new_currentaxes = kid;
3816  break;
3817  }
3818  }
3819 
3820  m_currentaxes = new_currentaxes;
3821  }
3822 }
3823 
3826 {
3827  if (m_integerhandle.is_on ())
3828  return m___myhandle__.value ();
3829  else
3830  return Matrix ();
3831 }
3832 
3833 octave::graphics_toolkit
3835 {
3836  return m_toolkit;
3837 }
3838 
3839 void
3841 {
3842  if (! val.is_string ())
3843  error ("set___graphics_toolkit__: toolkit must be a string");
3844 
3845  std::string nm = val.string_value ();
3846 
3847  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
3848 
3849  octave::graphics_toolkit b = gtk_mgr.find_toolkit (nm);
3850 
3851  if (b.get_name () != nm)
3852  error ("set___graphics_toolkit__: invalid graphics toolkit");
3853 
3854  if (nm != get___graphics_toolkit__ ())
3855  {
3856  set_toolkit (b);
3857  mark_modified ();
3858  }
3859 }
3860 
3861 void
3863 {
3865 
3866  if (! get_currentaxes ().ok ())
3867  {
3869 
3870  graphics_object go = gh_mgr.get_object (h);
3871 
3872  if (go.type () == "axes")
3873  set_currentaxes (h.as_octave_value ());
3874  }
3875 }
3876 
3877 /*
3878 %!test
3879 %! hf1 = figure ("visible", "off");
3880 %! ax1 = subplot (1,2,1);
3881 %! ax2 = subplot (1,2,2);
3882 %! hf2 = figure ("visible", "off");
3883 %! unwind_protect
3884 %! set (ax2, "parent", hf2);
3885 %! assert (get (hf2, "currentaxes"), ax2);
3886 %! assert (get (hf1, "currentaxes"), ax1);
3887 %! set (ax1, "parent", hf2);
3888 %! assert (get (hf2, "currentaxes"), ax2);
3889 %! unwind_protect_cleanup
3890 %! close (hf1);
3891 %! close (hf2);
3892 %! end_unwind_protect
3893 */
3894 
3895 void
3897 {
3898  std::string sval = val.string_value ();
3899 
3900  if (sval == "on")
3901  xset (0, "currentfigure", m___myhandle__.value ());
3902 
3903  m_visible = val;
3904 }
3905 
3906 Matrix
3907 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3908 {
3909  Matrix screen_size = screen_size_pixels ();
3910  Matrix pos = (internal ?
3911  get_position ().matrix_value () :
3912  get_outerposition ().matrix_value ());
3913 
3914  pos = convert_position (pos, get_units (), "pixels", screen_size);
3915 
3916  pos(0)--;
3917  pos(1)--;
3918  pos(1) = screen_size(1) - pos(1) - pos(3);
3919 
3920  return pos;
3921 }
3922 
3923 Matrix
3925 {
3926  Matrix screen_size = screen_size_pixels ();
3927  Matrix pos = bb;
3928 
3929  pos(1) = screen_size(1) - pos(1) - pos(3);
3930  pos(1)++;
3931  pos(0)++;
3932  pos = convert_position (pos, "pixels", get_units (), screen_size);
3933  return pos;
3934 }
3935 
3936 void
3938  bool do_notify_toolkit)
3939 {
3940  Matrix screen_size = screen_size_pixels ();
3941  Matrix pos = bbox2position (bb);
3942 
3943  if (internal)
3944  set_position (pos, do_notify_toolkit);
3945  else
3946  set_outerposition (pos, do_notify_toolkit);
3947 }
3948 
3949 Matrix
3951 {
3952  Matrix bb = get_boundingbox (true);
3953  Matrix pos (1, 2, 0.0);
3954 
3955  pos(0) = x;
3956  pos(1) = y;
3957 
3958  pos(1) = bb(3) - pos(1);
3959  pos(0)++;
3960  pos = convert_position (pos, "pixels", get_units (),
3961  bb.extract_n (0, 2, 1, 2));
3962 
3963  return pos;
3964 }
3965 
3966 Matrix
3968 {
3969  Matrix bb = get_boundingbox (true);
3970  Matrix pos (1, 2, 0.0);
3971 
3972  pos(0) = x;
3973  pos(1) = y;
3974 
3975  pos = convert_position (pos, get_units (), "pixels",
3976  bb.extract_n (0, 2, 1, 2));
3977  pos(0)--;
3978  pos(1) = bb(3) - pos(1);
3979 
3980  return pos;
3981 }
3982 
3983 void
3985  bool do_notify_toolkit)
3986 {
3987  Matrix old_bb, new_bb;
3988  bool modified = false;
3989 
3990  old_bb = get_boundingbox (true);
3991  modified = m_position.set (v, false, do_notify_toolkit);
3992  new_bb = get_boundingbox (true);
3993 
3994  if (old_bb != new_bb)
3995  {
3996  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
3997  {
3999 
4000  if (! get_resizefcn ().isempty ())
4001  gh_mgr.post_callback (m___myhandle__, "resizefcn");
4002 
4003  if (! get_sizechangedfcn ().isempty ())
4004  gh_mgr.post_callback (m___myhandle__, "sizechangedfcn");
4005 
4006  update_boundingbox ();
4007  }
4008  }
4009 
4010  if (modified)
4011  {
4012  m_position.run_listeners (GCB_POSTSET);
4013  mark_modified ();
4014  }
4015 
4016  if (m_paperpositionmode.is ("auto"))
4017  m_paperposition.set (get_auto_paperposition ());
4018 }
4019 
4020 void
4022  bool do_notify_toolkit)
4023 {
4024  if (m_outerposition.set (v, true, do_notify_toolkit))
4025  mark_modified ();
4026 }
4027 
4028 void
4030 {
4031  caseless_str punits = val.string_value ();
4032  caseless_str ptype = get_papertype ();
4033 
4034  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4035  error ("set: can't set paperunits to normalized when papertype is custom");
4036 
4037  caseless_str old_paperunits = get_paperunits ();
4038  if (m_paperunits.set (val, true))
4039  {
4040  update_paperunits (old_paperunits);
4041  mark_modified ();
4042  }
4043 }
4044 
4045 void
4047 {
4048  caseless_str ptype = val.string_value ();
4049  caseless_str punits = get_paperunits ();
4050 
4051  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
4052  error ("set: can't set paperunits to normalized when papertype is custom");
4053 
4054  if (m_papertype.set (val, true))
4055  {
4056  update_papertype ();
4057  mark_modified ();
4058  }
4059 }
4060 
4061 static Matrix
4062 papersize_from_type (const caseless_str punits, const caseless_str ptype)
4063 {
4064  Matrix retval (1, 2, 1.0);
4065 
4066  if (! punits.compare ("normalized"))
4067  {
4068  double in2units;
4069  double mm2units;
4070 
4071  if (punits.compare ("inches"))
4072  {
4073  in2units = 1.0;
4074  mm2units = 1 / 25.4;
4075  }
4076  else if (punits.compare ("centimeters"))
4077  {
4078  in2units = 2.54;
4079  mm2units = 1 / 10.0;
4080  }
4081  else // points
4082  {
4083  in2units = 72.0;
4084  mm2units = 72.0 / 25.4;
4085  }
4086 
4087  if (ptype.compare ("usletter"))
4088  {
4089  retval(0) = 8.5 * in2units;
4090  retval(1) = 11.0 * in2units;
4091  }
4092  else if (ptype.compare ("uslegal"))
4093  {
4094  retval(0) = 8.5 * in2units;
4095  retval(1) = 14.0 * in2units;
4096  }
4097  else if (ptype.compare ("tabloid"))
4098  {
4099  retval(0) = 11.0 * in2units;
4100  retval(1) = 17.0 * in2units;
4101  }
4102  else if (ptype.compare ("a0"))
4103  {
4104  retval(0) = 841.0 * mm2units;
4105  retval(1) = 1189.0 * mm2units;
4106  }
4107  else if (ptype.compare ("a1"))
4108  {
4109  retval(0) = 594.0 * mm2units;
4110  retval(1) = 841.0 * mm2units;
4111  }
4112  else if (ptype.compare ("a2"))
4113  {
4114  retval(0) = 420.0 * mm2units;
4115  retval(1) = 594.0 * mm2units;
4116  }
4117  else if (ptype.compare ("a3"))
4118  {
4119  retval(0) = 297.0 * mm2units;
4120  retval(1) = 420.0 * mm2units;
4121  }
4122  else if (ptype.compare ("a4"))
4123  {
4124  retval(0) = 210.0 * mm2units;
4125  retval(1) = 297.0 * mm2units;
4126  }
4127  else if (ptype.compare ("a5"))
4128  {
4129  retval(0) = 148.0 * mm2units;
4130  retval(1) = 210.0 * mm2units;
4131  }
4132  else if (ptype.compare ("b0"))
4133  {
4134  retval(0) = 1029.0 * mm2units;
4135  retval(1) = 1456.0 * mm2units;
4136  }
4137  else if (ptype.compare ("b1"))
4138  {
4139  retval(0) = 728.0 * mm2units;
4140  retval(1) = 1028.0 * mm2units;
4141  }
4142  else if (ptype.compare ("b2"))
4143  {
4144  retval(0) = 514.0 * mm2units;
4145  retval(1) = 728.0 * mm2units;
4146  }
4147  else if (ptype.compare ("b3"))
4148  {
4149  retval(0) = 364.0 * mm2units;
4150  retval(1) = 514.0 * mm2units;
4151  }
4152  else if (ptype.compare ("b4"))
4153  {
4154  retval(0) = 257.0 * mm2units;
4155  retval(1) = 364.0 * mm2units;
4156  }
4157  else if (ptype.compare ("b5"))
4158  {
4159  retval(0) = 182.0 * mm2units;
4160  retval(1) = 257.0 * mm2units;
4161  }
4162  else if (ptype.compare ("arch-a"))
4163  {
4164  retval(0) = 9.0 * in2units;
4165  retval(1) = 12.0 * in2units;
4166  }
4167  else if (ptype.compare ("arch-b"))
4168  {
4169  retval(0) = 12.0 * in2units;
4170  retval(1) = 18.0 * in2units;
4171  }
4172  else if (ptype.compare ("arch-c"))
4173  {
4174  retval(0) = 18.0 * in2units;
4175  retval(1) = 24.0 * in2units;
4176  }
4177  else if (ptype.compare ("arch-d"))
4178  {
4179  retval(0) = 24.0 * in2units;
4180  retval(1) = 36.0 * in2units;
4181  }
4182  else if (ptype.compare ("arch-e"))
4183  {
4184  retval(0) = 36.0 * in2units;
4185  retval(1) = 48.0 * in2units;
4186  }
4187  else if (ptype.compare ("a"))
4188  {
4189  retval(0) = 8.5 * in2units;
4190  retval(1) = 11.0 * in2units;
4191  }
4192  else if (ptype.compare ("b"))
4193  {
4194  retval(0) = 11.0 * in2units;
4195  retval(1) = 17.0 * in2units;
4196  }
4197  else if (ptype.compare ("c"))
4198  {
4199  retval(0) = 17.0 * in2units;
4200  retval(1) = 22.0 * in2units;
4201  }
4202  else if (ptype.compare ("d"))
4203  {
4204  retval(0) = 22.0 * in2units;
4205  retval(1) = 34.0 * in2units;
4206  }
4207  else if (ptype.compare ("e"))
4208  {
4209  retval(0) = 34.0 * in2units;
4210  retval(1) = 43.0 * in2units;
4211  }
4212  }
4213 
4214  return retval;
4215 }
4216 
4217 Matrix
4218 figure::properties::get_auto_paperposition ()
4219 {
4220  Matrix pos = get_position ().matrix_value ();
4221  Matrix sz;
4222 
4223  caseless_str funits = get_units ();
4224  caseless_str punits = get_paperunits ();
4225 
4226  // Convert position from figure units to paperunits
4227  if (funits == "normalized" || punits == "normalized")
4228  {
4229  sz = screen_size_pixels ();
4230  pos = convert_position (pos, funits, "inches", sz);
4231 
4232  if (punits == "normalized")
4233  sz = papersize_from_type ("points", get_papertype ());
4234 
4235  pos = convert_position (pos, "inches", punits, sz);
4236  }
4237  else
4238  pos = convert_position (pos, funits, punits, sz);
4239 
4240  // Center the figure on the page
4241  sz = get_papersize ().matrix_value ();
4242 
4243  pos(0) = sz(0)/2 - pos(2)/2;
4244  pos(1) = sz(1)/2 - pos(3)/2;
4245 
4246  return pos;
4247 }
4248 
4249 /*
4250 %!test
4251 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4252 %! in_pos = [0 0 4 5];
4253 %! tol = 20 * eps ();
4254 %! unwind_protect
4255 %! ## paperpositionmode "auto" converts figure size to paper units
4256 %! set (hf, "units", "inches");
4257 %! set (hf, "position", in_pos);
4258 %! set (hf, "paperunits", "centimeters");
4259 %! psz = get (hf, "papersize");
4260 %! fsz = in_pos(3:4) * 2.54;
4261 %! pos = [(psz/2 - fsz/2) fsz];
4262 %! set (hf, "paperpositionmode", "auto");
4263 %! assert (get (hf, "paperposition"), pos, tol);
4264 %! unwind_protect_cleanup
4265 %! close (hf);
4266 %! end_unwind_protect
4267 
4268 %!test
4269 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4270 %! in_pos = [0 0 4 5];
4271 %! tol = 20 * eps ();
4272 %! unwind_protect
4273 %! ## likewise with normalized units
4274 %! set (hf, "units", "inches");
4275 %! set (hf, "position", in_pos);
4276 %! psz = get (hf, "papersize");
4277 %! set (hf, "paperunits", "normalized");
4278 %! fsz = in_pos(3:4) ./ psz;
4279 %! pos = [([0.5 0.5] - fsz/2) fsz];
4280 %! assert (get (hf, "paperposition"), pos, tol);
4281 %! unwind_protect_cleanup
4282 %! close (hf);
4283 %! end_unwind_protect
4284 
4285 %!test
4286 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4287 %! in_pos = [0 0 4 5];
4288 %! tol = 20 * eps ();
4289 %! unwind_protect
4290 %! ## changing papertype updates paperposition
4291 %! set (hf, "units", "inches");
4292 %! set (hf, "position", in_pos);
4293 %! set (hf, "papertype", "a4");
4294 %! psz = get (hf, "papersize");
4295 %! fsz = in_pos(3:4);
4296 %! pos = [(psz/2 - fsz/2) fsz];
4297 %! assert (get (hf, "paperposition"), pos, tol);
4298 %! unwind_protect_cleanup
4299 %! close (hf);
4300 %! end_unwind_protect
4301 
4302 %!test
4303 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4304 %! in_pos = [0 0 4 5];
4305 %! tol = 20 * eps ();
4306 %! unwind_protect
4307 %! ## lanscape updates paperposition
4308 %! set (hf, "units", "inches");
4309 %! set (hf, "position", in_pos);
4310 %! set (hf, "paperorientation", "landscape");
4311 %! psz = get (hf, "papersize");
4312 %! fsz = in_pos(3:4);
4313 %! pos = [(psz/2 - fsz/2) fsz];
4314 %! assert (get (hf, "paperposition"), pos, tol);
4315 %! unwind_protect_cleanup
4316 %! close (hf);
4317 %! end_unwind_protect
4318 
4319 %!test
4320 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4321 %! in_pos = [0 0 4 5];
4322 %! unwind_protect
4323 %! ## back to manual mode
4324 %! set (hf, "paperposition", in_pos * 1.1);
4325 %! assert (get (hf, "paperpositionmode"), "manual");
4326 %! assert (get (hf, "paperposition"), in_pos * 1.1);
4327 %! unwind_protect_cleanup
4328 %! close (hf);
4329 %! end_unwind_protect
4330 */
4331 
4332 void
4334 {
4335  Matrix pos = get_paperposition ().matrix_value ();
4336  Matrix sz = get_papersize ().matrix_value ();
4337 
4338  pos(0) /= sz(0);
4339  pos(1) /= sz(1);
4340  pos(2) /= sz(0);
4341  pos(3) /= sz(1);
4342 
4343  std::string porient = get_paperorientation ();
4344  caseless_str punits = get_paperunits ();
4345  caseless_str ptype = get_papertype ();
4346 
4347  if (ptype.compare ("<custom>"))
4348  {
4349  if (old_paperunits.compare ("centimeters"))
4350  {
4351  sz(0) /= 2.54;
4352  sz(1) /= 2.54;
4353  }
4354  else if (old_paperunits.compare ("points"))
4355  {
4356  sz(0) /= 72.0;
4357  sz(1) /= 72.0;
4358  }
4359 
4360  if (punits.compare ("centimeters"))
4361  {
4362  sz(0) *= 2.54;
4363  sz(1) *= 2.54;
4364  }
4365  else if (punits.compare ("points"))
4366  {
4367  sz(0) *= 72.0;
4368  sz(1) *= 72.0;
4369  }
4370  }
4371  else
4372  {
4373  sz = papersize_from_type (punits, ptype);
4374  if (porient == "landscape")
4375  std::swap (sz(0), sz(1));
4376  }
4377 
4378  pos(0) *= sz(0);
4379  pos(1) *= sz(1);
4380  pos(2) *= sz(0);
4381  pos(3) *= sz(1);
4382 
4383  m_papersize.set (octave_value (sz));
4384  m_paperposition.set (octave_value (pos));
4385 }
4386 
4387 void
4389 {
4390  std::string typ = get_papertype ();
4391  if (typ != "<custom>")
4392  {
4393  Matrix sz = papersize_from_type (get_paperunits (), typ);
4394  if (get_paperorientation () == "landscape")
4395  std::swap (sz(0), sz(1));
4396  // Call papersize.set rather than set_papersize to avoid loops
4397  // between update_papersize and update_papertype.
4398  m_papersize.set (octave_value (sz));
4399  }
4400 
4401  if (m_paperpositionmode.is ("auto"))
4402  m_paperposition.set (get_auto_paperposition ());
4403 }
4404 
4405 void
4407 {
4408  Matrix sz = get_papersize ().matrix_value ();
4409  if (sz(0) > sz(1))
4410  {
4411  std::swap (sz(0), sz(1));
4412  m_papersize.set (octave_value (sz));
4413  m_paperorientation.set (octave_value ("landscape"));
4414  }
4415  else
4416  {
4417  m_paperorientation.set ("portrait");
4418  }
4419 
4420  std::string punits = get_paperunits ();
4421  if (punits == "centimeters")
4422  {
4423  sz(0) /= 2.54;
4424  sz(1) /= 2.54;
4425  }
4426  else if (punits == "points")
4427  {
4428  sz(0) /= 72.0;
4429  sz(1) /= 72.0;
4430  }
4431  if (punits == "normalized")
4432  {
4433  if (get_papertype () == "<custom>")
4434  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4435  }
4436  else
4437  {
4438  // FIXME: The papersizes info is also in papersize_from_type().
4439  // Both should be rewritten to avoid the duplication.
4440  // Don't Repeat Yourself (DRY) principle.
4441  std::string ptype = "<custom>";
4442  const double mm2in = 1.0 / 25.4;
4443  const double tol = 0.01;
4444 
4445  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4446  ptype = "usletter";
4447  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4448  ptype = "uslegal";
4449  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4450  ptype = "tabloid";
4451  else if (std::abs (sz(0) - 841.0 * mm2in)
4452  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4453  ptype = "a0";
4454  else if (std::abs (sz(0) - 594.0 * mm2in)
4455  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4456  ptype = "a1";
4457  else if (std::abs (sz(0) - 420.0 * mm2in)
4458  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4459  ptype = "a2";
4460  else if (std::abs (sz(0) - 297.0 * mm2in)
4461  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4462  ptype = "a3";
4463  else if (std::abs (sz(0) - 210.0 * mm2in)
4464  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4465  ptype = "a4";
4466  else if (std::abs (sz(0) - 148.0 * mm2in)
4467  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4468  ptype = "a5";
4469  else if (std::abs (sz(0) - 1029.0 * mm2in)
4470  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4471  ptype = "b0";
4472  else if (std::abs (sz(0) - 728.0 * mm2in)
4473  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4474  ptype = "b1";
4475  else if (std::abs (sz(0) - 514.0 * mm2in)
4476  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4477  ptype = "b2";
4478  else if (std::abs (sz(0) - 364.0 * mm2in)
4479  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4480  ptype = "b3";
4481  else if (std::abs (sz(0) - 257.0 * mm2in)
4482  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4483  ptype = "b4";
4484  else if (std::abs (sz(0) - 182.0 * mm2in)
4485  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4486  ptype = "b5";
4487  else if (std::abs (sz(0) - 9.0)
4488  + std::abs (sz(1) - 12.0) < tol)
4489  ptype = "arch-a";
4490  else if (std::abs (sz(0) - 12.0)
4491  + std::abs (sz(1) - 18.0) < tol)
4492  ptype = "arch-b";
4493  else if (std::abs (sz(0) - 18.0)
4494  + std::abs (sz(1) - 24.0) < tol)
4495  ptype = "arch-c";
4496  else if (std::abs (sz(0) - 24.0)
4497  + std::abs (sz(1) - 36.0) < tol)
4498  ptype = "arch-d";
4499  else if (std::abs (sz(0) - 36.0)
4500  + std::abs (sz(1) - 48.0) < tol)
4501  ptype = "arch-e";
4502  else if (std::abs (sz(0) - 8.5)
4503  + std::abs (sz(1) - 11.0) < tol)
4504  ptype = "a";
4505  else if (std::abs (sz(0) - 11.0)
4506  + std::abs (sz(1) - 17.0) < tol)
4507  ptype = "b";
4508  else if (std::abs (sz(0) - 17.0)
4509  + std::abs (sz(1) - 22.0) < tol)
4510  ptype = "c";
4511  else if (std::abs (sz(0) - 22.0)
4512  + std::abs (sz(1) - 34.0) < tol)
4513  ptype = "d";
4514  else if (std::abs (sz(0) - 34.0)
4515  + std::abs (sz(1) - 43.0) < tol)
4516  ptype = "e";
4517  // Call papertype.set rather than set_papertype to avoid loops between
4518  // update_papersize and update_papertype
4519  m_papertype.set (ptype);
4520  }
4521  if (punits == "centimeters")
4522  {
4523  sz(0) *= 2.54;
4524  sz(1) *= 2.54;
4525  }
4526  else if (punits == "points")
4527  {
4528  sz(0) *= 72.0;
4529  sz(1) *= 72.0;
4530  }
4531  if (get_paperorientation () == "landscape")
4532  {
4533  std::swap (sz(0), sz(1));
4534  m_papersize.set (octave_value (sz));
4535  }
4536 
4537  if (m_paperpositionmode.is ("auto"))
4538  m_paperposition.set (get_auto_paperposition ());
4539 }
4540 
4541 /*
4542 %!test
4543 %! hf = figure ("visible", "off");
4544 %! unwind_protect
4545 %! set (hf, "paperunits", "inches");
4546 %! set (hf, "papersize", [5, 4]);
4547 %! set (hf, "paperunits", "points");
4548 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4549 %! papersize = get (hf, "papersize");
4550 %! set (hf, "papersize", papersize + 1);
4551 %! set (hf, "papersize", papersize);
4552 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4553 %! unwind_protect_cleanup
4554 %! close (hf);
4555 %! end_unwind_protect
4556 
4557 %!test
4558 %! hf = figure ("visible", "off");
4559 %! unwind_protect
4560 %! set (hf, "paperunits", "inches");
4561 %! set (hf, "papersize", [5, 4]);
4562 %! set (hf, "paperunits", "centimeters");
4563 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4564 %! papersize = get (hf, "papersize");
4565 %! set (hf, "papersize", papersize + 1);
4566 %! set (hf, "papersize", papersize);
4567 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4568 %! unwind_protect_cleanup
4569 %! close (hf);
4570 %! end_unwind_protect
4571 */
4572 
4573 void
4575 {
4576  std::string porient = get_paperorientation ();
4577  Matrix sz = get_papersize ().matrix_value ();
4578  if ((sz(0) > sz(1) && porient == "portrait")
4579  || (sz(0) < sz(1) && porient == "landscape"))
4580  {
4581  std::swap (sz(0), sz(1));
4582  // Call papertype.set rather than set_papertype to avoid loops
4583  // between update_papersize and update_papertype
4584  m_papersize.set (octave_value (sz));
4585  }
4586 
4587  if (m_paperpositionmode.is ("auto"))
4588  m_paperposition.set (get_auto_paperposition ());
4589 }
4590 
4591 /*
4592 %!test
4593 %! hf = figure ("visible", "off");
4594 %! unwind_protect
4595 %! tol = 100 * eps ();
4596 %! ## UPPER case and MiXed case is part of test and should not be changed.
4597 %! set (hf, "paperorientation", "PORTRAIT");
4598 %! set (hf, "paperunits", "inches");
4599 %! set (hf, "papertype", "USletter");
4600 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4601 %! set (hf, "paperorientation", "Landscape");
4602 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4603 %! set (hf, "paperunits", "centimeters");
4604 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4605 %! set (hf, "papertype", "a4");
4606 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4607 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4608 %! assert (get (hf, "papertype"), "usletter");
4609 %! assert (get (hf, "paperorientation"), "portrait");
4610 %! set (hf, "papersize", [11.0, 8.5]);
4611 %! assert (get (hf, "papertype"), "usletter");
4612 %! assert (get (hf, "paperorientation"), "landscape");
4613 %! unwind_protect_cleanup
4614 %! close (hf);
4615 %! end_unwind_protect
4616 */
4617 
4618 void
4620 {
4621  caseless_str old_units = get_units ();
4622 
4623  if (m_units.set (val, true))
4624  {
4625  update_units (old_units);
4626  mark_modified ();
4627  }
4628 }
4629 
4630 void
4632 {
4633  m_position.set (convert_position (get_position ().matrix_value (),
4634  old_units, get_units (),
4635  screen_size_pixels ()), false);
4636 }
4637 
4638 /*
4639 %!test
4640 %! hf = figure ("visible", "off");
4641 %! old_units = get (0, "units");
4642 %! unwind_protect
4643 %! set (0, "units", "pixels");
4644 %! rsz = get (0, "screensize");
4645 %! set (gcf (), "units", "pixels");
4646 %! fsz = get (gcf (), "position");
4647 %! set (gcf (), "units", "normalized");
4648 %! pos = get (gcf (), "position");
4649 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
4650 %! unwind_protect_cleanup
4651 %! close (hf);
4652 %! set (0, "units", old_units);
4653 %! end_unwind_protect
4654 */
4655 
4656 std::string
4658 {
4659  std::string title;
4660  if (! get_number ().isempty () && is_numbertitle ())
4661  {
4662  std::ostringstream os;
4663  std::string nm = get_name ();
4664 
4665  os << "Figure " << m___myhandle__.value ();
4666  if (! nm.empty ())
4667  os << ": " << get_name ();
4668 
4669  title = os.str ();
4670  }
4671  else
4672  title = get_name ();
4673 
4674  // Qt will use QCoreApplication name (set in main-window.cc)
4675  // if the name is empty, so force blank.
4676  if (title.empty ())
4677  title = " ";
4678 
4679  return title;
4680 }
4681 
4684 {
4685  octave_value retval = m_default_properties.lookup (name);
4686 
4687  if (retval.is_undefined ())
4688  {
4689  graphics_handle parent_h = get_parent ();
4690 
4692 
4693  graphics_object parent_go = gh_mgr.get_object (parent_h);
4694 
4695  retval = parent_go.get_default (name);
4696  }
4697 
4698  return retval;
4699 }
4700 
4701 void
4703 {
4704  // empty list of local defaults
4705  m_default_properties = property_list ();
4706  property_list::pval_map_type plist = m_properties.factory_defaults ();
4707 
4708  plist.erase ("units");
4709  plist.erase ("position");
4710  plist.erase ("outerposition");
4711  plist.erase ("paperunits");
4712  plist.erase ("paperposition");
4713  plist.erase ("windowstyle");
4714 
4716  xreset_default_properties (get_handle (), plist);
4717 }
4718 
4719 // ---------------------------------------------------------------------
4720 
4721 void
4723 {
4724  m_position.add_constraint (dim_vector (1, 4));
4725  m_outerposition.add_constraint (dim_vector (1, 4));
4726  m_tightinset.add_constraint (dim_vector (1, 4));
4727  m_looseinset.add_constraint (dim_vector (1, 4));
4728  m_colororder.add_constraint (dim_vector (-1, 3));
4729  m_dataaspectratio.add_constraint (3);
4730  m_dataaspectratio.add_constraint ("min", 0, false);
4731  m_dataaspectratio.add_constraint (FINITE);
4732  m_plotboxaspectratio.add_constraint (3);
4733  m_plotboxaspectratio.add_constraint ("min", 0, false);
4734  m_plotboxaspectratio.add_constraint (FINITE);
4735  // FIXME: Should these use dimension vectors? Currently can set 'xlim' to
4736  // any matrix size, but only first two elements are used.
4737  m_alim.add_constraint (2);
4738  m_alim.add_constraint (NOT_NAN);
4739  m_clim.add_constraint (2);
4740  m_clim.add_constraint (NOT_NAN);
4741  m_xlim.add_constraint (2);
4742  m_xlim.add_constraint (NOT_NAN);
4743  m_ylim.add_constraint (2);
4744  m_ylim.add_constraint (NOT_NAN);
4745  m_zlim.add_constraint (2);
4746  m_zlim.add_constraint (NOT_NAN);
4747  m_xtick.add_constraint (dim_vector (1, -1));
4748  m_xtick.add_constraint (FINITE);
4749  m_ytick.add_constraint (dim_vector (1, -1));
4750  m_ytick.add_constraint (FINITE);
4751  m_ztick.add_constraint (dim_vector (1, -1));
4752  m_ztick.add_constraint (FINITE);
4753  m_ticklength.add_constraint (dim_vector (1, 2));
4754  Matrix vw (1, 2, 0);
4755  vw(1) = 90;
4756  m_view = vw;
4757  m_view.add_constraint (dim_vector (1, 2));
4758  m_cameraposition.add_constraint (3);
4759  m_cameraposition.add_constraint (FINITE);
4760  m_cameratarget.add_constraint (3);
4761  m_cameratarget.add_constraint (FINITE);
4762  Matrix upv (1, 3, 0.0);
4763  upv(2) = 1.0;
4764  m_cameraupvector = upv;
4765  m_cameraupvector.add_constraint (3);
4766  m_cameraupvector.add_constraint (FINITE);
4767  m_cameraviewangle.add_constraint (FINITE);
4768  m_currentpoint.add_constraint (dim_vector (2, 3));
4769 
4770  // Range constraints for double properties
4771  m_fontsize.add_constraint ("min", 0.0, false);
4772  m_gridalpha.add_constraint ("min", 0.0, true);
4773  m_gridalpha.add_constraint ("max", 1.0, true);
4774  m_labelfontsizemultiplier.add_constraint ("min", 0.0, false);
4775  m_linewidth.add_constraint ("min", 0.0, false);
4776  m_minorgridalpha.add_constraint ("min", 0.0, true);
4777  m_minorgridalpha.add_constraint ("max", 1.0, true);
4778  m_titlefontsizemultiplier.add_constraint ("min", 0.0, false);
4779 
4780  // No constraints for hidden transform properties
4781  update_font ();
4782 
4783  m_x_zlim.resize (1, 2);
4784 
4785  m_sx = "linear";
4786  m_sy = "linear";
4787  m_sz = "linear";
4788 
4789  calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
4790  xaxislocation_is ("origin"),
4791  m_yscale.is ("log") ? 2 : (yaxislocation_is ("origin") ? 0 :
4792  (yaxislocation_is ("left") ? -1 : 1)), m_xlim);
4793  calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
4794  yaxislocation_is ("origin"),
4795  m_xscale.is ("log") ? 2 : (xaxislocation_is ("origin") ? 0 :
4796  (xaxislocation_is ("bottom") ? -1 : 1)), m_ylim);
4797  calc_ticklabels (m_ztick, m_zticklabel, m_zscale.is ("log"),
4798  false, 2, m_zlim);
4799 
4800  xset (m_xlabel.handle_value (), "handlevisibility", "off");
4801  xset (m_ylabel.handle_value (), "handlevisibility", "off");
4802  xset (m_zlabel.handle_value (), "handlevisibility", "off");
4803  xset (m_title.handle_value (), "handlevisibility", "off");
4804 
4805  xset (m_xlabel.handle_value (), "horizontalalignment", "center");
4806  xset (m_xlabel.handle_value (), "horizontalalignmentmode", "auto");
4807  xset (m_ylabel.handle_value (), "horizontalalignment", "center");
4808  xset (m_ylabel.handle_value (), "horizontalalignmentmode", "auto");
4809  xset (m_zlabel.handle_value (), "horizontalalignment", "right");
4810  xset (m_zlabel.handle_value (), "horizontalalignmentmode", "auto");
4811  xset (m_title.handle_value (), "horizontalalignment", "center");
4812  xset (m_title.handle_value (), "horizontalalignmentmode", "auto");
4813 
4814  xset (m_xlabel.handle_value (), "verticalalignment", "top");
4815  xset (m_xlabel.handle_value (), "verticalalignmentmode", "auto");
4816  xset (m_ylabel.handle_value (), "verticalalignment", "bottom");
4817  xset (m_ylabel.handle_value (), "verticalalignmentmode", "auto");
4818  xset (m_title.handle_value (), "verticalalignment", "bottom");
4819  xset (m_title.handle_value (), "verticalalignmentmode", "auto");
4820 
4821  xset (m_ylabel.handle_value (), "rotation", 90.0);
4822  xset (m_ylabel.handle_value (), "rotationmode", "auto");
4823 
4824  xset (m_zlabel.handle_value (), "visible", "off");
4825 
4826  xset (m_xlabel.handle_value (), "clipping", "off");
4827  xset (m_ylabel.handle_value (), "clipping", "off");
4828  xset (m_zlabel.handle_value (), "clipping", "off");
4829  xset (m_title.handle_value (), "clipping", "off");
4830 
4831  xset (m_xlabel.handle_value (), "__autopos_tag__", "xlabel");
4832  xset (m_ylabel.handle_value (), "__autopos_tag__", "ylabel");
4833  xset (m_zlabel.handle_value (), "__autopos_tag__", "zlabel");
4834  xset (m_title.handle_value (), "__autopos_tag__", "title");
4835 
4836  double fs = m_labelfontsizemultiplier.double_value () *
4837  m_fontsize.double_value ();
4838  xset (m_xlabel.handle_value (), "fontsize", octave_value (fs));
4839  xset (m_ylabel.handle_value (), "fontsize", octave_value (fs));
4840  xset (m_zlabel.handle_value (), "fontsize", octave_value (fs));
4841  fs = m_titlefontsizemultiplier.double_value () * m_fontsize.double_value ();
4842  xset (m_title.handle_value (), "fontsize", octave_value (fs));
4843  xset (m_title.handle_value (), "fontweight", m_titlefontweight.get ());
4844 
4845  adopt (m_xlabel.handle_value ());
4846  adopt (m_ylabel.handle_value ());
4847  adopt (m_zlabel.handle_value ());
4848  adopt (m_title.handle_value ());
4849 
4850  Matrix tlooseinset = default_axes_position ();
4851  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4852  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4853  m_looseinset = tlooseinset;
4854 }
4855 
4856 /*
4857 ## Test validation of axes double properties range
4858 %!test
4859 %! hf = figure ("visible", "off");
4860 %! unwind_protect
4861 %! hax = axes ("parent", hf);
4862 %! try
4863 %! set (hax, "linewidth", -1);
4864 %! catch
4865 %! err = lasterr ();
4866 %! end_try_catch
4867 %! assert (err, 'set: "linewidth" must be greater than 0');
4868 %! try
4869 %! set (hax, "minorgridalpha", 1.5);
4870 %! catch
4871 %! err = lasterr ();
4872 %! end_try_catch
4873 %! assert (err, 'set: "minorgridalpha" must be less than or equal to 1');
4874 %! unwind_protect_cleanup
4875 %! delete (hf);
4876 %! end_unwind_protect
4877 */
4878 
4879 Matrix
4880 axes::properties::calc_tightbox (const Matrix& init_pos)
4881 {
4882  Matrix pos = init_pos;
4883 
4885 
4886  graphics_object go = gh_mgr.get_object (get_parent ());
4887 
4888  Matrix parent_bb = go.get_properties ().get_boundingbox (true);
4889 
4890  // FIXME: The layout should be clean at this stage and we should not have to
4891  // update ticks and labels positions here again. See bug #48718.
4892  update_ticklength ();
4893 
4894  Matrix ext = get_extent (true, true);
4895  ext(1) = parent_bb(3) - ext(1) - ext(3);
4896  ext(0)++;
4897  ext(1)++;
4898  ext = convert_position (ext, "pixels", get_units (),
4899  parent_bb.extract_n (0, 2, 1, 2));
4900  if (ext(0) < pos(0))
4901  {
4902  pos(2) += pos(0)-ext(0);
4903  pos(0) = ext(0);
4904  }
4905  if (ext(0)+ext(2) > pos(0)+pos(2))
4906  pos(2) = ext(0)+ext(2)-pos(0);
4907 
4908  if (ext(1) < pos(1))
4909  {
4910  pos(3) += pos(1)-ext(1);
4911  pos(1) = ext(1);
4912  }
4913  if (ext(1)+ext(3) > pos(1)+pos(3))
4914  pos(3) = ext(1)+ext(3)-pos(1);
4915 
4916  return pos;
4917 }
4918 
4919 void
4921 {
4922  // First part is equivalent to 'update_tightinset ()'
4923  if (m_positionconstraint.is ("innerposition"))
4924  update_position ();
4925  else
4926  update_outerposition ();
4927  caseless_str old_units = get_units ();
4928  set_units ("normalized");
4929  Matrix pos = m_position.get ().matrix_value ();
4930  Matrix outpos = m_outerposition.get ().matrix_value ();
4931  Matrix tightpos = calc_tightbox (pos);
4932  Matrix tinset (1, 4, 1.0);
4933  tinset(0) = pos(0)-tightpos(0);
4934  tinset(1) = pos(1)-tightpos(1);
4935  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4936  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4937  m_tightinset = tinset;
4938  set_units (old_units);
4939  update_transform ();
4940  if (m_positionconstraint.is ("innerposition"))
4941  update_position ();
4942  else
4943  update_outerposition ();
4944 }
4945 
4946 /*
4947 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
4948 %! hf = figure ("visible", "off");
4949 %! graphics_toolkit (hf, "qt");
4950 %! unwind_protect
4951 %! subplot (2,1,1); plot (rand (10,1)); subplot (2,1,2); plot (rand (10,1));
4952 %! hax = findall (gcf (), "type", "axes");
4953 %! positions = cell2mat (get (hax, "position"));
4954 %! outerpositions = cell2mat (get (hax, "outerposition"));
4955 %! looseinsets = cell2mat (get (hax, "looseinset"));
4956 %! tightinsets = cell2mat (get (hax, "tightinset"));
4957 %! subplot (2,1,1); plot (rand (10,1)); subplot (2,1,2); plot (rand (10,1));
4958 %! hax = findall (gcf (), "type", "axes");
4959 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4960 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4961 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4962 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4963 %! unwind_protect_cleanup
4964 %! close (hf);
4965 %! end_unwind_protect
4966 
4967 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
4968 %! hf = figure ("visible", "off");
4969 %! graphics_toolkit (hf, "qt");
4970 %! fpos = get (hf, "position");
4971 %! unwind_protect
4972 %! plot (rand (3));
4973 %! position = get (gca, "position");
4974 %! outerposition = get (gca, "outerposition");
4975 %! looseinset = get (gca, "looseinset");
4976 %! tightinset = get (gca, "tightinset");
4977 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4978 %! set (hf, "position", fpos);
4979 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4980 %! assert (get (gca, "position"), position, 0.001);
4981 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4982 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4983 %! unwind_protect_cleanup
4984 %! close (hf);
4985 %! end_unwind_protect
4986 
4987 %!testif HAVE_OPENGL, HAVE_QT; have_window_system () && any (strcmp ("qt", available_graphics_toolkits ()))
4988 %! hf = figure ("visible", "off");
4989 %! graphics_toolkit (hf, "qt");
4990 %! fpos = get (hf, "position");
4991 %! set (gca, "positionconstraint", "innerposition");
4992 %! unwind_protect
4993 %! plot (rand (3));
4994 %! position = get (gca, "position");
4995 %! outerposition = get (gca, "outerposition");
4996 %! looseinset = get (gca, "looseinset");
4997 %! tightinset = get (gca, "tightinset");
4998 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4999 %! set (hf, "position", fpos);
5000 %! assert (get (gca, "position"), position, 0.001);
5001 %! assert (get (gca, "outerposition"), outerposition, 0.001);
5002 %! assert (get (gca, "looseinset"), looseinset, 0.001);
5003 %! assert (get (gca, "tightinset"), tightinset, 0.001);
5004 %! unwind_protect_cleanup
5005 %! close (hf);
5006 %! end_unwind_protect
5007 */
5008 
5009 void
5010 axes::properties::set_text_child (handle_property& hp,
5011  const std::string& who,
5012  const octave_value& v)
5013 {
5014  if (v.is_string ())
5015  {
5016  xset (hp.handle_value (), "string", v);
5017  return;
5018  }
5019 
5020  graphics_handle val;
5021 
5023 
5024  graphics_object go = gh_mgr.get_object (gh_mgr.lookup (v));
5025 
5026  if (go.isa ("text"))
5027  val = octave::reparent (v, "set", who, m___myhandle__, false);
5028  else
5029  {
5030  std::string cname = v.class_name ();
5031 
5032  error ("set: expecting text graphics object or character string for %s property, found %s",
5033  who.c_str (), cname.c_str ());
5034  }
5035 
5036  xset (val, "handlevisibility", "off");
5037 
5038  gh_mgr.free (hp.handle_value ());
5039 
5040  hp = val;
5041 
5042  adopt (hp.handle_value ());
5043 }
5044 
5045 void
5047 {
5048  set_text_child (m_xlabel, "xlabel", v);
5049  xset (m_xlabel.handle_value (), "positionmode", "auto");
5050  xset (m_xlabel.handle_value (), "rotationmode", "auto");
5051  xset (m_xlabel.handle_value (), "horizontalalignmentmode", "auto");
5052  xset (m_xlabel.handle_value (), "verticalalignmentmode", "auto");
5053  xset (m_xlabel.handle_value (), "clipping", "off");
5054  xset (m_xlabel.handle_value (), "color", get_xcolor ());
5055  xset (m_xlabel.handle_value (), "__autopos_tag__", "xlabel");
5056  update_xlabel_position ();
5057 }
5058 
5059 void
5061 {
5062  set_text_child (m_ylabel, "ylabel", v);
5063  xset (m_ylabel.handle_value (), "positionmode", "auto");
5064  xset (m_ylabel.handle_value (), "rotationmode", "auto");
5065  xset (m_ylabel.handle_value (), "horizontalalignmentmode", "auto");
5066  xset (m_ylabel.handle_value (), "verticalalignmentmode", "auto");
5067  xset (m_ylabel.handle_value (), "clipping", "off");
5068  xset (m_ylabel.handle_value (), "color", get_ycolor ());
5069  xset (m_ylabel.handle_value (), "__autopos_tag__", "ylabel");
5070  update_ylabel_position ();
5071 }
5072 
5073 void
5075 {
5076  set_text_child (m_zlabel, "zlabel", v);
5077  xset (m_zlabel.handle_value (), "positionmode", "auto");
5078  xset (m_zlabel.handle_value (), "rotationmode", "auto");
5079  xset (m_zlabel.handle_value (), "horizontalalignmentmode", "auto");
5080  xset (m_zlabel.handle_value (), "verticalalignmentmode", "auto");
5081  xset (m_zlabel.handle_value (), "clipping", "off");
5082  xset (m_zlabel.handle_value (), "color", get_zcolor ());
5083  xset (m_zlabel.handle_value (), "__autopos_tag__", "zlabel");
5084  update_zlabel_position ();
5085 }
5086 
5087 void
5089 {
5090  set_text_child (m_title, "title", v);
5091  xset (m_title.handle_value (), "positionmode", "auto");
5092  xset (m_title.handle_value (), "horizontalalignment", "center");
5093  xset (m_title.handle_value (), "horizontalalignmentmode", "auto");
5094  xset (m_title.handle_value (), "verticalalignment", "bottom");
5095  xset (m_title.handle_value (), "verticalalignmentmode", "auto");
5096  xset (m_title.handle_value (), "clipping", "off");
5097  xset (m_title.handle_value (), "__autopos_tag__", "title");
5098  update_title_position ();
5099 }
5100 
5101 void
5103  const std::string& mode)
5104 {
5105  // FIXME: Should this have all properties in it?
5106  // Including ones we do don't implement?
5107 
5108  // FIXME: This function is probably never called without mode == "reset".
5109  // Verify this is the case with error_unless() (1/6/2017).
5110  // If there are reports of problems then figure out what code is
5111  // calling it with the mode set to something else.
5112  error_unless (mode == "reset");
5113 
5114  Matrix tlim (1, 2, 0.0);
5115  tlim(1) = 1;
5116  m_alim = tlim;
5117  m_clim = tlim;
5118  m_xlim = tlim;
5119  m_ylim = tlim;
5120  m_zlim = tlim;
5121 
5122  m_alimmode = "auto";
5123  m_climmode = "auto";
5124  m_xlimmode = "auto";
5125  m_ylimmode = "auto";
5126  m_zlimmode = "auto";
5127 
5128  m_alphamap = Matrix ();
5129  m_alphascale = "linear";
5130 
5131  m_ambientlightcolor = Matrix (1, 3, 1.0);
5132 
5133  m_box = "off";
5134  m_boxstyle = "back";
5135 
5136  // Note: camera properties (not mode) will be set in update_transform
5137  m_camerapositionmode = "auto";
5138  m_cameratargetmode = "auto";
5139  m_cameraupvectormode = "auto";
5140  m_cameraviewanglemode = "auto";
5141 
5142  m_clippingstyle = "3dbox";
5143 
5144  m_color = color_values ("white");
5145  m_colormap = Matrix ();
5146  m_colororder = default_colororder ();
5147  m_colororderindex = 1.0;
5148  m_colorscale = "linear";
5149 
5150  // Note: dataspectratio (not mode) will be set through update_aspectratios
5151  m_dataaspectratiomode = "auto";
5152 
5153  m_fontangle = "normal";
5154  m_fontname = OCTAVE_DEFAULT_FONTNAME;
5155  m_fontsize = 10;
5156  m_fontsizemode = "auto";
5157  m_fontsmoothing = "on";
5158  m_fontunits = "points";
5159  m_fontweight = "normal";
5160 
5161  m_gridalpha = 0.15;
5162  m_gridalphamode = "auto";
5163  m_gridcolor = color_values (0.15, 0.15, 0.15);
5164  m_gridcolormode = "auto";
5165  m_gridlinestyle = "-";
5166 
5167  m_labelfontsizemultiplier = 1.1;
5168 
5169  m_layer = "bottom";
5170 
5171  m_linestyleorder = "-";
5172  m_linestyleorderindex = 1.0;
5173 
5174  m_linewidth = 0.5;
5175 
5176  m_minorgridalpha = 0.25;
5177  m_minorgridalphamode = "auto";
5178  m_minorgridcolor = color_values (0.1, 0.1, 0.1);
5179  m_minorgridcolormode = "auto";
5180  m_minorgridlinestyle = ":";
5181 
5182  m_nextplot = "replace";
5183 
5184  // Note: plotboxaspectratio will be set through update_aspectratios
5185  m_plotboxaspectratiomode = "auto";
5186  m_projection = "orthographic";
5187 
5188  m_sortmethod = "depth";
5189 
5190  m_tickdir = "in";
5191  m_tickdirmode = "auto";
5192  m_ticklabelinterpreter = "tex";
5193  m_ticklength = default_axes_ticklength ();
5194 
5195  m_tightinset = Matrix (1, 4, 0.0);
5196 
5197  m_titlefontsizemultiplier = 1.1;
5198  m_titlefontweight = "bold";
5199 
5200  Matrix tview (1, 2, 0.0);
5201  tview(1) = 90;
5202  m_view = tview;
5203 
5204  m_xaxislocation = "bottom";
5205 
5206  m_xcolor = color_values (0.15, 0.15, 0.15);
5207  m_xcolormode = "auto";
5208  m_xdir = "normal";
5209  m_xgrid = "off";
5210  m_xlimitmethod = "tickaligned";
5211  m_xminorgrid = "off";
5212  m_xminortick = "off";
5213  m_xscale = "linear";
5214  m_xtick = Matrix ();
5215  m_xticklabel = "";
5216  m_xticklabelmode = "auto";
5217  m_xticklabelrotation = 0.0;
5218  m_xtickmode = "auto";
5219 
5220  m_yaxislocation = "left";
5221 
5222  m_ycolor = color_values (0.15, 0.15, 0.15);
5223  m_ycolormode = "auto";
5224  m_ydir = "normal";
5225  m_ygrid = "off";
5226  m_ylimitmethod = "tickaligned";
5227  m_yminorgrid = "off";
5228  m_yminortick = "off";
5229  m_yscale = "linear";
5230  m_ytick = Matrix ();
5231  m_yticklabel = "";
5232  m_yticklabelmode = "auto";
5233  m_yticklabelrotation = 0.0;
5234  m_ytickmode = "auto";
5235 
5236  m_zcolor = color_values (0.15, 0.15, 0.15);
5237  m_zcolormode = "auto";
5238  m_zdir = "normal";
5239  m_zgrid = "off";
5240  m_zlimitmethod = "tickaligned";
5241  m_zminorgrid = "off";
5242  m_zminortick = "off";
5243  m_zscale = "linear";
5244  m_ztick = Matrix ();
5245  m_zticklabel = "";
5246  m_zticklabelmode = "auto";
5247  m_zticklabelrotation = 0.0;
5248  m_ztickmode = "auto";
5249 
5250  m_sx = "linear";
5251  m_sy = "linear";
5252  m_sz = "linear";
5253 
5254  m_visible = "on";
5255 
5257 
5258  graphics_object go = gh_mgr.get_object (m_xlabel.handle_value ());
5260  go = gh_mgr.get_object (m_ylabel.handle_value ());
5262  go = gh_mgr.get_object (m_zlabel.handle_value ());
5264  go = gh_mgr.get_object (m_title.handle_value ());
5266 
5267  xset (m_xlabel.handle_value (), "handlevisibility", "off");
5268  xset (m_ylabel.handle_value (), "handlevisibility", "off");
5269  xset (m_zlabel.handle_value (), "handlevisibility", "off");
5270  xset (m_title.handle_value (), "handlevisibility", "off");
5271 
5272  xset (m_xlabel.handle_value (), "horizontalalignment", "center");
5273  xset (m_xlabel.handle_value (), "horizontalalignmentmode", "auto");
5274  xset (m_ylabel.handle_value (), "horizontalalignment", "center");
5275  xset (m_ylabel.handle_value (), "horizontalalignmentmode", "auto");
5276  xset (m_zlabel.handle_value (), "horizontalalignment", "right");
5277  xset (m_zlabel.handle_value (), "horizontalalignmentmode", "auto");
5278  xset (m_title.handle_value (), "horizontalalignment", "center");
5279  xset (m_title.handle_value (), "horizontalalignmentmode", "auto");
5280 
5281  xset (m_xlabel.handle_value (), "verticalalignment", "top");
5282  xset (m_xlabel.handle_value (), "verticalalignmentmode", "auto");
5283  xset (m_ylabel.handle_value (), "verticalalignment", "bottom");
5284  xset (m_ylabel.handle_value (), "verticalalignmentmode", "auto");
5285  xset (m_title.handle_value (), "verticalalignment", "bottom");
5286  xset (m_title.handle_value (), "verticalalignmentmode", "auto");
5287 
5288  xset (m_ylabel.handle_value (), "rotation", 90.0);
5289  xset (m_ylabel.handle_value (), "rotationmode", "auto");
5290 
5291  xset (m_zlabel.handle_value (), "visible", "off");
5292 
5293  xset (m_xlabel.handle_value (), "clipping", "off");
5294  xset (m_ylabel.handle_value (), "clipping", "off");
5295  xset (m_zlabel.handle_value (), "clipping", "off");
5296  xset (m_title.handle_value (), "clipping", "off");
5297 
5298  xset (m_xlabel.handle_value (), "__autopos_tag__", "xlabel");
5299  xset (m_ylabel.handle_value (), "__autopos_tag__", "ylabel");
5300  xset (m_zlabel.handle_value (), "__autopos_tag__", "zlabel");
5301  xset (m_title.handle_value (), "__autopos_tag__", "title");
5302 
5303  double fs;
5304  fs = m_labelfontsizemultiplier.double_value () * m_fontsize.double_value ();
5305  xset (m_xlabel.handle_value (), "fontsize", octave_value (fs));
5306  xset (m_ylabel.handle_value (), "fontsize", octave_value (fs));
5307  xset (m_zlabel.handle_value (), "fontsize", octave_value (fs));
5308  fs = m_titlefontsizemultiplier.double_value () * m_fontsize.double_value ();
5309  xset (m_title.handle_value (), "fontsize", octave_value (fs));
5310  xset (m_title.handle_value (), "fontweight", m_titlefontweight.get ());
5311 
5312  update_transform ();
5313  sync_positions ();
5314  override_defaults (bgo);
5315 }
5316 
5318 axes::properties::get_colormap () const
5319 {
5320  if (m___colormap__.get ().isempty ())
5321  {
5323 
5324  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
5325  graphics_object go_f (go.get_ancestor ("figure"));
5326  figure::properties& figure_props
5327  = reinterpret_cast<figure::properties&> (go_f.get_properties ());
5328  return figure_props.get_colormap ();
5329  }
5330 
5331  return get___colormap__ ();
5332 }
5333 
5334 void
5335 axes::properties::delete_text_child (handle_property& hp, bool from_root)
5336 {
5337  graphics_handle h = hp.handle_value ();
5338 
5340 
5341  if (h.ok ())
5342  {
5343  graphics_object go = gh_mgr.get_object (h);
5344 
5345  if (go.valid_object ())
5346  gh_mgr.free (h, from_root);
5347  }
5348 
5349  // FIXME: is it necessary to check whether the axes object is
5350  // being deleted now? I think this function is only called when an
5351  // individual child object is delete and not when the parent axes
5352  // object is deleted.
5353 
5354  if (! is_beingdeleted ())
5355  {
5356  hp = gh_mgr.make_graphics_handle ("text", m___myhandle__, false, false);
5357 
5358  xset (hp.handle_value (), "handlevisibility", "off");
5359 
5360  adopt (hp.handle_value ());
5361  }
5362 }
5363 
5364 void
5366 {
5368 
5369  graphics_object go = gh_mgr.get_object (h);
5370 
5371  if (m_xlabel.handle_value ().ok () && h == m_xlabel.handle_value ())
5372  {
5373  delete_text_child (m_xlabel, from_root);
5374  update_xlabel_position ();
5375  }
5376  else if (m_ylabel.handle_value ().ok () && h == m_ylabel.handle_value ())
5377  {
5378  delete_text_child (m_ylabel, from_root);
5379  update_ylabel_position ();
5380  }
5381  else if (m_zlabel.handle_value ().ok () && h == m_zlabel.handle_value ())
5382  {
5383  delete_text_child (m_zlabel, from_root);
5384  update_zlabel_position ();
5385  }
5386  else if (m_title.handle_value ().ok () && h == m_title.handle_value ())
5387  {
5388  delete_text_child (m_title, from_root);
5389  update_title_position ();
5390  }
5391  else if (get_num_lights () > 0 && go.isa ("light")
5392  && go.get_properties ().is_visible ())
5393  decrease_num_lights ();
5394 
5395  if (go.valid_object ())
5396  base_properties::remove_child (h, from_root);
5397 
5398 }
5399 
5400 void
5402 {
5404 
5405  graphics_object go (gh_mgr.get_object (h));
5406 
5407  if (go.isa ("light") && go.get_properties ().is_visible ())
5408  increase_num_lights ();
5409 
5411 
5412  // FIXME: For performance reasons, we would like to call
5413  // update_axis_limits ("xlim", h);
5414  // which updates the limits based ONLY on the new data from h.
5415  // But this isn't working properly at the moment, so we
5416  // call the other form which invokes a full tree traversal of all
5417  // of the axes children.
5418  if (xlimmode_is ("auto"))
5419  update_axis_limits ("xlim");
5420 
5421  if (ylimmode_is ("auto"))
5422  update_axis_limits ("ylim");
5423 
5424  if (zlimmode_is ("auto"))
5425  update_axis_limits ("zlim");
5426 
5427  if (climmode_is ("auto"))
5428  update_axis_limits ("clim");
5429 
5430  if (climmode_is ("auto"))
5431  update_axis_limits ("alim");
5432 }
5433 
5434 inline Matrix
5436 {
5437  Matrix m (4, 4, 0.0);
5438 
5439  for (int i = 0; i < 4; i++)
5440  m(i, i) = 1;
5441 
5442  return m;
5443 }
5444 
5445 inline ColumnVector
5447 {
5448  ColumnVector v (4, 0.0);
5449 
5450  v(3) = 1;
5451 
5452  return v;
5453 }
5454 
5455 inline ColumnVector
5456 xform_vector (double x, double y, double z)
5457 {
5458  ColumnVector v (4, 1.0);
5459 
5460  v(0) = x;
5461  v(1) = y;
5462  v(2) = z;
5463 
5464  return v;
5465 }
5466 
5467 inline ColumnVector
5468 transform (const Matrix& m, double x, double y, double z)
5469 {
5470  return (m * xform_vector (x, y, z));
5471 }
5472 
5473 inline Matrix
5474 xform_scale (double x, double y, double z)
5475 {
5476  Matrix m (4, 4, 0.0);
5477 
5478  m(0, 0) = x;
5479  m(1, 1) = y;
5480  m(2, 2) = z;
5481  m(3, 3) = 1;
5482 
5483  return m;
5484 }
5485 
5486 inline Matrix
5487 xform_translate (double x, double y, double z)
5488 {
5489  Matrix m = xform_matrix ();
5490 
5491  m(0, 3) = x;
5492  m(1, 3) = y;
5493  m(2, 3) = z;
5494  m(3, 3) = 1;
5495 
5496  return m;
5497 }
5498 
5499 inline void
5500 scale (Matrix& m, double x, double y, double z)
5501 {
5502  m = m * xform_scale (x, y, z);
5503 }
5504 
5505 inline void
5506 translate (Matrix& m, double x, double y, double z)
5507 {
5508  m = m * xform_translate (x, y, z);
5509 }
5510 
5511 inline void
5513 {
5514  v = m * v;
5515 }
5516 
5517 inline void
5518 scale (ColumnVector& v, double x, double y, double z)
5519 {
5520  v(0) *= x;
5521  v(1) *= y;
5522  v(2) *= z;
5523 }
5524 
5525 inline void
5526 translate (ColumnVector& v, double x, double y, double z)
5527 {
5528  v(0) += x;
5529  v(1) += y;
5530  v(2) += z;
5531 }
5532 
5533 inline void
5535 {
5536  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5537  scale (v, fact, fact, fact);
5538 }
5539 
5540 inline double
5541 dot (const ColumnVector& v1, const ColumnVector& v2)
5542 {
5543  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5544 }
5545 
5546 inline double
5547 norm (const ColumnVector& v)
5548 {
5549  return sqrt (dot (v, v));
5550 }
5551 
5552 inline ColumnVector
5553 cross (const ColumnVector& v1, const ColumnVector& v2)
5554 {
5556 
5557  r(0) = v1(1)*v2(2) - v1(2)*v2(1);
5558  r(1) = v1(2)*v2(0) - v1(0)*v2(2);
5559  r(2) = v1(0)*v2(1) - v1(1)*v2(0);
5560 
5561  return r;
5562 }
5563 
5564 inline Matrix
5566 {
5567  static double data[32] =
5568  {
5569  0, 0, 0, 1,
5570  1, 0, 0, 1,
5571  0, 1, 0, 1,
5572  0, 0, 1, 1,
5573  1, 1, 0, 1,
5574  1, 0, 1, 1,
5575  0, 1, 1, 1,
5576  1, 1, 1, 1
5577  };
5578  Matrix m (4, 8);
5579 
5580  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5581 
5582  return m;
5583 }
5584 
5585 inline ColumnVector
5587 {
5588  ColumnVector retval (4, 1.0);
5589 
5590  memcpy (retval.fortran_vec (), m.data (), sizeof (double)*3);
5591 
5592  return retval;
5593 }
5594 
5595 inline RowVector
5597 {
5598  return v.extract_n (0, 3).transpose ();
5599 }
5600 
5601 void
5603 {
5604  double xd = (xdir_is ("normal") ? 1 : -1);
5605  double yd = (ydir_is ("normal") ? 1 : -1);
5606  double zd = (zdir_is ("normal") ? 1 : -1);
5607 
5608  Matrix xlimits = m_sx.scale (get_xlim ().matrix_value ());
5609  Matrix ylimits = m_sy.scale (get_ylim ().matrix_value ());
5610  Matrix zlimits = m_sz.scale (get_zlim ().matrix_value ());
5611 
5612  double xo = xlimits(xd > 0 ? 0 : 1);
5613  double yo = ylimits(yd > 0 ? 0 : 1);
5614  double zo = zlimits(zd > 0 ? 0 : 1);
5615 
5616  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5617 
5618  bool autocam = (camerapositionmode_is ("auto")
5619  && cameratargetmode_is ("auto")
5620  && cameraupvectormode_is ("auto")
5621  && cameraviewanglemode_is ("auto"));
5622  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5623  && plotboxaspectratiomode_is ("auto"));
5624 
5625  ColumnVector c_eye (xform_vector ());
5626  ColumnVector c_center (xform_vector ());
5627  ColumnVector c_upv (xform_vector ());
5628 
5629  if (cameratargetmode_is ("auto"))
5630  {
5631  c_center(0) = (xlimits(0) + xlimits(1)) / 2;
5632  c_center(1) = (ylimits(0) + ylimits(1)) / 2;
5633  c_center(2) = (zlimits(0) + zlimits(1)) / 2;
5634 
5635  m_cameratarget = xform2cam (c_center);
5636  }
5637  else
5638  c_center = cam2xform (get_cameratarget ().matrix_value ());
5639 
5640  if (camerapositionmode_is ("auto"))
5641  {
5642  Matrix tview = get_view ().matrix_value ();
5643  double az = tview(0);
5644  double el = tview(1);
5645  double d = 5 * sqrt (pb(0)*pb(0) + pb(1)*pb(1) + pb(2)*pb(2));
5646 
5647  if (el == 90 || el == -90)
5648  c_eye(2) = d*octave::math::signum (el);
5649  else
5650  {
5651  az *= M_PI/180.0;
5652  el *= M_PI/180.0;
5653  c_eye(0) = d * cos (el) * sin (az);
5654  c_eye(1) = -d* cos (el) * cos (az);
5655  c_eye(2) = d * sin (el);
5656  }
5657  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
5658  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
5659  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
5660 
5661  m_cameraposition = xform2cam (c_eye);
5662  }
5663  else
5664  c_eye = cam2xform (get_cameraposition ().matrix_value ());
5665 
5666  if (cameraupvectormode_is ("auto"))
5667  {
5668  Matrix tview = get_view ().matrix_value ();
5669  double az = tview(0);
5670  double el = tview(1);
5671 
5672  if (el == 90 || el == -90)
5673  {
5674  c_upv(0) = -octave::math::signum (el)
5675  * sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
5676  c_upv(1) = octave::math::signum (el)
5677  * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
5678  }
5679  else
5680  c_upv(2) = 1;
5681 
5682  m_cameraupvector = xform2cam (c_upv);
5683  }
5684  else
5685  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
5686 
5687  Matrix x_view = xform_matrix ();
5688  Matrix x_projection = xform_matrix ();
5689  Matrix x_viewport = xform_matrix ();
5690  Matrix x_normrender;
5691  Matrix x_pre = xform_matrix ();
5692 
5693  m_x_render = xform_matrix ();
5694  m_x_render_inv = xform_matrix ();
5695 
5696  scale (x_pre, pb(0), pb(1), pb(2));
5697  translate (x_pre, -0.5, -0.5, -0.5);
5698  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5699  zd/(zlimits(1)-zlimits(0)));
5700  translate (x_pre, -xo, -yo, -zo);
5701 
5702  xform (c_eye, x_pre);
5703  xform (c_center, x_pre);
5704  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
5705  pb(2)/(zlimits(1)-zlimits(0)));
5706  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
5707 
5708  ColumnVector F (c_center), f (F), UP (c_upv);
5709  normalize (f);
5710  normalize (UP);
5711 
5712  if (std::abs (dot (f, UP)) > 1e-15)
5713  {
5714  double fa = 1 / sqrt (1 - f(2)*f(2));
5715  scale (UP, fa, fa, fa);
5716  }
5717 
5718  ColumnVector s = cross (f, UP);
5719  ColumnVector u = cross (s, f);
5720 
5721  scale (x_view, 1, 1, -1);
5722  Matrix l = xform_matrix ();
5723  l(0, 0) = s(0); l(0, 1) = s(1); l(0, 2) = s(2);
5724  l(1, 0) = u(0); l(1, 1) = u(1); l(1, 2) = u(2);
5725  l(2, 0) = -f(0); l(2, 1) = -f(1); l(2, 2) = -f(2);
5726  x_view = x_view * l;
5727  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
5728  scale (x_view, pb(0), pb(1), pb(2));
5729  translate (x_view, -0.5, -0.5, -0.5);
5730 
5731  Matrix x_cube = x_view * unit_cube ();
5732  ColumnVector cmin = x_cube.row_min ();
5733  ColumnVector cmax = x_cube.row_max ();
5734  double xM = cmax(0) - cmin(0);
5735  double yM = cmax(1) - cmin(1);
5736 
5737  Matrix bb = get_boundingbox (true);
5738 
5739  double v_angle;
5740 
5741  if (cameraviewanglemode_is ("auto"))
5742  {
5743  double af;
5744 
5745  // FIXME: was this really needed? When compared to Matlab, it
5746  // does not seem to be required. Need investigation with concrete
5747  // graphics toolkit to see results visually.
5748  if (false && dowarp)
5749  af = (1.0 / (xM > yM ? xM : yM));
5750  else
5751  {
5752  if ((bb(2)/bb(3)) > (xM/yM))
5753  af = 1.0 / yM;
5754  else
5755  af = 1.0 / xM;
5756  }
5757  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
5758 
5759  m_cameraviewangle = v_angle;
5760  }
5761  else
5762  v_angle = get_cameraviewangle ();
5763 
5764  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
5765  scale (x_projection, pf, pf, 1);
5766 
5767  if (dowarp)
5768  {
5769  xM *= pf;
5770  yM *= pf;
5771  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5772  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
5773  }
5774  else
5775  {
5776  double pix = 1;
5777  if (autocam)
5778  {
5779  if ((bb(2)/bb(3)) > (xM/yM))
5780  pix = bb(3);
5781  else
5782  pix = bb(2);
5783  }
5784  else
5785  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
5786  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5787  scale (x_viewport, pix, -pix, 1);
5788  }
5789 
5790  x_normrender = x_viewport * x_projection * x_view;
5791 
5792  x_cube = x_normrender * unit_cube ();
5793  cmin = x_cube.row_min ();
5794  cmax = x_cube.row_max ();
5795  m_x_zlim.resize (1, 2);
5796  m_x_zlim(0) = cmin(2);
5797  m_x_zlim(1) = cmax(2);
5798 
5799  m_x_render = x_normrender;
5800  scale (m_x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5801  zd/(zlimits(1)-zlimits(0)));
5802  translate (m_x_render, -xo, -yo, -zo);
5803 
5804  m_x_render_inv = m_x_render.inverse ();
5805 
5806  // Note: these matrices are a slight modified version of the regular matrices,
5807  // more suited for OpenGL rendering (m_x_gl_mat1 => light => m_x_gl_mat2)
5808  m_x_gl_mat1 = x_view;
5809  scale (m_x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5810  zd/(zlimits(1)-zlimits(0)));
5811  translate (m_x_gl_mat1, -xo, -yo, -zo);
5812  m_x_gl_mat2 = x_viewport * x_projection;
5813 }
5814 
5815 static bool updating_axes_layout = false;
5816 
5817 void
5819 {
5820  if (updating_axes_layout)
5821  return;
5822 
5823  graphics_xform xform = get_transform ();
5824 
5825  double xd = (xdir_is ("normal") ? 1 : -1);
5826  double yd = (ydir_is ("normal") ? 1 : -1);
5827  double zd = (zdir_is ("normal") ? 1 : -1);
5828 
5829  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
5830  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
5831  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
5832 
5833  double x_min, x_max, y_min, y_max, z_min, z_max;
5834  x_min = xlims(0), x_max = xlims(1);
5835  y_min = ylims(0), y_max = ylims(1);
5836  z_min = zlims(0), z_max = zlims(1);
5837 
5838  ColumnVector p1, p2, dir (3);
5839 
5840  m_xstate = m_ystate = m_zstate = AXE_ANY_DIR;
5841 
5842  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
5843  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
5844  dir(0) = octave::math::round (p2(0) - p1(0));
5845  dir(1) = octave::math::round (p2(1) - p1(1));
5846  dir(2) = (p2(2) - p1(2));
5847  if (dir(0) == 0 && dir(1) == 0)
5848  m_xstate = AXE_DEPTH_DIR;
5849  else if (dir(2) == 0)
5850  {
5851  if (dir(0) == 0)
5852  m_xstate = AXE_VERT_DIR;
5853  else if (dir(1) == 0)
5854  m_xstate = AXE_HORZ_DIR;
5855  }
5856 
5857  if (dir(2) == 0)
5858  {
5859  if (dir(1) == 0)
5860  m_xPlane = (dir(0) > 0 ? x_max : x_min);
5861  else
5862  m_xPlane = (dir(1) < 0 ? x_max : x_min);
5863  }
5864  else
5865  m_xPlane = (dir(2) < 0 ? x_min : x_max);
5866 
5867  m_xPlaneN = (m_xPlane == x_min ? x_max : x_min);
5868  m_fx = (x_max - x_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5869 
5870  p1 = xform.transform ((x_min + x_max)/2, y_min, (z_min + z_max)/2, false);
5871  p2 = xform.transform ((x_min + x_max)/2, y_max, (z_min + z_max)/2, false);
5872  dir(0) = octave::math::round (p2(0) - p1(0));
5873  dir(1) = octave::math::round (p2(1) - p1(1));
5874  dir(2) = (p2(2) - p1(2));
5875  if (dir(0) == 0 && dir(1) == 0)
5876  m_ystate = AXE_DEPTH_DIR;
5877  else if (dir(2) == 0)
5878  {
5879  if (dir(0) == 0)
5880  m_ystate = AXE_VERT_DIR;
5881  else if (dir(1) == 0)
5882  m_ystate = AXE_HORZ_DIR;
5883  }
5884 
5885  if (dir(2) == 0)
5886  {
5887  if (dir(1) == 0)
5888  m_yPlane = (dir(0) > 0 ? y_max : y_min);
5889  else
5890  m_yPlane = (dir(1) < 0 ? y_max : y_min);
5891  }
5892  else
5893  m_yPlane = (dir(2) < 0 ? y_min : y_max);
5894 
5895  m_yPlaneN = (m_yPlane == y_min ? y_max : y_min);
5896  m_fy = (y_max - y_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5897 
5898  p1 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_min, false);
5899  p2 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_max, false);
5900  dir(0) = octave::math::round (p2(0) - p1(0));
5901  dir(1) = octave::math::round (p2(1) - p1(1));
5902  dir(2) = (p2(2) - p1(2));
5903  if (dir(0) == 0 && dir(1) == 0)
5904  m_zstate = AXE_DEPTH_DIR;
5905  else if (dir(2) == 0)
5906  {
5907  if (dir(0) == 0)
5908  m_zstate = AXE_VERT_DIR;
5909  else if (dir(1) == 0)
5910  m_zstate = AXE_HORZ_DIR;
5911  }
5912 
5913  if (dir(2) == 0)
5914  {
5915  if (dir(1) == 0)
5916  m_zPlane = (dir(0) > 0 ? z_min : z_max);
5917  else
5918  m_zPlane = (dir(1) < 0 ? z_min : z_max);
5919  }
5920  else
5921  m_zPlane = (dir(2) < 0 ? z_min : z_max);
5922 
5923  m_zPlaneN = (m_zPlane == z_min ? z_max : z_min);
5924  m_fz = (z_max - z_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5925 
5926  octave::unwind_protect_var<bool> restore_var (updating_axes_layout, true);
5927 
5928  m_xySym = (xd*yd*(m_xPlane-m_xPlaneN)*(m_yPlane-m_yPlaneN) > 0);
5929  m_zSign = (zd*(m_zPlane-m_zPlaneN) <= 0);
5930  m_xyzSym = (m_zSign ? m_xySym : ! m_xySym);
5931  m_xpTick = (m_zSign ? m_xPlaneN : m_xPlane);
5932  m_ypTick = (m_zSign ? m_yPlaneN : m_yPlane);
5933  m_zpTick = (m_zSign ? m_zPlane : m_zPlaneN);
5934  m_xpTickN = (m_zSign ? m_xPlane : m_xPlaneN);
5935  m_ypTickN = (m_zSign ? m_yPlane : m_yPlaneN);
5936  m_zpTickN = (m_zSign ? m_zPlaneN : m_zPlane);
5937 
5938  // 2D mode
5939  m_x2Dtop = false;
5940  m_y2Dright = false;
5941  m_layer2Dtop = false;
5942  if (m_xstate == AXE_HORZ_DIR && m_ystate == AXE_VERT_DIR)
5943  {
5944  Matrix ylimits = get_ylim ().matrix_value ();
5945  if (xaxislocation_is ("top")
5946  || (yscale_is ("log") && xaxislocation_is ("origin")
5947  && (ylimits(1) < 0.)))
5948  {
5949  std::swap (m_yPlane, m_yPlaneN);
5950  m_x2Dtop = true;
5951  }
5952  m_ypTick = m_yPlaneN;
5953  m_ypTickN = m_yPlane;
5954  Matrix xlimits = get_xlim ().matrix_value ();
5955  if (yaxislocation_is ("right")
5956  || (xscale_is ("log") && yaxislocation_is ("origin")
5957  && (xlimits(1) < 0.)))
5958  {
5959  std::swap (m_xPlane, m_xPlaneN);
5960  m_y2Dright = true;
5961  }
5962  m_xpTick = m_xPlaneN;
5963  m_xpTickN = m_xPlane;
5964  if (layer_is ("top"))
5965  {
5966  m_zpTick = m_zPlaneN;
5967  m_layer2Dtop = true;
5968  }
5969  else
5970  m_zpTick = m_zPlane;
5971  }
5972 
5973  Matrix viewmat = get_view ().matrix_value ();
5974  m_nearhoriz = std::abs (viewmat(1)) <= 5;
5975  m_is2D = viewmat(1) == 90;
5976 
5977  update_ticklength ();
5978 }
5979 
5980 void
5981 axes::properties::update_ticklength ()
5982 {
5983  bool mode2D = (((m_xstate > AXE_DEPTH_DIR ? 1 : 0) +
5984  (m_ystate > AXE_DEPTH_DIR ? 1 : 0) +
5985  (m_zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5986 
5987  if (tickdirmode_is ("auto"))
5988  m_tickdir.set (mode2D ? "in" : "out", true);
5989 
5990  double ticksign;
5991  std::string tickdir = get_tickdir ();
5992  if (tickdir == "in")
5993  ticksign = -1;
5994  else if (tickdir == "out")
5995  ticksign = 1;
5996  else if (tickdir == "both")
5997  ticksign = 2;
5998  else // tickdir == "none"
5999  ticksign = 0;
6000 
6001  Matrix bbox = get_boundingbox (true);
6002  Matrix ticklen = get_ticklength ().matrix_value ();
6003  ticklen(0) *= std::max (bbox(2), bbox(3));
6004  // FIXME: This algorithm is not Matlab-compatible. See bug #55483.
6005  // Scale the results of Octave's algorithm for better visuals.
6006  ticklen(1) *= (0.76 * std::max (bbox(2), bbox(3)));
6007 
6008  m_xticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6009  m_yticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6010  m_zticklen = ticksign * (mode2D ? ticklen(0) : ticklen(1));
6011 
6012  double offset = get___fontsize_points__ () / 2;
6013 
6014  m_xtickoffset = (mode2D ? std::max (0., m_xticklen) : std::abs (m_xticklen)) +
6015  (m_xstate == AXE_HORZ_DIR ? offset*1.5 : offset);
6016  m_ytickoffset = (mode2D ? std::max (0., m_yticklen) : std::abs (m_yticklen)) +
6017  (m_ystate == AXE_HORZ_DIR ? offset*1.5 : offset);
6018  m_ztickoffset = (mode2D ? std::max (0., m_zticklen) : std::abs (m_zticklen)) +
6019  (m_zstate == AXE_HORZ_DIR ? offset*1.5 : offset);
6020 
6021  update_xlabel_position ();
6022  update_ylabel_position ();
6023  update_zlabel_position ();
6024  update_title_position ();
6025 }
6026 
6027 /*
6028 ## FIXME: A demo can't be called in a C++ file. This should be made a test
6029 ## or moved to a .m file where it can be called.
6030 %!demo
6031 %! clf;
6032 %! subplot (2,1,1);
6033 %! plot (rand (3));
6034 %! xlabel xlabel;
6035 %! ylabel ylabel;
6036 %! title title;
6037 %! subplot (2,1,2);
6038 %! plot (rand (3));
6039 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
6040 %! xlabel xlabel;
6041 %! ylabel ylabel;
6042 %! title title;
6043 */
6044 
6045 static ColumnVector
6046 convert_label_position (const ColumnVector& p,
6047  const text::properties& props,
6048  const graphics_xform& xform,
6049  const Matrix& bbox)
6050 {
6051  ColumnVector retval;
6052 
6053  std::string to_units = props.get_units ();
6054 
6055  if (to_units != "data")
6056  {
6057  ColumnVector v = xform.transform (p(0), p(1), p(2));
6058 
6059  retval.resize (3);
6060 
6061  retval(0) = v(0) - bbox(0) + 1;
6062  retval(1) = bbox(1) + bbox(3) - v(1) + 1;
6063  retval(2) = 0;
6064 
6065  retval = convert_position (retval, "pixels", to_units,
6066  bbox.extract_n (0, 2, 1, 2));
6067  }
6068  else
6069  retval = p;
6070 
6071  return retval;
6072 }
6073 
6074 static bool updating_xlabel_position = false;
6075 
6076 void
6078 {
6079  if (updating_xlabel_position)
6080  return;
6081 
6083 
6084  graphics_object go = gh_mgr.get_object (get_xlabel ());
6085 
6086  if (! go.valid_object ())
6087  return;
6088 
6089  text::properties& xlabel_props
6090  = reinterpret_cast<text::properties&> (go.get_properties ());
6091 
6092  bool isempty = xlabel_props.get_string ().isempty ();
6093 
6094  octave::unwind_protect_var<bool>
6095  restore_var (updating_xlabel_position, true);
6096 
6097  if (! isempty)
6098  {
6099  if (xlabel_props.horizontalalignmentmode_is ("auto"))
6100  {
6101  xlabel_props.set_horizontalalignment
6102  (m_xstate > AXE_DEPTH_DIR ? "center"
6103  : (m_xyzSym ? "left" : "right"));
6104 
6105  xlabel_props.set_horizontalalignmentmode ("auto");
6106  }
6107 
6108  if (xlabel_props.verticalalignmentmode_is ("auto"))
6109  {
6110  xlabel_props.set_verticalalignment
6111  (m_xstate == AXE_VERT_DIR || m_x2Dtop ? "bottom" : "top");
6112 
6113  xlabel_props.set_verticalalignmentmode ("auto");
6114  }
6115  }
6116 
6117  if (xlabel_props.positionmode_is ("auto")
6118  || xlabel_props.rotationmode_is ("auto"))
6119  {
6120  graphics_xform xform = get_transform ();
6121 
6122  Matrix ext (1, 2, 0.0);
6123  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6124  get_xticklabel ().string_vector_value (),
6125  get_xlim ().matrix_value ());
6126 
6127  double margin = 5;
6128  double wmax = ext(0) + margin;
6129  double hmax = ext(1) + margin;
6130  double angle = 0.0;
6131  ColumnVector p
6132  = graphics_xform::xform_vector ((m_xpTickN + m_xpTick)/2, m_ypTick, m_zpTick);
6133 
6134  bool tick_along_z = m_nearhoriz || octave::math::isinf (m_fy);
6135  if (tick_along_z)
6136  p(2) += (octave::math::signum (m_zpTick - m_zpTickN) * m_fz * m_xtickoffset);
6137  else
6138  p(1) += (octave::math::signum (m_ypTick - m_ypTickN) * m_fy * m_xtickoffset);
6139 
6140  p = xform.transform (p(0), p(1), p(2), false);
6141 
6142  switch (m_xstate)
6143  {
6144  case AXE_ANY_DIR:
6145  p(0) += (m_xyzSym ? wmax : -wmax);
6146  p(1) += hmax;
6147  break;
6148 
6149  case AXE_VERT_DIR:
6150  p(0) -= wmax;
6151  angle = 90;
6152  break;
6153 
6154  case AXE_HORZ_DIR:
6155  p(1) += (m_x2Dtop ? -hmax : hmax);
6156  break;
6157  }
6158 
6159  if (xlabel_props.positionmode_is ("auto"))
6160  {
6161  p = xform.untransform (p(0), p(1), p(2), true);
6162 
6163  p = convert_label_position (p, xlabel_props, xform,
6164  get_extent (false));
6165 
6166  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
6167  xlabel_props.set_positionmode ("auto");
6168  }
6169 
6170  if (! isempty && xlabel_props.rotationmode_is ("auto"))
6171  {
6172  xlabel_props.set_rotation (angle);
6173  xlabel_props.set_rotationmode ("auto");
6174  }
6175  }
6176 }
6177 
6178 static bool updating_ylabel_position = false;
6179 
6180 void
6182 {
6183  if (updating_ylabel_position)
6184  return;
6185 
6187 
6188  graphics_object go = gh_mgr.get_object (get_ylabel ());
6189 
6190  if (! go.valid_object ())
6191  return;
6192 
6193  text::properties& ylabel_props
6194  = reinterpret_cast<text::properties&> (go.get_properties ());
6195 
6196  bool isempty = ylabel_props.get_string ().isempty ();
6197 
6198  octave::unwind_protect_var<bool>
6199  restore_var (updating_ylabel_position, true);
6200 
6201  if (! isempty)
6202  {
6203  if (ylabel_props.horizontalalignmentmode_is ("auto"))
6204  {
6205  ylabel_props.set_horizontalalignment
6206  (m_ystate > AXE_DEPTH_DIR ? "center"
6207  : (! m_xyzSym ? "left" : "right"));
6208 
6209  ylabel_props.set_horizontalalignmentmode ("auto");
6210  }
6211 
6212  if (ylabel_props.verticalalignmentmode_is ("auto"))
6213  {
6214  ylabel_props.set_verticalalignment
6215  (m_ystate == AXE_VERT_DIR && ! m_y2Dright ? "bottom" : "top");
6216 
6217  ylabel_props.set_verticalalignmentmode ("auto");
6218  }
6219  }
6220 
6221  if (ylabel_props.positionmode_is ("auto")
6222  || ylabel_props.rotationmode_is ("auto"))
6223  {
6224  graphics_xform xform = get_transform ();
6225 
6226  Matrix ext (1, 2, 0.0);
6227 
6228  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
6229  get_yticklabel ().string_vector_value (),
6230  get_ylim ().matrix_value ());
6231  double margin = 5;
6232  double wmax = ext(0) + margin;
6233  double hmax = ext(1) + margin;
6234  double angle = 0.0;
6235  ColumnVector p
6236  = graphics_xform::xform_vector (m_xpTick, (m_ypTickN + m_ypTick)/2, m_zpTick);
6237 
6238  bool tick_along_z = m_nearhoriz || octave::math::isinf (m_fx);
6239  if (tick_along_z)
6240  p(2) += (octave::math::signum (m_zpTick - m_zpTickN) * m_fz * m_ytickoffset);
6241  else
6242  p(0) += (octave::math::signum (m_xpTick - m_xpTickN) * m_fx * m_ytickoffset);
6243 
6244  p = xform.transform (p(0), p(1), p(2), false);
6245 
6246  switch (m_ystate)
6247  {
6248  case AXE_ANY_DIR:
6249  p(0) += (! m_xyzSym ? wmax : -wmax);
6250  p(1) += hmax;
6251  break;
6252 
6253  case AXE_VERT_DIR:
6254  p(0) += (m_y2Dright ? wmax : -wmax);
6255  angle = 90;
6256  break;
6257 
6258  case AXE_HORZ_DIR:
6259  p(1) += hmax;
6260  break;
6261  }
6262 
6263  if (ylabel_props.positionmode_is ("auto"))
6264  {
6265  p = xform.untransform (p(0), p(1), p(2), true);
6266 
6267  p = convert_label_position (p, ylabel_props, xform,
6268  get_extent (false));
6269 
6270  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
6271  ylabel_props.set_positionmode ("auto");
6272  }
6273 
6274  if (! isempty && ylabel_props.rotationmode_is ("auto"))
6275  {
6276  ylabel_props.set_rotation (angle);
6277  ylabel_props.set_rotationmode ("auto");
6278  }
6279  }
6280 }
6281 
6282 static bool updating_zlabel_position = false;
6283 
6284 void
6286 {
6287  if (updating_zlabel_position)
6288  return;
6289 
6291 
6292  graphics_object go = gh_mgr.get_object (get_zlabel ());
6293 
6294  if (! go.valid_object ())
6295  return;
6296 
6297  text::properties& zlabel_props
6298  = reinterpret_cast<text::properties&> (go.get_properties ());
6299 
6300  bool camAuto = cameraupvectormode_is ("auto");
6301  bool isempty = zlabel_props.get_string ().isempty ();
6302 
6303  octave::unwind_protect_var<bool>
6304  restore_updating_zlabel_position (updating_zlabel_position, true);
6305 
6306  if (! isempty)
6307  {
6308  if (zlabel_props.horizontalalignmentmode_is ("auto"))
6309  {
6310  zlabel_props.set_horizontalalignment
6311  ((m_zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
6312 
6313  zlabel_props.set_horizontalalignmentmode ("auto");
6314  }
6315 
6316  if (zlabel_props.verticalalignmentmode_is ("auto"))
6317  {
6318  zlabel_props.set_verticalalignment
6319  (m_zstate == AXE_VERT_DIR
6320  ? "bottom" : ((m_zSign || camAuto) ? "bottom" : "top"));
6321 
6322  zlabel_props.set_verticalalignmentmode ("auto");
6323  }
6324  }
6325 
6326  if (zlabel_props.positionmode_is ("auto")
6327  || zlabel_props.rotationmode_is ("auto"))
6328  {
6329  graphics_xform xform = get_transform ();
6330 
6331  Matrix ext (1, 2, 0.0);
6332  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
6333  get_zticklabel ().string_vector_value (),
6334  get_zlim ().matrix_value ());
6335 
6336  double margin = 5;
6337  double wmax = ext(0) + margin;
6338  double hmax = ext(1) + margin;
6339  double angle = 0.0;
6340  ColumnVector p;
6341 
6342  if (m_xySym)
6343  {
6344  p = graphics_xform::xform_vector (m_xPlaneN, m_yPlane,
6345  (m_zpTickN + m_zpTick)/2);
6346  if (octave::math::isinf (m_fy))
6347  p(0) += octave::math::signum (m_xPlaneN - m_xPlane) * m_fx * m_ztickoffset;
6348  else
6349  p(1) += octave::math::signum (m_yPlane - m_yPlaneN) * m_fy * m_ztickoffset;
6350  }
6351  else
6352  {
6353  p = graphics_xform::xform_vector (m_xPlane, m_yPlaneN,
6354  (m_zpTickN + m_zpTick)/2);
6355  if (octave::math::isinf (m_fx))
6356  p(1) += octave::math::signum (m_yPlaneN - m_yPlane) * m_fy * m_ztickoffset;
6357  else
6358  p(0) += octave::math::signum (m_xPlane - m_xPlaneN) * m_fx * m_ztickoffset;
6359  }
6360 
6361  p = xform.transform (p(0), p(1), p(2), false);
6362 
6363  switch (m_zstate)
6364  {
6365  case AXE_ANY_DIR:
6366  if (camAuto)
6367  {
6368  p(0) -= wmax;
6369  angle = 90;
6370  }
6371 
6372  // FIXME: what's the correct offset?
6373  //
6374  // p[0] += (! m_xySym ? wmax : -wmax);
6375  // p[1] += (m_zSign ? hmax : -hmax);
6376 
6377  break;
6378 
6379  case AXE_VERT_DIR:
6380  p(0) -= wmax;
6381  angle = 90;
6382  break;
6383 
6384  case AXE_HORZ_DIR:
6385  p(1) += hmax;
6386  break;
6387  }
6388 
6389  if (zlabel_props.positionmode_is ("auto"))
6390  {
6391  p = xform.untransform (p(0), p(1), p(2), true);
6392 
6393  p = convert_label_position (p, zlabel_props, xform,
6394  get_extent (false));
6395 
6396  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
6397  zlabel_props.set_positionmode ("auto");
6398  }
6399 
6400  if (! isempty && zlabel_props.rotationmode_is ("auto"))
6401  {
6402  zlabel_props.set_rotation (angle);
6403  zlabel_props.set_rotationmode ("auto");
6404  }
6405  }
6406 }
6407 
6408 static bool updating_title_position = false;
6409 
6410 void
6412 {
6413  if (updating_title_position)
6414  return;
6415 
6417 
6418  graphics_object go = gh_mgr.get_object (get_title ());
6419 
6420  if (! go.valid_object ())
6421  return;
6422 
6423  text::properties& title_props
6424  = reinterpret_cast<text::properties&> (go.get_properties ());
6425 
6426  octave::unwind_protect_var<bool> restore_var (updating_title_position, true);
6427 
6428  if (title_props.positionmode_is ("auto"))
6429  {
6430  graphics_xform xform = get_transform ();
6431 
6432  // FIXME: bbox should be stored in axes::properties
6433  Matrix bbox = get_extent (false);
6434 
6435  ColumnVector p
6436  = graphics_xform::xform_vector (bbox(0) + bbox(2)/2, bbox(1) - 10,
6437  (m_x_zlim(0) + m_x_zlim(1))/2);
6438 
6439  if (m_x2Dtop)
6440  {
6441  Matrix ext (1, 2, 0.0);
6442  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
6443  get_xticklabel ().string_vector_value (),
6444  get_xlim ().matrix_value ());
6445  p(1) -= ext(1);
6446  }
6447 
6448  p = xform.untransform (p(0), p(1), p(2), true);
6449 
6450  p = convert_label_position (p, title_props, xform, bbox);
6451 
6452  title_props.set_position (p.extract_n (0, 3).transpose ());
6453  title_props.set_positionmode ("auto");
6454  }
6455 }
6456 
6457 void
6458 axes::properties::update_autopos (const std::string& elem_type)
6459 {
6460  if (elem_type == "xlabel")
6461  update_xlabel_position ();
6462  else if (elem_type == "ylabel")
6463  update_ylabel_position ();
6464  else if (elem_type == "zlabel")
6465  update_zlabel_position ();
6466  else if (elem_type == "title")
6467  update_title_position ();
6468  else if (elem_type == "sync")
6469  sync_positions ();
6470 }
6471 
6472 static void
6473 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
6474  double xlength, double ylength, double zlength)
6475 {
6476  double xval = xlength / scalefactors(0);
6477  double yval = ylength / scalefactors(1);
6478  double zval = zlength / scalefactors(2);
6479 
6480  double minval = octave::math::min (octave::math::min (xval, yval), zval);
6481 
6482  aspectratios(0) = xval / minval;
6483  aspectratios(1) = yval / minval;
6484  aspectratios(2) = zval / minval;
6485 }
6486 
6487 static void
6488 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
6489  double pbfactor, double dafactor, char limit_type, bool tight)
6490 {
6491  if (tight)
6492  {
6493  double minval = octave::numeric_limits<double>::Inf ();
6494  double maxval = -octave::numeric_limits<double>::Inf ();
6495  double min_pos = octave::numeric_limits<double>::Inf ();
6496  double max_neg = -octave::numeric_limits<double>::Inf ();
6497  get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
6498  if (octave::math::isfinite (minval) && octave::math::isfinite (maxval))
6499  {
6500  limits(0) = minval;
6501  limits(1) = maxval;
6502  s = octave::math::max (s, (maxval - minval) / (pbfactor * dafactor));
6503  }
6504  }
6505  else
6506  s = octave::math::max (s, (limits(1) - limits(0)) / (pbfactor * dafactor));
6507 }
6508 
6509 static std::set<double> updating_aspectratios;
6510 
6511 void
6513 {
6514  if (updating_aspectratios.find (get___myhandle__ ().value ())
6515  != updating_aspectratios.end ())
6516  return;
6517 
6518  Matrix xlimits = get_xlim ().matrix_value ();
6519  Matrix ylimits = get_ylim ().matrix_value ();
6520  Matrix zlimits = get_zlim ().matrix_value ();
6521 
6522  double dx = (xlimits(1) - xlimits(0));
6523  double dy = (ylimits(1) - ylimits(0));
6524  double dz = (zlimits(1) - zlimits(0));
6525 
6526  Matrix da = get_dataaspectratio ().matrix_value ();
6527  Matrix pba = get_plotboxaspectratio ().matrix_value ();
6528 
6529  if (dataaspectratiomode_is ("auto"))
6530  {
6531  if (plotboxaspectratiomode_is ("auto"))
6532  {
6533  pba = Matrix (1, 3, 1.0);
6534  m_plotboxaspectratio.set (pba, false);
6535  }
6536 
6537  normalized_aspectratios (da, pba, dx, dy, dz);
6538  m_dataaspectratio.set (da, false);
6539  }
6540  else if (plotboxaspectratiomode_is ("auto"))
6541  {
6542  normalized_aspectratios (pba, da, dx, dy, dz);
6543  m_plotboxaspectratio.set (pba, false);
6544  }
6545  else
6546  {
6547  double s = -octave::numeric_limits<double>::Inf ();
6548  bool modified_limits = false;
6549  Matrix kids;
6550 
6551  if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
6552  {
6553  modified_limits = true;
6554  kids = get_children ();
6555  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
6556  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
6557  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
6558  }
6559  else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
6560  {
6561  modified_limits = true;
6562  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
6563  }
6564  else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
6565  {
6566  modified_limits = true;
6567  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
6568  }
6569  else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
6570  {
6571  modified_limits = true;
6572  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
6573  }
6574 
6575  if (modified_limits)
6576  {
6577  octave::unwind_protect_var<std::set<double>>
6578  restore_var (updating_aspectratios);
6579 
6580  updating_aspectratios.insert (get___myhandle__ ().value ());
6581 
6582  dx = pba(0) * da(0);
6583  dy = pba(1) * da(1);
6584  dz = pba(2) * da(2);
6585  if (octave::math::isinf (s))
6586  s = 1 / octave::math::min (octave::math::min (dx, dy), dz);
6587 
6588  if (xlimmode_is ("auto"))
6589  {
6590  dx = s * dx;
6591  xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
6592  xlimits(1) = xlimits(0) + dx;
6593  set_xlim (xlimits);
6594  set_xlimmode ("auto");
6595  }
6596 
6597  if (ylimmode_is ("auto"))
6598  {
6599  dy = s * dy;
6600  ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
6601  ylimits(1) = ylimits(0) + dy;
6602  set_ylim (ylimits);
6603  set_ylimmode ("auto");
6604  }
6605 
6606  if (zlimmode_is ("auto"))
6607  {
6608  dz = s * dz;
6609  zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
6610  zlimits(1) = zlimits(0) + dz;
6611  set_zlim (zlimits);
6612  set_zlimmode ("auto");
6613  }
6614  }
6615  else
6616  {
6617  normalized_aspectratios (pba, da, dx, dy, dz);
6618  m_plotboxaspectratio.set (pba, false);
6619  }
6620  }
6621 }
6622 
6623 void
6624 axes::properties::update_label_color (handle_property label,
6625  color_property col)
6626 {
6628 
6629  gh_mgr.get_object (label.handle_value ()).set ("color", col.get ());
6630 }
6631 
6632 void
6634 {
6636 
6637  if (! prop.empty ())
6638  {
6639  octave_value val = get (prop);
6640  octave_value tval = val;
6641  if (prop == "fontsize")
6642  {
6643  tval = octave_value (val.double_value () *
6644  get_titlefontsizemultiplier ());
6645  val = octave_value (val.double_value () *
6646  get_labelfontsizemultiplier ());
6647  }
6648  else if (prop == "fontweight")
6649  tval = get ("titlefontweight");
6650 
6651  gh_mgr.get_object (get_xlabel ()).set (prop, val);
6652  gh_mgr.get_object (get_ylabel ()).set (prop, val);
6653  gh_mgr.get_object (get_zlabel ()).set (prop, val);
6654  gh_mgr.get_object (get_title ()).set (prop, tval);
6655  }
6656 
6657  double dpr = device_pixel_ratio (get___myhandle__ ());
6658 
6659  octave::autolock guard (gh_mgr.graphics_lock ());
6660 
6661  m_txt_renderer.set_font (get ("fontname").string_value (),
6662  get ("fontweight").string_value (),
6663  get ("fontangle").string_value (),
6664  get ("__fontsize_points__").double_value () * dpr);
6665 }
6666 
6667 // The INTERNAL flag defines whether position or outerposition is used.
6668 
6669 Matrix
6671  const Matrix& parent_pix_size) const
6672 {
6673  Matrix pos = (internal ? get_position ().matrix_value ()
6674  : get_outerposition ().matrix_value ());
6675  Matrix parent_size (parent_pix_size);
6676 
6677  if (parent_size.isempty ())
6678  {
6680 
6681  graphics_object go = gh_mgr.get_object (get_parent ());
6682 
6683  if (go.valid_object ())
6684  parent_size
6685  = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
6686  else
6687  parent_size = default_figure_position ();
6688  }
6689 
6690  pos = convert_position (pos, get_units (), "pixels", parent_size);
6691 
6692  pos(0)--;
6693  pos(1)--;
6694  pos(1) = parent_size(1) - pos(1) - pos(3);
6695 
6696  return pos;
6697 }
6698 
6699 Matrix
6700 axes::properties::get_extent (bool with_text, bool only_text_height) const
6701 {
6702  graphics_xform xform = get_transform ();
6703 
6704  Matrix ext (1, 4, 0.0);
6705  ext(0) = ext(1) = octave::numeric_limits<double>::Inf ();
6706  ext(2) = ext(3) = -octave::numeric_limits<double>::Inf ();
6707  for (int i = 0; i <= 1; i++)
6708  for (int j = 0; j <= 1; j++)
6709  for (int k = 0; k <= 1; k++)
6710  {
6711  ColumnVector p = xform.transform (i ? m_xPlaneN : m_xPlane,
6712  j ? m_yPlaneN : m_yPlane,
6713  k ? m_zPlaneN : m_zPlane, false);
6714  ext(0) = std::min (ext(0), p(0));
6715  ext(1) = std::min (ext(1), p(1));
6716  ext(2) = std::max (ext(2), p(0));
6717  ext(3) = std::max (ext(3), p(1));
6718  }
6719 
6720  if (with_text)
6721  {
6722  for (int i = 0; i < 4; i++)
6723  {
6724  graphics_handle htext;
6725  if (i == 0)
6726  htext = get_title ();
6727  else if (i == 1)
6728  htext = get_xlabel ();
6729  else if (i == 2)
6730  htext = get_ylabel ();
6731  else if (i == 3)
6732  htext = get_zlabel ();
6733 
6735 
6736  text::properties& text_props
6737  = reinterpret_cast<text::properties&>
6738  (gh_mgr.get_object (htext).get_properties ());
6739 
6740  Matrix text_pos = text_props.get_data_position ();
6741  text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
6742  if (text_props.get_string ().isempty ())
6743  {
6744  ext(0) = std::min (ext(0), text_pos(0));
6745  ext(1) = std::min (ext(1), text_pos(1));
6746  ext(2) = std::max (ext(2), text_pos(0));
6747  ext(3) = std::max (ext(3), text_pos(1));
6748  }
6749  else
6750  {
6751  Matrix text_ext = text_props.get_extent_matrix (true);
6752 
6753  // The text extent is returned in device pixels. Unscale and
6754  // work with logical pixels
6755  double dpr = device_pixel_ratio (get___myhandle__ ());
6756  if (dpr != 1.0)
6757  for (int j = 0; j < 4; j++)
6758  text_ext(j) /= dpr;
6759 
6760  bool ignore_horizontal = false;
6761  bool ignore_vertical = false;
6762  if (only_text_height)
6763  {
6764  double text_rotation = text_props.get_rotation ();
6765  if (text_rotation == 0. || text_rotation == 180.)
6766  ignore_horizontal = true;
6767  else if (text_rotation == 90. || text_rotation == 270.)
6768  ignore_vertical = true;
6769  }
6770 
6771  if (! ignore_horizontal)
6772  {
6773  ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
6774  ext(2) = std::max (ext(2),
6775  text_pos(0)+text_ext(0)+text_ext(2));
6776  }
6777 
6778  if (! ignore_vertical)
6779  {
6780  ext(1) = std::min (ext(1),
6781  text_pos(1)-text_ext(1)-text_ext(3));
6782  ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
6783  }
6784  }
6785  }
6786  }
6787 
6788  ext(2) = ext(2) - ext(0);
6789  ext(3) = ext(3) - ext(1);
6790 
6791  return ext;
6792 }
6793 
6794 static octave_value
6795 convert_ticklabel_string (const octave_value& val)
6796 {
6797  octave_value retval = val;
6798 
6799  if (val.iscellstr ())
6800  {
6801  // Always return a column vector for Matlab compatibility
6802  if (val.columns () > 1)
6803  retval = val.reshape (dim_vector (val.numel (), 1));
6804  }
6805  else
6806  {
6807  string_vector sv;
6808  if (val.isnumeric ())
6809  {
6810  NDArray data = val.array_value ();
6811  std::ostringstream oss;
6812  oss.precision (5);
6813  for (octave_idx_type i = 0; i < val.numel (); i++)
6814  {
6815  oss.str ("");
6816  // FIXME: Code should probably call out to display routines
6817  // within Octave, rather than hack things up with C++ library.
6818  // See FIXME in calc_ticklabels().
6819  if (std::abs (data(i)) < 1.0)
6820  oss.precision (4);
6821  else
6822  oss.precision (5);
6823  oss << data(i);
6824  sv.append (oss.str ());
6825  }
6826  }
6827  else if (val.is_string () && val.rows () == 1)
6828  {
6829  std::string valstr = val.string_value ();
6830  std::istringstream iss (valstr);
6831  std::string tmpstr;
6832 
6833  // Split string with delimiter '|'
6834  while (std::getline (iss, tmpstr, '|'))
6835  sv.append (tmpstr);
6836 
6837  // If string ends with '|' Matlab appends a null string
6838  if (*valstr.rbegin () == '|')
6839  sv.append (std::string (""));
6840  }
6841  else
6842  return retval;
6843 
6844  charMatrix chmat (sv, ' ');
6845 
6846  retval = octave_value (chmat);
6847  }
6848 
6849  return retval;
6850 }
6851 
6852 void
6854 {
6855  if (m_xticklabel.set (convert_ticklabel_string (val), false))
6856  {
6857  set_xticklabelmode ("manual");
6858  m_xticklabel.run_listeners (GCB_POSTSET);
6859  mark_modified ();
6860  }
6861  else
6862  set_xticklabelmode ("manual");
6863 
6864  sync_positions ();
6865 }
6866 
6867 void
6869 {
6870  if (m_yticklabel.set (convert_ticklabel_string (val), false))
6871  {
6872  set_yticklabelmode ("manual");
6873  m_yticklabel.run_listeners (GCB_POSTSET);
6874  mark_modified ();
6875  }
6876  else
6877  set_yticklabelmode ("manual");
6878 
6879  sync_positions ();
6880 }
6881 
6882 void
6884 {
6885  if (m_zticklabel.set (convert_ticklabel_string (val), false))
6886  {
6887  set_zticklabelmode ("manual");
6888  m_zticklabel.run_listeners (GCB_POSTSET);
6889  mark_modified ();
6890  }
6891  else
6892  set_zticklabelmode ("manual");
6893 
6894  sync_positions ();
6895 }
6896 
6897 // Almost identical to convert_ticklabel_string but it only accepts
6898 // cellstr or string, not numeric input.
6899 static octave_value
6900 convert_linestyleorder_string (const octave_value& val)
6901 {
6902  octave_value retval = val;
6903 
6904  if (val.iscellstr ())
6905  {
6906  // Always return a column vector for Matlab Compatibility
6907  if (val.columns () > 1)
6908  retval = val.reshape (dim_vector (val.numel (), 1));
6909  }
6910  else
6911  {
6912  string_vector sv;
6913  if (val.is_string () && val.rows () == 1)
6914  {
6915  std::string valstr = val.string_value ();
6916  std::istringstream iss (valstr);
6917  std::string tmpstr;
6918 
6919  // Split string with delimiter '|'
6920  while (std::getline (iss, tmpstr, '|'))
6921  sv.append (tmpstr);
6922 
6923  // If string ends with '|' Matlab appends a null string
6924  if (*valstr.rbegin () == '|')
6925  sv.append (std::string (""));
6926  }
6927  else
6928  return retval;
6929 
6930  charMatrix chmat (sv, ' ');
6931 
6932  retval = octave_value (chmat);
6933  }
6934 
6935  return retval;
6936 }
6937 
6938 void
6940 {
6941  m_linestyleorder.set (convert_linestyleorder_string (val), false);
6942 }
6943 
6944 void
6946 {
6947  caseless_str old_units = get_units ();
6948 
6949  if (m_units.set (val, true))
6950  {
6951  update_units (old_units);
6952  mark_modified ();
6953  }
6954 }
6955 
6956 void
6958 {
6960 
6961  graphics_object parent_go = gh_mgr.get_object (get_parent ());
6962 
6963  Matrix parent_bb
6964  = parent_go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
6965 
6966  caseless_str new_units = get_units ();
6967  m_position.set (octave_value
6968  (convert_position (get_position ().matrix_value (),
6969  old_units, new_units, parent_bb)),
6970  false);
6971  m_outerposition.set (octave_value
6972  (convert_position (get_outerposition ().matrix_value (),
6973  old_units, new_units, parent_bb)),
6974  false);
6975  m_tightinset.set (octave_value
6976  (convert_position (get_tightinset ().matrix_value (),
6977  old_units, new_units, parent_bb)),
6978  false);
6979  m_looseinset.set (octave_value
6980  (convert_position (get_looseinset ().matrix_value (),
6981  old_units, new_units, parent_bb)),
6982  false);
6983 }
6984 
6985 void
6987 {
6988  caseless_str old_fontunits = get_fontunits ();
6989 
6990  if (m_fontunits.set (val, true))
6991  {
6992  update_fontunits (old_fontunits);
6993  mark_modified ();
6994  }
6995 }
6996 
6997 void
6999 {
7000  caseless_str new_units = get_fontunits ();
7001  double parent_height = get_boundingbox (true).elem (3);
7002  double fontsz = get_fontsize ();
7003 
7004  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
7005 
7006  set_fontsize (octave_value (fontsz));
7007 }
7008 
7009 double
7010 axes::properties::get___fontsize_points__ (double box_pix_height) const
7011 {
7012  double fontsz = get_fontsize ();
7013  double parent_height = box_pix_height;
7014 
7015  if (fontunits_is ("normalized") && parent_height <= 0)
7016  parent_height = get_boundingbox (true).elem (3);
7017 
7018  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
7019 }
7020 
7022 graphics_xform::xform_vector (double x, double y, double z)
7023 {
7024  return octave::xform_vector (x, y, z);
7025 }
7026 
7027 Matrix
7029 {
7030  return octave::xform_matrix ();
7031 }
7032 
7034 graphics_xform::transform (double x, double y, double z, bool use_scale) const
7035 {
7036  if (use_scale)
7037  {
7038  x = m_sx.scale (x);
7039  y = m_sy.scale (y);
7040  z = m_sz.scale (z);
7041  }
7042 
7043  return octave::transform (m_xform, x, y, z);
7044 }
7045 
7047 graphics_xform::untransform (double x, double y, double z,
7048  bool use_scale) const
7049 {
7050  ColumnVector v = octave::transform (m_xform_inv, x, y, z);
7051 
7052  if (use_scale)
7053  {
7054  v(0) = m_sx.unscale (v(0));
7055  v(1) = m_sy.unscale (v(1));
7056  v(2) = m_sz.unscale (v(2));
7057  }
7058 
7059  return v;
7060 }
7061 
7063 axes::get_default (const caseless_str& pname) const
7064 {
7065  octave_value retval = m_default_properties.lookup (pname);
7066 
7067  if (retval.is_undefined ())
7068  {
7069  graphics_handle parent_h = get_parent ();
7070 
7072 
7073  graphics_object parent_go = gh_mgr.get_object (parent_h);
7074 
7075  retval = parent_go.get_default (pname);
7076  }
7077 
7078  return retval;
7079 }
7080 
7081 // FIXME: remove.
7082 // FIXME: maybe this should go into array_property class?
7083 /*
7084 static void
7085 check_limit_vals (double& min_val, double& max_val,
7086  double& min_pos, double& max_neg,
7087  const array_property& data)
7088 {
7089  double val = data.min_val ();
7090  if (octave::math::isfinite (val) && val < min_val)
7091  min_val = val;
7092  val = data.max_val ();
7093  if (octave::math::isfinite (val) && val > max_val)
7094  max_val = val;
7095  val = data.min_pos ();
7096  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7097  min_pos = val;
7098  val = data.max_neg ();
7099  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7100  max_neg = val;
7101 }
7102 */
7103 
7104 static void
7105 check_limit_vals (double& min_val, double& max_val,
7106  double& min_pos, double& max_neg,
7107  const octave_value& data)
7108 {
7109  Matrix m;
7110 
7111  if (data.is_matrix_type ())
7112  m = data.matrix_value ();
7113 
7114  if (m.numel () != 4)
7115  {
7116  m = Matrix (1, 4, 0.0);
7119  }
7120 
7121  double val;
7122 
7123  val = m(0);
7124  if (octave::math::isfinite (val) && val < min_val)
7125  min_val = val;
7126 
7127  val = m(1);
7128  if (octave::math::isfinite (val) && val > max_val)
7129  max_val = val;
7130 
7131  val = m(2);
7132  if (octave::math::isfinite (val) && val > 0 && val < min_pos)
7133  min_pos = val;
7134 
7135  val = m(3);
7136  if (octave::math::isfinite (val) && val < 0 && val > max_neg)
7137  max_neg = val;
7138 }
7139 
7140 // magform(x) Returns (a, b),
7141 // where x = a * 10^b, abs (a) >= 1., and b is integer.
7142 
7143 static void
7144 magform (double x, double& a, int& b)
7145 {
7146  if (x == 0)
7147  {
7148  a = 0;
7149  b = 0;
7150  }
7151  else
7152  {
7153  b = static_cast<int> (std::floor (std::log10 (std::abs (x))));
7154  a = x / std::pow (10.0, b);
7155  }
7156 }
7157 
7158 void
7159 axes::properties::update_outerposition ()
7160 {
7161  set_positionconstraint ("outerposition");
7162  caseless_str old_units = get_units ();
7163  set_units ("normalized");
7164 
7165  Matrix outerbox = m_outerposition.get ().matrix_value ();
7166 
7167  double outer_left = outerbox(0);
7168  double outer_bottom = outerbox(1);
7169  double outer_width = outerbox(2);
7170  double outer_height = outerbox(3);
7171 
7172  double outer_right = outer_width + outer_left;
7173  double outer_top = outer_height + outer_bottom;
7174 
7175  Matrix linset = m_looseinset.get ().matrix_value ();
7176  Matrix tinset = m_tightinset.get ().matrix_value ();
7177 
7178  double left_margin = std::max (linset(0), tinset(0));
7179  double bottom_margin = std::max (linset(1), tinset(1));
7180  double right_margin = std::max (linset(2), tinset(2));
7181  double top_margin = std::max (linset(3), tinset(3));
7182 
7183  double inner_left = outer_left;
7184  double inner_right = outer_right;
7185 
7186  if ((left_margin + right_margin) < outer_width)
7187  {
7188  inner_left += left_margin;
7189  inner_right -= right_margin;
7190  }
7191 
7192  double inner_bottom = outer_bottom;
7193  double inner_top = outer_top;
7194 
7195  if ((bottom_margin + top_margin) < outer_height)
7196  {
7197  inner_bottom += bottom_margin;
7198  inner_top -= top_margin;
7199  }
7200 
7201  double inner_width = inner_right - inner_left;
7202  double inner_height = inner_top - inner_bottom;
7203 
7204  Matrix innerbox (1, 4);
7205 
7206  innerbox(0) = inner_left;
7207  innerbox(1) = inner_bottom;
7208  innerbox(2) = inner_width;
7209  innerbox(3) = inner_height;
7210 
7211  m_position = innerbox;
7212 
7213  set_units (old_units);
7214  update_transform ();
7215 }
7216 
7217 void
7218 axes::properties::update_position ()
7219 {
7220  set_positionconstraint ("innerposition");
7221  caseless_str old_units = get_units ();
7222  set_units ("normalized");
7223 
7224  Matrix innerbox = m_position.get ().matrix_value ();
7225 
7226  double inner_left = innerbox(0);
7227  double inner_bottom = innerbox(1);
7228  double inner_width = innerbox(2);
7229  double inner_height = innerbox(3);
7230 
7231  double inner_right = inner_width + inner_left;
7232  double inner_top = inner_height + inner_bottom;
7233 
7234  Matrix linset = m_looseinset.get ().matrix_value ();
7235  Matrix tinset = m_tightinset.get ().matrix_value ();
7236 
7237  double left_margin = std::max (linset(0), tinset(0));
7238  double bottom_margin = std::max (linset(1), tinset(1));
7239  double right_margin = std::max (linset(2), tinset(2));
7240  double top_margin = std::max (linset(3), tinset(3));
7241 
7242  // FIXME: do we need to place limits on any of these?
7243 
7244  double outer_left = inner_left - left_margin;
7245  double outer_bottom = inner_bottom - bottom_margin;
7246  double outer_right = inner_right + right_margin;
7247  double outer_top = inner_top + top_margin;
7248 
7249  double outer_width = outer_right - outer_left;
7250  double outer_height = outer_top - outer_bottom;
7251 
7252  Matrix outerbox (1, 4);
7253 
7254  outerbox(0) = outer_left;
7255  outerbox(1) = outer_bottom;
7256  outerbox(2) = outer_width;
7257  outerbox(3) = outer_height;
7258 
7259  m_outerposition = outerbox;
7260 
7261  set_units (old_units);
7262  update_transform ();
7263 }
7264 
7265 void
7266 axes::properties::update_looseinset ()
7267 {
7268  caseless_str old_units = get_units ();
7269  set_units ("normalized");
7270 
7271  Matrix linset = m_looseinset.get ().matrix_value ();
7272  Matrix tinset = m_tightinset.get ().matrix_value ();
7273 
7274  double left_margin = std::max (linset(0), tinset(0));
7275  double bottom_margin = std::max (linset(1), tinset(1));
7276  double right_margin = std::max (linset(2), tinset(2));
7277  double top_margin = std::max (linset(3), tinset(3));
7278 
7279  if (m_positionconstraint.is ("innerposition"))
7280  {
7281  Matrix innerbox = m_position.get ().matrix_value ();
7282 
7283  double inner_left = innerbox(0);
7284  double inner_bottom = innerbox(1);
7285  double inner_width = innerbox(2);
7286  double inner_height = innerbox(3);
7287 
7288  double inner_right = inner_width + inner_left;
7289  double inner_top = inner_height + inner_bottom;
7290 
7291  // FIXME: do we need to place limits on any of these?
7292 
7293  double outer_left = inner_left - left_margin;
7294  double outer_bottom = inner_bottom - bottom_margin;
7295  double outer_right = inner_right + right_margin;
7296  double outer_top = inner_top + top_margin;
7297 
7298  double outer_width = outer_right - outer_left;
7299  double outer_height = outer_top - outer_bottom;
7300 
7301  Matrix outerbox (1, 4);
7302 
7303  outerbox(0) = outer_left;
7304  outerbox(1) = outer_bottom;
7305  outerbox(2) = outer_width;
7306  outerbox(3) = outer_height;
7307 
7308  m_outerposition = outerbox;
7309  }
7310  else
7311  {
7312  Matrix outerbox = m_outerposition.get ().matrix_value ();
7313 
7314  double outer_left = outerbox(0);
7315  double outer_bottom = outerbox(1);
7316  double outer_width = outerbox(2);
7317  double outer_height = outerbox(3);
7318 
7319  double outer_right = outer_width + outer_left;
7320  double outer_top = outer_height + outer_bottom;
7321 
7322  double inner_left = outer_left;
7323  double inner_right = outer_right;
7324 
7325  if ((left_margin + right_margin) < outer_width)
7326  {
7327  inner_left += left_margin;
7328  inner_right -= right_margin;
7329  }
7330 
7331  double inner_bottom = outer_bottom;
7332  double inner_top = outer_top;
7333 
7334  if ((bottom_margin + top_margin) < outer_height)
7335  {
7336  inner_bottom += bottom_margin;
7337  inner_top -= top_margin;
7338  }
7339 
7340  double inner_width = inner_right - inner_left;
7341  double inner_height = inner_top - inner_bottom;
7342 
7343  Matrix innerbox (1, 4);
7344 
7345  innerbox(0) = inner_left;
7346  innerbox(1) = inner_bottom;
7347  innerbox(2) = inner_width;
7348  innerbox(3) = inner_height;
7349 
7350  m_position = innerbox;
7351  }
7352 
7353  set_units (old_units);
7354  update_transform ();
7355 }
7356 
7357 // A translation from Tom Holoryd's python code at
7358 // http://kurage.nimh.nih.gov/tomh/tics.py
7359 // FIXME: add log ticks
7360 
7361 double
7362 axes::properties::calc_tick_sep (double lo, double hi)
7363 {
7364  int ticint = 5;
7365 
7366  // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and SCALE3 for
7367  // Determination of Scales on Computer Generated Plots", Communications of
7368  // the ACM, 10 (1973), 639-640.
7369  // Also cited as ACM Algorithm 463.
7370 
7371  double a;
7372  int b, x;
7373 
7374  magform ((hi - lo) / ticint, a, b);
7375 
7376  static const double sqrt_2 = sqrt (2.0);
7377  static const double sqrt_10 = sqrt (10.0);
7378  static const double sqrt_50 = sqrt (50.0);
7379 
7380  if (a < sqrt_2)
7381  x = 1;
7382  else if (a < sqrt_10)
7383  x = 2;
7384  else if (a < sqrt_50)
7385  x = 5;
7386  else
7387  x = 10;
7388 
7389  return x * std::pow (10., b);
7390 }
7391 
7392 // Attempt to make "nice" limits from the actual max and min of the data.
7393 // For log plots, we will also use the smallest strictly positive value.
7394 
7395 Matrix
7397  double min_pos, double max_neg,
7398  const bool logscale,
7399  const std::string& method)
7400 {
7401  Matrix retval;
7402 
7403  double min_val = xmin;
7404  double max_val = xmax;
7405 
7406  if (octave::math::isinf (min_val) && min_val > 0
7407  && octave::math::isinf (max_val) && max_val < 0)
7408  {
7409  retval = default_lim (logscale);
7410  return retval;
7411  }
7412  else if (! (octave::math::isinf (min_val) || octave::math::isinf (max_val)))
7413  {
7414  if (logscale)
7415  {
7416  if (octave::math::isinf (min_pos) && octave::math::isinf (max_neg))
7417  {
7418  // FIXME: max_neg is needed for "loglog ([0 -Inf])"
7419  // This is the *only* place where max_neg is needed.
7420  // Is there another way?
7421  retval = default_lim (logscale);
7422  return retval;
7423  }
7424  if (min_val <= 0)
7425  {
7426  if (max_val > 0)
7427  {
7428  warning_with_id ("Octave:negative-data-log-axis",
7429  "axis: omitting non-positive data in log plot");
7430  min_val = min_pos;
7431  }
7432  else if (max_val == 0)
7433  max_val = max_neg;
7434  }
7435  // FIXME: maybe this test should also be relative?
7436  if (std::abs (min_val - max_val)
7437  < sqrt (std::numeric_limits<double>::epsilon ()))
7438  {
7439  // Widen range when too small
7440  if (min_val >= 0)
7441  {
7442  min_val *= 0.9;
7443  max_val *= 1.1;
7444  }
7445  else
7446  {
7447  min_val *= 1.1;
7448  max_val *= 0.9;
7449  }
7450  }
7451 
7452  if (method == "tickaligned")
7453  {
7454  if (min_val > 0)
7455  {
7456  // Log plots with all positive data
7457  min_val = std::pow (10, std::floor (log10 (min_val)));
7458  max_val = std::pow (10, std::ceil (log10 (max_val)));
7459  }
7460  else
7461  {
7462  // Log plots with all negative data
7463  min_val = -std::pow (10, std::ceil (log10 (-min_val)));
7464  max_val = -std::pow (10, std::floor (log10 (-max_val)));
7465  }
7466  }
7467  else if (method == "padded")
7468  {
7469  if (min_val > 0)
7470  {
7471  // Log plots with all positive data
7472  double pad = (log10 (max_val) - log10 (min_val)) * 0.07;
7473  min_val = std::pow (10, log10 (min_val) - pad);
7474  max_val = std::pow (10, log10 (max_val) + pad);
7475  }
7476  else
7477  {
7478  // Log plots with all negative data
7479  double pad = (log10 (-min_val) - log10 (-max_val)) * 0.07;
7480  min_val = -std::pow (10, log10 (-min_val) + pad);
7481  max_val = -std::pow (10, log10 (-max_val) - pad);
7482  }
7483  }
7484  }
7485  else
7486  {
7487  if (min_val == 0 && max_val == 0)
7488  {
7489  min_val = -1;
7490  max_val = 1;
7491  }
7492  // FIXME: maybe this test should also be relative?
7493  else if (std::abs (min_val - max_val)
7494  < sqrt (std::numeric_limits<double>::epsilon ()))
7495  {
7496  min_val -= 0.1 * std::abs (min_val);
7497  max_val += 0.1 * std::abs (max_val);
7498  }
7499 
7500  if (method == "tickaligned")
7501  {
7502  double tick_sep = calc_tick_sep (min_val, max_val);
7503  double min_tick = std::floor (min_val / tick_sep);
7504  double max_tick = std::ceil (max_val / tick_sep);
7505  // Prevent round-off from cropping ticks
7506  min_val = std::min (min_val, tick_sep * min_tick);
7507  max_val = std::max (max_val, tick_sep * max_tick);
7508  }
7509  else if (method == "padded")
7510  {
7511  double pad = 0.07 * (max_val - min_val);
7512  min_val -= pad;
7513  max_val += pad;
7514  }
7515  }
7516  }
7517 
7518  retval.resize (1, 2);
7519 
7520  retval(0) = min_val;
7521  retval(1) = max_val;
7522 
7523  return retval;
7524 }
7525 
7526 void
7528  const bool logscale, char& update_type)
7529 {
7530  double min_val = octave::numeric_limits<double>::Inf ();
7531  double max_val = -octave::numeric_limits<double>::Inf ();
7532  double min_pos = octave::numeric_limits<double>::Inf ();
7533  double max_neg = -octave::numeric_limits<double>::Inf ();
7534  double eps = std::numeric_limits<double>::epsilon ();
7535  bool do_update = false;
7536  bool have_children_limits = false;
7537 
7538  // check whether we need to get children limits
7539  if (! octave::math::isfinite (limits(0))
7540  || ! octave::math::isfinite (limits(1)))
7541  {
7542  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7543  update_type);
7544  have_children_limits = true;
7545  }
7546  if (! octave::math::isfinite (limits(0)))
7547  {
7548  limits(0) = min_val;
7549  do_update = true;
7550  }
7551  if (! octave::math::isfinite (limits(1)))
7552  {
7553  limits(1) = max_val;
7554  do_update = true;
7555  }
7556  if (limits(0) == 0 && limits(1) == 0)
7557  {
7558  limits = default_lim (logscale);
7559  do_update = true;
7560  }
7561  // FIXME: maybe this test should also be relative?
7562  else if (! logscale && (std::abs (limits(0) - limits(1)) < sqrt (eps)))
7563  {
7564  limits(0) -= 0.1 * std::abs (limits(0));
7565  limits(1) += 0.1 * std::abs (limits(1));
7566  do_update = true;
7567  }
7568  else if (logscale
7569  && (std::abs (std::log10 (limits(0) / limits(1))) < sqrt (eps)))
7570  {
7571  limits(0) = (limits(0) < 0 ? 10.0 * limits(0) : 0.1 * limits(0));
7572  limits(1) = (limits(1) < 0 ? 0.1 * limits(1) : 10.0 * limits(1));
7573  do_update = true;
7574  }
7575 
7576  if (logscale && limits(0)*limits(1) <= 0)
7577  {
7578  if (! have_children_limits)
7579  get_children_limits (min_val, max_val, min_pos, max_neg, kids,
7580  update_type);
7581 
7582  if (limits(1) > 0)
7583  {
7584  warning_with_id ("Octave:axis-non-positive-log-limits",
7585  "Non-positive limit for logarithmic axis ignored\n");
7586  if (octave::math::isfinite (min_pos))
7587  limits(0) = min_pos;
7588  else
7589  limits(0) = 0.1 * limits(1);
7590  }
7591  else
7592  {
7593  warning_with_id ("Octave:axis-non-negative-log-limits",
7594  "Non-negative limit for logarithmic axis ignored\n");
7595  if (octave::math::isfinite (max_neg))
7596  limits(1) = max_neg;
7597  else
7598  limits(1) = 0.1 * limits(0);
7599  }
7600  // FIXME: maybe this test should also be relative?
7601  if (std::abs (limits(0) - limits(1)) < sqrt (eps))
7602  {
7603  // Widen range when too small
7604  if (limits(0) > 0)
7605  {
7606  limits(0) *= 0.9;
7607  limits(1) *= 1.1;
7608  }
7609  else
7610  {
7611  limits(0) *= 1.1;
7612  limits(1) *= 0.9;
7613  }
7614  }
7615  do_update = true;
7616  }
7617 
7618  if (! do_update)
7619  update_type = 0;
7620 
7621 }
7622 
7623 /*
7624 ## Test validation of auto and manual axis limits
7625 %!test
7626 %! hf = figure ("visible", "off");
7627 %! unwind_protect
7628 %! hax = axes ("parent", hf);
7629 %! plot (0, pi);
7630 %! assert (get (hax, "xlim"), [-1, 1]);
7631 %! assert (get (hax, "xlimmode"), "auto");
7632 %! assert (get (hax, "ylim"), [2.8, 3.5], 2*eps);
7633 %! assert (get (hax, "ylimmode"), "auto");
7634 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7635 %! assert (get (hax, "xlim"), [0.9, 1.1]);
7636 %! assert (get (hax, "xlimmode"), "manual");
7637 %! assert (get (hax, "ylim"), [0, 1]);
7638 %! assert (get (hax, "ylimmode"), "manual");
7639 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7640 %! ## Matlab does not update the properties
7641 %! assert (get (hax, "xlim"), [0, 1]);
7642 %! assert (get (hax, "ylim"), [0.9, 1.1]*pi, 2*eps);
7643 %! unwind_protect_cleanup
7644 %! delete (hf);
7645 %! end_unwind_protect
7646 
7647 %!test
7648 %! hf = figure ("visible", "off");
7649 %! unwind_protect
7650 %! hax = axes ("parent", hf);
7651 %! plot ([0, exp(1)], [-pi, 3]);
7652 %! assert (get (hax, "xlim"), [0, 3]);
7653 %! assert (get (hax, "xlimmode"), "auto");
7654 %! assert (get (hax, "ylim"), [-4, 3]);
7655 %! assert (get (hax, "ylimmode"), "auto");
7656 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7657 %! ## Matlab does not update the properties but uses tight limits on screen
7658 %! assert (get (hax, "xlim"), [0, exp(1)]);
7659 %! assert (get (hax, "xlimmode"), "manual");
7660 %! assert (get (hax, "ylim"), [-pi, 3]);
7661 %! assert (get (hax, "ylimmode"), "manual");
7662 %! unwind_protect_cleanup
7663 %! delete (hf);
7664 %! end_unwind_protect
7665 
7666 %!test
7667 %! hf = figure ("visible", "off");
7668 %! unwind_protect
7669 %! hax = axes ("parent", hf);
7670 %! loglog (0, pi);
7671 %! assert (get (hax, "xlim"), [0.1, 1.0]);
7672 %! assert (get (hax, "xlimmode"), "auto");
7673 %! assert (get (hax, "ylim"), [1, 10]);
7674 %! assert (get (hax, "ylimmode"), "auto");
7675 %! set (hax, "xlim", [1, 1], "ylim", [0, 0]);
7676 %! assert (get (hax, "xlim"), [0.1, 10]);
7677 %! assert (get (hax, "xlimmode"), "manual");
7678 %! assert (get (hax, "ylim"), [0.1, 1.0]);
7679 %! assert (get (hax, "ylimmode"), "manual");
7680 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7681 %! ## Matlab does not update the properties
7682 %! assert (get (hax, "xlim"), [0.1, 1.0]);
7683 %! assert (get (hax, "ylim"), [0.1, 10]*pi);
7684 %! unwind_protect_cleanup
7685 %! delete (hf);
7686 %! end_unwind_protect
7687 
7688 %!test
7689 %! hf = figure ("visible", "off");
7690 %! unwind_protect
7691 %! hax = axes ("parent", hf);
7692 %! loglog ([0 -1], [0 1]);
7693 %! assert (get (hax, "xlim"), [-10, -0.1]);
7694 %! assert (get (hax, "xlimmode"), "auto");
7695 %! assert (get (hax, "ylim"), [0.1, 10]);
7696 %! assert (get (hax, "ylimmode"), "auto");
7697 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7698 %! ## Matlab does not update the properties
7699 %! assert (get (hax, "xlim"), [-1.1, -0.9]);
7700 %! assert (get (hax, "ylim"), [0.9, 1.1]);
7701 %! unwind_protect_cleanup
7702 %! delete (hf);
7703 %! end_unwind_protect
7704 
7705 %!test
7706 %! hf = figure ("visible", "off");
7707 %! unwind_protect
7708 %! hax = axes ("parent", hf);
7709 %! loglog ([1 -1], [1 pi]);
7710 %! assert (get (hax, "xlim"), [0.1, 10]);
7711 %! assert (get (hax, "xlimmode"), "auto");
7712 %! assert (get (hax, "ylim"), [1, 10]);
7713 %! assert (get (hax, "ylimmode"), "auto");
7714 %! set (hax, "xlim", [-Inf, Inf], "ylim", [-Inf, Inf]);
7715 %! ## Matlab does not update the properties but uses tight limits on screen
7716 %! assert (get (hax, "xlim"), [0.9, 1.1]);
7717 %! assert (get (hax, "ylim"), [1, pi]);
7718 %! unwind_protect_cleanup
7719 %! delete (hf);
7720 %! end_unwind_protect
7721 
7722 ## Check that graphics objects with hidden handle visibility are included in
7723 ## axis limit calculation.
7724 %!test <*63095>
7725 %! hf = figure ("visible", "off");
7726 %! unwind_protect
7727 %! hax = axes ("parent", hf);
7728 %! plot (hax, [0, 1]);
7729 %! assert (get (hax, "ylim"), [0, 1]);
7730 %! hold (hax, "on");
7731 %! plot (hax, [2, 0], "handlevisibility", "off");
7732 %! assert (get (hax, "ylim"), [0, 2]);
7733 %! unwind_protect_cleanup
7734 %! delete (hf);
7735 %! end_unwind_protect
7736 
7737 ## Check automatically determined axes ticks with "tickaligned" (default) and
7738 ## "tight" xlimitmethod.
7739 %!test <*63624>
7740 %! hf = figure ("visible", "off");
7741 %! unwind_protect
7742 %! hax = axes ("parent", hf);
7743 %! plot (hax, [1, 201], [0, 1]);
7744 %! assert (get (hax, "xtick"), 0:50:250);
7745 %! axis (hax, "tight");
7746 %! assert (get (hax, "xtick"), 50:50:200);
7747 %! unwind_protect_cleanup
7748 %! delete (hf);
7749 %! end_unwind_protect
7750 */
7751 
7752 void
7753 axes::properties::calc_ticks_and_lims (array_property& lims,
7754  array_property& ticks,
7755  array_property& mticks,
7756  bool limmode_is_auto,
7757  bool tickmode_is_auto,
7758  bool is_logscale,
7759  bool method_is_padded,
7760  bool method_is_tight)
7761 {
7762  if (lims.get ().isempty ())
7763  return;
7764 
7765  double lo = (lims.get ().matrix_value ())(0);
7766  double hi = (lims.get ().matrix_value ())(1);
7767 
7768  double lo_lim = lo;
7769  double hi_lim = hi;
7770  bool is_negative = lo < 0 && hi < 0;
7771 
7772  // FIXME: should this be checked for somewhere else? (i.e., set{x,y,z}lim)
7773  if (hi < lo)
7774  std::swap (hi, lo);
7775 
7776  if (is_logscale)
7777  {
7778  if (is_negative)
7779  {
7780  double tmp = hi;
7781  hi = std::log10 (-lo);
7782  lo = std::log10 (-tmp);
7783  }
7784  else
7785  {
7786  hi = std::log10 (hi);
7787  lo = std::log10 (lo);
7788  }
7789  }
7790 
7791  Matrix tmp_ticks;
7792  if (tickmode_is_auto)
7793  {
7794  double tick_sep;
7795 
7796  if (is_logscale)
7797  {
7798  if (! (octave::math::isinf (hi) || octave::math::isinf (lo)))
7799  tick_sep = 1; // Tick is every order of magnitude (bug #39449)
7800  else
7801  tick_sep = 0;
7802  }
7803  else
7804  tick_sep = calc_tick_sep (lo, hi);
7805 
7806  double i1 = std::floor (lo / tick_sep);
7807  double i2 = std::ceil (hi / tick_sep);
7808 
7809  if (limmode_is_auto)
7810  {
7811  Matrix tmp_lims (1, 2);
7812 
7813  if (! method_is_padded && ! method_is_tight)
7814  {
7815  // Adjust limits to include min and max ticks
7816  tmp_lims(0) = std::min (tick_sep * i1, lo);
7817  tmp_lims(1) = std::max (tick_sep * i2, hi);
7818  }
7819  else
7820  {
7821  tmp_lims(0) = lo;
7822  tmp_lims(1) = hi;
7823 
7824  // adjust min and max ticks to be within limits
7825  if (i1*tick_sep < lo)
7826  i1++;
7827  if (i2*tick_sep > hi && i2 > i1)
7828  i2--;
7829  }
7830 
7831  if (is_logscale)
7832  {
7833  tmp_lims(0) = std::pow (10., tmp_lims(0));
7834  tmp_lims(1) = std::pow (10., tmp_lims(1));
7835 
7836  if (tmp_lims(0) <= 0)
7837  tmp_lims(0) = std::pow (10., lo);
7838 
7839  if (is_negative)
7840  {
7841  double tmp = tmp_lims(0);
7842  tmp_lims(0) = -tmp_lims(1);
7843  tmp_lims(1) = -tmp;
7844  }
7845  }
7846 
7847  lims = tmp_lims;
7848  }
7849  else
7850  {
7851  // adjust min and max ticks to be within limits
7852  if (i1*tick_sep < lo)
7853  i1++;
7854  if (i2*tick_sep > hi && i2 > i1)
7855  i2--;
7856  }
7857 
7858  tmp_ticks = Matrix (1, i2-i1+1);
7859  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
7860  {
7861  tmp_ticks(i) = tick_sep * (i+i1);
7862  if (is_logscale)
7863  tmp_ticks(i) = std::pow (10., tmp_ticks(i));
7864  }
7865  if (is_logscale && is_negative)
7866  {
7867  Matrix rev_ticks (1, i2-i1+1);
7868  rev_ticks = -tmp_ticks;
7869  for (int i = 0; i <= static_cast<int> (i2-i1); i++)
7870  tmp_ticks(i) = rev_ticks(i2-i1-i);
7871  }
7872 
7873  ticks = tmp_ticks;
7874  }
7875  else
7876  tmp_ticks = ticks.get ().matrix_value ();
7877 
7878  octave_idx_type n_ticks = tmp_ticks.numel ();
7879  if (n_ticks < 2)
7880  return;
7881 
7882  // minor ticks between, above, and below min and max ticks
7883  const int MAX_MINOR_TICKS = 1000;
7884  int n = (is_logscale ? 8 : 4);
7885  double mult_below = (is_logscale ? tmp_ticks(1) / tmp_ticks(0) : 1);
7886  double mult_above = (is_logscale ? tmp_ticks(n_ticks-1) / tmp_ticks(n_ticks-2)
7887  : 1);
7888 
7889  double d_below = (tmp_ticks(1) - tmp_ticks(0)) / mult_below / (n+1);
7890  int n_below = static_cast<int> (std::floor ((tmp_ticks(0)-lo_lim) / d_below));
7891  if (n_below < 0)
7892  n_below = 0;
7893  else if (n_below > MAX_MINOR_TICKS)
7894  n_below = MAX_MINOR_TICKS;
7895 
7896  int n_between = n * (n_ticks - 1);
7897  double d_above = (tmp_ticks(n_ticks-1) - tmp_ticks(n_ticks-2)) * mult_above
7898  / (n+1);
7899  int n_above = static_cast<int> (std::floor ((hi_lim-tmp_ticks(n_ticks-1))
7900  / d_above));
7901  if (n_above < 0)
7902  n_above = 0;
7903  else if (n_above > MAX_MINOR_TICKS)
7904  n_above = MAX_MINOR_TICKS;
7905 
7906  Matrix tmp_mticks (1, n_below + n_between + n_above);
7907  for (int i = 0; i < n_below; i++)
7908  tmp_mticks(i) = tmp_ticks(0) - (n_below-i) * d_below;
7909  for (int i = 0; i < n_ticks-1; i++)
7910  {
7911  double d = (tmp_ticks(i+1) - tmp_ticks(i)) / (n + 1);
7912  for (int j = 0; j < n; j++)
7913  tmp_mticks(n_below+n*i+j) = tmp_ticks(i) + d * (j+1);
7914  }
7915  for (int i = 0; i < n_above; i++)
7916  tmp_mticks(n_below+n_between+i) = tmp_ticks(n_ticks-1) + (i + 1) * d_above;
7917 
7918  mticks = tmp_mticks;
7919 }
7920 
7921 /*
7922 %!test <*45356>
7923 %! hf = figure ("visible", "off");
7924 %! unwind_protect
7925 %! plot (1:10);
7926 %! xlim ([4.75, 8.5]);
7927 %! tics = get (gca, "xtick");
7928 %! assert (tics, [5 6 7 8]);
7929 %! unwind_protect_cleanup
7930 %! close (hf);
7931 %! end_unwind_protect
7932 */
7933 
7934 void
7935 axes::properties::calc_ticklabels (const array_property& ticks,
7936  any_property& labels, bool logscale,
7937  const bool is_origin,
7938  const int other_axislocation,
7939  const array_property& axis_lims)
7940 {
7941  Matrix values = ticks.get ().matrix_value ();
7942  Matrix lims = axis_lims.get ().matrix_value ();
7943  Cell c (dim_vector (values.numel (), 1)); // column vector for ML compat.
7944  std::ostringstream os;
7945 
7946  // omit tick labels depending on location of other axis
7948  if (get_is2D () && is_origin)
7949  {
7950  if (other_axislocation == 0)
7951  {
7952  omit_ticks(0) = octave::math::max (octave::math::min (0., lims(1)),
7953  lims(0));
7954  }
7955  else if (other_axislocation == 1)
7956  omit_ticks(0) = lims(1);
7957  else if (other_axislocation == -1)
7958  omit_ticks(0) = lims(0);
7959  if (is_box ())
7960  {
7961  omit_ticks(1) = lims(0);
7962  omit_ticks(2) = lims(1);
7963  }
7964  }
7965 
7966  if (logscale)
7967  {
7968  double significand;
7969  double exponent;
7970  bool is_2digit_exp = false;
7971 
7972  for (int i = 0; i < values.numel (); i++)
7973  {
7974  double exp = std::abs (std::log10 (values(i)));
7975  if (exp >= 10.0)
7976  {
7977  is_2digit_exp = true;
7978  break;
7979  }
7980  }
7981 
7982  for (int i = 0; i < values.numel (); i++)
7983  {
7984  bool omit_tick = false;
7985  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
7986  if (values(i) == omit_ticks(i_omit))
7987  omit_tick = true;
7988  if (omit_tick)
7989  {
7990  c(i) = "";
7991  continue;
7992  }
7993 
7994  if (values(i) < 0.0)
7995  exponent = std::floor (std::log10 (-values(i)));
7996  else
7997  exponent = std::floor (std::log10 (values(i)));
7998  significand = values(i) * std::pow (10.0, -exponent);
7999 
8000  os.precision (5);
8001  os.str ("");
8002  if ((std::abs (significand) - 1) >
8003  10*std::numeric_limits<double>::epsilon())
8004  os << significand << 'x';
8005  else if (significand < 0)
8006  os << '-';
8007 
8008  os << "10^{";
8009 
8010  if (exponent < 0.0)
8011  {
8012  os << '-';
8013  exponent = -exponent;
8014  }
8015  if (exponent < 10.0 && is_2digit_exp)
8016  os << '0';
8017  os << exponent << '}';
8018 
8019  if (m_ticklabelinterpreter.is ("latex"))
8020  c(i) = "$" + os.str () + "$";
8021  else
8022  c(i) = os.str ();
8023  }
8024  }
8025  else
8026  {
8027  for (int i = 0; i < values.numel (); i++)
8028  {
8029  bool omit_tick = false;
8030  for (int i_omit = 0; i_omit < omit_ticks.numel (); i_omit++)
8031  if (values(i) == omit_ticks(i_omit))
8032  omit_tick = true;
8033  if (omit_tick)
8034  c(i) = "";
8035  else
8036  {
8037  os.str ("");
8038  // FIXME: Code should probably call out to display routines
8039  // within Octave, rather than hack things up with C++ library.
8040  // In particular, this fails for values much less than 1 where
8041  // 4 significant digits will be preceded by zeros making the
8042  // overall field length large. For example, pi/1000.
8043  if (std::abs (values(i)) < 1.0)
8044  os.precision (4);
8045  else
8046  os.precision (5);
8047  os << values(i);
8048  c(i) = os.str ();
8049  }
8050  }
8051  }
8052 
8053  labels = c;
8054 }
8055 
8056 Matrix
8057 axes::properties::get_ticklabel_extents (const Matrix& ticks,
8058  const string_vector& ticklabels,
8059  const Matrix& limits)
8060 {
8061  Matrix ext (1, 2, 0.0);
8062  double wmax, hmax;
8063  double dpr = device_pixel_ratio (get___myhandle__ ());
8064  wmax = hmax = 0.0;
8065  int n = std::min (ticklabels.numel (), ticks.numel ());
8066  for (int i = 0; i < n; i++)
8067  {
8068  double val = ticks(i);
8069  if (limits(0) <= val && val <= limits(1))
8070  {
8071  std::string label (ticklabels(i));
8072  label.erase (0, label.find_first_not_of (' '));
8073  label = label.substr (0, label.find_last_not_of (' ')+1);
8074 
8075  if (m_txt_renderer.ok ())
8076  {
8078 
8079  octave::autolock guard (gh_mgr.graphics_lock ());
8080 
8081  ext = m_txt_renderer.get_extent (label, 0.0,
8082  get_ticklabelinterpreter ());
8083 
8084  wmax = std::max (wmax, ext(0) / dpr);
8085  hmax = std::max (hmax, ext(1) / dpr);
8086  }
8087  else
8088  {
8089  // FIXME: find a better approximation
8090  double fsize = get ("fontsize").double_value ();
8091  int len = label.length ();
8092 
8093  wmax = std::max (wmax, 0.5*fsize*len);
8094  hmax = fsize;
8095  }
8096  }
8097  }
8098 
8099  ext(0) = wmax;
8100  ext(1) = hmax;
8101  return ext;
8102 }
8103 
8104 void
8105 get_children_limits (double& min_val, double& max_val,
8106  double& min_pos, double& max_neg,
8107  const Matrix& kids, char limit_type)
8108 {
8109  octave_idx_type n = kids.numel ();
8110 
8112 
8113  switch (limit_type)
8114  {
8115  case 'x':
8116  for (octave_idx_type i = 0; i < n; i++)
8117  {
8118  graphics_object go = gh_mgr.get_object (kids(i));
8119 
8120  if (go.is_xliminclude ())
8121  {
8122  octave_value lim = go.get_xlim ();
8123 
8124  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8125  }
8126  }
8127  break;
8128 
8129  case 'y':
8130  for (octave_idx_type i = 0; i < n; i++)
8131  {
8132  graphics_object go = gh_mgr.get_object (kids(i));
8133 
8134  if (go.is_yliminclude ())
8135  {
8136  octave_value lim = go.get_ylim ();
8137 
8138  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8139  }
8140  }
8141  break;
8142 
8143  case 'z':
8144  for (octave_idx_type i = 0; i < n; i++)
8145  {
8146  graphics_object go = gh_mgr.get_object (kids(i));
8147 
8148  if (go.is_zliminclude ())
8149  {
8150  octave_value lim = go.get_zlim ();
8151 
8152  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8153  }
8154  }
8155  break;
8156 
8157  case 'c':
8158  for (octave_idx_type i = 0; i < n; i++)
8159  {
8160  graphics_object go = gh_mgr.get_object (kids(i));
8161 
8162  if (go.is_climinclude ())
8163  {
8164  octave_value lim = go.get_clim ();
8165 
8166  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8167  }
8168  }
8169  break;
8170 
8171  case 'a':
8172  for (octave_idx_type i = 0; i < n; i++)
8173  {
8174  graphics_object go = gh_mgr.get_object (kids(i));
8175 
8176  if (go.is_aliminclude ())
8177  {
8178  octave_value lim = go.get_alim ();
8179 
8180  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
8181  }
8182  }
8183  break;
8184 
8185  default:
8186  break;
8187  }
8188 }
8189 
8190 static std::set<double> updating_axis_limits;
8191 
8192 void
8193 axes::update_axis_limits (const std::string& axis_type,
8194  const graphics_handle& h)
8195 {
8196  if (updating_axis_limits.find (get_handle ().value ())
8197  != updating_axis_limits.end ())
8198  return;
8199 
8200  Matrix kids = Matrix (1, 1, h.value ());
8201 
8202  double min_val = octave::numeric_limits<double>::Inf ();
8203  double max_val = -octave::numeric_limits<double>::Inf ();
8204  double min_pos = octave::numeric_limits<double>::Inf ();
8205  double max_neg = -octave::numeric_limits<double>::Inf ();
8206 
8207  char update_type = 0;
8208 
8209  Matrix limits (1, 2);
8210  double val;
8211 
8212 #define FIX_LIMITS \
8213  val = limits(0); \
8214  if (octave::math::isfinite (val)) \
8215  min_val = val; \
8216  val = limits(1); \
8217  if (octave::math::isfinite (val)) \
8218  max_val = val;
8219 
8220  if (axis_type == "xdata" || axis_type == "xscale"
8221  || axis_type == "xlimmode" || axis_type == "xliminclude"
8222  || axis_type == "xlim")
8223  {
8224  limits = m_properties.get_xlim ().matrix_value ();
8225  FIX_LIMITS;
8226 
8227  update_type = 'x';
8228  if (m_properties.xlimmode_is ("auto"))
8229  {
8230  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8231 
8232  std::string method = m_properties.get_xlimitmethod ();
8233  limits = m_properties.get_axis_limits (min_val, max_val,
8234  min_pos, max_neg,
8235  m_properties.xscale_is ("log"),
8236  method);
8237  }
8238  else
8239  m_properties.check_axis_limits (limits, kids,
8240  m_properties.xscale_is ("log"),
8241  update_type);
8242  }
8243  else if (axis_type == "ydata" || axis_type == "yscale"
8244  || axis_type == "ylimmode" || axis_type == "yliminclude"
8245  || axis_type == "ylim")
8246  {
8247  limits = m_properties.get_ylim ().matrix_value ();
8248  FIX_LIMITS;
8249 
8250  update_type = 'y';
8251  if (m_properties.ylimmode_is ("auto"))
8252  {
8253  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8254 
8255  std::string method = m_properties.get_ylimitmethod ();
8256  limits = m_properties.get_axis_limits (min_val, max_val,
8257  min_pos, max_neg,
8258  m_properties.yscale_is ("log"),
8259  method);
8260  }
8261  else
8262  m_properties.check_axis_limits (limits, kids,
8263  m_properties.yscale_is ("log"),
8264  update_type);
8265  }
8266  else if (axis_type == "zdata" || axis_type == "zscale"
8267  || axis_type == "zlimmode" || axis_type == "zliminclude"
8268  || axis_type == "zlim")
8269  {
8270  limits = m_properties.get_zlim ().matrix_value ();
8271  FIX_LIMITS;
8272 
8273  update_type = 'z';
8274  if (m_properties.zlimmode_is ("auto"))
8275  {
8276  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8277 
8278  m_properties.set_has3Dkids ((max_val - min_val) >
8279  std::numeric_limits<double>::epsilon ());
8280 
8281  std::string method = m_properties.get_zlimitmethod ();
8282  limits = m_properties.get_axis_limits (min_val, max_val,
8283  min_pos, max_neg,
8284  m_properties.zscale_is ("log"),
8285  method);
8286  }
8287  else
8288  {
8289  // FIXME: get_children_limits is only needed here in order to know
8290  // if there are 3D children. Is there a way to avoid this call?
8291  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8292 
8293  m_properties.set_has3Dkids ((max_val - min_val) >
8294  std::numeric_limits<double>::epsilon ());
8295 
8296  m_properties.check_axis_limits (limits, kids,
8297  m_properties.zscale_is ("log"),
8298  update_type);
8299  }
8300  }
8301  else if (axis_type == "cdata" || axis_type == "climmode"
8302  || axis_type == "cdatamapping" || axis_type == "climinclude"
8303  || axis_type == "clim")
8304  {
8305  if (m_properties.climmode_is ("auto"))
8306  {
8307  limits = m_properties.get_clim ().matrix_value ();
8308  FIX_LIMITS;
8309 
8310  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8311 
8312  if (min_val > max_val)
8313  {
8314  min_val = min_pos = 0;
8315  max_val = 1;
8316  }
8317  else if (min_val == max_val)
8318  {
8319  max_val = min_val + 1;
8320  min_val -= 1;
8321  }
8322 
8323  limits(0) = min_val;
8324  limits(1) = max_val;
8325 
8326  update_type = 'c';
8327  }
8328  }
8329  else if (axis_type == "alphadata" || axis_type == "alimmode"
8330  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8331  || axis_type == "alim")
8332  {
8333  if (m_properties.alimmode_is ("auto"))
8334  {
8335  limits = m_properties.get_alim ().matrix_value ();
8336  FIX_LIMITS;
8337 
8338  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8339 
8340  if (min_val > max_val)
8341  {
8342  min_val = min_pos = 0;
8343  max_val = 1;
8344  }
8345  else if (min_val == max_val)
8346  max_val = min_val + 1;
8347 
8348  limits(0) = min_val;
8349  limits(1) = max_val;
8350 
8351  update_type = 'a';
8352  }
8353  }
8354 
8355 #undef FIX_LIMITS
8356 
8357  octave::unwind_protect_var<std::set<double>>
8358  restore_var (updating_axis_limits);
8359 
8360  updating_axis_limits.insert (get_handle ().value ());
8361  bool is_auto;
8362 
8363  switch (update_type)
8364  {
8365  case 'x':
8366  is_auto = m_properties.xlimmode_is ("auto");
8367  m_properties.set_xlim (limits);
8368  if (is_auto)
8369  m_properties.set_xlimmode ("auto");
8370  m_properties.update_xlim ();
8371  break;
8372 
8373  case 'y':
8374  is_auto = m_properties.ylimmode_is ("auto");
8375  m_properties.set_ylim (limits);
8376  if (is_auto)
8377  m_properties.set_ylimmode ("auto");
8378  m_properties.update_ylim ();
8379  break;
8380 
8381  case 'z':
8382  is_auto = m_properties.zlimmode_is ("auto");
8383  m_properties.set_zlim (limits);
8384  if (is_auto)
8385  m_properties.set_zlimmode ("auto");
8386  m_properties.update_zlim ();
8387  break;
8388 
8389  case 'c':
8390  m_properties.set_clim (limits);
8391  m_properties.set_climmode ("auto");
8392  break;
8393 
8394  case 'a':
8395  m_properties.set_alim (limits);
8396  m_properties.set_alimmode ("auto");
8397  break;
8398 
8399  default:
8400  break;
8401  }
8402 
8403  m_properties.update_transform ();
8404 }
8405 
8406 // FIXME: This function is called repeatedly while the axes are being set up.
8407 // There is probably some way to make this more efficient.
8408 
8409 void
8410 axes::update_axis_limits (const std::string& axis_type)
8411 {
8412  if ((updating_axis_limits.find (get_handle ().value ())
8413  != updating_axis_limits.end ())
8414  || (updating_aspectratios.find (get_handle ().value ())
8415  != updating_aspectratios.end ()))
8416  return;
8417 
8418  Matrix kids = m_properties.get_all_children ();
8419 
8420  double min_val = octave::numeric_limits<double>::Inf ();
8421  double max_val = -octave::numeric_limits<double>::Inf ();
8422  double min_pos = octave::numeric_limits<double>::Inf ();
8423  double max_neg = -octave::numeric_limits<double>::Inf ();
8424 
8425  char update_type = 0;
8426 
8427  Matrix limits;
8428 
8429  if (axis_type == "xdata" || axis_type == "xscale"
8430  || axis_type == "xlimmode" || axis_type == "xliminclude"
8431  || axis_type == "xlim")
8432  {
8433  update_type = 'x';
8434  if (m_properties.xlimmode_is ("auto"))
8435  {
8436  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
8437 
8438  std::string method = m_properties.get_xlimitmethod ();
8439  limits = m_properties.get_axis_limits (min_val, max_val,
8440  min_pos, max_neg,
8441  m_properties.xscale_is ("log"),
8442  method);
8443  }
8444  else
8445  {
8446  limits = m_properties.get_xlim ().matrix_value ();
8447  m_properties.check_axis_limits (limits, kids,
8448  m_properties.xscale_is ("log"),
8449  update_type);
8450  if (axis_type == "xscale")
8451  update_type = 'x';
8452  }
8453  }
8454  else if (axis_type == "ydata" || axis_type == "yscale"
8455  || axis_type == "ylimmode" || axis_type == "yliminclude"
8456  || axis_type == "ylim")
8457  {
8458  update_type = 'y';
8459  if (m_properties.ylimmode_is ("auto"))
8460  {
8461  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
8462 
8463  std::string method = m_properties.get_ylimitmethod ();
8464  limits = m_properties.get_axis_limits (min_val, max_val,
8465  min_pos, max_neg,
8466  m_properties.yscale_is ("log"),
8467  method);
8468  }
8469  else
8470  {
8471  limits = m_properties.get_ylim ().matrix_value ();
8472  m_properties.check_axis_limits (limits, kids,
8473  m_properties.yscale_is ("log"),
8474  update_type);
8475  if (axis_type == "yscale")
8476  update_type = 'y';
8477  }
8478  }
8479  else if (axis_type == "zdata" || axis_type == "zscale"
8480  || axis_type == "zlimmode" || axis_type == "zliminclude"
8481  || axis_type == "zlim")
8482  {
8483  update_type = 'z';
8484  if (m_properties.zlimmode_is ("auto"))
8485  {
8486  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8487 
8488  m_properties.set_has3Dkids ((max_val - min_val) >
8489  std::numeric_limits<double>::epsilon ());
8490 
8491  // FIXME: How to correctly handle (positive or negative) log scale?
8492  if ((! octave::math::isfinite (min_val)
8493  || ! octave::math::isfinite (max_val))
8494  && ! m_properties.zscale_is ("log"))
8495  min_val = max_val = 0.;
8496 
8497  std::string method = m_properties.get_zlimitmethod ();
8498  limits = m_properties.get_axis_limits (min_val, max_val,
8499  min_pos, max_neg,
8500  m_properties.zscale_is ("log"),
8501  method);
8502  }
8503  else
8504  {
8505  // FIXME: get_children_limits is only needed here in order to know
8506  // if there are 3D children. Is there a way to avoid this call?
8507  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
8508 
8509  m_properties.set_has3Dkids ((max_val - min_val) >
8510  std::numeric_limits<double>::epsilon ());
8511 
8512  limits = m_properties.get_zlim ().matrix_value ();
8513  m_properties.check_axis_limits (limits, kids,
8514  m_properties.zscale_is ("log"),
8515  update_type);
8516  if (axis_type == "zscale")
8517  update_type = 'z';
8518  }
8519  }
8520  else if (axis_type == "cdata" || axis_type == "climmode"
8521  || axis_type == "cdatamapping" || axis_type == "climinclude"
8522  || axis_type == "clim")
8523  {
8524  if (m_properties.climmode_is ("auto"))
8525  {
8526  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
8527 
8528  if (min_val > max_val)
8529  {
8530  min_val = min_pos = 0;
8531  max_val = 1;
8532  }
8533  else if (min_val == max_val)
8534  {
8535  max_val = min_val + 1;
8536  min_val -= 1;
8537  }
8538 
8539  limits.resize (1, 2);
8540 
8541  limits(0) = min_val;
8542  limits(1) = max_val;
8543 
8544  update_type = 'c';
8545  }
8546 
8547  }
8548  else if (axis_type == "alphadata" || axis_type == "alimmode"
8549  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
8550  || axis_type == "alim")
8551  {
8552  if (m_properties.alimmode_is ("auto"))
8553  {
8554  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
8555 
8556  if (min_val > max_val)
8557  {
8558  min_val = min_pos = 0;
8559  max_val = 1;
8560  }
8561  else if (min_val == max_val)
8562  max_val = min_val + 1;
8563 
8564  limits.resize (1, 2);
8565 
8566  limits(0) = min_val;
8567  limits(1) = max_val;
8568 
8569  update_type = 'a';
8570  }
8571 
8572  }
8573 
8574  octave::unwind_protect_var<std::set<double>>
8575  restore_var (updating_axis_limits);
8576 
8577  updating_axis_limits.insert (get_handle ().value ());
8578  bool is_auto;
8579 
8580  switch (update_type)
8581  {
8582  case 'x':
8583  is_auto = m_properties.xlimmode_is ("auto");
8584  m_properties.set_xlim (limits);
8585  if (is_auto)
8586  m_properties.set_xlimmode ("auto");
8587  m_properties.update_xlim ();
8588  break;
8589 
8590  case 'y':
8591  is_auto = m_properties.ylimmode_is ("auto");
8592  m_properties.set_ylim (limits);
8593  if (is_auto)
8594  m_properties.set_ylimmode ("auto");
8595  m_properties.update_ylim ();
8596  break;
8597 
8598  case 'z':
8599  is_auto = m_properties.zlimmode_is ("auto");
8600  m_properties.set_zlim (limits);
8601  if (is_auto)
8602  m_properties.set_zlimmode ("auto");
8603  m_properties.update_zlim ();
8604  break;
8605 
8606  case 'c':
8607  m_properties.set_clim (limits);
8608  m_properties.set_climmode ("auto");
8609  break;
8610 
8611  case 'a':
8612  m_properties.set_alim (limits);
8613  m_properties.set_alimmode ("auto");
8614  break;
8615 
8616  default:
8617  break;
8618  }
8619 
8620  m_properties.update_transform ();
8621 }
8622 
8623 inline double
8624 force_in_range (double x, double lower, double upper)
8625 {
8626  if (x < lower)
8627  return lower;
8628  else if (x > upper)
8629  return upper;
8630  else
8631  return x;
8632 }
8633 
8634 static Matrix
8635 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
8636 {
8637  Matrix new_lims = lims;
8638 
8639  double lo = lims(0);
8640  double hi = lims(1);
8641 
8642  bool is_negative = lo < 0 && hi < 0;
8643 
8644  if (is_logscale)
8645  {
8646  if (is_negative)
8647  {
8648  double tmp = hi;
8649  hi = std::log10 (-lo);
8650  lo = std::log10 (-tmp);
8651  val = std::log10 (-val);
8652  }
8653  else
8654  {
8655  hi = std::log10 (hi);
8656  lo = std::log10 (lo);
8657  val = std::log10 (val);
8658  }
8659  }
8660 
8661  // Perform the zooming
8662  lo = val + (lo - val) / factor;
8663  hi = val + (hi - val) / factor;
8664 
8665  if (is_logscale)
8666  {
8667  if (is_negative)
8668  {
8669  double tmp = -std::pow (10.0, hi);
8670  hi = -std::pow (10.0, lo);
8671  lo = tmp;
8672  }
8673  else
8674  {
8675  lo = std::pow (10.0, lo);
8676  hi = std::pow (10.0, hi);
8677  }
8678  }
8679 
8680  new_lims(0) = lo;
8681  new_lims(1) = hi;
8682 
8683  return new_lims;
8684 }
8685 
8686 void
8687 axes::properties::zoom_about_point (const std::string& mode,
8688  double x, double y, double factor,
8689  bool push_to_zoom_stack)
8690 {
8691  // FIXME: Do we need error checking here?
8692  Matrix xlims = get_xlim ().matrix_value ();
8693  Matrix ylims = get_ylim ().matrix_value ();
8694 
8695  // Get children axes limits
8696  Matrix kids = get_children ();
8697  double minx = octave::numeric_limits<double>::Inf ();
8698  double maxx = -octave::numeric_limits<double>::Inf ();
8699  double min_pos_x = octave::numeric_limits<double>::Inf ();
8700  double max_neg_x = -octave::numeric_limits<double>::Inf ();
8701  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
8702 
8703  double miny = octave::numeric_limits<double>::Inf ();
8704  double maxy = -octave::numeric_limits<double>::Inf ();
8705  double min_pos_y = octave::numeric_limits<double>::Inf ();
8706  double max_neg_y = -octave::numeric_limits<double>::Inf ();
8707  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
8708 
8709  xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
8710  ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
8711 
8712  zoom (mode, xlims, ylims, push_to_zoom_stack);
8713 }
8714 
8715 void
8716 axes::properties::zoom (const std::string& mode, double factor,
8717  bool push_to_zoom_stack)
8718 {
8719  // FIXME: Do we need error checking here?
8720  Matrix xlims = get_xlim ().matrix_value ();
8721  Matrix ylims = get_ylim ().matrix_value ();
8722 
8723  double x = (xlims(0) + xlims(1)) / 2;
8724  double y = (ylims(0) + ylims(1)) / 2;
8725 
8726  zoom_about_point (mode, x, y, factor, push_to_zoom_stack);
8727 }
8728 
8729 void
8731 {
8732  if (m_zoom_stack.empty ())
8733  {
8734  m_zoom_stack.push_front (m_xlimmode.get ());
8735  m_zoom_stack.push_front (m_xlim.get ());
8736  m_zoom_stack.push_front (m_ylimmode.get ());
8737  m_zoom_stack.push_front (m_ylim.get ());
8738  m_zoom_stack.push_front (m_zlimmode.get ());
8739  m_zoom_stack.push_front (m_zlim.get ());
8740  m_zoom_stack.push_front (m_view.get ());
8741  }
8742 }
8743 
8744 void
8745 axes::properties::zoom (const std::string& mode,
8746  const Matrix& xl, const Matrix& yl,
8747  bool push_to_zoom_stack)
8748 {
8749  if (xl(0) == xl(1) || yl(0) == yl(1))
8750  {
8751  warning ("invalid zoom region");
8752  return;
8753  }
8754 
8755  if (push_to_zoom_stack)
8756  push_zoom_stack ();
8757 
8758  if (mode == "horizontal" || mode == "both")
8759  {
8760  m_xlim = xl;
8761  m_xlimmode = "manual";
8762  }
8763 
8764  if (mode == "vertical" || mode == "both")
8765  {
8766  m_ylim = yl;
8767  m_ylimmode = "manual";
8768  }
8769 
8770  update_transform ();
8771 
8772  if (mode == "horizontal" || mode == "both")
8773  update_xlim ();
8774 
8775  if (mode == "vertical" || mode == "both")
8776  update_ylim ();
8777 }
8778 
8779 static Matrix
8780 do_translate (double x0, double x1, const Matrix& lims, bool is_logscale)
8781 {
8782  Matrix new_lims = lims;
8783 
8784  double lo = lims(0);
8785  double hi = lims(1);
8786 
8787  bool is_negative = lo < 0 && hi < 0;
8788 
8789  double delta;
8790 
8791  if (is_logscale)
8792  {
8793  if (is_negative)
8794  {
8795  double tmp = hi;
8796  hi = std::log10 (-lo);
8797  lo = std::log10 (-tmp);
8798  x0 = -x0;
8799  x1 = -x1;
8800  }
8801  else
8802  {
8803  hi = std::log10 (hi);
8804  lo = std::log10 (lo);
8805  }
8806 
8807  delta = std::log10 (x0) - std::log10 (x1);
8808  }
8809  else
8810  {
8811  delta = x0 - x1;
8812  }
8813 
8814  // Perform the translation
8815  lo += delta;
8816  hi += delta;
8817 
8818  if (is_logscale)
8819  {
8820  if (is_negative)
8821  {
8822  double tmp = -std::pow (10.0, hi);
8823  hi = -std::pow (10.0, lo);
8824  lo = tmp;
8825  }
8826  else
8827  {
8828  lo = std::pow (10.0, lo);
8829  hi = std::pow (10.0, hi);
8830  }
8831  }
8832 
8833  new_lims(0) = lo;
8834  new_lims(1) = hi;
8835 
8836  return new_lims;
8837 }
8838 
8839 void
8840 axes::properties::translate_view (const std::string& mode,
8841  double x0, double x1, double y0, double y1,
8842  bool push_to_zoom_stack)
8843 {
8844  // FIXME: Do we need error checking here?
8845  Matrix xlims = get_xlim ().matrix_value ();
8846  Matrix ylims = get_ylim ().matrix_value ();
8847 
8848  // Get children axes limits
8849  Matrix kids = get_children ();
8850  double minx = octave::numeric_limits<double>::Inf ();
8851  double maxx = -octave::numeric_limits<double>::Inf ();
8852  double min_pos_x = octave::numeric_limits<double>::Inf ();
8853  double max_neg_x = -octave::numeric_limits<double>::Inf ();
8854  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
8855 
8856  double miny = octave::numeric_limits<double>::Inf ();
8857  double maxy = -octave::numeric_limits<double>::Inf ();
8858  double min_pos_y = octave::numeric_limits<double>::Inf ();
8859  double max_neg_y = -octave::numeric_limits<double>::Inf ();
8860  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
8861 
8862  xlims = do_translate (x0, x1, xlims, xscale_is ("log"));
8863  ylims = do_translate (y0, y1, ylims, yscale_is ("log"));
8864 
8865  zoom (mode, xlims, ylims, push_to_zoom_stack);
8866 }
8867 
8868 void
8869 axes::properties::pan (const std::string& mode, double factor,
8870  bool push_to_zoom_stack)
8871 {
8872  // FIXME: Do we need error checking here?
8873  Matrix xlims = get_xlim ().matrix_value ();
8874  Matrix ylims = get_ylim ().matrix_value ();
8875 
8876  double x0 = (xlims(0) + xlims(1)) / 2;
8877  double y0 = (ylims(0) + ylims(1)) / 2;
8878 
8879  double x1 = x0 + (xlims(1) - xlims(0)) * factor;
8880  double y1 = y0 + (ylims(1) - ylims(0)) * factor;
8881 
8882  translate_view (mode, x0, x1, y0, y1, push_to_zoom_stack);
8883 }
8884 
8885 void
8886 axes::properties::rotate3d (double x0, double x1, double y0, double y1,
8887  bool push_to_zoom_stack)
8888 {
8889  if (push_to_zoom_stack)
8890  push_zoom_stack ();
8891 
8892  Matrix bb = get_boundingbox (true);
8893  Matrix new_view = get_view ().matrix_value ();
8894 
8895  // Compute new view angles
8896  new_view(0) += ((x0 - x1) * (180.0 / bb(2)));
8897  new_view(1) += ((y1 - y0) * (180.0 / bb(3)));
8898 
8899  // Clipping
8900  new_view(1) = std::min (new_view(1), 90.0);
8901  new_view(1) = std::max (new_view(1), -90.0);
8902  if (new_view(0) > 180.0)
8903  new_view(0) -= 360.0;
8904  else if (new_view(0) < -180.0)
8905  new_view(0) += 360.0;
8906 
8907  // Snapping
8908  double snapmargin = 1.0;
8909  for (int a = -90; a <= 90; a += 90)
8910  {
8911  if ((a - snapmargin) < new_view(1) && new_view(1) < (a + snapmargin))
8912  {
8913  new_view(1) = a;
8914  break;
8915  }
8916  }
8917 
8918  for (int a = -180; a <= 180; a += 180)
8919  if ((a - snapmargin) < new_view(0) && new_view(0) < (a + snapmargin))
8920  {
8921  if (a == 180)
8922  new_view(0) = -180;
8923  else
8924  new_view(0) = a;
8925  break;
8926  }
8927 
8928  // Update axes properties
8929  set_view (new_view);
8930 }
8931 
8932 void
8933 axes::properties::rotate_view (double delta_el, double delta_az,
8934  bool push_to_zoom_stack)
8935 {
8936  if (push_to_zoom_stack)
8937  push_zoom_stack ();
8938 
8939  Matrix v = get_view ().matrix_value ();
8940 
8941  v(1) += delta_el;
8942 
8943  if (v(1) > 90)
8944  v(1) = 90;
8945  if (v(1) < -90)
8946  v(1) = -90;
8947 
8948  v(0) = fmod (v(0) - delta_az + 720, 360);
8949 
8950  set_view (v);
8951 
8952  update_transform ();
8953 }
8954 
8955 void
8957 {
8958  if (m_zoom_stack.size () >= 7)
8959  {
8960  m_view = m_zoom_stack.front ();
8961  m_zoom_stack.pop_front ();
8962 
8963  m_zlim = m_zoom_stack.front ();
8964  m_zoom_stack.pop_front ();
8965 
8966  m_zlimmode = m_zoom_stack.front ();
8967  m_zoom_stack.pop_front ();
8968 
8969  m_ylim = m_zoom_stack.front ();
8970  m_zoom_stack.pop_front ();
8971 
8972  m_ylimmode = m_zoom_stack.front ();
8973  m_zoom_stack.pop_front ();
8974 
8975  m_xlim = m_zoom_stack.front ();
8976  m_zoom_stack.pop_front ();
8977 
8978  m_xlimmode = m_zoom_stack.front ();
8979  m_zoom_stack.pop_front ();
8980 
8981  update_transform ();
8982 
8983  update_xlim ();
8984  update_ylim ();
8985  update_zlim ();
8986 
8987  update_view ();
8988  }
8989 }
8990 
8991 void
8993 {
8994  if (! is_handle_visible ())
8995  {
8997 
8998  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
8999 
9000  graphics_object fig (go.get_ancestor ("figure"));
9001  octave_value ca = fig.get ("currentaxes");
9002  if (! ca.isempty () && ca.double_value () == m___myhandle__)
9003  {
9004  octave::autolock guard (gh_mgr.graphics_lock ());
9005 
9006  octave_value kids = fig.get ("children");
9007  if (kids.isempty ())
9008  fig.set ("currentaxes", Matrix ());
9009  else
9010  {
9011  NDArray kidsarray = kids.array_value ();
9012  fig.set ("currentaxes", kidsarray(0));
9013  }
9014  }
9015  }
9016 
9018 }
9019 
9020 void
9021 figure::properties::init_toolkit ()
9022 {
9023  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ();
9024 
9025  m_toolkit = gtk_mgr.get_toolkit ();
9026 }
9027 
9028 void
9030 {
9031  std::size_t items_to_leave_on_stack = (do_unzoom ? 7 : 0);
9032 
9033  while (m_zoom_stack.size () > items_to_leave_on_stack)
9034  m_zoom_stack.pop_front ();
9035 
9036  if (do_unzoom)
9037  unzoom ();
9038 }
9039 
9040 void
9042 {
9043  // Find all patch (and surface) objects within axes
9044  std::list<graphics_object> children_list;
9045  std::list<graphics_object>::iterator children_list_iter;
9046  get_children_of_type ("patch", false, true, children_list);
9047  get_children_of_type ("surface", false, true, children_list);
9048 
9049  // trigger normals calculation for these objects
9050  for (children_list_iter = children_list.begin ();
9051  children_list_iter != children_list.end (); children_list_iter++)
9052  {
9053  graphics_object kid = *children_list_iter;
9054  if (kid.isa ("patch"))
9055  {
9056  patch::properties& patch_props
9057  = dynamic_cast<patch::properties&> (kid.get_properties ());
9058  patch_props.update_normals (false);
9059  }
9060  else
9061  {
9062  surface::properties& surface_props
9063  = dynamic_cast<surface::properties&> (kid.get_properties ());
9064  surface_props.update_normals (false);
9065  }
9066  }
9067 }
9068 
9069 void
9071 {
9072  // empty list of local defaults
9073  m_default_properties = property_list ();
9074 
9075  // Save warning state of "Octave:deprecated-property"
9076  int state = toggle_warn ("Octave:deprecated-property", false);
9077 
9078  // reset factory defaults
9080  set_defaults ("reset");
9081 
9082  toggle_warn ("Octave:deprecated-property", true, state);
9083 }
9084 
9085 void
9087 {
9089 
9090  xinitialize (m_properties.get_title ());
9091  xinitialize (m_properties.get_xlabel ());
9092  xinitialize (m_properties.get_ylabel ());
9093  xinitialize (m_properties.get_zlabel ());
9094 
9095  m_properties.sync_positions ();
9096 }
9097 
9098 // ---------------------------------------------------------------------
9099 
9100 Matrix
9101 line::properties::compute_xlim () const
9102 {
9103  Matrix m (1, 4);
9104 
9105  m(0) = m_xdata.min_val ();
9106  m(1) = m_xdata.max_val ();
9107  m(2) = m_xdata.min_pos ();
9108  m(3) = m_xdata.max_neg ();
9109 
9110  return m;
9111 }
9112 
9113 Matrix
9114 line::properties::compute_ylim () const
9115 {
9116  Matrix m (1, 4);
9117 
9118  m(0) = m_ydata.min_val ();
9119  m(1) = m_ydata.max_val ();
9120  m(2) = m_ydata.min_pos ();
9121  m(3) = m_ydata.max_neg ();
9122 
9123  return m;
9124 }
9125 
9126 // ---------------------------------------------------------------------
9127 
9128 Matrix
9130 {
9131  Matrix pos = get_position ().matrix_value ();
9132 
9133  if (! units_is ("data"))
9134  pos = convert_text_position (pos, *this, get_units (), "data");
9135 
9136  return pos;
9137 }
9138 
9139 Matrix
9141 {
9142  // FIXME: Should this function also add the (x,y) base position?
9143  Matrix ext = m_extent.get ().matrix_value ();
9144 
9145  if (rotated && get_rotation () != 0)
9146  {
9147  double rot = get_rotation () * 4.0 * atan (1.0) / 180;
9148  double x0 = ext(0) * cos (rot) - ext(1) * sin (rot);
9149  double x1 = x0;
9150  double y0 = ext(0) * sin (rot) + ext(1) * cos (rot);
9151  double y1 = y0;
9152 
9153  double tmp = (ext(0)+ext(2)) * cos (rot) - ext(1) * sin (rot);
9154  x0 = std::min (x0, tmp);
9155  x1 = std::max (x1, tmp);
9156  tmp = (ext(0)+ext(2)) * sin (rot) + ext(1) * cos (rot);
9157  y0 = std::min (y0, tmp);
9158  y1 = std::max (y1, tmp);
9159 
9160  tmp = (ext(0)+ext(2)) * cos (rot) - (ext(1)+ext(3)) * sin (rot);
9161  x0 = std::min (x0, tmp);
9162  x1 = std::max (x1, tmp);
9163  tmp = (ext(0)+ext(2)) * sin (rot) + (ext(1)+ext(3)) * cos (rot);
9164  y0 = std::min (y0, tmp);
9165  y1 = std::max (y1, tmp);
9166 
9167  tmp = ext(0) * cos (rot) - (ext(1)+ext(3)) * sin (rot);
9168  x0 = std::min (x0, tmp);
9169  x1 = std::max (x1, tmp);
9170  tmp = ext(0) * sin (rot) + (ext(1)+ext(3)) * cos (rot);
9171  y0 = std::min (y0, tmp);
9172  y1 = std::max (y1, tmp);
9173 
9174  ext(0) = x0;
9175  ext(1) = y0;
9176  ext(2) = x1 - x0;
9177  ext(3) = y1 - y0;
9178  }
9179 
9180  return ext;
9181 }
9182 
9185 {
9186  // FIXME: This doesn't work right for 3D plots.
9187  // (It doesn't in Matlab either, at least not in version 6.5.)
9188  Matrix m = get_extent_matrix (true);
9189  Matrix pos = get_position ().matrix_value ();
9190  Matrix p = convert_text_position (pos, *this, get_units (), "pixels");
9191 
9192  m(0) += p(0);
9193  m(1) += p(1);
9194 
9195  Matrix bbox = convert_text_position (m, *this, "pixels", get_units ());
9196 
9197  double dpr = device_pixel_ratio (get___myhandle__ ());
9198 
9199  for (octave_idx_type ii = 0; ii < bbox.numel (); ii++)
9200  bbox(ii) = bbox(ii) / dpr;
9201 
9202  return bbox;
9203 }
9204 
9205 void
9207 {
9208  caseless_str old_fontunits = get_fontunits ();
9209 
9210  if (m_fontunits.set (val, true))
9211  {
9212  update_fontunits (old_fontunits);
9213  mark_modified ();
9214  }
9215 }
9216 
9217 void
9219 {
9220  caseless_str new_units = get_fontunits ();
9221  double parent_height = 0;
9222  double fontsz = get_fontsize ();
9223 
9224  if (new_units == "normalized" || old_units == "normalized")
9225  {
9227 
9228  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9229 
9230  graphics_object ax (go.get_ancestor ("axes"));
9231 
9232  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
9233  }
9234 
9235  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
9236 
9237  set_fontsize (octave_value (fontsz));
9238 }
9239 
9240 void
9242 {
9243  double dpr = device_pixel_ratio (get___myhandle__ ());
9244 
9246 
9247  octave::autolock guard (gh_mgr.graphics_lock ());
9248 
9249  m_txt_renderer.set_font (get ("fontname").string_value (),
9250  get ("fontweight").string_value (),
9251  get ("fontangle").string_value (),
9252  get ("__fontsize_points__").double_value () * dpr);
9253 
9254  m_txt_renderer.set_anti_aliasing (is_fontsmoothing ());
9255 
9256  Matrix c = get_color_rgb ();
9257  if (! c.isempty ())
9258  m_txt_renderer.set_color (c);
9259 
9260 }
9261 
9262 void
9264 {
9265  int halign = 0;
9266  int valign = 0;
9267 
9268  if (horizontalalignment_is ("center"))
9269  halign = 1;
9270  else if (horizontalalignment_is ("right"))
9271  halign = 2;
9272 
9273  if (verticalalignment_is ("middle"))
9274  valign = 1;
9275  else if (verticalalignment_is ("top"))
9276  valign = 2;
9277  else if (verticalalignment_is ("baseline"))
9278  valign = 3;
9279  else if (verticalalignment_is ("cap"))
9280  valign = 4;
9281 
9282  Matrix bbox;
9283 
9284  // FIXME: string should be parsed only when modified, for efficiency
9285 
9286  octave_value string_prop = get_string ();
9287 
9288  string_vector sv = string_prop.string_vector_value ();
9289 
9291 
9292  octave::autolock guard (gh_mgr.graphics_lock ());
9293 
9294  m_txt_renderer.text_to_pixels (sv.join ("\n"), m_pixels, bbox,
9295  halign, valign, 0.0, get_interpreter ());
9296  // The bbox is relative to the text's position. We'll leave it that
9297  // way, because get_position does not return valid results when the
9298  // text is first constructed. Conversion to proper coordinates is
9299  // performed in get_extent.
9300  set_extent (bbox);
9301 
9302  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
9303  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
9304  update_autopos ("sync");
9305 }
9306 
9307 void
9308 text::properties::request_autopos ()
9309 {
9310  if (__autopos_tag___is ("xlabel") || __autopos_tag___is ("ylabel")
9311  || __autopos_tag___is ("zlabel") || __autopos_tag___is ("title"))
9312  update_autopos (get___autopos_tag__ ());
9313 }
9314 
9315 void
9316 text::properties::update_units ()
9317 {
9318  if (! units_is ("data"))
9319  {
9320  set_xliminclude ("off");
9321  set_yliminclude ("off");
9322  set_zliminclude ("off");
9323  }
9324 
9325  Matrix pos = get_position ().matrix_value ();
9326 
9327  pos = convert_text_position (pos, *this, m_cached_units, get_units ());
9328 
9329  // FIXME: if the current axes view is 2D, then one should probably drop
9330  // the z-component of "pos" and leave "zliminclude" to "off".
9331 
9332  bool autopos = positionmode_is ("auto");
9333 
9334  set_position (pos);
9335 
9336  if (autopos)
9337  set_positionmode ("auto");
9338 
9339  if (units_is ("data"))
9340  {
9341  set_xliminclude ("on");
9342  set_yliminclude ("on");
9343  // FIXME: see above
9344  set_zliminclude ("off");
9345  }
9346 
9347  m_cached_units = get_units ();
9348 }
9349 
9350 double
9351 text::properties::get___fontsize_points__ (double box_pix_height) const
9352 {
9353  double fontsz = get_fontsize ();
9354  double parent_height = box_pix_height;
9355 
9357 
9358  graphics_object go (gh_mgr.get_object (get___myhandle__ ()));
9359 
9360  if (fontunits_is ("normalized") && parent_height <= 0)
9361  {
9362  graphics_object ax (go.get_ancestor ("axes"));
9363 
9364  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
9365  }
9366 
9367  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
9368 }
9369 
9370 // ---------------------------------------------------------------------
9371 
9374 {
9375  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
9376 }
9377 
9378 // ---------------------------------------------------------------------
9379 
9380 void
9382 {
9384 
9385  // trigger normals calculation for the respective children of this axes object
9386  axes::properties& parent_axes_prop
9387  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
9388  parent_axes_prop.trigger_normals_calc ();
9389 }
9390 
9391 void
9392 light::properties::update_visible ()
9393 {
9395 
9396  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
9397 
9398  axes::properties& ax_props = dynamic_cast<axes::properties&>
9399  (go.get_ancestor ("axes").get_properties ());
9400  if (is_visible ())
9401  ax_props.increase_num_lights ();
9402  else
9403  ax_props.decrease_num_lights ();
9404 }
9405 
9406 // ---------------------------------------------------------------------
9407 
9408 bool
9410 {
9412 
9413  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
9414 
9415  axes::properties& ax_props = dynamic_cast<axes::properties&>
9416  (go.get_ancestor ("axes").get_properties ());
9417 
9418  return (ax_props.get_num_lights () > 0);
9419 }
9420 
9423 {
9424  octave_value fvc = get_facevertexcdata ();
9425  if (fvc.is_undefined () || fvc.isempty ())
9426  return Matrix ();
9427  else
9428  return convert_cdata (*this, fvc, cdatamapping_is ("scaled"), 2);
9429 }
9430 
9431 static bool updating_patch_data = false;
9432 
9433 void
9434 patch::properties::update_fvc ()
9435 {
9436  if (updating_patch_data)
9437  return;
9438 
9439  Matrix xd = get_xdata ().matrix_value ();
9440  Matrix yd = get_ydata ().matrix_value ();
9441  Matrix zd = get_zdata ().matrix_value ();
9442  NDArray cd = get_cdata ().array_value ();
9443 
9444  m_bad_data_msg = "";
9445  if (xd.dims () != yd.dims ()
9446  || (xd.dims () != zd.dims () && ! zd.isempty ()))
9447  {
9448  m_bad_data_msg = "x/y/zdata must have the same dimensions";
9449  return;
9450  }
9451 
9452  octave_idx_type nv = xd.rows ();
9453  octave_idx_type nf = xd.columns ();
9454  octave_idx_type ncv = cd.rows ();
9455  octave_idx_type ncf = cd.columns ();
9456  if ((ncf > 1 || ncv > 1) && ((ncf != nf) || (ncv != 1 && ncv != nv)))
9457  {
9458  m_bad_data_msg = "cdata does not match number of faces "
9459  "or number of vertices per face";
9460  return;
9461  }
9462 
9463  // Faces and Vertices
9464  dim_vector dv;
9465  bool is3D = false;
9466  if (nv == 1 && nf > 1)
9467  {
9468  nv = nf;
9469  nf = 1;
9470  xd = xd.as_column ();
9471  yd = yd.as_column ();
9472  zd = zd.as_column ();
9473  }
9474 
9475  dv(0) = nv * nf;
9476  if (zd.isempty ())
9477  dv(1) = 2;
9478  else
9479  {
9480  dv(1) = 3;
9481  is3D = true;
9482  }
9483 
9484  Matrix vert (dv);
9485  Matrix idx (nf, nv);
9486 
9487  // create list of vertices from x/y/zdata
9488  // FIXME: It might be possible to share vertices between adjacent faces.
9489  octave_idx_type kk = 0;
9490  for (octave_idx_type jj = 0; jj < nf; jj++)
9491  {
9492  for (octave_idx_type ii = 0; ii < nv; ii++)
9493  {
9494  vert(kk, 0) = xd(ii, jj);
9495  vert(kk, 1) = yd(ii, jj);
9496  if (is3D)
9497  vert(kk, 2) = zd(ii, jj);
9498 
9499  idx(jj, ii) = static_cast<double> (kk+1);
9500 
9501  kk++;
9502  }
9503  }
9504 
9505  // facevertexcdata
9506  Matrix fvc;
9507  if (cd.ndims () == 3)
9508  {
9509  dv(0) = cd.rows () * cd.columns ();
9510  dv(1) = cd.dims ()(2);
9511  fvc = cd.reshape (dv);
9512  }
9513  else
9514  fvc = cd.as_column ();
9515 
9516  // FIXME: shouldn't we update facevertexalphadata here ?
9517 
9518  octave::unwind_protect_var<bool> restore_var (updating_patch_data, true);
9519 
9520  m_faces.set (idx);
9521  m_vertices.set (vert);
9522  m_facevertexcdata.set (fvc);
9523 }
9524 
9525 // core coplanar tester
9526 bool
9527 is_coplanar (const Matrix& cov)
9528 {
9529  // Accuracy note: this test will also accept single precision input (although
9530  // stored in double precision). This is because the error threshold is
9531  // sqrt(tol) = 1.5e-7.
9532  double tol = 100 * std::numeric_limits<double>::epsilon ();
9533  EIG eig (cov, false, false, true);
9534  ColumnVector ev = real (eig.eigenvalues ());
9535  return ev.min () <= tol * ev.max ();
9536 }
9537 
9538 std::vector<octave_idx_type>
9539 coplanar_partition (const Matrix& vert, const Matrix& idx,
9541 {
9542  std::vector<octave_idx_type> coplanar_ends;
9543 
9544  Matrix plane_pivot = Matrix (1, 3, 0.0);
9545  for (octave_idx_type i = 0; i < 3; i++)
9546  plane_pivot(0, i) = vert(idx(0, jj)-1, i);
9547 
9548  Matrix fc = Matrix (0, 3, 0.0); // face corner vertex coordinates
9549  Matrix fa = Matrix (1, 3, 0.0); // for append face corner
9550  Matrix coor_cov = Matrix (3, 3, 0.0);
9551 
9552  if (nc >= 5)
9553  {
9554  // Coplanar test that involves all points.
9555  // For nc == 4, this initial test is not beneficial at all.
9556  // If the probability of coplanar input is more than half, for
9557  // the best average performance, we should use nc >= 5.
9558  // Higher threshold is meaningful only when input is known to be
9559  // non-coplanar and nc is small.
9560 
9561  fc.resize (nc - 1, 3);
9562  for (octave_idx_type j = 1; j < nc; j++)
9563  for (octave_idx_type i = 0; i < 3; i++)
9564  fc(j-1, i) = vert(idx(j, jj)-1, i) - plane_pivot(i);
9565 
9566  coor_cov = fc.transpose () * fc;
9567  if (is_coplanar (coor_cov))
9568  {
9569  coplanar_ends.push_back (nc - 1);
9570  return coplanar_ends;
9571  }
9572  }
9573 
9574  fc.resize (3, 3);
9575  octave_idx_type i_start = 1;
9576  octave_idx_type i_end = 2;
9577 
9578  // Split the polygon into coplanar segments.
9579  // The first point is common corner of all planes.
9580  while (i_start < nc - 1)
9581  {
9582  i_end = i_start + 2;
9583  if (i_end > nc - 1)
9584  {
9585  coplanar_ends.push_back (nc - 1);
9586  break;
9587  }
9588 
9589  // Algorithm: Start from 3 points, keep adding points until the point set
9590  // is no more in a plane. Record the coplanar point set, then advance
9591  // i_start.
9592 
9593  // Prepare 1+3 points for coplanar test.
9594  // The first point is implicitly included.
9595  for (octave_idx_type j = 0; j < 3; j++)
9596  for (octave_idx_type i = 0; i < 3; i++)
9597  fc(j, i) = vert(idx(j+i_start, jj)-1, i) - plane_pivot(i);
9598 
9599  // covariance matrix between coordinates of vertices
9600  coor_cov = fc.transpose () * fc;
9601 
9602  while (true)
9603  {
9604  // coplanar test
9605  if (! is_coplanar (coor_cov))
9606  break;
9607 
9608  i_end++;
9609  if (i_end > nc - 1)
9610  break;
9611 
9612  // add a point to plane
9613  for (octave_idx_type i = 0; i < 3; i++)
9614  fa(0, i) = vert(idx(i_end, jj)-1, i) - plane_pivot(i);
9615  coor_cov += fa.transpose () * fa;
9616  }
9617 
9618  i_start = i_end - 1;
9619  coplanar_ends.push_back (i_start);
9620  }
9621  return coplanar_ends;
9622 }
9623 
9624 void
9625 patch::properties::update_data ()
9626 {
9627  if (updating_patch_data)
9628  return;
9629 
9630  Matrix idx = get_faces ().matrix_value ().transpose ();
9631  Matrix vert = get_vertices ().matrix_value ();
9632  NDArray fvc = get_facevertexcdata ().array_value ();
9633 
9634  octave_idx_type nfaces = idx.columns ();
9635  octave_idx_type nvert = vert.rows ();
9636 
9637  // Check all vertices in faces are defined
9638  m_bad_data_msg = "";
9639  if (static_cast<double> (nvert) < idx.row_max ().max ())
9640  {
9641  m_bad_data_msg = R"(some vertices in "faces" property are undefined)";
9642  return;
9643  }
9644 
9645  // Check if number of color values matches number of faces or vertices
9646  octave_idx_type nfvc = fvc.rows ();
9647  if (nfvc > 1 && nfvc != nfaces && nfvc != nvert)
9648  {
9649  m_bad_data_msg = "number of facevertexcdata values matches "
9650  "neither number of faces nor number of vertices";
9651  return;
9652  }
9653 
9654  // Replace NaNs
9655  if (idx.any_element_is_inf_or_nan ())
9656  {
9657  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
9658  {
9659  double valid_vert = idx(0, jj);
9660  bool turn_valid = false;
9661  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
9662  {
9663  if (octave::math::isnan (idx(ii, jj)) || turn_valid)
9664  {
9665  idx(ii, jj) = valid_vert;
9666  turn_valid = true;
9667  }
9668  else
9669  valid_vert = idx(ii, jj);
9670  }
9671  }
9672  }
9673 
9674  // check coplanarity for 3D-faces with more than 3 corners
9675  int fcmax = idx.rows ();
9676  if (fcmax > 3 && vert.columns () > 2
9677  && ! (facecolor_is ("none") && edgecolor_is ("none")))
9678  {
9679  m_coplanar_last_idx.resize (idx.columns ());
9680  for (octave_idx_type jj = 0; jj < idx.columns (); jj++)
9681  {
9682  if (octave::math::isnan (idx(3, jj)))
9683  continue;
9684 
9685  // find first element that is NaN to get number of corners
9686  octave_idx_type nc = 3;
9687  while (nc < fcmax && ! octave::math::isnan (idx(nc, jj)))
9688  nc++;
9689 
9690  // If any of the corners is NaN or Inf, skip coplanar test.
9691  // FIXME: Add support for non-coplanar faces with unclosed contour.
9692  bool is_unclosed = false;
9693  for (octave_idx_type j = 0; j < nc; j++)
9694  {
9695  const octave_idx_type k = idx(j, jj) - 1;
9696  if (! (octave::math::isfinite (vert(k, 0))
9697  && octave::math::isfinite (vert(k, 1))
9698  && octave::math::isfinite (vert(k, 2))))
9699  {
9700  is_unclosed = true;
9701  break;
9702  }
9703  }
9704  if (is_unclosed)
9705  continue;
9706 
9707  m_coplanar_last_idx[jj] = coplanar_partition (vert, idx, nc, jj);
9708  }
9709  }
9710  else
9711  m_coplanar_last_idx.resize (0);
9712 
9713  // Build cdata
9714  dim_vector dv = dim_vector::alloc (3);
9715  NDArray cd;
9716  bool pervertex = false;
9717 
9718  if (fvc.rows () == nfaces || fvc.rows () == 1)
9719  {
9720  // "facevertexcdata" holds color data per face or same color for all
9721  dv(0) = 1;
9722  dv(1) = fvc.rows ();
9723  dv(2) = fvc.columns ();
9724  cd = fvc.reshape (dv);
9725  }
9726  else if (! fvc.isempty ())
9727  {
9728  // "facevertexcdata" holds color data per vertex
9729  dv(0) = idx.rows ();
9730  dv(1) = nfaces;
9731  dv(2) = fvc.columns ();
9732  cd.resize (dv);
9733  pervertex = true;
9734  }
9735 
9736  // Build x,y,zdata and eventually per vertex cdata
9737  Matrix xd (idx.dims ());
9738  Matrix yd (idx.dims ());
9739  Matrix zd;
9740  bool has_zd = false;
9741  if (vert.columns () > 2)
9742  {
9743  zd = Matrix (idx.dims ());
9744  has_zd = true;
9745  }
9746 
9747  for (octave_idx_type jj = 0; jj < nfaces; jj++)
9748  {
9749  for (octave_idx_type ii = 0; ii < idx.rows (); ii++)
9750  {
9751  octave_idx_type row = static_cast<octave_idx_type> (idx(ii, jj)-1);
9752  xd(ii, jj) = vert(row, 0);
9753  yd(ii, jj) = vert(row, 1);
9754 
9755  if (has_zd)
9756  zd(ii, jj) = vert(row, 2);
9757 
9758  if (pervertex)
9759  for (int kk = 0; kk < fvc.columns (); kk++)
9760  cd(ii, jj, kk) = fvc(row, kk);
9761  }
9762  }
9763 
9764  // Update normals
9765  update_normals (true);
9766 
9767  octave::unwind_protect_var<bool> restore_var (updating_patch_data, true);
9768 
9769  set_xdata (xd);
9770  set_ydata (yd);
9771  set_zdata (zd);
9772  set_cdata (cd);
9773 }
9774 
9775 inline void
9776 cross_product (double x1, double y1, double z1,
9777  double x2, double y2, double z2,
9778  double& x, double& y, double& z)
9779 {
9780  x += (y1 * z2 - z1 * y2);
9781  y += (z1 * x2 - x1 * z2);
9782  z += (x1 * y2 - y1 * x2);
9783 }
9784 
9785 void
9786 patch::properties::calc_face_normals (Matrix& fn)
9787 {
9788  Matrix v = get_vertices ().matrix_value ();
9789  Matrix f = get_faces ().matrix_value ();
9790 
9791  bool is_3D = (v.columns () == 3); // 2D or 3D patches
9792  octave_idx_type num_f = f.rows (); // number of faces
9793  octave_idx_type max_nc = f.columns (); // max. number of polygon corners
9794 
9795  // In which cases can we skip updating the normals?
9796  if (max_nc < 3)
9797  {
9798  fn = Matrix ();
9799  return;
9800  }
9801 
9802  // Calculate normals for all faces
9803  octave_idx_type i1, i2, i3;
9804  octave_idx_type j1, j2;
9805  for (octave_idx_type i = 0; i < num_f; i++)
9806  {
9807  bool is_coplanar = true;
9808  if (m_coplanar_last_idx.size () > 0 && m_coplanar_last_idx[i].size () > 1)
9809  is_coplanar = false;
9810 
9811  // get number of corners
9812  octave_idx_type nc = 3;
9813  if (max_nc > 3)
9814  {
9815  while (nc < max_nc && ! octave::math::isnan (f(i, nc)))
9816  nc++;
9817  }
9818 
9819  RowVector fnc (3, 0.0);
9820  double& nx = fnc(0);
9821  double& ny = fnc(1);
9822  double& nz = fnc(2);
9823 
9824  if (is_coplanar)
9825  {
9826  // fast way for coplanar polygons
9827  i1 = f(i, 0) - 1; i2 = f(i, 1) - 1; i3 = f(i, nc-1) - 1;
9828 
9829  if (is_3D)
9831  (v(i3, 0) - v(i1, 0), v(i3, 1) - v(i1, 1), v(i3, 2) - v(i1, 2),
9832  v(i2, 0) - v(i1, 0), v(i2, 1) - v(i1, 1), v(i2, 2) - v(i1, 2),
9833  nx, ny, nz);
9834  else
9835  {
9836  nz = (v(i2, 0) - v(i1, 0)) * (v(i3, 1) - v(i1, 1)) -
9837  (v(i2, 1) - v(i1, 1)) * (v(i3, 0) - v(i1, 0));
9838  // 2-d vertices always point towards +z
9839  nz = (nz < 0) ? -nz : nz;
9840  }
9841  }
9842  else
9843  {
9844  // more general for non-planar polygons
9845 
9846  // calculate face normal with Newell's method
9847  // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method
9848 
9849  j1 = nc - 1; j2 = 0;
9850  i1 = f(i, j1) - 1; i2 = f(i, j2) - 1;
9851 
9852  nx = (v(i2, 1) - v(i1, 1)) * (v(i1, 2) + v(i2, 2));
9853  ny = (v(i2, 2) - v(i1, 2)) * (v(i1, 0) + v(i2, 0));
9854  nz = (v(i2, 0) - v(i1, 0)) * (v(i1, 1) + v(i2, 1));
9855 
9856  for (octave_idx_type j = 1; j < nc; j++)
9857  {
9858  j1 = j-1; j2 = j;
9859  i1 = f(i, j1) - 1; i2 = f(i, j2) - 1;
9860 
9861  nx += (v(i2, 1) - v(i1, 1)) * (v(i1, 2) + v(i2, 2));
9862  ny += (v(i2, 2) - v(i1, 2)) * (v(i1, 0) + v(i2, 0));
9863  nz += (v(i2, 0) - v(i1, 0)) * (v(i1, 1) + v(i2, 1));
9864  }
9865  }
9866 
9867  // normalize normal vector
9868  double n_len = sqrt (nx*nx+ny*ny+nz*nz);
9869 
9870  // assign normal to current face
9871  if ( n_len < std::numeric_limits<double>::epsilon () )
9872  for (octave_idx_type j = 0; j < 3; j++)
9873  fn(i, j) = 0.0;
9874  else
9875  for (octave_idx_type j = 0; j < 3; j++)
9876  fn(i, j) = fnc(j) / n_len;
9877  }
9878 }
9879 
9880 void
9881 patch::properties::update_face_normals (bool reset, bool force)
9882 {
9883  if (updating_patch_data || ! facenormalsmode_is ("auto"))
9884  return;
9885 
9886  if (force || ((facelighting_is ("flat") || edgelighting_is ("flat"))
9887  && get_do_lighting ()))
9888  {
9889  Matrix f = get_faces ().matrix_value ();
9890 
9891  octave_idx_type num_f = f.rows (); // number of faces
9892  Matrix fn (num_f, 3, 0.0);
9893 
9894  calc_face_normals (fn);
9895  m_facenormals = fn;
9896  }
9897  else if (reset)
9898  m_facenormals = Matrix ();
9899 }
9900 
9901 void
9902 patch::properties::update_vertex_normals (bool reset, bool force)
9903 {
9904  if (updating_patch_data || ! vertexnormalsmode_is ("auto"))
9905  return;
9906 
9907  if (force || ((facelighting_is ("gouraud") || facelighting_is ("phong")
9908  || edgelighting_is ("gouraud") || edgelighting_is ("phong"))
9909  && get_do_lighting ()))
9910  {
9911  Matrix v = get_vertices ().matrix_value ();
9912  Matrix f = get_faces ().matrix_value ();
9913 
9914  octave_idx_type num_v = v.rows (); // number of vertices
9915  octave_idx_type num_f = f.rows (); // number of faces
9916  octave_idx_type max_nc = f.columns (); // max. number of polygon corners
9917 
9918  // In which cases can we skip updating the normals?
9919  if (max_nc < 3)
9920  return;
9921 
9922  // First step: Calculate the normals for all faces
9923  Matrix fn = get_facenormals ().matrix_value ();
9924  if ( fn.isempty () )
9925  {
9926  // calculate facenormals here
9927  fn = Matrix (num_f, 3, 0.0);
9928  calc_face_normals (fn);
9929  }
9930 
9931  // Second step: assign normals to the respective vertices
9932 
9933  // The following code collects the face normals for all faces adjacent to
9934  // each vertex. For this, a std::vector of length NUM_V (which might be
9935  // very large) is used so that memory is allocated from the heap rather
9936  // than the stack. Each element of this vector corresponds to one vertex
9937  // of the patch. The element itself is a variable length std::vector.
9938  // This second vector contains the face normals (of type RowVector) of
9939  // the adjacent faces.
9940  std::vector<std::vector<RowVector>> vec_vn (num_v);
9941  for (octave_idx_type i = 0; i < num_f; i++)
9942  {
9943  // get number of corners
9944  octave_idx_type nc = 3;
9945  if (max_nc > 3)
9946  {
9947  while (nc < max_nc && ! octave::math::isnan (f(i, nc)))
9948  nc++;
9949  }
9950 
9951  for (octave_idx_type j = 0; j < nc; j++)
9952  vec_vn[static_cast<octave_idx_type> (f(i, j) - 1)].push_back (fn.row (i));
9953  }
9954 
9955  // Third step: Calculate the normal for the vertices taking the average
9956  // of the normals determined from all adjacent faces
9957  Matrix vn (num_v, 3, 0.0);
9958  for (octave_idx_type i = 0; i < num_v; i++)
9959  {
9960  std::vector<RowVector>::iterator it = vec_vn[i].begin ();
9961 
9962  // The normal of unused vertices is NaN.
9963  RowVector vn0 (3, octave_NaN);
9964 
9965  if (it != vec_vn[i].end ())
9966  {
9967  // FIXME: Currently, the first vector also determines the
9968  // direction of the normal. How to determine the inner and outer
9969  // faces of all parts of the patch and point the normals outwards?
9970  // (Necessary for correct lighting with "backfacelighting" set to
9971  // "lit" or "unlit".) Matlab does not seem to do it correctly
9972  // either. So should we bother?
9973 
9974  vn0 = *it;
9975 
9976  for (++it; it != vec_vn[i].end (); ++it)
9977  {
9978  RowVector vn1 = *it;
9979  // Use sign of dot product to point vectors in a similar
9980  // direction before taking the average.
9981  double dir = (vn0(0)*vn1(0) + vn0(1)*vn1(1) + vn0(2)*vn1(2) < 0) ? -1 : 1;
9982  for (octave_idx_type j = 0; j < 3; j++)
9983  vn0(j) += dir * vn1(j);
9984  }
9985 
9986  // normalize normal vector
9987  double n_len = sqrt (vn0(0)*vn0(0)+vn0(1)*vn0(1)+vn0(2)*vn0(2));
9988 
9989  // save normal in matrix
9990  for (octave_idx_type j = 0; j < 3; j++)
9991  vn(i, j) = vn0(j)/n_len;
9992  }
9993  }
9994 
9995  m_vertexnormals = vn;
9996  }
9997  else if (reset)
9998  m_vertexnormals = Matrix ();
9999 }
10000 
10001 void
10003 {
10005 
10006  // calculate normals for default data
10007  // This is done because the normals for the default data do not match
10008  // get(0, "DefaultPatchVertexNormals") in Matlab.
10009  m_properties.update_normals (true);
10010 }
10011 
10012 
10013 void
10015 {
10016  // empty list of local defaults
10017  m_default_properties = property_list ();
10018  xreset_default_properties (get_handle (), m_properties.factory_defaults ());
10019 
10020  // calculate normals for default data
10021  // This is done because the normals for the default data do not match
10022  // get(0, "DefaultPatchVertexNormals") in Matlab.
10023  m_properties.update_normals (true);
10024 }
10025 
10026 // ---------------------------------------------------------------------
10027 
10030 {
10031  octave_value c = get_cdata ();
10032  if (c.is_undefined () || c.isempty ())
10033  return Matrix ();
10034  else
10035  return convert_cdata (*this, c, c.columns () == 1, 2);
10036 }
10037 
10038 void
10039 scatter::properties::update_data ()
10040 {
10041  Matrix xd = get_xdata ().matrix_value ();
10042  Matrix yd = get_ydata ().matrix_value ();
10043  Matrix zd = get_zdata ().matrix_value ();
10044  Matrix cd = get_cdata ().matrix_value ();
10045  Matrix sd = get_sizedata ().matrix_value ();
10046 
10047  m_bad_data_msg = "";
10048  if (xd.dims () != yd.dims ()
10049  || (xd.dims () != zd.dims () && ! zd.isempty ()))
10050  {
10051  m_bad_data_msg = "x/y/zdata must have the same dimensions";
10052  return;
10053  }
10054 
10055  octave_idx_type x_rows = xd.rows ();
10056  octave_idx_type c_cols = cd.columns ();
10057  octave_idx_type c_rows = cd.rows ();
10058 
10059  if (! cd.isempty () && (c_rows != 1 || c_cols != 3)
10060  && (c_rows != x_rows || (c_cols != 1 && c_cols != 3)))
10061  {
10062  m_bad_data_msg = "cdata must be an rgb triplet or have the same number "
10063  "of rows as X and one or three columns";
10064  return;
10065  }
10066 
10067  octave_idx_type s_rows = sd.rows ();
10068  if (s_rows != 1 && s_rows != x_rows)
10069  {
10070  m_bad_data_msg = "sizedata must be a scalar or a vector with the same "
10071  "dimensions as X";
10072  return;
10073  }
10074 }
10075 
10076 static bool updating_scatter_cdata = false;
10077 
10078 void
10080 {
10081  if (updating_scatter_cdata)
10082  return;
10083 
10084  Matrix series_idx = get_seriesindex ().matrix_value ();
10085  if (series_idx.isempty ())
10086  return;
10087 
10089 
10090  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10091 
10092  axes::properties& parent_axes_prop
10093  = dynamic_cast<axes::properties&>
10094  (go.get_ancestor ("axes").get_properties ());
10095 
10096  Matrix color_order = parent_axes_prop.get_colororder ().matrix_value ();
10097  octave_idx_type s = (static_cast<octave_idx_type> (series_idx(0)) - 1)
10098  % color_order.rows ();
10099 
10100  Matrix color = Matrix (1, 3, 0.);
10101  color(0) = color_order(s, 0);
10102  color(1) = color_order(s, 1);
10103  color(2) = color_order(s, 2);
10104 
10105  octave::unwind_protect_var<bool> restore_var (updating_scatter_cdata, true);
10106 
10107  set_cdata (color);
10108  set_cdatamode ("auto");
10109 }
10110 
10111 void
10113 {
10115 
10116  Matrix series_idx = m_properties.get_seriesindex ().matrix_value ();
10117  if (series_idx.isempty ())
10118  {
10119  // Increment series index counter in parent axes
10120  axes::properties& parent_axes_prop
10121  = dynamic_cast<axes::properties&>
10122  (go.get_ancestor ("axes").get_properties ());
10123 
10124  if (! parent_axes_prop.nextplot_is ("add"))
10125  parent_axes_prop.set_nextseriesindex (1);
10126 
10127  series_idx.resize (1, 1);
10128  series_idx(0) = parent_axes_prop.get_nextseriesindex ();
10129  m_properties.set_seriesindex (series_idx);
10130 
10131  parent_axes_prop.set_nextseriesindex
10132  (parent_axes_prop.get_nextseriesindex () + 1);
10133  }
10134 
10135  if (m_properties.cdatamode_is ("auto"))
10136  m_properties.update_color ();
10137 }
10138 
10139 // ---------------------------------------------------------------------
10140 
10143 {
10144  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
10145 }
10146 
10147 bool
10149 {
10151 
10152  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10153 
10154  axes::properties& ax_prop = dynamic_cast<axes::properties&>
10155  (go.get_ancestor ("axes").get_properties ());
10156 
10157  return (ax_prop.get_num_lights () > 0);
10158 }
10159 
10160 void
10161 surface::properties::update_face_normals (bool reset, bool force)
10162 {
10163  if (! facenormalsmode_is ("auto"))
10164  return;
10165 
10166  if (force || ((facelighting_is ("flat") || edgelighting_is ("flat"))
10167  && get_do_lighting ()))
10168  {
10169  Matrix x = get_xdata ().matrix_value ();
10170  Matrix y = get_ydata ().matrix_value ();
10171  Matrix z = get_zdata ().matrix_value ();
10172 
10173  int p = z.columns ();
10174  int q = z.rows ();
10175 
10176  // FIXME: There might be a cleaner way to do this. When data is changed
10177  // the update_xdata, update_ydata, update_zdata routines are called in a
10178  // serial fashion. Until the final call to update_zdata the matrices
10179  // will be of mismatched dimensions which can cause an out-of-bound
10180  // indexing in the code below. This one-liner prevents calculating
10181  // normals until dimensions match.
10182  if (x.columns () != p || y.rows () != q)
10183  return;
10184 
10185  bool x_mat = (x.rows () == q);
10186  bool y_mat = (y.columns () == p);
10187 
10188  NDArray n (dim_vector (q-1, p-1, 3), 1);
10189 
10190  int i1, i2, j1, j2;
10191  i1 = i2 = 0;
10192  j1 = j2 = 0;
10193  double x0, x1, x2, x3, y0, y1, y2, y3, z0, z1, z2, z3;
10194  double x1m0, x2m1, x3m2, x0m3, y1m0, y2m1, y3m2, y0m3;
10195  double x1p0, x2p1, x3p2, x0p3, y1p0, y2p1, y3p2, y0p3;
10196  x3m2 = y0m3 = -1;
10197  x2m1 = x0m3 = y1m0 = y3m2 = 0;
10198  x1m0 = y2m1 = 1;
10199  x0p3 = y1p0 = 0;
10200  x1p0 = x3p2 = y2p1 = y0p3 = 1;
10201  x2p1 = y3p2 = 2;
10202 
10203  for (int i = 0; i < p-1; i++)
10204  {
10205  i1 = i;
10206  i2 = i + 1;
10207 
10208  for (int j = 0; j < q-1; j++)
10209  {
10210  j1 = j;
10211  j2 = j + 1;
10212 
10213  if (x_mat || y_mat)
10214  {
10215  x0 = x(x_mat?j1:0, y_mat?i1:0);
10216  x1 = x(x_mat?j1:0, y_mat?i2:0);
10217  x2 = x(x_mat?j2:0, y_mat?i2:0);
10218  x3 = x(x_mat?j2:0, y_mat?i1:0);
10219  x1m0 = x1 - x0;
10220  x2m1 = x2 - x1;
10221  x3m2 = x3 - x2;
10222  x0m3 = x0 - x3;
10223  x1p0 = x1 + x0;
10224  x2p1 = x2 + x1;
10225  x3p2 = x3 + x2;
10226  x0p3 = x0 + x3;
10227  y0 = y(x_mat?j1:0, y_mat?i1:0);
10228  y1 = y(x_mat?j1:0, y_mat?i2:0);
10229  y2 = y(x_mat?j2:0, y_mat?i2:0);
10230  y3 = y(x_mat?j2:0, y_mat?i1:0);
10231  y1m0 = y1 - y0;
10232  y2m1 = y2 - y1;
10233  y3m2 = y3 - y2;
10234  y0m3 = y0 - y3;
10235  y1p0 = y1 + y0;
10236  y2p1 = y2 + y1;
10237  y3p2 = y3 + y2;
10238  y0p3 = y0 + y3;
10239  }
10240 
10241  double& nx = n(j, i, 0);
10242  double& ny = n(j, i, 1);
10243  double& nz = n(j, i, 2);
10244 
10245  z0 = z(j1, i1);
10246  z1 = z(j1, i2);
10247  z2 = z(j2, i2);
10248  z3 = z(j2, i1);
10249 
10250  // calculate face normal with Newell's method
10251  // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method
10252 
10253  nx = y1m0 * (z1 + z0) + y2m1 * (z2 + z1)
10254  + y3m2 * (z3 + z2) + y0m3 * (z0 + z3);
10255  ny = (z1 - z0) * x1p0 + (z2 - z1) * x2p1
10256  + (z3 - z2) * x3p2 + (z0 - z3) * x0p3;
10257  nz = x1m0 * y1p0 + x2m1 * y2p1 + x3m2 * y3p2 + x0m3 * y0p3;
10258 
10259  double d = std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
10260 
10261  nx /= d;
10262  ny /= d;
10263  nz /= d;
10264  }
10265  }
10266  m_facenormals = n;
10267  }
10268  else if (reset)
10269  m_facenormals = Matrix ();
10270 }
10271 
10272 void
10273 surface::properties::update_vertex_normals (bool reset, bool force)
10274 {
10275  if (! vertexnormalsmode_is ("auto"))
10276  return;
10277 
10278  if (force || ((facelighting_is ("gouraud") || facelighting_is ("phong")
10279  || edgelighting_is ("gouraud") || edgelighting_is ("phong"))
10280  && get_do_lighting ()))
10281  {
10282  Matrix x = get_xdata ().matrix_value ();
10283  Matrix y = get_ydata ().matrix_value ();
10284  Matrix z = get_zdata ().matrix_value ();
10285 
10286  int p = z.columns ();
10287  int q = z.rows ();
10288 
10289  // FIXME: There might be a cleaner way to do this. When data is changed
10290  // the update_xdata, update_ydata, update_zdata routines are called in a
10291  // serial fashion. Until the final call to update_zdata the matrices
10292  // will be of mismatched dimensions which can cause an out-of-bound
10293  // indexing in the code below. This one-liner prevents calculating
10294  // normals until dimensions match.
10295  if (x.columns () != p || y.rows () != q)
10296  return;
10297 
10298  NDArray n (dim_vector (q, p, 3), 0.0);
10299 
10300  bool x_mat = (x.rows () == q);
10301  bool y_mat = (y.columns () == p);
10302 
10303  int i1, i2, i3, j1, j2, j3;
10304  i1 = i2 = i3 = 0;
10305  j1 = j2 = j3 = 0;
10306 
10307  for (int i = 0; i < p; i++)
10308  {
10309  if (y_mat)
10310  {
10311  i1 = i - 1;
10312  i2 = i;
10313  i3 = i + 1;
10314  }
10315 
10316  for (int j = 0; j < q; j++)
10317  {
10318  if (x_mat)
10319  {
10320  j1 = j - 1;
10321  j2 = j;
10322  j3 = j + 1;
10323  }
10324 
10325  double& nx = n(j, i, 0);
10326  double& ny = n(j, i, 1);
10327  double& nz = n(j, i, 2);
10328 
10329  if ((j > 0) && (i > 0))
10330  // upper left quadrangle
10332  (x(j1, i-1)-x(j2, i), y(j-1, i1)-y(j, i2), z(j-1, i-1)-z(j, i),
10333  x(j2, i-1)-x(j1, i), y(j, i1)-y(j-1, i2), z(j, i-1)-z(j-1, i),
10334  nx, ny, nz);
10335 
10336  if ((j > 0) && (i < (p -1)))
10337  // upper right quadrangle
10339  (x(j1, i+1)-x(j2, i), y(j-1, i3)-y(j, i2), z(j-1, i+1)-z(j, i),
10340  x(j1, i)-x(j2, i+1), y(j-1, i2)-y(j, i3), z(j-1, i)-z(j, i+1),
10341  nx, ny, nz);
10342 
10343  if ((j < (q - 1)) && (i > 0))
10344  // lower left quadrangle
10346  (x(j2, i-1)-x(j3, i), y(j, i1)-y(j+1, i2), z(j, i-1)-z(j+1, i),
10347  x(j3, i-1)-x(j2, i), y(j+1, i1)-y(j, i2), z(j+1, i-1)-z(j, i),
10348  nx, ny, nz);
10349 
10350  if ((j < (q - 1)) && (i < (p -1)))
10351  // lower right quadrangle
10353  (x(j3, i)-x(j2, i+1), y(j+1, i2)-y(j, i3), z(j+1, i)-z(j, i+1),
10354  x(j3, i+1)-x(j2, i), y(j+1, i3)-y(j, i2), z(j+1, i+1)-z(j, i),
10355  nx, ny, nz);
10356 
10357  double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
10358 
10359  nx /= d;
10360  ny /= d;
10361  nz /= d;
10362  }
10363  }
10364  m_vertexnormals = n;
10365  }
10366  else if (reset)
10367  m_vertexnormals = Matrix ();
10368 }
10369 
10370 DEFMETHOD (__update_normals__, interp, args, ,
10371  doc: /* -*- texinfo -*-
10372 @deftypefn {} {} __update_normals__ (@var{h})
10373 Update FaceNormals and VertexNormals of the patch or surface referred to by
10374 @var{h}.
10375 
10376 @end deftypefn */)
10377 {
10378  gh_manager& gh_mgr = interp.get_gh_manager ();
10379 
10380  octave::autolock guard (gh_mgr.graphics_lock ());
10381 
10382  if (args.length () != 1)
10383  print_usage ();
10384 
10385  octave_value val = args(0);
10386 
10387  graphics_object go = gh_mgr.get_object (val);
10388 
10389  if (go.isa ("surface"))
10390  {
10391  surface::properties& props
10392  = dynamic_cast <surface::properties&> (go.get_properties ());
10393  props.update_normals (false, true);
10394  }
10395  else if (go.isa ("patch"))
10396  {
10397  patch::properties& props
10398  = dynamic_cast <patch::properties&> (go.get_properties ());
10399  props.update_normals (false, true);
10400  }
10401  else
10402  error ("__update_normals__: "
10403  "H must be a handle to a valid surface or patch object.");
10404 
10405  return ovl ();
10406 }
10407 
10408 /*
10409 %!test
10410 %! hf = figure ("visible", "off");
10411 %! unwind_protect
10412 %! Z = peaks ();
10413 %! hs = surf (Z, "facelighting", "none");
10414 %! assert (isempty (get (hs, "vertexnormals")));
10415 %! assert (isempty (get (hs, "facenormals")));
10416 %! __update_normals__ (hs);
10417 %! assert (! isempty (get (hs, "vertexnormals")));
10418 %! assert (! isempty (get (hs, "facenormals")));
10419 %! unwind_protect_cleanup
10420 %! close (hf);
10421 %! end_unwind_protect
10422 
10423 %!test
10424 %! hf = figure ("visible", "off");
10425 %! unwind_protect
10426 %! hp = patch ("facelighting", "none");
10427 %! assert (isempty (get (hp, "vertexnormals")));
10428 %! assert (isempty (get (hp, "facenormals")));
10429 %! __update_normals__ (hp);
10430 %! assert (! isempty (get (hp, "vertexnormals")));
10431 %! assert (! isempty (get (hp, "facenormals")));
10432 %! unwind_protect_cleanup
10433 %! close (hf);
10434 %! end_unwind_protect
10435 */
10436 
10437 // ---------------------------------------------------------------------
10438 
10439 void
10441 {
10443 
10444  graphics_object go = gh_mgr.get_object (h);
10445 
10446  if (! from_root && go.isa ("light") && go.get_properties ().is_visible ())
10447  {
10448  axes::properties& ax_props
10449  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
10450  ax_props.decrease_num_lights ();
10451  }
10452  base_properties::remove_child (h, from_root);
10453  update_limits ();
10454 }
10455 
10456 void
10458 {
10460 
10461  graphics_object go = gh_mgr.get_object (h);
10462 
10463  if (go.isa ("light") && go.get_properties ().is_visible ())
10464  {
10465  axes::properties& ax_props
10466  = dynamic_cast<axes::properties&> (go.get_ancestor ("axes").get_properties ());
10467  ax_props.increase_num_lights ();
10468  }
10470  update_limits (h);
10471 }
10472 
10473 void
10474 hggroup::properties::update_limits () const
10475 {
10477 
10478  graphics_object go = gh_mgr.get_object (m___myhandle__);
10479 
10480  if (go)
10481  {
10482  go.update_axis_limits ("xlim");
10483  go.update_axis_limits ("ylim");
10484  go.update_axis_limits ("zlim");
10485  go.update_axis_limits ("clim");
10486  go.update_axis_limits ("alim");
10487  }
10488 }
10489 
10490 void
10491 hggroup::properties::update_limits (const graphics_handle& h) const
10492 {
10494 
10495  graphics_object go = gh_mgr.get_object (m___myhandle__);
10496 
10497  if (go)
10498  {
10499  go.update_axis_limits ("xlim", h);
10500  go.update_axis_limits ("ylim", h);
10501  go.update_axis_limits ("zlim", h);
10502  go.update_axis_limits ("clim", h);
10503  go.update_axis_limits ("alim", h);
10504  }
10505 }
10506 
10507 static bool updating_hggroup_limits = false;
10508 
10509 void
10510 hggroup::update_axis_limits (const std::string& axis_type,
10511  const graphics_handle& h)
10512 {
10513  if (updating_hggroup_limits)
10514  return;
10515 
10516  Matrix kids = Matrix (1, 1, h.value ());
10517 
10518  double min_val = octave::numeric_limits<double>::Inf ();
10519  double max_val = -octave::numeric_limits<double>::Inf ();
10520  double min_pos = octave::numeric_limits<double>::Inf ();
10521  double max_neg = -octave::numeric_limits<double>::Inf ();
10522 
10523  Matrix limits;
10524  double val;
10525 
10526  char update_type = 0;
10527 
10528  if (axis_type == "xlim" || axis_type == "xliminclude")
10529  {
10530  limits = m_properties.get_xlim ().matrix_value ();
10531  update_type = 'x';
10532  }
10533  else if (axis_type == "ylim" || axis_type == "yliminclude")
10534  {
10535  limits = m_properties.get_ylim ().matrix_value ();
10536  update_type = 'y';
10537  }
10538  else if (axis_type == "zlim" || axis_type == "zliminclude")
10539  {
10540  limits = m_properties.get_zlim ().matrix_value ();
10541  update_type = 'z';
10542  }
10543  else if (axis_type == "clim" || axis_type == "climinclude")
10544  {
10545  limits = m_properties.get_clim ().matrix_value ();
10546  update_type = 'c';
10547  }
10548  else if (axis_type == "alim" || axis_type == "aliminclude")
10549  {
10550  limits = m_properties.get_alim ().matrix_value ();
10551  update_type = 'a';
10552  }
10553 
10554  if (limits.numel () == 4)
10555  {
10556  val = limits(0);
10557  if (octave::math::isfinite (val))
10558  min_val = val;
10559  val = limits(1);
10560  if (octave::math::isfinite (val))
10561  max_val = val;
10562  val = limits(2);
10563  if (octave::math::isfinite (val))
10564  min_pos = val;
10565  val = limits(3);
10566  if (octave::math::isfinite (val))
10567  max_neg = val;
10568  }
10569  else
10570  {
10571  limits.resize (1, 4);
10572  limits(0) = min_val;
10573  limits(1) = max_val;
10574  limits(2) = min_pos;
10575  limits(3) = max_neg;
10576  }
10577 
10578  get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
10579 
10580  octave::unwind_protect_var<bool> restore_var (updating_hggroup_limits, true);
10581 
10582  if (limits(0) != min_val || limits(1) != max_val
10583  || limits(2) != min_pos || limits(3) != max_neg)
10584  {
10585  limits(0) = min_val;
10586  limits(1) = max_val;
10587  limits(2) = min_pos;
10588  limits(3) = max_neg;
10589 
10590  switch (update_type)
10591  {
10592  case 'x':
10593  m_properties.set_xlim (limits);
10594  break;
10595 
10596  case 'y':
10597  m_properties.set_ylim (limits);
10598  break;
10599 
10600  case 'z':
10601  m_properties.set_zlim (limits);
10602  break;
10603 
10604  case 'c':
10605  m_properties.set_clim (limits);
10606  break;
10607 
10608  case 'a':
10609  m_properties.set_alim (limits);
10610  break;
10611 
10612  default:
10613  break;
10614  }
10615 
10616  graphics_handle hg = m_properties.get___myhandle__ ();
10618  }
10619 }
10620 
10621 void
10622 hggroup::update_axis_limits (const std::string& axis_type)
10623 {
10624  if (updating_hggroup_limits)
10625  return;
10626 
10627  Matrix kids = m_properties.get_children ();
10628 
10629  double min_val = octave::numeric_limits<double>::Inf ();
10630  double max_val = -octave::numeric_limits<double>::Inf ();
10631  double min_pos = octave::numeric_limits<double>::Inf ();
10632  double max_neg = -octave::numeric_limits<double>::Inf ();
10633 
10634  char update_type = 0;
10635 
10636  if (axis_type == "xlim" || axis_type == "xliminclude")
10637  {
10638  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
10639 
10640  update_type = 'x';
10641  }
10642  else if (axis_type == "ylim" || axis_type == "yliminclude")
10643  {
10644  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
10645 
10646  update_type = 'y';
10647  }
10648  else if (axis_type == "zlim" || axis_type == "zliminclude")
10649  {
10650  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
10651 
10652  update_type = 'z';
10653  }
10654  else if (axis_type == "clim" || axis_type == "climinclude")
10655  {
10656  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
10657 
10658  update_type = 'c';
10659  }
10660  else if (axis_type == "alim" || axis_type == "aliminclude")
10661  {
10662  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
10663 
10664  update_type = 'a';
10665  }
10666 
10667  octave::unwind_protect_var<bool> restore_var (updating_hggroup_limits, true);
10668 
10669  Matrix limits (1, 4);
10670 
10671  limits(0) = min_val;
10672  limits(1) = max_val;
10673  limits(2) = min_pos;
10674  limits(3) = max_neg;
10675 
10676  switch (update_type)
10677  {
10678  case 'x':
10679  m_properties.set_xlim (limits);
10680  break;
10681 
10682  case 'y':
10683  m_properties.set_ylim (limits);
10684  break;
10685 
10686  case 'z':
10687  m_properties.set_zlim (limits);
10688  break;
10689 
10690  case 'c':
10691  m_properties.set_clim (limits);
10692  break;
10693 
10694  case 'a':
10695  m_properties.set_alim (limits);
10696  break;
10697 
10698  default:
10699  break;
10700  }
10701 
10703 }
10704 
10705 // ---------------------------------------------------------------------
10706 
10707 void
10708 uicontextmenu::properties::update_beingdeleted ()
10709 {
10710  // Clear the uicontextmenu property of dependent objects
10711  if (m_beingdeleted.is ("on"))
10712  {
10714 
10715  std::list<graphics_handle> lst = get_dependent_obj_list ();
10716 
10717  for (auto& hobj : lst)
10718  {
10719  graphics_object go = gh_mgr.get_object (hobj);
10720 
10721  if (go.valid_object ()
10722  && go.get ("contextmenu") == get___myhandle__ ())
10723  go.set ("contextmenu", Matrix ());
10724  }
10725  }
10726 }
10727 
10728 /*
10729 ## Test deletion/reset of uicontextmenu
10730 %!test
10731 %! hf = figure ("visible", "off");
10732 %! hax = axes ("parent", hf);
10733 %! unwind_protect
10734 %! hctx1 = uicontextmenu ("parent", hf);
10735 %! hctx2 = uicontextmenu ("parent", hf);
10736 %! set (hf, "uicontextmenu", hctx2);
10737 %! set (hax, "uicontextmenu", hctx2);
10738 %! assert (get (hf, "uicontextmenu"), hctx2);
10739 %! assert (get (hax, "uicontextmenu"), hctx2);
10740 %! assert (get (hf, "children"), [hctx2; hctx1; hax]);
10741 %! delete (hctx2);
10742 %! assert (get (hf, "uicontextmenu"), []);
10743 %! assert (get (hax, "uicontextmenu"), []);
10744 %! assert (get (hf, "children"), [hctx1; hax]);
10745 %! set (hf, "uicontextmenu", hctx1);
10746 %! assert (get (hf, "uicontextmenu"), hctx1);
10747 %! set (hf, "uicontextmenu", []);
10748 %! assert (get (hf, "uicontextmenu"), []);
10749 %! assert (get (hf, "children"), [hctx1; hax]);
10750 %! unwind_protect_cleanup
10751 %! close (hf);
10752 %! end_unwind_protect
10753 */
10754 
10755 // ---------------------------------------------------------------------
10756 
10759 {
10760  Matrix m = m_extent.get ().matrix_value ();
10761 
10763 
10764  graphics_object parent_go = gh_mgr.get_object (get_parent ());
10765 
10766  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
10767  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
10768 
10769  return convert_position (m, "pixels", get_units (), parent_size);
10770 }
10771 
10772 void
10774 {
10775  // FIXME: support multiline text
10776 
10778 
10779  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10780 
10781  set_extent (go.get_toolkit ().get_text_extent (go));
10782 }
10783 
10784 void
10786 {
10787  Matrix pos = get_position ().matrix_value ();
10788 
10790 
10791  graphics_object parent_go = gh_mgr.get_object (get_parent ());
10792 
10793  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
10794  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
10795 
10796  pos = convert_position (pos, m_cached_units, get_units (), parent_size);
10797  set_position (pos);
10798 
10799  m_cached_units = get_units ();
10800 }
10801 
10802 void
10804 {
10806 
10807  graphics_object go_parent = gh_mgr.get_object (get_parent ());
10808  if (go_parent.valid_object () && go_parent.isa ("uibuttongroup"))
10809  {
10810  bool was_button = style_is ("radiobutton") || style_is ("togglebutton");
10811  m_style = st;
10812  bool now_button = style_is ("radiobutton") || style_is ("togglebutton");
10813  uibuttongroup::properties& props =
10814  dynamic_cast<uibuttongroup::properties&> (go_parent.get_properties ());
10815  // update selectedobject
10816  if (! was_button && now_button && ! props.get_selectedobject ().ok ())
10817  {
10818  props.set_selectedobject (get___myhandle__ ().value ());
10819  m_value.set (octave_value (1));
10820  }
10821  else if (was_button && ! now_button
10822  && (props.get_selectedobject ().value ()
10823  == get___myhandle__ ().value ()))
10824  props.set_selectedobject (Matrix ());
10825  }
10826 
10827  // Don't notify the style change until the "value" property is fixed
10828  bool modified = m_style.set (st, true, false);
10829 
10830  // Override "value" property for listbox and popupmenu.
10831  if (modified)
10832  {
10833  if (style_is ("listbox") || style_is ("popupmenu"))
10834  {
10835  Matrix v = m_value.get ().matrix_value ();
10836  if (v.numel () == 1 && v(0) == 0)
10837  m_value.set (octave_value (1), true, false);
10838  }
10839 
10840  // Notify toolkit
10841 
10842  graphics_object go = gh_mgr.get_object (get___myhandle__ ());
10843 
10844  if (go)
10845  go.update (m_style.get_id ());
10846  }
10847 }
10848 
10849 Matrix
10851  const Matrix& parent_pix_size) const
10852 {
10853  Matrix pos = get_position ().matrix_value ();
10854  Matrix parent_size (parent_pix_size);
10855 
10856  if (parent_size.isempty ())
10857  {
10859 
10860  graphics_object go = gh_mgr.get_object (get_parent ());
10861 
10862  if (go.valid_object ())
10863  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
10864  else
10865  parent_size = default_figure_position ();
10866  }
10867 
10868  pos = convert_position (pos, get_units (), "pixels", parent_size);
10869 
10870  pos(0)--;
10871  pos(1)--;
10872  pos(1) = parent_size(1) - pos(1) - pos(3);
10873 
10874  return pos;
10875 }
10876 
10877 void
10879 {
10880  caseless_str old_fontunits = get_fontunits ();
10881 
10882  if (m_fontunits.set (val, true))
10883  {
10884  update_fontunits (old_fontunits);
10885  mark_modified ();
10886  }
10887 }
10888 
10889 void
10891 {
10892  caseless_str new_units = get_fontunits ();
10893  double parent_height = get_boundingbox (false).elem (3);
10894  double fontsz = get_fontsize ();
10895 
10896  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
10897 
10898  m_fontsize.set (octave_value (fontsz), true);
10899 }
10900 
10901 double
10903 {
10904  double fontsz = get_fontsize ();
10905  double parent_height = box_pix_height;
10906 
10907  if (fontunits_is ("normalized") && parent_height <= 0)
10908  parent_height = get_boundingbox (false).elem (3);
10909 
10910  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
10911 }
10912 
10913 // ---------------------------------------------------------------------
10914 
10915 Matrix
10917  const Matrix& parent_pix_size) const
10918 {
10919  Matrix pos = get_position ().matrix_value ();
10920  Matrix parent_size (parent_pix_size);
10921 
10922  if (parent_size.isempty ())
10923  {
10925 
10926  graphics_object go = gh_mgr.get_object (get_parent ());
10927 
10928  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
10929  }
10930 
10931  pos = convert_position (pos, get_units (), "pixels", parent_size);
10932 
10933  pos(0)--;
10934  pos(1)--;
10935  pos(1) = parent_size(1) - pos(1) - pos(3);
10936 
10937  if (internal)
10938  {
10939  double outer_height = pos(3);
10940 
10941  pos(0) = pos(1) = 0;
10942 
10943  if (! bordertype_is ("none"))
10944  {
10945  double bw = get_borderwidth ();
10946  double mul = 1.0;
10947 
10948  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
10949  mul = 2.0;
10950 
10951  pos(0) += mul * bw;
10952  pos(1) += mul * bw;
10953  pos(2) -= 2 * mul * bw;
10954  pos(3) -= 2 * mul * bw;
10955  }
10956 
10957  if (! get_title ().empty ())
10958  {
10959  double fontsz = get_fontsize ();
10960 
10961  if (! fontunits_is ("pixels"))
10962  {
10963  double res = xget (0, "screenpixelsperinch").double_value ();
10964 
10965  if (fontunits_is ("points"))
10966  fontsz *= (res / 72.0);
10967  else if (fontunits_is ("inches"))
10968  fontsz *= res;
10969  else if (fontunits_is ("centimeters"))
10970  fontsz *= (res / 2.54);
10971  else if (fontunits_is ("normalized"))
10972  fontsz *= outer_height;
10973  }
10974 
10975  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
10976  || titleposition_is ("righttop"))
10977  pos(1) += (fontsz / 2);
10978  pos(3) -= (fontsz / 2);
10979  }
10980  }
10981 
10982  return pos;
10983 }
10984 
10985 void
10987 {
10988  Matrix old_bb, new_bb;
10989  bool modified = false;
10990 
10991  old_bb = get_boundingbox (true);
10992  modified = m_position.set (v, false);
10993  new_bb = get_boundingbox (true);
10994 
10995  if (old_bb != new_bb)
10996  {
10997  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
10998  {
11000 
11001  if (! get_resizefcn ().isempty ())
11002  gh_mgr.post_callback (m___myhandle__, "resizefcn");
11003 
11004  if (! get_sizechangedfcn ().isempty ())
11005  gh_mgr.post_callback (m___myhandle__, "sizechangedfcn");
11006 
11007  update_boundingbox ();
11008  }
11009  }
11010 
11011  if (modified)
11012  {
11013  m_position.run_listeners (GCB_POSTSET);
11014  mark_modified ();
11015  }
11016 }
11017 
11018 void
11020 {
11021  caseless_str old_units = get_units ();
11022 
11023  if (m_units.set (val, true))
11024  {
11025  update_units (old_units);
11026  mark_modified ();
11027  }
11028 }
11029 
11030 void
11032 {
11033  Matrix pos = get_position ().matrix_value ();
11034 
11036 
11037  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11038 
11039  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11040  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11041 
11042  pos = convert_position (pos, old_units, get_units (), parent_size);
11043  set_position (pos);
11044 }
11045 
11046 void
11048 {
11049  caseless_str old_fontunits = get_fontunits ();
11050 
11051  if (m_fontunits.set (val, true))
11052  {
11053  update_fontunits (old_fontunits);
11054  mark_modified ();
11055  }
11056 }
11057 
11058 void
11060 {
11061  caseless_str new_units = get_fontunits ();
11062  double parent_height = get_boundingbox (false).elem (3);
11063  double fontsz = get_fontsize ();
11064 
11065  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11066 
11067  set_fontsize (octave_value (fontsz));
11068 }
11069 
11070 double
11072 {
11073  double fontsz = get_fontsize ();
11074  double parent_height = box_pix_height;
11075 
11076  if (fontunits_is ("normalized") && parent_height <= 0)
11077  parent_height = get_boundingbox (false).elem (3);
11078 
11079  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11080 }
11081 
11082 void
11084 {
11085  graphics_handle current_selectedobject = get_selectedobject();
11086  m_selectedobject = current_selectedobject;
11087  if (v.isempty ())
11088  {
11089  if (current_selectedobject.ok ())
11090  {
11091  m_selectedobject = graphics_handle ();
11092  mark_modified ();
11093  }
11094  return;
11095  }
11096 
11097  graphics_handle val (v);
11098  if (val.ok ())
11099  {
11101 
11102  graphics_object go (gh_mgr.get_object (val));
11103 
11104  base_properties& gop = go.get_properties ();
11105 
11106  if (go.valid_object ()
11107  && gop.get_parent () == get___myhandle__ ()
11108  && go.isa ("uicontrol"))
11109  {
11111  = dynamic_cast<uicontrol::properties&> (go.get_properties ());
11112  const caseless_str& style = cop.get_style ();
11113  if (style.compare ("radiobutton") || style.compare ("togglebutton"))
11114  {
11115  m_selectedobject = val;
11116  mark_modified ();
11117  return;
11118  }
11119  }
11120  }
11121  err_set_invalid ("selectedobject");
11122 }
11123 
11124 void
11126  bool from_root)
11127 {
11128  graphics_handle current_selected = get_selectedobject ();
11129  if (h.value () == current_selected.value ())
11130  set_selectedobject (Matrix ());
11131 
11132  base_properties::remove_child (h, from_root);
11133 }
11134 
11135 void
11137 {
11139 
11140  graphics_handle current_selected = get_selectedobject ();
11141  bool has_selected = current_selected.ok ();
11142 
11144 
11145  graphics_object go = gh_mgr.get_object (h);
11146 
11147  if (! has_selected && go.valid_object () && go.isa ("uicontrol"))
11148  {
11149  const uicontrol::properties& props =
11150  dynamic_cast<const uicontrol::properties&> (go.get_properties ());
11151  if (props.style_is ("radiobutton") || props.style_is ("togglebutton"))
11152  set_selectedobject (h.value ());
11153  }
11154 }
11155 
11156 // ---------------------------------------------------------------------
11157 
11158 Matrix
11160  const Matrix& parent_pix_size) const
11161 {
11162  Matrix pos = get_position ().matrix_value ();
11163  Matrix parent_size (parent_pix_size);
11164 
11165  if (parent_size.isempty ())
11166  {
11168 
11169  graphics_object go = gh_mgr.get_object (get_parent ());
11170 
11171  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11172  }
11173 
11174  pos = convert_position (pos, get_units (), "pixels", parent_size);
11175 
11176  pos(0)--;
11177  pos(1)--;
11178  pos(1) = parent_size(1) - pos(1) - pos(3);
11179 
11180  if (internal)
11181  {
11182  double outer_height = pos(3);
11183 
11184  pos(0) = pos(1) = 0;
11185 
11186  if (! bordertype_is ("none"))
11187  {
11188  double bw = get_borderwidth ();
11189  double mul = 1.0;
11190 
11191  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
11192  mul = 2.0;
11193 
11194  pos(0) += mul * bw;
11195  pos(1) += mul * bw;
11196  pos(2) -= 2 * mul * bw;
11197  pos(3) -= 2 * mul * bw;
11198  }
11199 
11200  if (! get_title ().empty ())
11201  {
11202  double fontsz = get_fontsize ();
11203 
11204  if (! fontunits_is ("pixels"))
11205  {
11206  double res = xget (0, "screenpixelsperinch").double_value ();
11207 
11208  if (fontunits_is ("points"))
11209  fontsz *= (res / 72.0);
11210  else if (fontunits_is ("inches"))
11211  fontsz *= res;
11212  else if (fontunits_is ("centimeters"))
11213  fontsz *= (res / 2.54);
11214  else if (fontunits_is ("normalized"))
11215  fontsz *= outer_height;
11216  }
11217 
11218  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
11219  || titleposition_is ("righttop"))
11220  pos(1) += (fontsz / 2);
11221  pos(3) -= (fontsz / 2);
11222  }
11223  }
11224 
11225  return pos;
11226 }
11227 
11228 void
11230 {
11231  Matrix old_bb, new_bb;
11232  bool modified = false;
11233 
11234  old_bb = get_boundingbox (true);
11235  modified = m_position.set (v, false);
11236  new_bb = get_boundingbox (true);
11237 
11238  if (old_bb != new_bb)
11239  {
11240  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
11241  {
11243 
11244  if (! get_resizefcn ().isempty ())
11245  gh_mgr.post_callback (m___myhandle__, "resizefcn");
11246 
11247  if (! get_sizechangedfcn ().isempty ())
11248  gh_mgr.post_callback (m___myhandle__, "sizechangedfcn");
11249 
11250  update_boundingbox ();
11251  }
11252  }
11253 
11254  if (modified)
11255  {
11256  m_position.run_listeners (GCB_POSTSET);
11257  mark_modified ();
11258  }
11259 }
11260 
11261 
11262 void
11264 {
11265  caseless_str old_units = get_units ();
11266 
11267  if (m_units.set (val, true))
11268  {
11269  update_units (old_units);
11270  mark_modified ();
11271  }
11272 }
11273 
11274 void
11276 {
11277  Matrix pos = get_position ().matrix_value ();
11278 
11280 
11281  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11282 
11283  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11284  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11285 
11286  pos = convert_position (pos, old_units, get_units (), parent_size);
11287  set_position (pos);
11288 }
11289 
11290 void
11292 {
11293  caseless_str old_fontunits = get_fontunits ();
11294 
11295  if (m_fontunits.set (val, true))
11296  {
11297  update_fontunits (old_fontunits);
11298  mark_modified ();
11299  }
11300 }
11301 
11302 void
11304 {
11305  caseless_str new_units = get_fontunits ();
11306  double parent_height = get_boundingbox (false).elem (3);
11307  double fontsz = get_fontsize ();
11308 
11309  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11310 
11311  set_fontsize (octave_value (fontsz));
11312 }
11313 
11314 double
11316 {
11317  double fontsz = get_fontsize ();
11318  double parent_height = box_pix_height;
11319 
11320  if (fontunits_is ("normalized") && parent_height <= 0)
11321  parent_height = get_boundingbox (false).elem (3);
11322 
11323  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11324 }
11325 
11326 // ---------------------------------------------------------------------
11327 
11328 Matrix
11330  const Matrix& parent_pix_size) const
11331 {
11332  Matrix pos = get_position ().matrix_value ();
11333  Matrix parent_size (parent_pix_size);
11334 
11335  if (parent_size.isempty ())
11336  {
11338 
11339  graphics_object go = gh_mgr.get_object (get_parent ());
11340 
11341  parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
11342  }
11343 
11344  pos = convert_position (pos, get_units (), "pixels", parent_size);
11345 
11346  pos(0)--;
11347  pos(1)--;
11348  pos(1) = parent_size(1) - pos(1) - pos(3);
11349 
11350  return pos;
11351 }
11352 
11353 void
11355 {
11356  /* Matlab only allows certain values for ColumnFormat. Here we only check the
11357  * structure of the argument. Values will be checked in Table.cc */
11358 
11359  if (val.iscellstr ())
11360  {
11361  if (m_columnformat.set (val, true))
11362  mark_modified ();
11363  }
11364  else if (val.iscell ())
11365  {
11366  Cell cell_value = val.cell_value ();
11367 
11368  for (int i = 0; i < cell_value.numel (); i++)
11369  {
11370  octave_value v = cell_value(i);
11371  if (v.iscell ())
11372  {
11373  /* We are in a pop-up menu selection.
11374  * Matlab only allows non-empty strings here. */
11375  Cell popup = v.cell_value ();
11376  for (int j = 0; j < popup.numel (); j++)
11377  {
11378  octave_value p = popup(j);
11379  if (! p.is_string () || p.isempty ())
11380  error ("set: pop-up menu definitions must be non-empty strings");
11381  }
11382  }
11383  else if (! (v.is_string () || v.isempty ()))
11384  {
11385  error ("set: columnformat definintions must be a cellstr of "
11386  "either 'char', 'short [e|g|eng]?', 'long [e|g|eng]?', "
11387  "'numeric', 'bank', '+', 'rat', 'logical', "
11388  "or a cellstr of non-empty pop-up menu definitions.");
11389  }
11390  }
11391 
11392  if (m_columnformat.set (val, true))
11393  mark_modified ();
11394  }
11395  else if (val.isempty ())
11396  {
11397  if (m_columnformat.set (Cell (), true))
11398  mark_modified ();
11399  }
11400  else
11401  {
11402  error ("set: expecting cell of strings");
11403  }
11404 }
11405 
11406 void
11408 {
11409  bool error_exists = false;
11410 
11411  if (val.is_string () && val.string_value (false) == "auto")
11412  error_exists = false;
11413  else if (val.iscell ())
11414  {
11415  Cell cell_value = val.cell_value ();
11416  for (int i = 0; i < cell_value.numel (); i++)
11417  {
11418  octave_value v = cell_value(i);
11419  if (v.is_string ())
11420  {
11421  if (v.string_value (false) != "auto")
11422  error_exists = true;
11423  }
11424  else if (v.iscell ())
11425  {
11426  error_exists = true;
11427  }
11428  else if (! v.is_scalar_type ())
11429  {
11430  error_exists = true;
11431  }
11432  }
11433  }
11434  else
11435  error_exists = true;
11436 
11437  if (error_exists)
11438  error ("set: expecting either 'auto' or a cell of pixel values or auto");
11439  else
11440  {
11441  if (m_columnwidth.set (val, true))
11442  mark_modified ();
11443  }
11444 }
11445 
11446 void
11448 {
11449  caseless_str old_units = get_units ();
11450 
11451  if (m_units.set (val, true))
11452  {
11453  update_units (old_units);
11454  mark_modified ();
11455  }
11456 }
11457 
11458 void
11460 {
11461  Matrix pos = get_position ().matrix_value ();
11462 
11464 
11465  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11466 
11467  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11468  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11469 
11470  pos = convert_position (pos, old_units, get_units (), parent_size);
11471  set_position (pos);
11472 }
11473 
11474 void
11476 {
11477  caseless_str old_fontunits = get_fontunits ();
11478 
11479  if (m_fontunits.set (val, true))
11480  {
11481  update_fontunits (old_fontunits);
11482  mark_modified ();
11483  }
11484 }
11485 
11486 void
11488 {
11489  caseless_str new_units = get_fontunits ();
11490  double parent_height = get_boundingbox (false).elem (3);
11491  double fontsz = get_fontsize ();
11492 
11493  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
11494 
11495  set_fontsize (octave_value (fontsz));
11496 }
11497 
11498 double
11500 {
11501  double fontsz = get_fontsize ();
11502  double parent_height = box_pix_height;
11503 
11504  if (fontunits_is ("normalized") && parent_height <= 0)
11505  parent_height = get_boundingbox (false).elem (3);
11506 
11507  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
11508 }
11509 
11510 double
11511 uitable::properties::get_fontsize_pixels (double box_pix_height) const
11512 {
11513  double fontsz = get_fontsize ();
11514  double parent_height = box_pix_height;
11515 
11516  if (fontunits_is ("normalized") && parent_height <= 0)
11517  parent_height = get_boundingbox (false).elem (3);
11518 
11519  return convert_font_size (fontsz, get_fontunits (), "pixels", parent_height);
11520 }
11521 
11522 Matrix
11524 {
11525  Matrix bg = m_backgroundcolor.get ().matrix_value ();
11526  return bg.row (0);
11527 }
11528 
11529 Matrix
11531 {
11532  int i = 0;
11533  Matrix bg = m_backgroundcolor.get ().matrix_value ();
11534  if (bg.rows () > 1)
11535  i = 1;
11536 
11537  return bg.row (i);
11538 }
11539 
11540 Matrix
11542 {
11543  return m_extent.get ().matrix_value ();
11544 }
11545 
11548 {
11549  // FIXME: Is it really acceptable to just let the toolkit update the extent?
11550  Matrix m = m_extent.get ().matrix_value ();
11551 
11553 
11554  graphics_object parent_go = gh_mgr.get_object (get_parent ());
11555 
11556  if (parent_go)
11557  {
11558  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
11559  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
11560 
11561  return convert_position (m, "pixels", get_units (), parent_size);
11562  }
11563 
11564  return m;
11565 }
11566 
11567 // ---------------------------------------------------------------------
11568 
11571 {
11572  octave_value retval = m_default_properties.lookup (pname);
11573 
11574  if (retval.is_undefined ())
11575  {
11576  graphics_handle parent_h = get_parent ();
11577 
11579 
11580  graphics_object parent_go = gh_mgr.get_object (parent_h);
11581 
11582  retval = parent_go.get_default (pname);
11583  }
11584 
11585  return retval;
11586 }
11587 
11588 void
11590 {
11591  // empty list of local defaults
11592  m_default_properties = property_list ();
11593 
11595  xreset_default_properties (get_handle (), m_properties.factory_defaults ());
11596 }
11597 
11598 // ---------------------------------------------------------------------
11599 
11602 {
11603  graphics_handle parent_h = get_parent ();
11604 
11606 
11607  graphics_object parent_go = gh_mgr.get_object (parent_h);
11608 
11609  return parent_go.get_default (type () + pname);
11610 }
11611 
11614 {
11616 
11617  graphics_object parent_go = gh_mgr.get_object (0);
11618 
11619  return parent_go.get_factory_default (type () + name);
11620 }
11621 
11622 class
11623 callback_event : public base_graphics_event
11624 {
11625 public:
11626  callback_event (const graphics_handle& h, const std::string& name,
11627  const octave_value& data = Matrix (),
11628  int busyaction = base_graphics_event::QUEUE)
11629  : base_graphics_event (busyaction), m_handle (h), m_callback_name (name),
11630  m_callback (), m_callback_data (data) { }
11631 
11632  callback_event (const graphics_handle& h, const octave_value& cb,
11633  const octave_value& data = Matrix (),
11634  int busyaction = base_graphics_event::QUEUE)
11635  : base_graphics_event (busyaction), m_handle (h), m_callback_name (),
11636  m_callback (cb), m_callback_data (data) { }
11637 
11638  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (callback_event)
11639 
11640  ~callback_event () = default;
11641 
11642  void execute ()
11643  {
11645 
11646  if (m_callback.is_defined ())
11647  gh_mgr.execute_callback (m_handle, m_callback, m_callback_data);
11648  else
11649  gh_mgr.execute_callback (m_handle, m_callback_name, m_callback_data);
11650  }
11651 
11652 private:
11653  graphics_handle m_handle;
11654  std::string m_callback_name;
11655  octave_value m_callback;
11656  octave_value m_callback_data;
11657 };
11658 
11659 class
11660 mcode_event : public base_graphics_event
11661 {
11662 public:
11663  mcode_event (const graphics_handle& h, const std::string& cmd,
11664  int busyaction = base_graphics_event::QUEUE)
11665  : base_graphics_event (busyaction), m_handle (h), m_mcode (cmd)
11666  { }
11667 
11668  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (mcode_event)
11669 
11670  ~mcode_event () = default;
11671 
11672  void execute ()
11673  {
11674  if (! m_mcode.empty ())
11675  {
11677 
11678  graphics_object go = gh_mgr.get_object (m_handle);
11679 
11680  if (go.valid_object ())
11681  {
11682  octave_value cb (m_mcode);
11683  gh_mgr.execute_callback (m_handle, cb);
11684  }
11685  }
11686  }
11687 
11688 private:
11689  graphics_handle m_handle;
11690  std::string m_mcode;
11691 };
11692 
11693 class
11694 function_event : public base_graphics_event
11695 {
11696 public:
11697 
11698  function_event (graphics_event::event_fcn fcn, void *data = nullptr)
11699  : base_graphics_event (), m_function (fcn), m_function_data (data)
11700  { }
11701 
11702  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (function_event)
11703 
11704  ~function_event () = default;
11705 
11706  void execute ()
11707  {
11708  m_function (m_function_data);
11709  }
11710 
11711 private:
11712 
11713  graphics_event::event_fcn m_function;
11714 
11715  void *m_function_data;
11716 };
11717 
11718 class
11719 set_event : public base_graphics_event
11720 {
11721 public:
11722  set_event (const graphics_handle& h, const std::string& name,
11723  const octave_value& value, bool do_notify_toolkit = true,
11724  bool redraw_figure = false)
11725  : base_graphics_event (), m_handle (h), m_property_name (name),
11726  m_property_value (value), m_notify_toolkit (do_notify_toolkit),
11727  m_redraw_figure (redraw_figure)
11728  { }
11729 
11730  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (set_event)
11731 
11732  ~set_event () = default;
11733 
11734  void execute ()
11735  {
11737 
11738  octave::autolock guard (gh_mgr.graphics_lock ());
11739 
11740  graphics_object go = gh_mgr.get_object (m_handle);
11741 
11742  if (go)
11743  {
11744  property p = go.get_properties ().get_property (m_property_name);
11745 
11746  if (p.ok ())
11747  {
11748  // FIXME: figure position and outerposition properties set_xxx have
11749  // a signature that allows passing the notify_toolkit argument.
11750  // Should we change all set_xxx signatures and allow
11751  // base_properties::set to accept this also? This would allow for
11752  // the use of high level set_xxx instead of directly changing the
11753  // property value.
11754  if (go.isa ("figure") && m_property_name == "position")
11755  {
11756  figure::properties& fprops
11757  = dynamic_cast<figure::properties&> (go.get_properties ());
11758  fprops.set_position (m_property_value, m_notify_toolkit);
11759  }
11760  else if (go.isa ("figure") && m_property_name == "outerposition")
11761  {
11762  figure::properties& fprops
11763  = dynamic_cast<figure::properties&> (go.get_properties ());
11764  fprops.set_outerposition (m_property_value, m_notify_toolkit);
11765  }
11766  else
11767  p.set (m_property_value, true, m_notify_toolkit);
11768 
11769  if (m_redraw_figure)
11770  {
11771  if (! go.isa ("figure"))
11772  go = go.get_ancestor ("figure");
11773 
11774  if (go.valid_object ())
11775  {
11776  figure::properties& fprops
11777  = dynamic_cast<figure::properties&> (go.get_properties ());
11778  fprops.get_toolkit ().redraw_figure (go);
11779  }
11780  }
11781  }
11782  }
11783  }
11784 
11785 private:
11786  graphics_handle m_handle;
11787  std::string m_property_name;
11788  octave_value m_property_value;
11789  bool m_notify_toolkit;
11790  bool m_redraw_figure;
11791 };
11792 
11795  const std::string& name,
11796  const octave_value& data,
11797  int busyaction)
11798 {
11799  return graphics_event (new callback_event (h, name, data, busyaction));
11800 }
11801 
11804  const octave_value& cb,
11805  const octave_value& data,
11806  int busyaction)
11807 {
11808  return graphics_event (new callback_event (h, cb, data, busyaction));
11809 }
11810 
11813  const std::string& cmd,
11814  int busyaction)
11815 {
11816  return graphics_event (new mcode_event (h, cmd, busyaction));
11817 }
11818 
11821  void *data)
11822 {
11823  return graphics_event (new function_event (fcn, data));
11824 }
11825 
11828  const std::string& name,
11829  const octave_value& data,
11830  bool notify_toolkit, bool redraw_figure)
11831 {
11832  return graphics_event (new set_event (h, name, data, notify_toolkit,
11833  redraw_figure));
11834 }
11835 
11837 root_figure::init_factory_properties ()
11838 {
11840 
11841  plist_map["figure"] = figure::properties::factory_defaults ();
11842  plist_map["axes"] = axes::properties::factory_defaults ();
11843  plist_map["line"] = line::properties::factory_defaults ();
11844  plist_map["text"] = text::properties::factory_defaults ();
11845  plist_map["image"] = image::properties::factory_defaults ();
11846  plist_map["patch"] = patch::properties::factory_defaults ();
11847  plist_map["scatter"] = scatter::properties::factory_defaults ();
11848  plist_map["surface"] = surface::properties::factory_defaults ();
11849  plist_map["light"] = light::properties::factory_defaults ();
11850  plist_map["hggroup"] = hggroup::properties::factory_defaults ();
11851  plist_map["uimenu"] = uimenu::properties::factory_defaults ();
11852  plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
11853  plist_map["uibuttongroup"] = uibuttongroup::properties::factory_defaults ();
11854  plist_map["uipanel"] = uipanel::properties::factory_defaults ();
11855  plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
11856  plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
11857  plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
11858  plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
11859 
11860  return plist_map;
11861 }
11862 
11863 // ---------------------------------------------------------------------
11864 
11865 DEFMETHOD (ishghandle, interp, args, ,
11866  doc: /* -*- texinfo -*-
11867 @deftypefn {} {@var{tf} =} ishghandle (@var{h})
11868 Return true if @var{h} is a graphics handle and false otherwise.
11869 
11870 @var{h} may also be a matrix of handles in which case a logical array is
11871 returned that is true where the elements of @var{h} are graphics handles and
11872 false where they are not.
11873 @seealso{isgraphics, isaxes, isfigure, ishandle}
11874 @end deftypefn */)
11875 {
11876  gh_manager& gh_mgr = interp.get_gh_manager ();
11877 
11878  octave::autolock guard (gh_mgr.graphics_lock ());
11879 
11880  if (args.length () != 1)
11881  print_usage ();
11882 
11883  return ovl (ishghandle (args(0)));
11884 }
11885 
11886 /*
11887 %!test
11888 %! hf = figure ("visible", "off");
11889 %! unwind_protect
11890 %! assert (ishghandle (hf));
11891 %! assert (! ishghandle (-hf));
11892 %! ax = gca ();
11893 %! l = line ();
11894 %! assert (ishghandle (ax));
11895 %! assert (! ishghandle (-ax));
11896 %! assert (ishghandle ([l, -1, ax, hf]), logical ([1, 0, 1, 1]));
11897 %! assert (ishghandle ([l, -1, ax, hf]'), logical ([1, 0, 1, 1]'));
11898 %! unwind_protect_cleanup
11899 %! close (hf);
11900 %! end_unwind_protect
11901 
11902 %!assert (ishghandle ([-1 0]), [false true])
11903 */
11904 
11905 static bool
11906 is_handle_visible (const graphics_handle& h)
11907 {
11909 
11910  return h.ok () && gh_mgr.is_handle_visible (h);
11911 }
11912 
11913 static bool
11914 is_handle_visible (double val)
11915 {
11917 
11918  return is_handle_visible (gh_mgr.lookup (val));
11919 }
11920 
11921 static octave_value
11922 is_handle_visible (const octave_value& val)
11923 {
11924  octave_value retval = false;
11925 
11926  if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
11927  retval = true;
11928  else if (val.isnumeric () && val.isreal ())
11929  {
11930  const NDArray handles = val.array_value ();
11931 
11932  boolNDArray result (handles.dims ());
11933 
11934  for (octave_idx_type i = 0; i < handles.numel (); i++)
11935  result.xelem (i) = is_handle_visible (handles(i));
11936 
11937  retval = result;
11938  }
11939 
11940  return retval;
11941 }
11942 
11943 DEFUN (__is_handle_visible__, args, ,
11944  doc: /* -*- texinfo -*-
11945 @deftypefn {} {@var{tf} =} __is_handle_visible__ (@var{h})
11946 Undocumented internal function.
11947 @end deftypefn */)
11948 {
11949  if (args.length () != 1)
11950  print_usage ();
11951 
11952  return ovl (is_handle_visible (args(0)));
11953 }
11954 
11955 DEFMETHOD (reset, interp, args, ,
11956  doc: /* -*- texinfo -*-
11957 @deftypefn {} {} reset (@var{h})
11958 Reset the properties of the graphic object @var{h} to their default values.
11959 
11960 For figures, the properties @qcode{"position"}, @qcode{"units"},
11961 @qcode{"windowstyle"}, and @qcode{"paperunits"} are not affected.
11962 For axes, the properties @qcode{"position"} and @qcode{"units"} are
11963 not affected.
11964 
11965 The input @var{h} may also be a vector of graphic handles in which case
11966 each individual object will be reset.
11967 @seealso{cla, clf, newplot}
11968 @end deftypefn */)
11969 {
11970  if (args.length () != 1)
11971  print_usage ();
11972 
11973  // get vector of graphics handles
11974  ColumnVector hcv = args(0).xvector_value ("reset: H must be a graphics handle");
11975 
11976  gh_manager& gh_mgr = interp.get_gh_manager ();
11977 
11978  // loop over graphics objects
11979  for (octave_idx_type n = 0; n < hcv.numel (); n++)
11980  gh_mgr.get_object (hcv(n)).reset_default_properties ();
11981 
11982  Vdrawnow_requested = true;
11983 
11984  return ovl ();
11985 }
11986 
11987 /*
11988 
11989 %!test # line object
11990 %! hf = figure ("visible", "off");
11991 %! unwind_protect
11992 %! tol = 20 * eps;
11993 %! hax = axes ("defaultlinelinewidth", 3);
11994 %!
11995 %! hli = line (1:10, 1:10, 1:10, "marker", "o",
11996 %! "markerfacecolor", "b", "linestyle", ":");
11997 %!
11998 %! reset (hli);
11999 %! assert (get (hli, "marker"), get (0, "defaultlinemarker"));
12000 %! assert (get (hli, "markerfacecolor"),
12001 %! get (0, "defaultlinemarkerfacecolor"));
12002 %! assert (get (hli, "linestyle"), get (0, "defaultlinelinestyle"));
12003 %! assert (get (hli, "linewidth"), 3, tol); # parent axes defaults
12004 %!
12005 %! unwind_protect_cleanup
12006 %! close (hf);
12007 %! end_unwind_protect
12008 
12009 %!test # patch object
12010 %! hf = figure ("visible", "off");
12011 %! unwind_protect
12012 %! tol = 20 * eps;
12013 %! t1 = (1/16:1/8:1)' * 2*pi;
12014 %! t2 = ((1/16:1/16:1)' + 1/32) * 2*pi;
12015 %! x1 = sin (t1) - 0.8;
12016 %! y1 = cos (t1);
12017 %! x2 = sin (t2) + 0.8;
12018 %! y2 = cos (t2);
12019 %! vert = [x1, y1; x2, y2];
12020 %! fac = [1:8,NaN(1,8);9:24];
12021 %! hpa = patch ("Faces",fac, "Vertices",vert, "FaceColor","r");
12022 %!
12023 %! reset (hpa);
12024 %! assert (get (hpa, "faces"), get (0, "defaultpatchfaces"), tol);
12025 %! assert (get (hpa, "vertices"), get (0, "defaultpatchvertices"), tol);
12026 %! assert (get (hpa, "facevertexcdata"),
12027 %! get (0, "defaultpatchfacevertexcdata"), tol);
12028 %! unwind_protect_cleanup
12029 %! close (hf);
12030 %! end_unwind_protect
12031 
12032 %!test # surface object
12033 %! hf = figure ("visible", "off");
12034 %! unwind_protect
12035 %! tol = 20 * eps;
12036 %! hsu = surface (peaks, "edgecolor", "none");
12037 %!
12038 %! reset (hsu);
12039 %! assert (get (hsu, "xdata"), get (0, "defaultsurfacexdata"), tol);
12040 %! assert (get (hsu, "ydata"), get (0, "defaultsurfaceydata"), tol);
12041 %! assert (get (hsu, "zdata"), get (0, "defaultsurfacezdata"), tol);
12042 %! assert (get (hsu, "edgecolor"), get (0, "defaultsurfaceedgecolor"), tol);
12043 %! unwind_protect_cleanup
12044 %! close (hf);
12045 %! end_unwind_protect
12046 
12047 %!test # image object
12048 %! hf = figure ("visible", "off");
12049 %! unwind_protect
12050 %! tol = 20 * eps;
12051 %! him = image (rand (10,10), "cdatamapping", "scaled");
12052 %!
12053 %! reset (him);
12054 %! assert (get (him, "cdata"), get (0, "defaultimagecdata"), tol);
12055 %! assert (get (him, "cdatamapping"),
12056 %! get (0, "defaultimagecdatamapping"), tol);
12057 %! unwind_protect_cleanup
12058 %! close (hf);
12059 %! end_unwind_protect
12060 
12061 %!test # text object
12062 %! hf = figure ("visible", "off");
12063 %! unwind_protect
12064 %! tol = 20 * eps;
12065 %! hte = text (5, 5, "Hi!", "fontsize", 20 ,"color", "r");
12066 %!
12067 %! reset (hte);
12068 %! assert (get (hte, "position"), get (0, "defaulttextposition"), tol);
12069 %! assert (get (hte, "fontsize"), get (0, "defaulttextfontsize"), tol);
12070 %! assert (get (hte, "color"), get (0, "defaulttextcolor"), tol);
12071 %! unwind_protect_cleanup
12072 %! close (hf);
12073 %! end_unwind_protect
12074 
12075 %!test # axes object
12076 %! hf = figure ("visible", "off");
12077 %! unwind_protect
12078 %! tol = 20 * eps;
12079 %! pos = get (0, "defaultaxesposition") * .5;
12080 %! hax = axes ("linewidth", 2, "position", pos);
12081 %! title ("Reset me, please!");
12082 %!
12083 %! reset (hax);
12084 %! assert (get (hax, "linewidth"), get (0, "defaultaxeslinewidth"), tol);
12085 %! assert (get (hax, "position"), pos, tol); # axes position is unchanged
12086 %! assert (get (hax, "default"), struct ()); # no more axes' defaults
12087 %! assert (get (get (hax, "title"), "string"), "");
12088 %! unwind_protect_cleanup
12089 %! close (hf);
12090 %! end_unwind_protect
12091 
12092 %!test # root object
12093 %! set (0, "defaultfigurevisible", "off");
12094 %! hf = figure ("visible", "off", "paperunits", "centimeters",
12095 %! "papertype", "a4");
12096 %! unwind_protect
12097 %! reset (hf);
12098 %! assert (get (hf, "papertype"), get (0, "defaultfigurepapertype"));
12099 %! assert (get (hf, "paperunits"), "centimeters"); # paperunits is unchanged
12100 %! assert (get (hf, "visible"), get (0, "defaultfigurevisible"));
12101 %! unwind_protect_cleanup
12102 %! close (hf);
12103 %! set (0, "defaultfigurevisible", "remove");
12104 %! end_unwind_protect
12105 
12106 */
12107 
12108 DEFMETHOD (set, interp, args, nargout,
12109  doc: /* -*- texinfo -*-
12110 @deftypefn {} {} set (@var{h}, @var{property}, @var{value}, @dots{})
12111 @deftypefnx {} {} set (@var{h}, @{@var{properties}@}, @{@var{values}@})
12112 @deftypefnx {} {} set (@var{h}, @var{pv})
12113 @deftypefnx {} {@var{value_list} =} set (@var{h}, @var{property})
12114 @deftypefnx {} {@var{all_value_list} =} set (@var{h})
12115 Set named property values for the graphics handle (or vector of graphics
12116 handles) @var{h}.
12117 
12118 There are three ways to give the property names and values:
12119 
12120 @itemize
12121 @item as a comma-separated list of @var{property}, @var{value} pairs
12122 
12123 Each @var{property} is a string containing the property name, each @var{value}
12124 is a value of the appropriate type for the property. When there are multiple
12125 handles in @var{h}, each one is assigned the same @var{value}. For example:
12126 
12127 @example
12128 @group
12129 h = plot ([0, 1]);
12130 set (h, 'color', 'green');
12131 @end group
12132 @end example
12133 
12134 @item as a cell array of strings @var{properties} containing property names
12135 and a cell array @var{values} containing property values.
12136 
12137 In this case, the number of columns of @var{values} must match the number of
12138 elements in @var{properties}. The first column of @var{values} contains
12139 values for the first entry in @var{properties}, etc. The number of rows of
12140 @var{values} must be 1 or match the number of elements of @var{h}. In the
12141 first case, each handle in @var{h} will be assigned the same values. In the
12142 second case, the first handle in @var{h} will be assigned the values from
12143 the first row of @var{values} and so on. For example:
12144 
12145 @example
12146 @group
12147 h = plot ([0, 1; 1, 0]);
12148 set (h, @{'color'@}, @{'green'; 'red'@});
12149 @end group
12150 @end example
12151 
12152 @item as a structure @var{pv}
12153 
12154 This is the same as the first case where the field names of @var{pv} represent
12155 the property names, and the field values give the property values. As with
12156 the first case, it is only possible to set one value for a property which will
12157 be applied to all handles in @var{h}. For example:
12158 
12159 @example
12160 @group
12161 h = plot ([0, 1]);
12162 props.color = 'green';
12163 set (h, props);
12164 @end group
12165 @end example
12166 
12167 @end itemize
12168 
12169 The three syntaxes for setting properties may appear in any combination.
12170 
12171 @code{set} is also used to query the list of values a named property will
12172 take. @code{@var{clist} = set (@var{h}, "property")} will return the list
12173 of possible values for @qcode{"property"} in the cell list @var{clist}.
12174 If no output variable is used then the list is formatted and printed to the
12175 screen.
12176 
12177 If no property is specified (@code{@var{slist} = set (@var{h})}) then a
12178 structure @var{slist} is returned where the fieldnames are the properties of
12179 the object @var{h} and the fields are the list of possible values for each
12180 property. If no output variable is used then the list is formatted and
12181 printed to the screen.
12182 
12183 When querying properties only a single graphics handle @var{h} for a single
12184 graphics object is permitted.
12185 
12186 Example Query
12187 
12188 @example
12189 @group
12190 hf = figure ();
12191 set (hf, "paperorientation")
12192 @result{} [ landscape | @{portrait@} ]
12193 @end group
12194 @end example
12195 
12196 @noindent
12197 shows the paperorientation property can take two values with the default
12198 being @qcode{"portrait"}.
12199 @seealso{get}
12200 @end deftypefn */)
12201 {
12202  int nargin = args.length ();
12203 
12204  if (nargin == 0)
12205  print_usage ();
12206 
12207  // get vector of graphics handles
12208  ColumnVector hcv = args(0).xvector_value ("set: H must be a graphics handle");
12209 
12210  gh_manager& gh_mgr = interp.get_gh_manager ();
12211 
12212  octave::autolock guard (gh_mgr.graphics_lock ());
12213 
12214  octave_value retval;
12215 
12216  // Process requests for default value(s)
12217  if (nargin == 1)
12218  {
12219  if (hcv.numel () > 1)
12220  error ("set: H must be a single graphics handle when querying properties");
12221 
12222  graphics_object go = gh_mgr.get_object (hcv(0));
12223  if (! go)
12224  error ("set: invalid handle (= %g)", hcv(0));
12225 
12226  if (nargout > 0)
12227  retval = go.values_as_struct ();
12228  else
12229  {
12230  std::string s = go.values_as_string ();
12231 
12232  octave_stdout << s;
12233  }
12234 
12235  return retval;
12236  }
12237  else if (nargin == 2 && args(1).is_string ())
12238  {
12239  if (hcv.numel () > 1)
12240  error ("set: H must be a single graphics handle when querying properties");
12241 
12242  std::string property = args(1).string_value ();
12243  std::transform (property.begin (), property.end (),
12244  property.begin (), tolower);
12245 
12246  graphics_object go = gh_mgr.get_object (hcv(0));
12247 
12248  if (! go)
12249  error ("set: invalid handle (= %g)", hcv(0));
12250 
12251  octave_map pmap = go.values_as_struct ();
12252 
12254  {
12255  if (nargout > 0)
12256  retval = Matrix ();
12257  else
12258  octave_stdout << "set: " << property << " is read-only"
12259  << std::endl;
12260  }
12261  else if (pmap.isfield (property))
12262  {
12263  if (nargout != 0)
12264  retval = pmap.getfield (property)(0);
12265  else
12266  {
12267  std::string s = go.value_as_string (property);
12268 
12269  octave_stdout << s;
12270  }
12271  }
12272  else
12273  error (R"(set: unknown property "%s")", property.c_str ());
12274 
12275  return retval;
12276  }
12277 
12278  bool request_drawnow = false;
12279 
12280  // Loop over graphics objects
12281  for (octave_idx_type n = 0; n < hcv.numel (); n++)
12282  {
12283  graphics_object go = gh_mgr.get_object (hcv(n));
12284 
12285  if (! go)
12286  error ("set: invalid handle (= %g)", hcv(n));
12287 
12288  // Loop over input arguments
12289  for (octave_idx_type i = 1; i < args.length (); )
12290  {
12291  if (args(i).is_string ())
12292  {
12293  if (i == nargin - 1)
12294  error ("set: PROPERTY name must be followed by a VALUE");
12295  const caseless_str pname = args(i).string_value ();
12296  const octave_value val = args(i+1);
12297  go.set_value_or_default (pname, val);
12298  i += 2;
12299  }
12300  else if (args(i).iscellstr ())
12301  {
12302  if ((i == nargin - 1) || ! args(i+1).iscell ())
12303  error ("set: cell array of PROPERTIES must be followed by cell array of VALUES");
12304  if (args(i+1).cell_value ().rows () == 1)
12305  go.set (args(i).cellstr_value (), args(i+1).cell_value (), 0);
12306  else if (hcv.numel () == args(i+1).cell_value ().rows ())
12307  go.set (args(i).cellstr_value (), args(i+1).cell_value (), n);
12308  else
12309  error ("set: number of graphics handles must match number of "
12310  "value rows (%" OCTAVE_IDX_TYPE_FORMAT " != "
12311  "%" OCTAVE_IDX_TYPE_FORMAT ")",
12312  hcv.numel (), args(i+1).cell_value ().rows ());
12313  i += 2;
12314  }
12315  else if (args(i).isstruct ())
12316  {
12317  go.set (args(i).map_value ());
12318  i += 1;
12319  }
12320  else
12321  error ("set: invalid syntax");
12322  }
12323 
12324  request_drawnow = true;
12325  }
12326 
12327  if (request_drawnow)
12328  Vdrawnow_requested = true;
12329 
12330  return retval;
12331 }
12332 
12333 /*
12334 ## test setting ticklabels for compatibility
12335 %!test
12336 %! hf = figure ("visible", "off");
12337 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
12338 %! xticklabel = get (gca (), "xticklabel");
12339 %! close (hf);
12340 %! assert (class (xticklabel), "char");
12341 %! assert (size (xticklabel), [6, 3]);
12342 
12343 %!test
12344 %! hf = figure ("visible", "off");
12345 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
12346 %! xticklabel = get (gca (), "xticklabel");
12347 %! close (hf);
12348 %! assert (class (xticklabel), "char");
12349 %! assert (size (xticklabel), [6, 3]);
12350 
12351 %!test
12352 %! hf = figure ("visible", "off");
12353 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
12354 %! xticklabel = get (gca (), "xticklabel");
12355 %! close (hf);
12356 %! assert (class (xticklabel), "char");
12357 %! assert (size (xticklabel), [6, 3]);
12358 
12359 %!test
12360 %! hf = figure ("visible", "off");
12361 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
12362 %! xticklabel = get (gca (), "xticklabel");
12363 %! close (hf);
12364 %! assert (class (xticklabel), "cell");
12365 %! assert (size (xticklabel), [6, 1]);
12366 */
12367 
12368 static std::string
12369 get_graphics_object_type (double val)
12370 {
12371  std::string retval;
12372 
12374 
12375  graphics_object go = gh_mgr.get_object (val);
12376 
12377  if (! go)
12378  error ("get: invalid handle (= %g)", val);
12379 
12380  return go.type ();
12381 }
12382 
12383 DEFMETHOD (get, interp, args, ,
12384  doc: /* -*- texinfo -*-
12385 @deftypefn {} {@var{val} =} get (@var{h})
12386 @deftypefnx {} {@var{val} =} get (@var{h}, @var{p})
12387 Return the value of the named property @var{p} from the graphics handle
12388 @var{h}.
12389 
12390 If @var{p} is omitted, return the complete property list for @var{h}.
12391 
12392 If @var{h} is a vector, return a cell array including the property values or
12393 lists respectively.
12394 @seealso{set}
12395 @end deftypefn */)
12396 {
12397  gh_manager& gh_mgr = interp.get_gh_manager ();
12398 
12399  octave::autolock guard (gh_mgr.graphics_lock ());
12400 
12401  int nargin = args.length ();
12402 
12403  if (nargin < 1 || nargin > 2)
12404  print_usage ();
12405 
12406  if (args(0).isempty ())
12407  return ovl (Matrix ());
12408 
12409  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
12410 
12411  octave_idx_type hcv_len = hcv.numel ();
12412 
12413  if (nargin == 1 && hcv_len > 1)
12414  {
12415  std::string typ0 = get_graphics_object_type (hcv(0));
12416 
12417  for (octave_idx_type n = 1; n < hcv_len; n++)
12418  {
12419  std::string typ = get_graphics_object_type (hcv(n));
12420 
12421  if (typ != typ0)
12422  error ("get: vector of handles must all have the same type");
12423  }
12424  }
12425 
12426  octave_value retval;
12427  Cell vals;
12428  bool use_cell_format = false;
12429 
12430  if (nargin > 1 && args(1).iscellstr ())
12431  {
12432  Array<std::string> plist = args(1).cellstr_value ();
12433 
12434  octave_idx_type plen = plist.numel ();
12435 
12436  use_cell_format = true;
12437 
12438  vals.resize (dim_vector (hcv_len, plen));
12439 
12440  for (octave_idx_type n = 0; n < hcv_len; n++)
12441  {
12442  graphics_object go = gh_mgr.get_object (hcv(n));
12443 
12444  if (! go)
12445  error ("get: invalid handle (= %g)", hcv(n));
12446 
12447  for (octave_idx_type m = 0; m < plen; m++)
12448  {
12449  caseless_str property = plist(m);
12450 
12451  vals(n, m) = go.get (property);
12452  }
12453  }
12454  }
12455  else
12456  {
12458 
12459  if (nargin > 1)
12460  property = args(1).xstring_value ("get: second argument must be property name or cell array of property names");
12461 
12462  vals.resize (dim_vector (hcv_len, 1));
12463 
12464  for (octave_idx_type n = 0; n < hcv_len; n++)
12465  {
12466  graphics_object go = gh_mgr.get_object (hcv(n));
12467 
12468  if (! go)
12469  error ("get: invalid handle (= %g)", hcv(n));
12470 
12471  if (nargin == 1)
12472  vals(n) = go.get ();
12473  else
12474  vals(n) = go.get (property);
12475  }
12476  }
12477 
12478  if (use_cell_format)
12479  retval = vals;
12480  else
12481  {
12482  octave_idx_type vals_len = vals.numel ();
12483 
12484  if (vals_len == 0)
12485  retval = Matrix ();
12486  else if (vals_len == 1)
12487  retval = vals(0);
12488  else if (vals_len > 1 && nargin == 1)
12489  {
12490  OCTAVE_LOCAL_BUFFER (octave_scalar_map, tmp, vals_len);
12491 
12492  for (octave_idx_type n = 0; n < vals_len; n++)
12493  tmp[n] = vals(n).scalar_map_value ();
12494 
12495  retval = octave_map::cat (0, vals_len, tmp);
12496  }
12497  else
12498  retval = vals;
12499  }
12500 
12501  return retval;
12502 }
12503 
12504 /*
12505 %!assert (get (findobj (0, "Tag", "nonexistenttag"), "nonexistentproperty"), [])
12506 */
12507 
12508 // Return all properties from the graphics handle @var{h}.
12509 // If @var{h} is a vector, return a cell array including the
12510 // property values or lists respectively.
12511 
12512 DEFMETHOD (__get__, interp, args, ,
12513  doc: /* -*- texinfo -*-
12514 @deftypefn {} {@var{props} =} __get__ (@var{h})
12515 Undocumented internal function.
12516 @end deftypefn */)
12517 {
12518  gh_manager& gh_mgr = interp.get_gh_manager ();
12519 
12520  octave::autolock guard (gh_mgr.graphics_lock ());
12521 
12522  if (args.length () != 1)
12523  print_usage ();
12524 
12525  ColumnVector hcv = args(0).xvector_value ("get: H must be a graphics handle");
12526 
12527  octave_idx_type hcv_len = hcv.numel ();
12528 
12529  Cell vals (dim_vector (hcv_len, 1));
12530 
12531 // vals.resize (dim_vector (hcv_len, 1));
12532 
12533  for (octave_idx_type n = 0; n < hcv_len; n++)
12534  {
12535  graphics_object go = gh_mgr.get_object (hcv(n));
12536 
12537  if (! go)
12538  error ("get: invalid handle (= %g)", hcv(n));
12539 
12540  // Disable "Octave:deprecated-property" warnings
12541  int state = toggle_warn ("Octave:deprecated-property", false);
12542 
12543  vals(n) = go.get (true);
12544 
12545  toggle_warn ("Octave:deprecated-property", true, state);
12546  }
12547 
12548  octave_idx_type vals_len = vals.numel ();
12549 
12550  if (vals_len > 1)
12551  return ovl (vals);
12552  else if (vals_len == 1)
12553  return ovl (vals(0));
12554  else
12555  return ovl ();
12556 }
12557 
12558 static octave_value
12559 make_graphics_object (const std::string& go_name,
12560  bool integer_figure_handle,
12561  const octave_value_list& args)
12562 {
12563  octave_value retval;
12564 
12565  double val = octave::numeric_limits<double>::NaN ();
12566 
12567  octave_value_list xargs = args.splice (0, 1);
12568 
12569  caseless_str p ("parent");
12570 
12571  // Remove all "parent" property overrides of the first argument to function
12572  // and accept only the last one (bug #55322).
12573  for (int i = 0; i < xargs.length (); i += 2)
12574  {
12575  if (xargs(i).is_string () && p.compare (xargs(i).string_value ()))
12576  {
12577  if (i >= (xargs.length () - 1))
12578  error ("__go_%s__: missing value for parent property",
12579  go_name.c_str ());
12580 
12581  val = xargs(i+1).double_value ();
12582 
12583  xargs = xargs.splice (i, 2);
12584  i -= 2;
12585  }
12586  }
12587 
12588  if (octave::math::isnan (val))
12589  val = args(0).xdouble_value ("__go_%s__: invalid parent", go_name.c_str ());
12590 
12592 
12593  graphics_handle parent = gh_mgr.lookup (val);
12594 
12595  if (! parent.ok ())
12596  error ("__go_%s__: invalid parent", go_name.c_str ());
12597 
12598  graphics_handle h;
12599 
12600  try
12601  {
12602  h = gh_mgr.make_graphics_handle (go_name, parent,
12603  integer_figure_handle, false, false);
12604  }
12605  catch (octave::execution_exception& ee)
12606  {
12607  error (ee, "__go_%s__: %s, unable to create graphics handle",
12608  go_name.c_str (), ee.message ().c_str ());
12609  }
12610 
12611  try
12612  {
12613  xset (h, xargs);
12614  }
12615  catch (octave::execution_exception& ee)
12616  {
12618  error (ee, "__go_%s__: %s, unable to create graphics handle",
12619  go_name.c_str (), ee.message ().c_str ());
12620  }
12621 
12622  adopt (parent, h);
12623 
12624  xcreatefcn (h);
12625  xinitialize (h);
12626 
12627  retval = h.value ();
12628 
12629  Vdrawnow_requested = true;
12630 
12631  return retval;
12632 }
12633 
12634 DEFMETHOD (__go_figure__, interp, args, ,
12635  doc: /* -*- texinfo -*-
12636 @deftypefn {} {@var{hfig} =} __go_figure__ (@var{fignum})
12637 Undocumented internal function.
12638 @end deftypefn */)
12639 {
12640  gh_manager& gh_mgr = interp.get_gh_manager ();
12641 
12642  octave::autolock guard (gh_mgr.graphics_lock ());
12643 
12644  if (args.length () == 0)
12645  print_usage ();
12646 
12647  double val = args(0).xdouble_value ("__go_figure__: figure number must be a double value");
12648 
12649  octave_value retval;
12650 
12651  if (isfigure (val))
12652  {
12653  graphics_handle h = gh_mgr.lookup (val);
12654 
12655  xset (h, args.splice (0, 1));
12656 
12657  retval = h.value ();
12658  }
12659  else
12660  {
12661  bool int_fig_handle = true;
12662 
12663  octave_value_list xargs = args.splice (0, 1);
12664 
12666 
12667  if (octave::math::isnan (val))
12668  {
12669  caseless_str pname ("integerhandle");
12670 
12671  for (int i = 0; i < xargs.length (); i++)
12672  {
12673  if (xargs(i).is_string ()
12674  && pname.compare (xargs(i).string_value ()))
12675  {
12676  if (i < (xargs.length () - 1))
12677  {
12678  std::string pval = xargs(i+1).string_value ();
12679 
12680  caseless_str on ("on");
12681  int_fig_handle = on.compare (pval);
12682  xargs = xargs.splice (i, 2);
12683 
12684  break;
12685  }
12686  }
12687  }
12688 
12689  h = gh_mgr.make_graphics_handle ("figure", 0, int_fig_handle,
12690  false, false);
12691 
12692  if (! int_fig_handle)
12693  {
12694  // We need to initialize the integerhandle property
12695  // without calling the set_integerhandle method,
12696  // because doing that will generate a new handle value...
12697  graphics_object go = gh_mgr.get_object (h);
12698  go.get_properties ().init_integerhandle ("off");
12699  }
12700  }
12701  else if (val > 0 && octave::math::x_nint (val) == val)
12702  h = gh_mgr.make_figure_handle (val, false);
12703 
12704  if (! h.ok ())
12705  error ("__go_figure__: failed to create figure handle");
12706 
12707  try
12708  {
12709  xset (h, xargs);
12710  }
12711  catch (octave::execution_exception& ee)
12712  {
12714  error (ee, "__go_figure__: unable to create figure handle");
12715  }
12716 
12717  adopt (0, h);
12718 
12719  gh_mgr.push_figure (h);
12720 
12721  xcreatefcn (h);
12722  xinitialize (h);
12723 
12724  retval = h.value ();
12725  }
12726 
12727  return retval;
12728 }
12729 
12730 #define GO_BODY(TYPE) \
12731  gh_manager& gh_mgr = interp.get_gh_manager (); \
12732  \
12733  octave::autolock guard (gh_mgr.graphics_lock ()); \
12734  \
12735  if (args.length () == 0) \
12736  print_usage (); \
12737  \
12738  return octave_value (make_graphics_object (#TYPE, false, args)); \
12739 
12740 int
12742 {
12743  int nd = 2;
12744 
12745  if (go.isa ("surface"))
12746  nd = 3;
12747  else if ((go.isa ("line") || go.isa ("patch") || go.isa ("scatter"))
12748  && ! go.get ("zdata").isempty ())
12749  nd = 3;
12750  else
12751  {
12752  Matrix kids = go.get_properties ().get_children ();
12753 
12755 
12756  for (octave_idx_type i = 0; i < kids.numel (); i++)
12757  {
12758  graphics_handle hkid = gh_mgr.lookup (kids(i));
12759 
12760  if (hkid.ok ())
12761  {
12762  const graphics_object& kid = gh_mgr.get_object (hkid);
12763 
12764  if (kid.valid_object ())
12765  nd = calc_dimensions (kid);
12766 
12767  if (nd == 3)
12768  break;
12769  }
12770  }
12771  }
12772 
12773  return nd;
12774 }
12775 
12776 DEFMETHOD (__calc_dimensions__, interp, args, ,
12777  doc: /* -*- texinfo -*-
12778 @deftypefn {} {@var{ndims} =} __calc_dimensions__ (@var{axes})
12779 Internal function.
12780 
12781 Determine the number of dimensions in a graphics object, either 2 or 3.
12782 @end deftypefn */)
12783 {
12784  gh_manager& gh_mgr = interp.get_gh_manager ();
12785 
12786  octave::autolock guard (gh_mgr.graphics_lock ());
12787 
12788  if (args.length () != 1)
12789  print_usage ();
12790 
12791  double h = args(0).xdouble_value ("__calc_dimensions__: first argument must be a graphics handle");
12792 
12793  return ovl (calc_dimensions (gh_mgr.get_object (h)));
12794 }
12795 
12796 DEFMETHOD (__go_axes__, interp, args, ,
12797  doc: /* -*- texinfo -*-
12798 @deftypefn {} {@var{hax} =} __go_axes__ (@var{parent})
12799 Undocumented internal function.
12800 @end deftypefn */)
12801 {
12802  GO_BODY (axes);
12803 }
12804 
12805 DEFMETHOD (__go_line__, interp, args, ,
12806  doc: /* -*- texinfo -*-
12807 @deftypefn {} {@var{hl} =} __go_line__ (@var{parent})
12808 Undocumented internal function.
12809 @end deftypefn */)
12810 {
12811  GO_BODY (line);
12812 }
12813 
12814 DEFMETHOD (__go_text__, interp, args, ,
12815  doc: /* -*- texinfo -*-
12816 @deftypefn {} {@var{ht} =} __go_text__ (@var{parent})
12817 Undocumented internal function.
12818 @end deftypefn */)
12819 {
12820  GO_BODY (text);
12821 }
12822 
12823 DEFMETHOD (__go_image__, interp, args, ,
12824  doc: /* -*- texinfo -*-
12825 @deftypefn {} {@var{hi} =} __go_image__ (@var{parent})
12826 Undocumented internal function.
12827 @end deftypefn */)
12828 {
12829  GO_BODY (image);
12830 }
12831 
12832 DEFMETHOD (__go_surface__, interp, args, ,
12833  doc: /* -*- texinfo -*-
12834 @deftypefn {} {@var{hs} =} __go_surface__ (@var{parent})
12835 Undocumented internal function.
12836 @end deftypefn */)
12837 {
12838  GO_BODY (surface);
12839 }
12840 
12841 DEFMETHOD (__go_patch__, interp, args, ,
12842  doc: /* -*- texinfo -*-
12843 @deftypefn {} {@var{hp} =} __go_patch__ (@var{parent})
12844 Undocumented internal function.
12845 @end deftypefn */)
12846 {
12847  GO_BODY (patch);
12848 }
12849 
12850 DEFMETHOD (__go_scatter__, interp, args, ,
12851  doc: /* -*- texinfo -*-
12852 @deftypefn {} {@var{hs} =} __go_scatter__ (@var{parent})
12853 Undocumented internal function.
12854 @end deftypefn */)
12855 {
12856  GO_BODY (scatter);
12857 }
12858 
12859 DEFMETHOD (__go_light__, interp, args, ,
12860  doc: /* -*- texinfo -*-
12861 @deftypefn {} {@var{hl} =} __go_light__ (@var{parent})
12862 Undocumented internal function.
12863 @end deftypefn */)
12864 {
12865  GO_BODY (light);
12866 }
12867 
12868 DEFMETHOD (__go_hggroup__, interp, args, ,
12869  doc: /* -*- texinfo -*-
12870 @deftypefn {} {@var{hgg} =} __go_hggroup__ (@var{parent})
12871 Undocumented internal function.
12872 @end deftypefn */)
12873 {
12874  GO_BODY (hggroup);
12875 }
12876 
12877 DEFMETHOD (__go_uimenu__, interp, args, ,
12878  doc: /* -*- texinfo -*-
12879 @deftypefn {} {@var{hui} =} __go_uimenu__ (@var{parent})
12880 Undocumented internal function.
12881 @end deftypefn */)
12882 {
12883  GO_BODY (uimenu);
12884 }
12885 
12886 DEFMETHOD (__go_uicontrol__, interp, args, ,
12887  doc: /* -*- texinfo -*-
12888 @deftypefn {} {@var{hui} =} __go_uicontrol__ (@var{parent})
12889 Undocumented internal function.
12890 @end deftypefn */)
12891 {
12892  GO_BODY (uicontrol);
12893 }
12894 
12895 DEFMETHOD (__go_uibuttongroup__, interp, args, ,
12896  doc: /* -*- texinfo -*-
12897 @deftypefn {} {@var{hui} =} __go_uibuttongroup__ (@var{parent})
12898 Undocumented internal function.
12899 @end deftypefn */)
12900 {
12902 }
12903 
12904 DEFMETHOD (__go_uipanel__, interp, args, ,
12905  doc: /* -*- texinfo -*-
12906 @deftypefn {} {@var{hui} =} __go_uipanel__ (@var{parent})
12907 Undocumented internal function.
12908 @end deftypefn */)
12909 {
12910  GO_BODY (uipanel);
12911 }
12912 
12913 DEFMETHOD (__go_uicontextmenu__, interp, args, ,
12914  doc: /* -*- texinfo -*-
12915 @deftypefn {} {@var{hui} =} __go_uicontextmenu__ (@var{parent})
12916 Undocumented internal function.
12917 @end deftypefn */)
12918 {
12920 }
12921 
12922 DEFMETHOD (__go_uitable__, interp, args, ,
12923  doc: /* -*- texinfo -*-
12924 @deftypefn {} {@var{hui} =} __go_uitable__ (@var{parent})
12925 Undocumented internal function.
12926 @end deftypefn */)
12927 {
12928  GO_BODY (uitable);
12929 }
12930 
12931 DEFMETHOD (__go_uitoolbar__, interp, args, ,
12932  doc: /* -*- texinfo -*-
12933 @deftypefn {} {@var{hui} =} __go_uitoolbar__ (@var{parent})
12934 Undocumented internal function.
12935 @end deftypefn */)
12936 {
12937  GO_BODY (uitoolbar);
12938 }
12939 
12940 DEFMETHOD (__go_uipushtool__, interp, args, ,
12941  doc: /* -*- texinfo -*-
12942 @deftypefn {} {@var{hui} =} __go_uipushtool__ (@var{parent})
12943 Undocumented internal function.
12944 @end deftypefn */)
12945 {
12946  GO_BODY (uipushtool);
12947 }
12948 
12949 DEFMETHOD (__go_uitoggletool__, interp, args, ,
12950  doc: /* -*- texinfo -*-
12951 @deftypefn {} {@var{hui} =} __go_uitoggletool__ (@var{parent})
12952 Undocumented internal function.
12953 @end deftypefn */)
12954 {
12956 }
12957 
12958 DEFMETHOD (__go_delete__, interp, args, ,
12959  doc: /* -*- texinfo -*-
12960 @deftypefn {} {} __go_delete__ (@var{h})
12961 Undocumented internal function.
12962 @end deftypefn */)
12963 {
12964  gh_manager& gh_mgr = interp.get_gh_manager ();
12965 
12966  octave::autolock guard (gh_mgr.graphics_lock ());
12967 
12968  if (args.length () != 1)
12969  print_usage ();
12970 
12972 
12973  const NDArray vals = args(0).xarray_value ("delete: invalid graphics object");
12974 
12975  // Check all the handles to delete are valid first,
12976  // as callbacks might delete one of the handles we later want to delete.
12977  for (octave_idx_type i = 0; i < vals.numel (); i++)
12978  {
12979  h = gh_mgr.lookup (vals(i));
12980 
12981  if (! h.ok ())
12982  error ("delete: invalid graphics object (= %g)", vals(i));
12983  }
12984 
12985  delete_graphics_objects (vals);
12986 
12987  return ovl ();
12988 }
12989 
12990 DEFMETHOD (__go_handles__, interp, args, ,
12991  doc: /* -*- texinfo -*-
12992 @deftypefn {} {@var{hg_list} =} __go_handles__ (@var{show_hidden})
12993 Undocumented internal function.
12994 @end deftypefn */)
12995 {
12996  gh_manager& gh_mgr = interp.get_gh_manager ();
12997 
12998  octave::autolock guard (gh_mgr.graphics_lock ());
12999 
13000  bool show_hidden = false;
13001 
13002  if (args.length () > 0)
13003  show_hidden = args(0).bool_value ();
13004 
13005  return ovl (gh_mgr.handle_list (show_hidden));
13006 }
13007 
13008 DEFMETHOD (__go_figure_handles__, interp, args, ,
13009  doc: /* -*- texinfo -*-
13010 @deftypefn {} {@var{hfig_list} =} __go_figure_handles__ (@var{show_hidden})
13011 Undocumented internal function.
13012 @end deftypefn */)
13013 {
13014  gh_manager& gh_mgr = interp.get_gh_manager ();
13015 
13016  octave::autolock guard (gh_mgr.graphics_lock ());
13017 
13018  bool show_hidden = false;
13019 
13020  if (args.length () > 0)
13021  show_hidden = args(0).bool_value ();
13022 
13023  return ovl (gh_mgr.figure_handle_list (show_hidden));
13024 }
13025 
13026 DEFMETHOD (__go_execute_callback__, interp, args, ,
13027  doc: /* -*- texinfo -*-
13028 @deftypefn {} {} __go_execute_callback__ (@var{h}, @var{name})
13029 @deftypefnx {} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})
13030 Undocumented internal function.
13031 @end deftypefn */)
13032 {
13033  int nargin = args.length ();
13034 
13035  if (nargin < 2 || nargin > 3)
13036  print_usage ();
13037 
13038  const NDArray vals = args(0).xarray_value ("__go_execute_callback__: invalid graphics object");
13039 
13040  std::string name = args(1).xstring_value ("__go_execute_callback__: invalid callback name");
13041 
13042  gh_manager& gh_mgr = interp.get_gh_manager ();
13043 
13044  for (octave_idx_type i = 0; i < vals.numel (); i++)
13045  {
13046  double val = vals(i);
13047 
13048  graphics_handle h = gh_mgr.lookup (val);
13049 
13050  if (! h.ok ())
13051  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
13052 
13053  if (nargin == 2)
13054  gh_mgr.execute_callback (h, name);
13055  else
13056  gh_mgr.execute_callback (h, name, args(2));
13057  }
13058 
13059  return ovl ();
13060 }
13061 
13062 DEFMETHOD (__go_post_callback__, interp, args, ,
13063  doc: /* -*- texinfo -*-
13064 @deftypefn {} {} __go_post_callback__ (@var{h}, @var{name})
13065 @deftypefnx {} {} __go_post_callback__ (@var{h}, @var{name}, @var{param})
13066 Undocumented internal function.
13067 @end deftypefn */)
13068 {
13069  int nargin = args.length ();
13070 
13071  if (nargin < 2 || nargin > 3)
13072  print_usage ();
13073 
13074  const NDArray vals = args(0).xarray_value ("__go_post_callback__: invalid graphics object");
13075 
13076  std::string name = args(1).xstring_value ("__go_post_callback__: invalid callback name");
13077 
13078  gh_manager& gh_mgr = interp.get_gh_manager ();
13079 
13080  for (octave_idx_type i = 0; i < vals.numel (); i++)
13081  {
13082  double val = vals(i);
13083 
13084  graphics_handle h = gh_mgr.lookup (val);
13085 
13086  if (! h.ok ())
13087  error ("__go_execute_callback__: invalid graphics object (= %g)", val);
13088 
13089  if (nargin == 2)
13090  gh_mgr.post_callback (h, name);
13091  else
13092  gh_mgr.post_callback (h, name, args(2));
13093  }
13094 
13095  return ovl ();
13096 }
13097 
13098 DEFMETHOD (__image_pixel_size__, interp, args, ,
13099  doc: /* -*- texinfo -*-
13100 @deftypefn {} {@var{sz} =} __image_pixel_size__ (@var{h})
13101 Internal function: returns the pixel size of the image in normalized units.
13102 @end deftypefn */)
13103 {
13104  if (args.length () != 1)
13105  print_usage ();
13106 
13107  gh_manager& gh_mgr = interp.get_gh_manager ();
13108 
13109  double h = args(0).xdouble_value ("__image_pixel_size__: argument is not a handle");
13110 
13111  graphics_object go = gh_mgr.get_object (h);
13112 
13113  if (! go || ! go.isa ("image"))
13114  error ("__image_pixel_size__: object is not an image");
13115 
13116  image::properties& ip
13117  = dynamic_cast<image::properties&> (go.get_properties ());
13118 
13119  Matrix dp = Matrix (1, 2);
13120  dp(0) = ip.pixel_xsize ();
13121  dp(1) = ip.pixel_ysize ();
13122  return ovl (dp);
13123 }
13124 
13125 DEFMETHOD (available_graphics_toolkits, interp, , ,
13126  doc: /* -*- texinfo -*-
13127 @deftypefn {} {@var{toolkits} =} available_graphics_toolkits ()
13128 Return a cell array of registered graphics toolkits.
13129 @seealso{graphics_toolkit, register_graphics_toolkit}
13130 @end deftypefn */)
13131 {
13132  gh_manager& gh_mgr = interp.get_gh_manager ();
13133 
13134  octave::autolock guard (gh_mgr.graphics_lock ());
13135 
13136  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13137 
13138  return ovl (gtk_mgr.available_toolkits_list ());
13139 }
13140 
13141 DEFMETHOD (register_graphics_toolkit, interp, args, ,
13142  doc: /* -*- texinfo -*-
13143 @deftypefn {} {} register_graphics_toolkit ("@var{toolkit}")
13144 List @var{toolkit} as an available graphics toolkit.
13145 
13146 Programming Note: No input validation is done on the input string; it is simply
13147 added to the list of possible graphics toolkits.
13148 @seealso{available_graphics_toolkits}
13149 @end deftypefn */)
13150 {
13151  gh_manager& gh_mgr = interp.get_gh_manager ();
13152 
13153  octave::autolock guard (gh_mgr.graphics_lock ());
13154 
13155  if (args.length () != 1)
13156  print_usage ();
13157 
13158  std::string name = args(0).xstring_value ("register_graphics_toolkit: TOOLKIT must be a string");
13159 
13160  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13161 
13162  gtk_mgr.register_toolkit (name);
13163 
13164  return ovl ();
13165 }
13166 
13167 DEFMETHOD (loaded_graphics_toolkits, interp, , ,
13168  doc: /* -*- texinfo -*-
13169 @deftypefn {} {@var{toolkits} =} loaded_graphics_toolkits ()
13170 Return a cell array of the currently loaded graphics toolkits.
13171 @seealso{available_graphics_toolkits}
13172 @end deftypefn */)
13173 {
13174  gh_manager& gh_mgr = interp.get_gh_manager ();
13175 
13176  octave::autolock guard (gh_mgr.graphics_lock ());
13177 
13178  octave::gtk_manager& gtk_mgr = interp.get_gtk_manager ();
13179 
13180  return ovl (gtk_mgr.loaded_toolkits_list ());
13181 }
13182 
13183 DEFMETHOD (__show_figure__, interp, args, ,
13184  doc: /* -*- texinfo -*-
13185 @deftypefn {} {} __show_figure__ (@var{n})
13186 Undocumented internal function.
13187 @end deftypefn */)
13188 {
13189  if (args.length () != 1)
13190  print_usage ();
13191 
13192  gh_manager& gh_mgr = interp.get_gh_manager ();
13193 
13194  double h = args(0).xdouble_value ("__show_figure__: invalid handle H");
13195 
13196  graphics_handle gh = gh_mgr.lookup (h);
13197 
13198  if (! gh.ok ())
13199  error ("__show_figure__: invalid graphics object (= %g)", h);
13200 
13201  graphics_object go = gh_mgr.get_object (gh);
13202 
13203  figure::properties& fprops
13204  = dynamic_cast<figure::properties&> (go.get_properties ());
13205 
13206  fprops.get_toolkit ().show_figure (go);
13207 
13208  return ovl ();
13209 }
13210 
13211 DEFMETHOD (drawnow, interp, args, ,
13212  doc: /* -*- texinfo -*-
13213 @deftypefn {} {} drawnow ()
13214 @deftypefnx {} {} drawnow ("expose")
13215 @deftypefnx {} {} drawnow (@var{term}, @var{file}, @var{debug_file})
13216 Update figure windows and their children.
13217 
13218 The event queue is flushed and any callbacks generated are executed.
13219 
13220 With the optional argument @qcode{"expose"}, only graphic objects are
13221 updated and no other events or callbacks are processed.
13222 
13223 The third calling form of @code{drawnow} is for debugging and is
13224 undocumented.
13225 @seealso{refresh}
13226 @end deftypefn */)
13227 {
13228  if (args.length () > 3)
13229  print_usage ();
13230 
13231  octave::unwind_protect_var<bool> restore_var (Vdrawnow_requested, false);
13232 
13233  // Redraw unless we are in the middle of a deletion.
13234 
13235  if (! delete_executing)
13236  {
13237  gh_manager& gh_mgr = interp.get_gh_manager ();
13238 
13239  octave::autolock guard (gh_mgr.graphics_lock ());
13240 
13241  if (args.length () <= 1)
13242  {
13243  // First process events so that the redraw happens when all
13244  // objects are in their definite state.
13245  bool do_events = true;
13246 
13247  if (args.length () == 1)
13248  {
13249  caseless_str val (args(0).xstring_value ("drawnow: first argument must be a string"));
13250 
13251  if (val.compare ("expose"))
13252  do_events = false;
13253  else
13254  error ("drawnow: invalid argument, 'expose' is only valid option");
13255  }
13256 
13257  if (do_events)
13258  {
13259  gh_mgr.unlock ();
13260 
13261  gh_mgr.process_events ();
13262 
13263  gh_mgr.lock ();
13264  }
13265 
13266  Matrix hlist = gh_mgr.figure_handle_list (true);
13267 
13268  // Redraw modified figures
13269  for (int i = 0; i < hlist.numel (); i++)
13270  {
13271  graphics_handle h = gh_mgr.lookup (hlist(i));
13272 
13273  if (h.ok () && h != 0)
13274  {
13275  graphics_object go = gh_mgr.get_object (h);
13276  figure::properties& fprops
13277  = dynamic_cast<figure::properties&> (go.get_properties ());
13278 
13279  if (fprops.is_modified ())
13280  {
13281  if (fprops.is_visible ())
13282  {
13283  gh_mgr.unlock ();
13284 
13285  fprops.get_toolkit ().redraw_figure (go);
13286 
13287  gh_mgr.lock ();
13288  }
13289 
13290  fprops.set_modified (false);
13291  }
13292  }
13293 
13294  }
13295  }
13296  else if (args.length () >= 2 && args.length () <= 3)
13297  {
13298  std::string term, file, debug_file;
13299 
13300  term = args(0).xstring_value ("drawnow: TERM must be a string");
13301 
13302  file = args(1).xstring_value ("drawnow: FILE must be a string");
13303 
13304  if (file.empty ())
13305  error ("drawnow: empty output ''");
13306  else if (file.length () == 1 && file[0] == '|')
13307  error ("drawnow: empty pipe '|'");
13308  else if (file[0] != '|')
13309  {
13310  std::size_t pos = file.find_last_of (octave::sys::file_ops::dir_sep_chars ());
13311 
13312  if (pos != std::string::npos)
13313  {
13314  std::string dirname = file.substr (0, pos+1);
13315 
13317  error ("drawnow: nonexistent directory '%s'",
13318  dirname.c_str ());
13319 
13320  }
13321  }
13322 
13323  debug_file = (args.length () > 2 ? args(2).xstring_value ("drawnow: DEBUG_FILE must be a string") :
13324  "");
13325 
13326  graphics_handle h = gcf ();
13327 
13328  if (! h.ok ())
13329  error ("drawnow: nothing to draw");
13330 
13331  graphics_object go = gh_mgr.get_object (h);
13332 
13333  gh_mgr.unlock ();
13334 
13335  go.get_toolkit ().print_figure (go, term, file, debug_file);
13336 
13337  gh_mgr.lock ();
13338  }
13339  }
13340 
13341  return ovl ();
13342 }
13343 
13344 DEFMETHOD (addlistener, interp, args, ,
13345  doc: /* -*- texinfo -*-
13346 @deftypefn {} {} addlistener (@var{h}, @var{prop}, @var{fcn})
13347 Register @var{fcn} as listener for the property @var{prop} of the graphics
13348 object @var{h}.
13349 
13350 Property listeners are executed (in order of registration) when the property
13351 is set. The new value is already available when the listeners are executed.
13352 
13353 @var{prop} must be a string naming a valid property in @var{h}.
13354 
13355 @var{fcn} can be a function handle, a string or a cell array whose first
13356 element is a function handle. If @var{fcn} is a function handle, the
13357 corresponding function should accept at least 2 arguments, that will be
13358 set to the object handle and the empty matrix respectively. If @var{fcn}
13359 is a string, it must be any valid octave expression. If @var{fcn} is a cell
13360 array, the first element must be a function handle with the same signature
13361 as described above. The next elements of the cell array are passed
13362 as additional arguments to the function.
13363 
13364 Example:
13365 
13366 @example
13367 @group
13368 function my_listener (h, dummy, p1)
13369  fprintf ("my_listener called with p1=%s\n", p1);
13370 endfunction
13371 
13372 addlistener (gcf, "position", @{@@my_listener, "my string"@})
13373 @end group
13374 @end example
13375 
13376 @seealso{dellistener, addproperty, hggroup}
13377 @end deftypefn */)
13378 {
13379  gh_manager& gh_mgr = interp.get_gh_manager ();
13380 
13381  octave::autolock guard (gh_mgr.graphics_lock ());
13382 
13383  int nargin = args.length ();
13384 
13385  if (nargin < 3 || nargin > 4)
13386  print_usage ();
13387 
13388  double h = args(0).xdouble_value ("addlistener: invalid handle H");
13389 
13390  std::string pname = args(1).xstring_value ("addlistener: PROP must be a string");
13391 
13392  graphics_handle gh = gh_mgr.lookup (h);
13393 
13394  if (! gh.ok ())
13395  error ("addlistener: invalid graphics object (= %g)", h);
13396 
13397  graphics_object go = gh_mgr.get_object (gh);
13398 
13399  go.add_property_listener (pname, args(2), GCB_POSTSET);
13400 
13401  if (args.length () == 4)
13402  {
13403  caseless_str persistent = args(3).string_value ();
13404  if (persistent.compare ("persistent"))
13405  go.add_property_listener (pname, args(2), GCB_PERSISTENT);
13406  }
13407 
13408  return ovl ();
13409 }
13410 
13411 DEFMETHOD (dellistener, interp, args, ,
13412  doc: /* -*- texinfo -*-
13413 @deftypefn {} {} dellistener (@var{h}, @var{prop}, @var{fcn})
13414 Remove the registration of @var{fcn} as a listener for the property
13415 @var{prop} of the graphics object @var{h}.
13416 
13417 The function @var{fcn} must be the same variable (not just the same value),
13418 as was passed to the original call to @code{addlistener}.
13419 
13420 If @var{fcn} is not defined then all listener functions of @var{prop}
13421 are removed.
13422 
13423 Example:
13424 
13425 @example
13426 @group
13427 function my_listener (h, dummy, p1)
13428  fprintf ("my_listener called with p1=%s\n", p1);
13429 endfunction
13430 
13431 c = @{@@my_listener, "my string"@};
13432 addlistener (gcf, "position", c);
13433 dellistener (gcf, "position", c);
13434 @end group
13435 @end example
13436 
13437 @seealso{addlistener}
13438 @end deftypefn */)
13439 {
13440  gh_manager& gh_mgr = interp.get_gh_manager ();
13441 
13442  octave::autolock guard (gh_mgr.graphics_lock ());
13443 
13444  if (args.length () < 2 || args.length () > 3)
13445  print_usage ();
13446 
13447  double h = args(0).xdouble_value ("dellistener: invalid handle");
13448 
13449  std::string pname = args(1).xstring_value ("dellistener: PROP must be a string");
13450 
13451  graphics_handle gh = gh_mgr.lookup (h);
13452 
13453  if (! gh.ok ())
13454  error ("dellistener: invalid graphics object (= %g)", h);
13455 
13456  graphics_object go = gh_mgr.get_object (gh);
13457 
13458  if (args.length () == 2)
13460  else
13461  {
13462  if (args(2).is_string ()
13463  && args(2).string_value () == "persistent")
13464  {
13465  go.delete_property_listener (pname, octave_value (),
13466  GCB_PERSISTENT);
13467  go.delete_property_listener (pname, octave_value (),
13468  GCB_POSTSET);
13469  }
13470  else
13471  go.delete_property_listener (pname, args(2), GCB_POSTSET);
13472  }
13473 
13474  return ovl ();
13475 }
13476 
13477 DEFMETHOD (addproperty, interp, args, ,
13478  doc: /* -*- texinfo -*-
13479 @deftypefn {} {} addproperty (@var{name}, @var{h}, @var{type})
13480 @deftypefnx {} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})
13481 Create a new property named @var{name} in graphics object @var{h}.
13482 
13483 @var{type} determines the type of the property to create. @var{args}
13484 usually contains the default value of the property, but additional
13485 arguments might be given, depending on the type of the property.
13486 
13487 The supported property types are:
13488 
13489 @table @code
13490 @item string
13491 A string property. @var{arg} contains the default string value.
13492 
13493 @item any
13494 An @nospell{un-typed} property. This kind of property can hold any octave
13495 value. @var{args} contains the default value.
13496 
13497 @item radio
13498 A string property with a limited set of accepted values. The first
13499 argument must be a string with all accepted values separated by
13500 a vertical bar ('|'). The default value can be marked by enclosing
13501 it with a '@{' '@}' pair. The default value may also be given as
13502 an optional second string argument.
13503 
13504 @item boolean
13505 A boolean property. This property type is equivalent to a radio
13506 property with "on|off" as accepted values. @var{arg} contains
13507 the default property value.
13508 
13509 @item double
13510 A scalar double property. @var{arg} contains the default value.
13511 
13512 @item handle
13513 A handle property. This kind of property holds the handle of a
13514 graphics object. @var{arg} contains the default handle value.
13515 When no default value is given, the property is initialized to
13516 the empty matrix.
13517 
13518 @item data
13519 A data (matrix) property. @var{arg} contains the default data
13520 value. When no default value is given, the data is initialized to
13521 the empty matrix.
13522 
13523 @item color
13524 A color property. @var{arg} contains the default color value.
13525 When no default color is given, the property is set to black.
13526 An optional second string argument may be given to specify an
13527 additional set of accepted string values (like a radio property).
13528 @end table
13529 
13530 @var{type} may also be the concatenation of a core object type and
13531 a valid property name for that object type. The property created
13532 then has the same characteristics as the referenced property (type,
13533 possible values, hidden state@dots{}). This allows one to clone an
13534 existing property into the graphics object @var{h}.
13535 
13536 Examples:
13537 
13538 @example
13539 @group
13540 addproperty ("my_property", gcf, "string", "a string value");
13541 addproperty ("my_radio", gcf, "radio", "val_1|val_2|@{val_3@}");
13542 addproperty ("my_style", gcf, "linelinestyle", "--");
13543 @end group
13544 @end example
13545 
13546 @seealso{addlistener, hggroup}
13547 @end deftypefn */)
13548 {
13549  gh_manager& gh_mgr = interp.get_gh_manager ();
13550 
13551  octave::autolock guard (gh_mgr.graphics_lock ());
13552 
13553  if (args.length () < 3)
13554  print_usage ();
13555 
13556  std::string name = args(0).xstring_value ("addproperty: NAME must be a string");
13557 
13558  double h = args(1).xdouble_value ("addproperty: invalid handle H");
13559 
13560  graphics_handle gh = gh_mgr.lookup (h);
13561 
13562  if (! gh.ok ())
13563  error ("addproperty: invalid graphics object (= %g)", h);
13564 
13565  graphics_object go = gh_mgr.get_object (gh);
13566 
13567  std::string type = args(2).xstring_value ("addproperty: TYPE must be a string");
13568 
13569  if (go.get_properties ().has_property (name))
13570  error ("addproperty: a '%s' property already exists in the graphics object",
13571  name.c_str ());
13572 
13573  property p = property::create (name, gh, type, args.splice (0, 3));
13574 
13575  go.get_properties ().insert_property (name, p);
13576 
13577  return ovl ();
13578 }
13579 
13581 get_property_from_handle (double handle, const std::string& property,
13582  const std::string& fcn)
13583 {
13585 
13586  octave::autolock guard (gh_mgr.graphics_lock ());
13587 
13588  graphics_object go = gh_mgr.get_object (handle);
13589 
13590  if (! go)
13591  error ("%s: invalid handle (= %g)", fcn.c_str (), handle);
13592 
13593  return go.get (caseless_str (property));
13594 }
13595 
13596 bool
13597 set_property_in_handle (double handle, const std::string& property,
13598  const octave_value& arg, const std::string& fcn)
13599 {
13601 
13602  octave::autolock guard (gh_mgr.graphics_lock ());
13603 
13604  graphics_object go = gh_mgr.get_object (handle);
13605 
13606  if (! go)
13607  error ("%s: invalid handle (= %g)", fcn.c_str (), handle);
13608 
13609  go.set (caseless_str (property), arg);
13610 
13611  return true;
13612 }
13613 
13614 static bool
13615 compare_property_values (octave::interpreter& interp,
13616  const octave_value& ov1, const octave_value& ov2)
13617 {
13618  octave_value_list args(2);
13619 
13620  args(0) = ov1;
13621  args(1) = ov2;
13622 
13623  octave_value_list result = interp.feval ("isequal", args, 1);
13624 
13625  if (result.length () > 0)
13626  return result(0).bool_value ();
13627 
13628  return false;
13629 }
13630 
13631 static std::map<uint32_t, bool> waitfor_results;
13632 
13633 static void
13634 cleanup_waitfor_id (uint32_t id)
13635 {
13636  waitfor_results.erase (id);
13637 }
13638 
13639 static void
13640 do_cleanup_waitfor_listener (const octave_value& listener,
13641  listener_mode mode = GCB_POSTSET)
13642 {
13643  Cell c = listener.cell_value ();
13644 
13645  if (c.numel () >= 4)
13646  {
13647  double h = c(2).double_value ();
13648 
13649  caseless_str pname = c(3).string_value ();
13650 
13652 
13653  octave::autolock guard (gh_mgr.graphics_lock ());
13654 
13655  graphics_handle gh = gh_mgr.lookup (h);
13656 
13657  if (gh.ok ())
13658  {
13659  graphics_object go = gh_mgr.get_object (gh);
13660 
13661  if (go.get_properties ().has_property (pname))
13662  {
13663  go.get_properties ().delete_listener (pname, listener, mode);
13664 
13665  if (mode == GCB_POSTSET)
13666  go.get_properties ().delete_listener (pname, listener,
13667  GCB_PERSISTENT);
13668  }
13669  }
13670  }
13671 }
13672 
13673 static void
13674 cleanup_waitfor_postset_listener (const octave_value& listener)
13675 { do_cleanup_waitfor_listener (listener, GCB_POSTSET); }
13676 
13677 static void
13678 cleanup_waitfor_predelete_listener (const octave_value& listener)
13679 { do_cleanup_waitfor_listener (listener, GCB_PREDELETE); }
13680 
13681 DECLARE_STATIC_METHODX (waitfor_listener, interp, args, )
13682 {
13683  if (args.length () > 3)
13684  {
13685  uint32_t id = args(2).uint32_scalar_value ().value ();
13686 
13687  if (args.length () > 5)
13688  {
13689  double h = args(0).double_value ();
13690 
13691  caseless_str pname = args(4).string_value ();
13692 
13694 
13695  octave::autolock guard (gh_mgr.graphics_lock ());
13696 
13697  graphics_handle gh = gh_mgr.lookup (h);
13698 
13699  if (gh.ok ())
13700  {
13701  graphics_object go = gh_mgr.get_object (gh);
13702  octave_value pvalue = go.get (pname);
13703 
13704  if (compare_property_values (interp, pvalue, args(5)))
13705  waitfor_results[id] = true;
13706  }
13707  }
13708  else
13709  waitfor_results[id] = true;
13710  }
13711 
13712  return ovl ();
13713 }
13714 
13715 DECLARE_STATIC_FUNX (waitfor_del_listener, args, )
13716 {
13717  if (args.length () > 2)
13718  {
13719  uint32_t id = args(2).uint32_scalar_value ().value ();
13720 
13721  waitfor_results[id] = true;
13722  }
13723 
13724  return ovl ();
13725 }
13726 
13727 DEFMETHOD (waitfor, interp, args, ,
13728  doc: /* -*- texinfo -*-
13729 @deftypefn {} {} waitfor (@var{h})
13730 @deftypefnx {} {} waitfor (@var{h}, @var{prop})
13731 @deftypefnx {} {} waitfor (@var{h}, @var{prop}, @var{value})
13732 @deftypefnx {} {} waitfor (@dots{}, "timeout", @var{timeout})
13733 Suspend the execution of the current program until a condition is
13734 satisfied on the graphics handle @var{h}.
13735 
13736 While the program is suspended graphics events are still processed normally,
13737 allowing callbacks to modify the state of graphics objects. This function
13738 is reentrant and can be called from a callback, while another @code{waitfor}
13739 call is pending at the top-level.
13740 
13741 In the first form, program execution is suspended until the graphics object
13742 @var{h} is destroyed. If the graphics handle is invalid or if @var{h} is
13743 the root graphics handle and no property @var{prop} was provided, the function
13744 returns immediately.
13745 
13746 In the second form, execution is suspended until the graphics object is
13747 destroyed or the property named @var{prop} is modified. If the graphics
13748 handle is invalid or the property does not exist, the function returns
13749 immediately.
13750 
13751 In the third form, execution is suspended until the graphics object is
13752 destroyed or the property named @var{prop} is set to @var{value}. The
13753 function @code{isequal} is used to compare property values. If the graphics
13754 handle is invalid, the property does not exist or the property is already
13755 set to @var{value}, the function returns immediately.
13756 
13757 An optional timeout can be specified using the property @qcode{"timeout"}.
13758 This timeout value is the number of seconds to wait for the condition to be
13759 true. @var{timeout} must be at least 1. If a smaller value is specified, a
13760 warning is issued and a value of 1 is used instead. If the timeout value is
13761 not an integer, it is truncated towards 0.
13762 
13763 To define a condition on a property named @qcode{"timeout"}, use the string
13764 @qcode{'@backslashchar{}timeout'} instead.
13765 
13766 In all cases, typing CTRL-C stops program execution immediately.
13767 @seealso{waitforbuttonpress, isequal}
13768 @end deftypefn */)
13769 {
13770  if (args.length () == 0)
13771  print_usage ();
13772 
13773  // return immediately if the graphics handle is invalid
13774  if (args(0).isempty ())
13775  return ovl ();
13776 
13777  double h = args(0).xdouble_value ("waitfor: invalid handle value");
13778 
13779  if (! ishghandle (h) || (h == 0 && args.length () == 1))
13780  return ovl ();
13781 
13782  caseless_str pname;
13783 
13784  octave::unwind_action cleanup_waitfor_id_action;
13785  octave::unwind_action cleanup_waitfor_postset_listener_action;
13786  octave::unwind_action cleanup_waitfor_predelete_listener_action;
13787 
13788  static uint32_t id_counter = 0;
13789  uint32_t id = 0;
13790 
13791  int max_arg_index = 0;
13792  int timeout_index = -1;
13793 
13794  double timeout = 0;
13795 
13796  gh_manager& gh_mgr = interp.get_gh_manager ();
13797 
13798  if (args.length () > 1)
13799  {
13800  pname = args(1).xstring_value ("waitfor: PROP must be a string");
13801 
13802  if (pname.empty ())
13803  error ("waitfor: PROP must be a non-empty string");
13804 
13805  if (pname != "timeout")
13806  {
13807  if (pname.compare (R"(\timeout)"))
13808  pname = "timeout";
13809 
13810  static octave_value wf_listener;
13811 
13812  if (! wf_listener.is_defined ())
13813  wf_listener
13814  = octave_value (new octave_builtin (waitfor_listener,
13815  "waitfor_listener"));
13816 
13817  max_arg_index++;
13818  if (args.length () > 2)
13819  {
13820  if (args(2).is_string ())
13821  {
13822  caseless_str s = args(2).string_value ();
13823 
13824  if (s.compare ("timeout"))
13825  timeout_index = 2;
13826  else
13827  max_arg_index++;
13828  }
13829  else
13830  max_arg_index++;
13831  }
13832 
13833  Cell listener (1, max_arg_index >= 2 ? 5 : 4);
13834 
13835  id = id_counter++;
13836  cleanup_waitfor_id_action.set (cleanup_waitfor_id, id);
13837  waitfor_results[id] = false;
13838 
13839  listener(0) = wf_listener;
13840  listener(1) = octave_uint32 (id);
13841  listener(2) = h;
13842  listener(3) = pname;
13843 
13844  if (max_arg_index >= 2)
13845  listener(4) = args(2);
13846 
13847  octave_value ov_listener (listener);
13848 
13849  octave::autolock guard (gh_mgr.graphics_lock ());
13850 
13851  graphics_handle gh = gh_mgr.lookup (h);
13852 
13853  if (gh.ok ())
13854  {
13855  graphics_object go = gh_mgr.get_object (gh);
13856 
13857  if (max_arg_index >= 2
13858  && compare_property_values (interp, go.get (pname), args(2)))
13859  waitfor_results[id] = true;
13860  else
13861  {
13862  cleanup_waitfor_postset_listener_action.set
13863  (cleanup_waitfor_postset_listener, ov_listener);
13864 
13865  go.add_property_listener (pname, ov_listener, GCB_POSTSET);
13866  go.add_property_listener (pname, ov_listener, GCB_PERSISTENT);
13867 
13868  if (go.get_properties ().has_dynamic_property (pname))
13869  {
13870  static octave_value wf_del_listener;
13871 
13872  if (! wf_del_listener.is_defined ())
13873  wf_del_listener
13875  (waitfor_del_listener,
13876  "waitfor_del_listener"));
13877 
13878  Cell del_listener (1, 4);
13879 
13880  del_listener(0) = wf_del_listener;
13881  del_listener(1) = octave_uint32 (id);
13882  del_listener(2) = h;
13883  del_listener(3) = pname;
13884 
13885  octave_value ov_del_listener (del_listener);
13886 
13887  cleanup_waitfor_predelete_listener_action.set
13888  (cleanup_waitfor_predelete_listener, ov_del_listener);
13889 
13890  go.add_property_listener (pname, ov_del_listener,
13891  GCB_PREDELETE);
13892  }
13893  }
13894  }
13895  }
13896  }
13897 
13898  if (timeout_index < 0 && args.length () > (max_arg_index + 1))
13899  {
13900  caseless_str s = args(max_arg_index +
13901  1).xstring_value ("waitfor: invalid parameter, expected 'timeout'");
13902 
13903  if (! s.compare ("timeout"))
13904  error ("waitfor: invalid parameter '%s'", s.c_str ());
13905 
13906  timeout_index = max_arg_index + 1;
13907  }
13908 
13909  if (timeout_index >= 0)
13910  {
13911  if (args.length () <= (timeout_index + 1))
13912  error ("waitfor: missing TIMEOUT value");
13913 
13914  timeout = args(timeout_index + 1).xscalar_value ("waitfor: TIMEOUT must be a scalar >= 1");
13915 
13916  if (timeout < 1)
13917  {
13918  warning ("waitfor: TIMEOUT value must be >= 1, using 1 instead");
13919  timeout = 1;
13920  }
13921  }
13922 
13923  // FIXME: There is still a "hole" in the following loop. The code
13924  // assumes that an object handle is unique, which is a fair
13925  // assumption, except for figures. If a figure is destroyed
13926  // then recreated with the same figure ID, within the same
13927  // run of event hooks, then the figure destruction won't be
13928  // caught and the loop will not stop. This is an unlikely
13929  // possibility in practice, though.
13930  //
13931  // Using deletefcn callback is also unreliable as it could be
13932  // modified during a callback execution and the waitfor loop
13933  // would not stop.
13934  //
13935  // The only "good" implementation would require object
13936  // listeners, similar to property listeners.
13937 
13938  octave::sys::time start;
13939 
13940  if (timeout > 0)
13941  start.stamp ();
13942 
13943  while (true)
13944  {
13945  if (true)
13946  {
13947  octave::autolock guard (gh_mgr.graphics_lock ());
13948 
13949  graphics_handle gh = gh_mgr.lookup (h);
13950 
13951  if (gh.ok ())
13952  {
13953  if (! pname.empty () && waitfor_results[id])
13954  break;
13955  }
13956  else
13957  break;
13958  }
13959 
13960  octave::sleep (0.1); // FIXME: really needed?
13961 
13962  octave_quit ();
13963 
13964  octave::command_editor::run_event_hooks ();
13965 
13966  if (timeout > 0)
13967  {
13968  octave::sys::time now;
13969 
13970  if (start + timeout < now)
13971  break;
13972  }
13973  }
13974 
13975  return ovl ();
13976 }
13977 
13978 DEFMETHOD (__zoom__, interp, args, ,
13979  doc: /* -*- texinfo -*-
13980 @deftypefn {} {} __zoom__ (@var{axes}, @var{mode}, @var{factor})
13981 @deftypefnx {} {} __zoom__ (@var{axes}, "out")
13982 @deftypefnx {} {} __zoom__ (@var{axes}, "reset")
13983 Undocumented internal function.
13984 @end deftypefn */)
13985 {
13986  int nargin = args.length ();
13987 
13988  if (nargin != 2 && nargin != 3)
13989  print_usage ();
13990 
13991  double h = args(0).double_value ();
13992 
13993  gh_manager& gh_mgr = interp.get_gh_manager ();
13994 
13995  octave::autolock guard (gh_mgr.graphics_lock ());
13996 
13997  graphics_handle handle = gh_mgr.lookup (h);
13998 
13999  if (! handle.ok ())
14000  error ("__zoom__: invalid handle");
14001 
14002  graphics_object ax = gh_mgr.get_object (handle);
14003 
14004  axes::properties& ax_props
14005  = dynamic_cast<axes::properties&> (ax.get_properties ());
14006 
14007  if (nargin == 2)
14008  {
14009  std::string opt = args(1).string_value ();
14010 
14011  if (opt == "out" || opt == "reset")
14012  {
14013  if (opt == "out")
14014  {
14015  ax_props.clear_zoom_stack ();
14016  Vdrawnow_requested = true;
14017  }
14018  else
14019  ax_props.clear_zoom_stack (false);
14020  }
14021  }
14022  else
14023  {
14024  std::string mode = args(1).string_value ();
14025  double factor = args(2).scalar_value ();
14026 
14027  ax_props.zoom (mode, factor);
14028  Vdrawnow_requested = true;
14029  }
14030 
14031  return ovl ();
14032 }
14033 
14034 DEFMETHOD (__get_frame__, interp, args, ,
14035  doc: /* -*- texinfo -*-
14036 @deftypefn {} {@var{cdata} =} __get_frame__ (@var{hfig})
14037 Internal function.
14038 
14039 Return the pixel cdata of figure hfig in the form of a height-by-width-by-3
14040 uint8 array.
14041 @end deftypefn */)
14042 {
14043  if (args.length () != 1)
14044  print_usage ();
14045 
14046  double h = args(0).xdouble_value ("__get_frame__: HFIG is not a handle");
14047 
14048  gh_manager& gh_mgr = interp.get_gh_manager ();
14049 
14050  graphics_object go = gh_mgr.get_object (h);
14051 
14052  if (! go || ! go.isa ("figure"))
14053  error ("__get_frame__: HFIG is not a figure");
14054 
14055  // For Matlab compatibility, getframe must flush the event queue.
14056  gh_mgr.process_events ();
14057 
14058  return ovl (go.get_toolkit ().get_pixels (go));
14059 }
14060 
14061 DEFMETHOD (__get_position__, interp, args, ,
14062  doc: /* -*- texinfo -*-
14063 @deftypefn {} {@var{pos} =} __get_position__ (@var{h}, @var{units})
14064 Internal function.
14065 
14066 Return the position of the graphics object @var{h} in the specified
14067 @var{units}.
14068 @end deftypefn */)
14069 {
14070  if (args.length () != 2)
14071  print_usage ();
14072 
14073  double h
14074  = args(0).xdouble_value ("__get_position__: H must be a graphics handle");
14075 
14076  std::string units
14077  = args(1).xstring_value ("__get_position__: UNITS must be a string");
14078 
14079  gh_manager& gh_mgr = interp.get_gh_manager ();
14080 
14081  graphics_object go = gh_mgr.get_object (h);
14082 
14083  if (h == 0 || ! go)
14084  error ("__get_position__: H must be a handle to a valid graphics object");
14085 
14086  graphics_object parent_go = gh_mgr.get_object (go.get_parent ());
14087  Matrix bbox = parent_go.get_properties ().get_boundingbox (true)
14088  .extract_n (0, 2, 1, 2);
14089 
14090  Matrix pos = convert_position (go.get ("position").matrix_value (),
14091  go.get ("units").string_value (),
14092  units, bbox);
14093 
14094  return ovl (pos);
14095 }
14096 
14097 DEFUN (__get_system_fonts__, args, ,
14098  doc: /* -*- texinfo -*-
14099 @deftypefn {} {@var{font_struct} =} __get_system_fonts__ ()
14100 Internal function.
14101 @end deftypefn */)
14102 {
14103  if (args.length () != 0)
14104  print_usage ();
14105 
14106  octave::text_renderer txt_renderer;
14107 
14108  return ovl (txt_renderer.get_system_fonts ());
14109 }
14110 
14111 OCTAVE_END_NAMESPACE(octave)
#define Inf
Definition: Faddeeva.cc:260
#define NaN
Definition: Faddeeva.cc:261
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:562
T * fortran_vec()
Size of the specified dimension.
Definition: Array-base.cc:1764
Array< T, Alloc > as_column() const
Return the array as a column vector.
Definition: Array.h:420
int ndims() const
Size of the specified dimension.
Definition: Array.h:671
octave_idx_type rows() const
Definition: Array.h:459
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array-base.cc:1023
octave_idx_type columns() const
Definition: Array.h:471
bool isempty() const
Size of the specified dimension.
Definition: Array.h:651
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:503
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:114
double min() const
Definition: dColVector.cc:245
RowVector transpose() const
Definition: dColVector.cc:125
double max() const
Definition: dColVector.cc:261
ColumnVector extract_n(octave_idx_type r1, octave_idx_type n) const
Definition: dColVector.cc:166
Definition: EIG.h:41
ComplexColumnVector eigenvalues() const
Definition: EIG.h:121
MArray< T > reshape(const dim_vector &new_dims) const
Definition: MArray.h:87
Definition: dMatrix.h:42
RowVector row(octave_idx_type i) const
Definition: dMatrix.cc:416
Matrix transpose() const
Definition: dMatrix.h:140
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() const
Definition: dMatrix.cc:2429
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:158
ColumnVector row_max() const
Definition: dMatrix.cc:2484
bool any_element_is_inf_or_nan() const
Definition: dNDArray.cc:324
double m_max_val
Definition: graphics.h:1523
double m_min_pos
Definition: graphics.h:1524
finite_type m_finite_constraint
Definition: graphics.h:1528
std::pair< double, bool > m_minval
Definition: graphics.h:1529
double m_max_neg
Definition: graphics.h:1525
octave_value m_data
Definition: graphics.h:1521
octave_value get() const
Definition: graphics.h:1436
std::set< std::string > m_type_constraints
Definition: graphics.h:1526
std::pair< double, bool > m_maxval
Definition: graphics.h:1529
double m_min_val
Definition: graphics.h:1522
std::list< dim_vector > m_size_constraints
Definition: graphics.h:1527
void update_axes_layout()
Definition: graphics.cc:5818
double get_nextseriesindex() const
Definition: graphics.h:5398
void update_xlabel_position()
Definition: graphics.cc:6077
void trigger_normals_calc()
Definition: graphics.cc:9041
void rotate_view(double delta_az, double delta_el, bool push_to_zoom_stack=true)
Definition: graphics.cc:8933
void set_units(const octave_value &val)
Definition: graphics.cc:6945
unsigned int get_num_lights() const
Definition: graphics.h:4858
void increase_num_lights()
Definition: graphics.h:4856
void set_ylabel(const octave_value &val)
Definition: graphics.cc:5060
void set_zticklabel(const octave_value &val)
Definition: graphics.cc:6883
void zoom(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8716
void sync_positions()
Definition: graphics.cc:4920
void rotate3d(double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:8886
void update_fontunits()
void update_ylabel_position()
Definition: graphics.cc:6181
void pan(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8869
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:5365
void set_zlabel(const octave_value &val)
Definition: graphics.cc:5074
void update_handlevisibility()
Definition: graphics.cc:8992
static bool has_core_property(const caseless_str &pname)
void set_yticklabel(const octave_value &val)
Definition: graphics.cc:6868
void update_font(std::string prop="")
Definition: graphics.cc:6633
void set_fontunits(const octave_value &val)
Definition: graphics.cc:6986
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:6670
Matrix get_extent(bool with_text=false, bool only_text_height=false) const
Definition: graphics.cc:6700
void set_defaults(base_graphics_object &obj, const std::string &mode)
Definition: graphics.cc:5102
void set_xticklabel(const octave_value &val)
Definition: graphics.cc:6853
void set_linestyleorder(const octave_value &val)
Definition: graphics.cc:6939
void set_nextseriesindex(const octave_value &val)
Definition: graphics.h:6074
void set_xlabel(const octave_value &val)
Definition: graphics.cc:5046
void set_title(const octave_value &val)
Definition: graphics.cc:5088
void update_autopos(const std::string &elem_type)
Definition: graphics.cc:6458
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:7010
graphics_xform get_transform() const
Definition: graphics.h:4756
void update_aspectratios()
Definition: graphics.cc:6512
void adopt(const graphics_handle &h)
Definition: graphics.cc:5401
void push_zoom_stack()
Definition: graphics.cc:8730
void update_camera()
Definition: graphics.cc:5602
octave_value get_colororder() const
Definition: graphics.h:5313
void update_zlabel_position()
Definition: graphics.cc:6285
void check_axis_limits(Matrix &limits, const Matrix kids, const bool logscale, char &update_type)
Definition: graphics.cc:7527
Matrix get_axis_limits(double xmin, double xmax, double min_pos, double max_neg, const bool logscale, const std::string &method)
Definition: graphics.cc:7396
void update_title_position()
Definition: graphics.cc:6411
void zoom_about_point(const std::string &mode, double x, double y, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8687
void clear_zoom_stack(bool do_unzoom=true)
Definition: graphics.cc:9029
bool nextplot_is(const std::string &v) const
Definition: graphics.h:5395
void decrease_num_lights()
Definition: graphics.h:4857
static property_list::pval_map_type factory_defaults()
void translate_view(const std::string &mode, double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:8840
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:7063
void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:8410
void reset_default_properties()
Definition: graphics.cc:9070
void initialize(const graphics_object &go)
Definition: graphics.cc:9086
virtual std::string value_as_string(const std::string &prop)
Definition: graphics.cc:3481
graphics_handle get_handle() const
Definition: graphics.h:2854
virtual void adopt(const graphics_handle &h)
Definition: graphics.h:2870
virtual octave_value get_default(const caseless_str &) const
Definition: graphics.cc:11601
virtual void mark_modified()
Definition: graphics.h:2744
virtual octave_value get_factory_default(const caseless_str &) const
Definition: graphics.cc:11613
virtual octave_scalar_map values_as_struct()
Definition: graphics.cc:3510
virtual std::string type() const
Definition: graphics.h:2918
virtual void initialize(const graphics_object &go)
Definition: graphics.h:2958
friend class graphics_object
Definition: graphics.h:2736
virtual void reset_default_properties()
Definition: graphics.cc:3431
virtual void set(const caseless_str &pname, const octave_value &pval)
Definition: graphics.h:2770
virtual void remove_all_listeners()
Definition: graphics.cc:3372
virtual graphics_handle get_parent() const
Definition: graphics.h:2846
bool isa(const std::string &go_name) const
Definition: graphics.h:2924
virtual void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:3343
void build_user_defaults_map(property_list::pval_map_type &def, const std::string go_name) const
Definition: graphics.cc:3405
virtual void defaults() const
Definition: graphics.h:2886
virtual std::string values_as_string()
Definition: graphics.cc:3446
virtual void set_defaults(const std::string &)
Definition: graphics.h:2778
bool is_beingdeleted() const
Definition: graphics.h:2513
virtual octave_value get_clim() const
Definition: graphics.h:2416
void mark_modified()
Definition: graphics.cc:3108
void set_from_list(base_graphics_object &obj, property_list &defaults)
Definition: graphics.cc:2947
void override_defaults(base_graphics_object &obj)
Definition: graphics.cc:3124
virtual octave_value get_xlim() const
Definition: graphics.h:2417
virtual void update_contextmenu() const
Definition: graphics.cc:3158
graphics_handle get_parent() const
Definition: graphics.h:2542
virtual octave::graphics_toolkit get_toolkit() const
Definition: graphics.cc:3183
virtual void update_autopos(const std::string &elem_type)
Definition: graphics.cc:3212
virtual property get_property(const caseless_str &pname)
virtual void update_boundingbox()
Definition: graphics.cc:3196
graphics_handle get___myhandle__() const
Definition: graphics.h:2567
bool has_dynamic_property(const std::string &pname) const
Definition: graphics.cc:3021
virtual void update_handlevisibility()
Definition: graphics.cc:3223
void get_children_of_type(const caseless_str &type, bool get_invisible, bool traverse, std::list< graphics_object > &children_list) const
Definition: graphics.cc:3310
virtual octave_value get_ylim() const
Definition: graphics.h:2418
virtual void update_axis_limits(const std::string &axis_type) const
Definition: graphics.cc:3135
octave_value get_dynamic(const caseless_str &pname) const
Definition: graphics.cc:2991
virtual void add_listener(const caseless_str &, const octave_value &, listener_mode=GCB_POSTSET)
Definition: graphics.cc:3288
void insert_property(const std::string &name, property p)
Definition: graphics.h:2266
property get_property_dynamic(const caseless_str &pname) const
Definition: graphics.cc:3048
virtual bool has_property(const caseless_str &) const
Definition: graphics.h:2294
bool is_visible() const
Definition: graphics.h:2559
void set_modified(const octave_value &val)
Definition: graphics.h:2365
virtual octave_value get_alim() const
Definition: graphics.h:2415
void set_parent(const octave_value &val)
Definition: graphics.cc:3060
virtual void adopt(const graphics_handle &h)
Definition: graphics.h:2311
bool is_handle_visible() const
Definition: graphics.cc:3176
std::set< std::string > dynamic_property_names() const
Definition: graphics.cc:3015
Matrix get_all_children() const
Definition: graphics.h:2350
virtual octave_value get_zlim() const
Definition: graphics.h:2419
Matrix get_children() const
Definition: graphics.h:2345
virtual void init_integerhandle(const octave_value &)
Definition: graphics.h:2255
void set_dynamic(const caseless_str &pname, const octave_value &val)
Definition: graphics.cc:3032
void execute_createfcn(const octave_value &new_data=octave_value()) const
Definition: graphics.h:2527
virtual void remove_child(const graphics_handle &h, bool=false)
Definition: graphics.h:2302
virtual void delete_listener(const caseless_str &, const octave_value &, listener_mode=GCB_POSTSET)
Definition: graphics.cc:3299
void renumber_child(graphics_handle old_gh, graphics_handle new_gh)
Definition: graphics.h:2398
virtual std::string graphics_object_name() const
Definition: graphics.h:2249
virtual Matrix get_boundingbox(bool=false, const Matrix &=Matrix()) const
Definition: graphics.h:2321
void renumber_parent(graphics_handle new_gh)
Definition: graphics.h:2403
bool is_modified() const
Definition: graphics.h:2300
graphics_handle get_parent() const
Definition: graphics.h:314
virtual bool do_set(const octave_value &)
Definition: graphics.h:417
void run_listeners(listener_mode mode=GCB_POSTSET)
Definition: graphics.cc:1280
std::string get_name() const
Definition: graphics.h:310
bool set(const octave_value &v, bool do_run=true, bool do_notify_toolkit=true)
Definition: graphics.cc:1255
void execute(const octave_value &data=octave_value()) const
Definition: graphics.cc:1983
bool compare(const std::string &s, std::size_t limit=std::string::npos) const
Definition: caseless-str.h:78
bool do_set(const octave_value &newval)
Definition: graphics.cc:1457
octave_value get() const
Definition: graphics.h:1121
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
static dim_vector alloc(int n)
Definition: dim-vector.h:202
octave_idx_type ndims() const
Number of dimensions.
Definition: dim-vector.h:257
bool do_set(const octave_value &v)
Definition: graphics.cc:1528
void update_paperunits(const caseless_str &old_paperunits)
Definition: graphics.cc:4333
void set___graphics_toolkit__(const octave_value &val)
Definition: graphics.cc:3840
void set_currentaxes(const octave_value &val)
Definition: graphics.cc:3784
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:3907
octave_value get_number() const
Definition: graphics.cc:3825
void update_papersize()
Definition: graphics.cc:4406
void update_paperorientation()
Definition: graphics.cc:4574
void set_paperunits(const octave_value &val)
Definition: graphics.cc:4029
void update___device_pixel_ratio__()
Definition: graphics.cc:2302
void set_integerhandle(const octave_value &val)
Definition: graphics.cc:3621
static bool has_core_property(const caseless_str &pname)
void set_visible(const octave_value &val)
Definition: graphics.cc:3896
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:3795
std::string get_title() const
Definition: graphics.cc:4657
Matrix bbox2position(const Matrix &bbox) const
Definition: graphics.cc:3924
Matrix map_to_boundingbox(double x, double y) const
Definition: graphics.cc:3967
Matrix map_from_boundingbox(double x, double y) const
Definition: graphics.cc:3950
void adopt(const graphics_handle &h)
Definition: graphics.cc:3862
void set_papertype(const octave_value &val)
Definition: graphics.cc:4046
void set_boundingbox(const Matrix &bb, bool internal=false, bool do_notify_toolkit=true)
Definition: graphics.cc:3937
void set_currentobject(const octave_value &val)
Definition: graphics.h:4038
octave_value get_colormap() const
Definition: graphics.h:3835
void set_toolkit(const octave::graphics_toolkit &b)
Definition: graphics.cc:2169
void set_position(const octave_value &val, bool do_notify_toolkit=true)
Definition: graphics.cc:3984
void set_units(const octave_value &val)
Definition: graphics.cc:4619
void set_outerposition(const octave_value &val, bool do_notify_toolkit=true)
Definition: graphics.cc:4021
octave::graphics_toolkit get_toolkit() const
Definition: graphics.cc:3834
void set___mouse_mode__(const octave_value &val)
Definition: graphics.cc:2185
void update_units(const caseless_str &old_units)
Definition: graphics.cc:4631
void update_papertype()
Definition: graphics.cc:4388
static property_list::pval_map_type factory_defaults()
property_list get_defaults_list() const
Definition: graphics.h:4544
void reset_default_properties()
Definition: graphics.cc:4702
octave_value get(const caseless_str &name) const
Definition: graphics.h:4525
bool valid_object() const
Definition: graphics.h:4553
void override_defaults(base_graphics_object &obj)
Definition: graphics.h:4500
void set(const caseless_str &name, const octave_value &value)
Definition: graphics.h:4514
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:4683
base_properties & get_properties()
Definition: graphics.h:4549
graphics_handle current_figure() const
Definition: gh-manager.h:94
Matrix handle_list(bool show_hidden=false)
Definition: gh-manager.h:107
octave::mutex graphics_lock()
Definition: gh-manager.h:207
graphics_object get_object(double val) const
Definition: gh-manager.h:68
void renumber_figure(const graphics_handle &old_gh, const graphics_handle &new_gh)
Definition: gh-manager.cc:154
void execute_listener(const graphics_handle &h, const octave_value &l)
Definition: gh-manager.cc:363
void unlock()
Definition: gh-manager.h:129
void push_figure(const graphics_handle &h)
Definition: gh-manager.cc:318
bool is_handle_visible(const graphics_handle &h) const
Definition: gh-manager.h:189
Matrix figure_handle_list(bool show_hidden=false)
Definition: gh-manager.h:131
graphics_handle lookup(double val) const
Definition: gh-manager.h:54
void free(const graphics_handle &h, bool from_root=false)
Definition: gh-manager.cc:91
void lock()
Definition: gh-manager.h:125
void pop_figure(const graphics_handle &h)
Definition: gh-manager.cc:326
void post_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: gh-manager.cc:494
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: gh-manager.cc:243
void execute_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: gh-manager.h:150
int process_events(bool force=false)
Definition: gh-manager.cc:548
graphics_handle make_figure_handle(double val, bool notify_toolkit=true)
Definition: gh-manager.cc:299
graphics_handle get_handle(bool integer_figure_handle)
Definition: gh-manager.cc:50
static graphics_event create_mcode_event(const graphics_handle &h, const std::string &cmd, int busyaction)
Definition: graphics.cc:11812
void(* event_fcn)(void *)
Definition: graphics.h:14452
static graphics_event create_function_event(event_fcn fcn, void *data=nullptr)
Definition: graphics.cc:11820
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:11827
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:11794
void update(int id)
Definition: graphics.h:3199
bool is_xliminclude() const
Definition: graphics.h:3170
std::string value_as_string(const std::string &prop)
Definition: graphics.h:3095
octave_value get_alim() const
Definition: graphics.h:3149
void mark_modified()
Definition: graphics.h:3000
void override_defaults(base_graphics_object &obj)
Definition: graphics.h:3002
octave_value get_ylim() const
Definition: graphics.h:3158
bool has_readonly_property(const caseless_str &pname) const
Definition: graphics.h:3084
bool is_climinclude() const
Definition: graphics.h:3167
octave_value get(bool all=false) const
Definition: graphics.h:3037
void reset_default_properties()
Definition: graphics.h:3201
void set(const caseless_str &name, const octave_value &val)
Definition: graphics.h:3020
graphics_object get_ancestor(const std::string &type) const
Definition: graphics.cc:3564
void update_axis_limits(const std::string &axis_type)
Definition: graphics.h:3127
std::string values_as_string()
Definition: graphics.h:3091
void delete_property_listener(const std::string &nm, const octave_value &v, listener_mode mode=GCB_POSTSET)
Definition: graphics.h:3189
void initialize()
Definition: graphics.h:3195
base_properties & get_properties()
Definition: graphics.h:3120
bool isa(const std::string &go_name) const
Definition: graphics.h:3118
octave_value get_zlim() const
Definition: graphics.h:3161
std::string type() const
Definition: graphics.h:3140
void finalize()
Definition: graphics.h:3197
void set_value_or_default(const caseless_str &name, const octave_value &val)
Definition: graphics.cc:2737
octave_value get_factory_default(const caseless_str &name) const
Definition: graphics.h:3062
octave_value get_xlim() const
Definition: graphics.h:3155
graphics_handle get_parent() const
Definition: graphics.h:3104
graphics_handle get_handle() const
Definition: graphics.h:3106
octave_value get_default(const caseless_str &name) const
Definition: graphics.h:3057
octave_value get_clim() const
Definition: graphics.h:3152
void build_user_defaults_map(property_list::pval_map_type &def, const std::string go_name) const
Definition: graphics.h:3012
bool valid_object() const
Definition: graphics.h:3138
void remove_child(const graphics_handle &h)
Definition: graphics.h:3110
property_list get_factory_defaults_list() const
Definition: graphics.h:3079
void add_property_listener(const std::string &nm, const octave_value &v, listener_mode mode=GCB_POSTSET)
Definition: graphics.h:3185
bool is_zliminclude() const
Definition: graphics.h:3176
void adopt(const graphics_handle &h)
Definition: graphics.h:3112
bool is_aliminclude() const
Definition: graphics.h:3164
octave::graphics_toolkit get_toolkit() const
Definition: graphics.h:3182
bool is_yliminclude() const
Definition: graphics.h:3173
octave_map values_as_struct()
Definition: graphics.h:3102
ColumnVector untransform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:7047
static ColumnVector xform_vector(double x, double y, double z)
Definition: graphics.cc:7022
ColumnVector transform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:7034
static Matrix xform_eye()
Definition: graphics.cc:7028
std::set< std::string > m_type_constraints
Definition: graphics.h:1722
graphics_handle handle_value() const
Definition: graphics.h:1698
bool do_set(const octave_value &v)
Definition: graphics.cc:1777
octave_value get() const
Definition: graphics.h:1696
void adopt(const graphics_handle &h)
Definition: graphics.cc:10457
static property_list::pval_map_type factory_defaults()
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:10440
static bool has_core_property(const caseless_str &pname)
void update_axis_limits(const std::string &axis_type)
Definition: graphics.cc:10622
static property_list::pval_map_type factory_defaults()
static bool has_core_property(const caseless_str &pname)
float pixel_ysize()
Definition: graphics.h:8809
octave_value get_color_data() const
Definition: graphics.cc:9373
float pixel_xsize()
Definition: graphics.h:8804
static property_list::pval_map_type factory_defaults()
void initialize(const graphics_object &go)
Definition: graphics.cc:9381
static property_list::pval_map_type factory_defaults()
static bool has_core_property(const caseless_str &pname)
double value() const
Definition: oct-handle.h:78
bool ok() const
Definition: oct-handle.h:113
octave_value as_octave_value() const
Definition: oct-handle.h:80
Cell getfield(const std::string &key) const
Definition: oct-map.cc:275
bool isfield(const std::string &name) const
Definition: oct-map.h:326
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
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:230
octave_value getfield(const std::string &key) const
Definition: oct-map.cc:183
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
octave_idx_type length() const
Definition: ovl.h:113
bool is_function_handle() const
Definition: ov.h:768
bool is_undefined() const
Definition: ov.h:595
bool is_uint32_type() const
Definition: ov.h:724
bool isinteger() const
Definition: ov.h:730
std::string class_name() const
Definition: ov.h:1347
bool is_real_scalar() const
Definition: ov.h:610
int32NDArray int32_array_value() const
Definition: ov.h:956
uint16NDArray uint16_array_value() const
Definition: ov.h:965
int16NDArray int16_array_value() const
Definition: ov.h:953
Cell cell_value() const
int8NDArray int8_array_value() const
Definition: ov.h:950
octave_idx_type rows() const
Definition: ov.h:545
bool is_scalar_type() const
Definition: ov.h:744
octave_scalar_map scalar_map_value() const
bool isreal() const
Definition: ov.h:738
bool is_string() const
Definition: ov.h:637
bool isnumeric() const
Definition: ov.h:750
bool is_int8_type() const
Definition: ov.h:706
bool is_single_type() const
Definition: ov.h:698
bool is_defined() const
Definition: ov.h:592
double xdouble_value(const char *fmt,...) const
bool isempty() const
Definition: ov.h:601
bool is_uint8_type() const
Definition: ov.h:718
bool is_uint64_type() const
Definition: ov.h:727
bool is_uint16_type() const
Definition: ov.h:721
uint64NDArray uint64_array_value() const
Definition: ov.h:971
bool is_int16_type() const
Definition: ov.h:709
octave_value reshape(const dim_vector &dv) const
Definition: ov.h:571
bool iscell() const
Definition: ov.h:604
octave_idx_type numel() const
Definition: ov.h:559
octave_map map_value() const
std::string string_value(bool force=false) const
Definition: ov.h:974
bool is_int64_type() const
Definition: ov.h:715
bool is_matrix_type() const
Definition: ov.h:747
bool iscomplex() const
Definition: ov.h:741
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:977
int64NDArray int64_array_value() const
Definition: ov.h:959
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:859
bool is_double_type() const
Definition: ov.h:695
bool is_int32_type() const
Definition: ov.h:712
bool is_bool_scalar() const
Definition: ov.h:622
uint8NDArray uint8_array_value() const
Definition: ov.h:962
std::string type_name() const
Definition: ov.h:1345
bool iscellstr() const
Definition: ov.h:607
uint32NDArray uint32_array_value() const
Definition: ov.h:968
octave_idx_type columns() const
Definition: ov.h:547
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:853
double double_value(bool frc_str_conv=false) const
Definition: ov.h:841
bool islogical() const
Definition: ov.h:735
dim_vector dims() const
Definition: ov.h:541
octave_value get_color_data() const
Definition: graphics.cc:9422
static property_list::pval_map_type factory_defaults()
bool get_do_lighting() const
Definition: graphics.cc:9409
void update_normals(bool reset, bool force=false)
Definition: graphics.h:9726
static bool has_core_property(const caseless_str &pname)
void reset_default_properties()
Definition: graphics.cc:10014
void initialize(const graphics_object &go)
Definition: graphics.cc:10002
octave_scalar_map as_struct(const std::string &prefix_arg) const
Definition: graphics.cc:2548
plist_map_iterator find(const std::string &go_name)
Definition: graphics.h:2216
void set(const caseless_str &name, const octave_value &val)
Definition: graphics.cc:2310
plist_map_type::const_iterator plist_map_const_iterator
Definition: graphics.h:2199
octave_value lookup(const caseless_str &name) const
Definition: graphics.cc:2453
pval_map_type::const_iterator pval_map_const_iterator
Definition: graphics.h:2196
std::map< std::string, pval_map_type > plist_map_type
Definition: graphics.h:2193
plist_map_iterator end()
Definition: graphics.h:2213
void delete_listener(const octave_value &v=octave_value(), listener_mode mode=GCB_POSTSET)
Definition: graphics.h:2103
static property create(const std::string &name, const graphics_handle &parent, const caseless_str &type, const octave_value_list &args)
Definition: graphics.cc:2009
void set_parent(const graphics_handle &h)
Definition: graphics.h:2052
property()
Definition: graphics.h:2023
property clone() const
Definition: graphics.h:2114
std::string values_as_string() const
Definition: graphics.h:2077
void erase(const std::string pname)
Definition: graphics.h:2175
const_iterator find(const std::string pname) const
Definition: graphics.h:2128
std::string values_as_string() const
Definition: graphics.cc:1332
bool contains(const std::string &val, std::string &match)
Definition: graphics.h:884
radio_values(const std::string &opt_string="")
Definition: graphics.cc:1290
Cell values_as_cell() const
Definition: graphics.cc:1361
std::string default_value() const
Definition: graphics.h:872
octave_idx_type nelem() const
Definition: graphics.h:927
void set_currentfigure(const octave_value &val)
Definition: graphics.cc:3601
bool is_showhiddenhandles() const
Definition: graphics.h:3346
void set_callbackobject(const octave_value &val)
Definition: graphics.cc:3588
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:3720
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:3757
void reset_default_properties()
Definition: graphics.cc:3771
static bool has_core_property(const caseless_str &pname)
static property_list::pval_map_type factory_defaults()
octave_value get_color_data() const
Definition: graphics.cc:10029
void initialize(const graphics_object &go)
Definition: graphics.cc:10112
string_vector & append(const std::string &s)
Definition: str-vec.cc:110
octave_idx_type numel() const
Definition: str-vec.h:100
std::string join(const std::string &sep="") const
Definition: str-vec.cc:137
bool get_do_lighting() const
Definition: graphics.cc:10148
octave_value get_color_data() const
Definition: graphics.cc:10142
static bool has_core_property(const caseless_str &pname)
static property_list::pval_map_type factory_defaults()
void update_normals(bool reset, bool force=false)
Definition: graphics.h:11364
bool verticalalignmentmode_is(const std::string &v) const
Definition: graphics.h:7947
void update_fontunits()
Matrix get_data_position() const
Definition: graphics.cc:9129
octave_value get_string() const
Definition: graphics.h:7915
void update_text_extent()
Definition: graphics.cc:9263
Matrix get_extent_matrix(bool rotated=false) const
Definition: graphics.cc:9140
bool horizontalalignmentmode_is(const std::string &v) const
Definition: graphics.h:7944
std::string get_units() const
Definition: graphics.h:7918
bool rotationmode_is(const std::string &v) const
Definition: graphics.h:7941
void set_rotation(const octave_value &val)
Definition: graphics.h:8090
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:9351
bool positionmode_is(const std::string &v) const
Definition: graphics.h:7938
static bool has_core_property(const caseless_str &pname)
void set_horizontalalignmentmode(const octave_value &val)
Definition: graphics.h:8212
void set_positionmode(const octave_value &val)
Definition: graphics.h:8194
void update_font()
Definition: graphics.cc:9241
octave_value get_extent() const
Definition: graphics.cc:9184
void set_verticalalignment(const octave_value &val)
Definition: graphics.h:8121
double get_rotation() const
Definition: graphics.h:7913
void set_rotationmode(const octave_value &val)
Definition: graphics.h:8203
void set_verticalalignmentmode(const octave_value &val)
Definition: graphics.h:8221
void set_position(const octave_value &val)
Definition: graphics.h:7712
static property_list::pval_map_type factory_defaults()
void set_fontunits(const octave_value &val)
Definition: graphics.cc:9206
void set_horizontalalignment(const octave_value &val)
Definition: graphics.h:8044
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:11071
void update_units(const caseless_str &old_units)
Definition: graphics.cc:11031
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:11059
static bool has_core_property(const caseless_str &pname)
void set_units(const octave_value &val)
Definition: graphics.cc:11019
graphics_handle get_selectedobject() const
Definition: graphics.h:12799
void set_fontunits(const octave_value &val)
Definition: graphics.cc:11047
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:10916
static property_list::pval_map_type factory_defaults()
void set_selectedobject(const octave_value &val)
Definition: graphics.cc:11083
void remove_child(const graphics_handle &h, bool from_root=false)
Definition: graphics.cc:11125
void adopt(const graphics_handle &h)
Definition: graphics.cc:11136
void set_position(const octave_value &val)
Definition: graphics.cc:10986
static bool has_core_property(const caseless_str &pname)
void add_dependent_obj(graphics_handle gh)
Definition: graphics.h:11999
static property_list::pval_map_type factory_defaults()
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:10890
void set_style(const octave_value &val)
Definition: graphics.cc:10803
static property_list::pval_map_type factory_defaults()
static bool has_core_property(const caseless_str &pname)
bool style_is(const std::string &v) const
Definition: graphics.h:12341
octave_value get_extent() const
Definition: graphics.cc:10758
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:10850
std::string get_style() const
Definition: graphics.h:12342
void set_fontunits(const octave_value &val)
Definition: graphics.cc:10878
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:10902
static property_list::pval_map_type factory_defaults()
static bool has_core_property(const caseless_str &pname)
static property_list::pval_map_type factory_defaults()
void set_fontunits(const octave_value &val)
Definition: graphics.cc:11291
void set_units(const octave_value &val)
Definition: graphics.cc:11263
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11159
void update_units(const caseless_str &old_units)
Definition: graphics.cc:11275
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:11315
void set_position(const octave_value &val)
Definition: graphics.cc:11229
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:11303
static bool has_core_property(const caseless_str &pname)
static property_list::pval_map_type factory_defaults()
static bool has_core_property(const caseless_str &pname)
void update_units(const caseless_str &old_units)
Definition: graphics.cc:11459
void set_columnformat(const octave_value &val)
Definition: graphics.cc:11354
double get_fontsize_pixels(double box_pix_height=0) const
Definition: graphics.cc:11511
octave_value get_extent() const
Definition: graphics.cc:11547
void set_columnwidth(const octave_value &val)
Definition: graphics.cc:11407
Matrix get_alternatebackgroundcolor_rgb()
Definition: graphics.cc:11530
void set_fontunits(const octave_value &val)
Definition: graphics.cc:11475
Matrix get_backgroundcolor_rgb()
Definition: graphics.cc:11523
void set_units(const octave_value &val)
Definition: graphics.cc:11447
void update_fontunits(const caseless_str &old_units)
Definition: graphics.cc:11487
Matrix get_extent_matrix() const
Definition: graphics.cc:11541
static bool has_core_property(const caseless_str &pname)
double get___fontsize_points__(double box_pix_height=0) const
Definition: graphics.cc:11499
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11329
static property_list::pval_map_type factory_defaults()
static property_list::pval_map_type factory_defaults()
static bool has_core_property(const caseless_str &pname)
octave_value get_default(const caseless_str &name) const
Definition: graphics.cc:11570
void reset_default_properties()
Definition: graphics.cc:11589
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
T eps(const T &x)
Definition: data.cc:4964
#define DECLARE_STATIC_FUNX(name, args_name, nargout_name)
Definition: defun-int.h:161
#define DECLARE_STATIC_METHODX(name, interp_name, args_name, nargout_name)
Definition: defun-int.h:165
void print_usage(void)
Definition: defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:111
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning(const char *fmt,...)
Definition: error.cc:1063
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1078
void() error(const char *fmt,...)
Definition: error.cc:988
void interpreter_try(octave::unwind_protect &)
#define error_unless(cond)
Definition: error.h:530
octave_value_list set_warning_state(const std::string &id, const std::string &state)
void disable_warning(const std::string &id)
int warning_enabled(const std::string &id)
std::string dirname(const std::string &path)
octave_handle graphics_handle
void xset(const graphics_handle &h, const caseless_str &pname, const octave_value &val)
bool delete_executing
bool isfigure(double val)
graphics_handle reparent(const octave_value &ov, const std::string &who, const std::string &pname, const graphics_handle &new_parent, bool adopt)
void delete_graphics_object(const graphics_handle &h, bool from_root)
octave_value xget(const graphics_handle &h, const caseless_str &pname)
void delete_graphics_objects(const NDArray vals, bool from_root)
ColumnVector xform_vector()
Definition: graphics.cc:5446
Matrix xform_matrix()
Definition: graphics.cc:5435
void cross_product(double x1, double y1, double z1, double x2, double y2, double z2, double &x, double &y, double &z)
Definition: graphics.cc:9776
double dot(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5541
void translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5506
bool set_property_in_handle(double handle, const std::string &property, const octave_value &arg, const std::string &fcn)
Definition: graphics.cc:13597
#define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL)
ColumnVector cross(const ColumnVector &v1, const ColumnVector &v2)
Definition: graphics.cc:5553
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:8105
#define FIX_LIMITS
double norm(const ColumnVector &v)
Definition: graphics.cc:5547
void xform(ColumnVector &v, const Matrix &m)
Definition: graphics.cc:5512
void normalize(ColumnVector &v)
Definition: graphics.cc:5534
Matrix xform_scale(double x, double y, double z)
Definition: graphics.cc:5474
std::vector< octave_idx_type > coplanar_partition(const Matrix &vert, const Matrix &idx, octave_idx_type nc, octave_idx_type jj)
Definition: graphics.cc:9539
ColumnVector cam2xform(const Array< double > &m)
Definition: graphics.cc:5586
int calc_dimensions(const graphics_object &go)
Definition: graphics.cc:12741
RowVector xform2cam(const ColumnVector &v)
Definition: graphics.cc:5596
graphics_handle gcf()
Definition: graphics.cc:2793
base_graphics_object * make_graphics_object_from_type(const caseless_str &type, const graphics_handle &h, const graphics_handle &p)
Definition: graphics.cc:1205
#define GO_BODY(TYPE)
Definition: graphics.cc:12730
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5500
Matrix xform_translate(double x, double y, double z)
Definition: graphics.cc:5487
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5468
Matrix unit_cube()
Definition: graphics.cc:5565
graphics_handle gca()
Definition: graphics.cc:2803
#define CHECK_ARRAY_EQUAL(T, F, A)
bool is_coplanar(const Matrix &cov)
Definition: graphics.cc:9527
double force_in_range(double x, double lower, double upper)
Definition: graphics.cc:8624
octave_value get_property_from_handle(double handle, const std::string &property, const std::string &fcn)
Definition: graphics.cc:13581
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:984
#define OCTAVE_DEFAULT_FONTNAME
Definition: graphics.h:63
@ NOT_INF
Definition: graphics.h:1184
@ NOT_NAN
Definition: graphics.h:1183
@ NO_CHECK
Definition: graphics.h:1181
@ FINITE
Definition: graphics.h:1182
@ AXE_ANY_DIR
Definition: graphics.h:4656
@ AXE_DEPTH_DIR
Definition: graphics.h:4657
@ AXE_HORZ_DIR
Definition: graphics.h:4658
@ AXE_VERT_DIR
Definition: graphics.h:4659
listener_mode
Definition: graphics.h:280
@ GCB_PREDELETE
Definition: graphics.h:280
@ GCB_POSTSET
Definition: graphics.h:280
@ GCB_PERSISTENT
Definition: graphics.h:280
bool Vdrawnow_requested
Definition: input.cc:90
display_info & __get_display_info__()
gh_manager & __get_gh_manager__()
gtk_manager & __get_gtk_manager__()
interpreter & __get_interpreter__()
#define octave_NaN
Definition: lo-ieee.h:46
Complex atan(const Complex &x)
Definition: lo-mappers.h:71
bool isfinite(double x)
Definition: lo-mappers.h:192
bool isinf(double x)
Definition: lo-mappers.h:203
double signum(double x)
Definition: lo-mappers.h:229
double round(double x)
Definition: lo-mappers.h:136
bool isnan(bool)
Definition: lo-mappers.h:178
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
std::complex< T > ceil(const std::complex< T > &x)
Definition: lo-mappers.h:103
double fix(double x)
Definition: lo-mappers.h:118
T x_nint(T x)
Definition: lo-mappers.h:269
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
bool dir_exists(const std::string &dirname)
Definition: lo-sysdep.cc:389
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
T * r
Definition: mx-inlines.cc:781
std::string dir_sep_chars()
Definition: file-ops.cc:252
void sleep(double seconds, bool do_graphics_events)
Definition: utils.cc:1548
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
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
const octave_char_matrix & v2
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219
#define octave_stdout
Definition: pager.h:309
template int8_t abs(int8_t)
F77_RET_T len
Definition: xerbla.cc:61