GNU Octave  4.2.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
graphics.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2017 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <cctype>
28 #include <cfloat>
29 #include <cstdlib>
30 
31 #include <algorithm>
32 #include <list>
33 #include <map>
34 #include <set>
35 #include <string>
36 #include <sstream>
37 
38 #include "cmd-edit.h"
39 #include "file-ops.h"
40 #include "file-stat.h"
41 #include "oct-locbuf.h"
42 #include "oct-time.h"
43 #include "singleton-cleanup.h"
44 
45 #include "builtin-defun-decls.h"
46 #include "defun.h"
47 #include "display.h"
48 #include "error.h"
49 #include "graphics.h"
50 #include "input.h"
51 #include "interpreter.h"
52 #include "ov.h"
53 #include "ovl.h"
54 #include "oct-map.h"
55 #include "ov-fcn-handle.h"
56 #include "pager.h"
57 #include "parse.h"
58 #include "text-renderer.h"
59 #include "unwind-prot.h"
60 #include "utils.h"
61 #include "octave-default-image.h"
62 
63 // forward declarations
64 static octave_value xget (const graphics_handle& h, const caseless_str& name);
65 
66 OCTAVE_NORETURN static
67 void
69 {
70  error ("set: invalid value for %s property", pname.c_str ());
71 }
72 
73 // Check to see that PNAME matches just one of PNAMES uniquely.
74 // Return the full name of the match, or an empty caseless_str object
75 // if there is no match, or the match is ambiguous.
76 
77 static caseless_str
79  const std::set<std::string>& pnames,
80  const caseless_str& pname)
81 {
82  size_t len = pname.length ();
83  std::set<std::string> matches;
84 
85  // Find exact or partial matches to property name
86  for (const auto& propnm : pnames)
87  {
88  if (pname.compare (propnm, len))
89  {
90  if (len == propnm.length ())
91  return pname; // Exact match.
92 
93  matches.insert (propnm);
94  }
95  }
96 
97  size_t num_matches = matches.size ();
98 
99  if (num_matches == 0)
100  error ("%s: unknown %s property %s",
101  who.c_str (), what.c_str (), pname.c_str ());
102  else if (num_matches > 1)
103  {
104  string_vector sv (matches);
105 
106  std::ostringstream os;
107 
108  sv.list_in_columns (os);
109 
110  std::string match_list = os.str ();
111 
112  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
113  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
114  }
115  else if (num_matches == 1)
116  {
117  // Exact match was handled above.
118  std::string possible_match = *(matches.begin ());
119 
120  warning_with_id ("Octave:abbreviated-property-match",
121  "%s: allowing %s to match %s property %s",
122  who.c_str (), pname.c_str (), what.c_str (),
123  possible_match.c_str ());
124 
125  return possible_match;
126  }
127 
128  return caseless_str ();
129 }
130 
131 static Matrix
133 {
134  // The values below have been produced by viridis (64)(:)
135  // It would be nice to be able to feval the
136  // viridis function but since there is a static property object that includes
137  // a colormap_property object, we need to initialize this before main is
138  // even called, so calling an interpreted function is not possible.
139 
140  const double cmapv[] =
141  {
142  2.67004010000000e-01, 2.72651720952381e-01, 2.77106307619048e-01,
143  2.80356151428571e-01, 2.82390045238095e-01, 2.83204606666667e-01,
144  2.82809341428571e-01, 2.81230763333333e-01, 2.78516153333333e-01,
145  2.74735528571429e-01, 2.69981791904762e-01, 2.64368580952381e-01,
146  2.58026184285714e-01, 2.51098684761905e-01, 2.43732853333333e-01,
147  2.36073294285714e-01, 2.28263191428571e-01, 2.20424955714286e-01,
148  2.12666598571429e-01, 2.05079113809524e-01, 1.97721880952381e-01,
149  1.90631350000000e-01, 1.83819438571429e-01, 1.77272360952381e-01,
150  1.70957518571429e-01, 1.64832915714286e-01, 1.58845368095238e-01,
151  1.52951235714286e-01, 1.47131626666667e-01, 1.41402210952381e-01,
152  1.35832975714286e-01, 1.30582113809524e-01, 1.25898377619048e-01,
153  1.22163105714286e-01, 1.19872409523810e-01, 1.19626570000000e-01,
154  1.22045948571429e-01, 1.27667691904762e-01, 1.36834947142857e-01,
155  1.49643331428571e-01, 1.65967274285714e-01, 1.85538397142857e-01,
156  2.08030450000000e-01, 2.33127309523809e-01, 2.60531475238095e-01,
157  2.90000730000000e-01, 3.21329971428571e-01, 3.54355250000000e-01,
158  3.88930322857143e-01, 4.24933143333333e-01, 4.62246770476190e-01,
159  5.00753620000000e-01, 5.40336957142857e-01, 5.80861172380952e-01,
160  6.22170772857143e-01, 6.64087320476191e-01, 7.06403823333333e-01,
161  7.48885251428571e-01, 7.91273132857143e-01, 8.33302102380952e-01,
162  8.74717527142857e-01, 9.15296319047619e-01, 9.54839555238095e-01,
163  9.93247890000000e-01, 4.87433000000000e-03, 2.58456800000000e-02,
164  5.09139004761905e-02, 7.42014957142857e-02, 9.59536042857143e-02,
165  1.16893314761905e-01, 1.37350195714286e-01, 1.57479940000000e-01,
166  1.77347967619048e-01, 1.96969168571429e-01, 2.16330337619048e-01,
167  2.35404660952381e-01, 2.54161735714286e-01, 2.72573219047619e-01,
168  2.90619516666667e-01, 3.08291041428571e-01, 3.25586450952381e-01,
169  3.42517215238095e-01, 3.59102207142857e-01, 3.75366067142857e-01,
170  3.91340913333333e-01, 4.07061480000000e-01, 4.22563764285714e-01,
171  4.37885543809524e-01, 4.53062984285714e-01, 4.68129543809524e-01,
172  4.83117059523810e-01, 4.98052961428571e-01, 5.12959473333333e-01,
173  5.27854311428571e-01, 5.42750087142857e-01, 5.57652481904762e-01,
174  5.72563073333333e-01, 5.87476284285714e-01, 6.02382410952381e-01,
175  6.17265840000000e-01, 6.32106955714286e-01, 6.46881817142857e-01,
176  6.61562926190476e-01, 6.76119717142857e-01, 6.90518987142857e-01,
177  7.04725181904762e-01, 7.18700950000000e-01, 7.32406441904762e-01,
178  7.45802021904762e-01, 7.58846480000000e-01, 7.71497934761905e-01,
179  7.83714033809524e-01, 7.95453081428571e-01, 8.06673890000000e-01,
180  8.17337565714286e-01, 8.27409135714286e-01, 8.36858167619048e-01,
181  8.45663399523809e-01, 8.53815582857143e-01, 8.61321019047619e-01,
182  8.68206316666667e-01, 8.74522215714286e-01, 8.80346158571429e-01,
183  8.85780083333333e-01, 8.90945338571429e-01, 8.95973498571429e-01,
184  9.01005800000000e-01, 9.06156570000000e-01, 3.29415190000000e-01,
185  3.53367293333333e-01, 3.76236064761905e-01, 3.97901482857143e-01,
186  4.18250757142857e-01, 4.37178920000000e-01, 4.54595888571429e-01,
187  4.70433883333333e-01, 4.84653865714286e-01, 4.97250492857143e-01,
188  5.08254501428571e-01, 5.17731949047619e-01, 5.25780221428571e-01,
189  5.32522206190476e-01, 5.38097133333333e-01, 5.42651800000000e-01,
190  5.46335411904762e-01, 5.49287148571429e-01, 5.51635008571429e-01,
191  5.53493173333333e-01, 5.54953478571429e-01, 5.56089070000000e-01,
192  5.56952166666667e-01, 5.57576145714286e-01, 5.57974025714286e-01,
193  5.58142745238095e-01, 5.58058673809524e-01, 5.57684744285714e-01,
194  5.56973310000000e-01, 5.55864478571429e-01, 5.54288677142857e-01,
195  5.52175699047619e-01, 5.49445382857143e-01, 5.46023368571429e-01,
196  5.41830633809524e-01, 5.36795616666667e-01, 5.30847985714286e-01,
197  5.23924198571429e-01, 5.15966779523810e-01, 5.06924262857143e-01,
198  4.96751861428571e-01, 4.85412122857143e-01, 4.72873300000000e-01,
199  4.59105875238095e-01, 4.44095883333333e-01, 4.27825852857143e-01,
200  4.10292713809524e-01, 3.91487632857143e-01, 3.71420688571429e-01,
201  3.50098750000000e-01, 3.27544678571429e-01, 3.03798967142857e-01,
202  2.78916748571429e-01, 2.53000856190476e-01, 2.26223670000000e-01,
203  1.98879439523810e-01, 1.71494930000000e-01, 1.45037631428572e-01,
204  1.21291048571429e-01, 1.03326155238095e-01, 9.53507900000000e-02,
205  1.00469958095238e-01, 1.17876387142857e-01, 1.43936200000000e-01
206  };
207 
208  // It would be nice if Matrix had a ctor allowing to do the
209  // following without a copy
210  Matrix cmap (64, 3, 0.0);
211  std::copy (cmapv, cmapv + (64*3), cmap.fortran_vec ());
212  return cmap;
213 }
214 
215 static double
217 {
218  return display_info::depth ();
219 }
220 
221 static Matrix
223 {
224  Matrix retval (1, 4);
225 
226  retval(0) = 1.0;
227  retval(1) = 1.0;
228  retval(2) = display_info::width ();
230 
231  return retval;
232 }
233 
234 static double
236 {
237  return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
238 }
239 
240 static Matrix
242 {
243  Matrix retval (7, 3, 0.0);
244 
245  retval(0,1) = 0.447;
246  retval(0,2) = 0.741;
247 
248  retval(1,0) = 0.850;
249  retval(1,1) = 0.325;
250  retval(1,2) = 0.098;
251 
252  retval(2,0) = 0.929;
253  retval(2,1) = 0.694;
254  retval(2,2) = 0.125;
255 
256  retval(3,0) = 0.494;
257  retval(3,1) = 0.184;
258  retval(3,2) = 0.556;
259 
260  retval(4,0) = 0.466;
261  retval(4,1) = 0.674;
262  retval(4,2) = 0.188;
263 
264  retval(5,0) = 0.301;
265  retval(5,1) = 0.745;
266  retval(5,2) = 0.933;
267 
268  retval(6,0) = 0.635;
269  retval(6,1) = 0.078;
270  retval(6,2) = 0.184;
271 
272  return retval;
273 }
274 
275 static Matrix
276 default_lim (bool logscale = false)
277 {
278  Matrix m (1, 2);
279 
280  if (logscale)
281  {
282  m(0) = 0.1;
283  m(1) = 1.0;
284  }
285  else
286  {
287  m(0) = 0.0;
288  m(1) = 1.0;
289  }
290 
291  return m;
292 }
293 
294 static Matrix
296 {
297  Matrix retval (1, 2);
298 
299  retval(0) = 0;
300  retval(1) = 1;
301 
302  return retval;
303 }
304 
305 static Matrix
307 {
308  Matrix m (64, 64);
309 
310  int i = 0;
311  for (int col = 0; col < 64; col++)
312  for (int row = 0; row < 64; row++)
313  {
314  m(col,row) = static_cast<double> (default_im_data[i]);
315  i++;
316  }
317 
318  return m;
319 }
320 
321 static Matrix
323 {
324  Matrix m (3, 3);
325 
326  for (int col = 0; col < 3; col++)
327  for (int row = 0; row < 3; row++)
328  m(row,col) = col+1;
329 
330  return m;
331 }
332 
333 static Matrix
335 {
336  Matrix m (3, 3);
337 
338  for (int row = 0; row < 3; row++)
339  for (int col = 0; col < 3; col++)
340  m(row,col) = row+1;
341 
342  return m;
343 }
344 
345 static Matrix
347 {
348  Matrix m (3, 3, 0.0);
349 
350  for (int row = 0; row < 3; row++)
351  m(row,row) = 1.0;
352 
353  return m;
354 }
355 
356 static Matrix
358 {
359  return default_surface_zdata ();
360 }
361 
362 static Matrix
364 {
365  Matrix m (1, 3);
366 
367  m(0) = 1.0;
368  m(1) = 2.0;
369  m(2) = 3.0;
370 
371  return m;
372 }
373 
374 static Matrix
376 {
377  Matrix m (3, 2, 0.0);
378 
379  m(1) = 1.0;
380  m(3) = 1.0;
381  m(4) = 1.0;
382 
383  return m;
384 }
385 
386 static Matrix
388 {
389  Matrix m (3, 1, 0.0);
390 
391  m(1) = 1.0;
392 
393  return m;
394 }
395 
396 static Matrix
398 {
399  Matrix m (3, 1, 1.0);
400 
401  m(2) = 0.0;
402 
403  return m;
404 }
405 
406 static Matrix
408 {
409  Matrix m (1, 4);
410 
411  m(0) = 0.13;
412  m(1) = 0.11;
413  m(2) = 0.775;
414  m(3) = 0.815;
415 
416  return m;
417 }
418 
419 static Matrix
421 {
422  Matrix m (1, 4);
423 
424  m(0) = 0.0;
425  m(1) = 0.0;
426  m(2) = 1.0;
427  m(3) = 1.0;
428 
429  return m;
430 }
431 
432 static Matrix
434 {
435  Matrix m (1, 2);
436 
437  m(0) = 0.0;
438  m(1) = 90.0;
439 
440  return m;
441 }
442 
443 static Matrix
445 {
446  Matrix m (1, 6);
447 
448  m(0) = 0.0;
449  m(1) = 0.2;
450  m(2) = 0.4;
451  m(3) = 0.6;
452  m(4) = 0.8;
453  m(5) = 1.0;
454 
455  return m;
456 }
457 
458 static Matrix
460 {
461  Matrix m (1, 2);
462 
463  m(0) = 0.01;
464  m(1) = 0.025;
465 
466  return m;
467 }
468 
469 static Matrix
471 {
472  Matrix m (1, 4);
473 
474  m(0) = 300;
475  m(1) = 200;
476  m(2) = 560;
477  m(3) = 420;
478 
479  return m;
480 }
481 
482 static Matrix
484 {
485  Matrix m (1, 2);
486 
487  m(0) = 8.5;
488  m(1) = 11.0;
489 
490  return m;
491 }
492 
493 static Matrix
495 {
496  Matrix m (1, 4);
497 
498  m(0) = 0.25;
499  m(1) = 2.50;
500  m(2) = 8.00;
501  m(3) = 6.00;
502 
503  return m;
504 }
505 
506 static Matrix
508 {
509  Matrix retval (1, 4);
510 
511  retval(0) = 0;
512  retval(1) = 0;
513  retval(2) = 80;
514  retval(3) = 30;
515 
516  return retval;
517 }
518 
519 static Matrix
521 {
522  Matrix retval (1, 2);
523 
524  retval(0) = 0.01;
525  retval(1) = 0.1;
526 
527  return retval;
528 }
529 
530 static Matrix
532 {
533  Matrix retval (1, 4);
534 
535  retval(0) = 0;
536  retval(1) = 0;
537  retval(2) = 1;
538  retval(3) = 1;
539 
540  return retval;
541 }
542 
543 static Matrix
545 {
546  Matrix m (1, 3);
547 
548  m(0) = 1.0;
549  m(1) = 0.0;
550  m(2) = 1.0;
551 
552  return m;
553 }
554 
555 static double
556 convert_font_size (double font_size, const caseless_str& from_units,
557  const caseless_str& to_units, double parent_height = 0)
558 {
559  // Simple case where from_units == to_units
560 
561  if (from_units.compare (to_units))
562  return font_size;
563 
564  // Converts the given fontsize using the following transformation:
565  // <old_font_size> => points => <new_font_size>
566 
567  double points_size = 0;
568  double res = 0;
569 
570  if (from_units.compare ("points"))
571  points_size = font_size;
572  else
573  {
574  res = xget (0, "screenpixelsperinch").double_value ();
575 
576  if (from_units.compare ("pixels"))
577  points_size = font_size * 72.0 / res;
578  else if (from_units.compare ("inches"))
579  points_size = font_size * 72.0;
580  else if (from_units.compare ("centimeters"))
581  points_size = font_size * 72.0 / 2.54;
582  else if (from_units.compare ("normalized"))
583  points_size = font_size * parent_height * 72.0 / res;
584  }
585 
586  double new_font_size = 0;
587 
588  if (to_units.compare ("points"))
589  new_font_size = points_size;
590  else
591  {
592  if (res <= 0)
593  res = xget (0, "screenpixelsperinch").double_value ();
594 
595  if (to_units.compare ("pixels"))
596  new_font_size = points_size * res / 72.0;
597  else if (to_units.compare ("inches"))
598  new_font_size = points_size / 72.0;
599  else if (to_units.compare ("centimeters"))
600  new_font_size = points_size * 2.54 / 72.0;
601  else if (to_units.compare ("normalized"))
602  {
603  // Avoid setting font size to (0/0) = NaN
604 
605  if (parent_height > 0)
606  new_font_size = points_size * res / (parent_height * 72.0);
607  }
608  }
609 
610  return new_font_size;
611 }
612 
613 static Matrix
614 convert_position (const Matrix& pos, const caseless_str& from_units,
615  const caseless_str& to_units, const Matrix& parent_dim)
616 {
617  Matrix retval (1, pos.numel ());
618  double res = 0;
619  bool is_rectangle = (pos.numel () == 4);
620  bool is_2d = (pos.numel () == 2);
621 
622  if (from_units.compare ("pixels"))
623  retval = pos;
624  else if (from_units.compare ("normalized"))
625  {
626  retval(0) = pos(0) * parent_dim(0) + 1;
627  retval(1) = pos(1) * parent_dim(1) + 1;
628  if (is_rectangle)
629  {
630  retval(2) = pos(2) * parent_dim(0);
631  retval(3) = pos(3) * parent_dim(1);
632  }
633  else if (! is_2d)
634  retval(2) = 0;
635  }
636  else if (from_units.compare ("characters"))
637  {
638  if (res <= 0)
639  res = xget (0, "screenpixelsperinch").double_value ();
640 
641  double f = 0.0;
642 
643  // FIXME: this assumes the system font is Helvetica 10pt
644  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
645  f = 12.0 * res / 74.951;
646 
647  if (f > 0)
648  {
649  retval(0) = 0.5 * pos(0) * f;
650  retval(1) = pos(1) * f;
651  if (is_rectangle)
652  {
653  retval(2) = 0.5 * pos(2) * f;
654  retval(3) = pos(3) * f;
655  }
656  else if (! is_2d)
657  retval(2) = 0;
658  }
659  }
660  else
661  {
662  if (res <= 0)
663  res = xget (0, "screenpixelsperinch").double_value ();
664 
665  double f = 0.0;
666 
667  if (from_units.compare ("points"))
668  f = res / 72.0;
669  else if (from_units.compare ("inches"))
670  f = res;
671  else if (from_units.compare ("centimeters"))
672  f = res / 2.54;
673 
674  if (f > 0)
675  {
676  retval(0) = pos(0) * f + 1;
677  retval(1) = pos(1) * f + 1;
678  if (is_rectangle)
679  {
680  retval(2) = pos(2) * f;
681  retval(3) = pos(3) * f;
682  }
683  else if (! is_2d)
684  retval(2) = 0;
685  }
686  }
687 
688  if (! to_units.compare ("pixels"))
689  {
690  if (to_units.compare ("normalized"))
691  {
692  retval(0) = (retval(0) - 1) / parent_dim(0);
693  retval(1) = (retval(1) - 1) / parent_dim(1);
694  if (is_rectangle)
695  {
696  retval(2) /= parent_dim(0);
697  retval(3) /= parent_dim(1);
698  }
699  else if (! is_2d)
700  retval(2) = 0;
701  }
702  else if (to_units.compare ("characters"))
703  {
704  if (res <= 0)
705  res = xget (0, "screenpixelsperinch").double_value ();
706 
707  double f = 0.0;
708 
709  f = 12.0 * res / 74.951;
710 
711  if (f > 0)
712  {
713  retval(0) = 2 * retval(0) / f;
714  retval(1) = retval(1) / f;
715  if (is_rectangle)
716  {
717  retval(2) = 2 * retval(2) / f;
718  retval(3) = retval(3) / f;
719  }
720  else if (! is_2d)
721  retval(2) = 0;
722  }
723  }
724  else
725  {
726  if (res <= 0)
727  res = xget (0, "screenpixelsperinch").double_value ();
728 
729  double f = 0.0;
730 
731  if (to_units.compare ("points"))
732  f = res / 72.0;
733  else if (to_units.compare ("inches"))
734  f = res;
735  else if (to_units.compare ("centimeters"))
736  f = res / 2.54;
737 
738  if (f > 0)
739  {
740  retval(0) = (retval(0) - 1) / f;
741  retval(1) = (retval(1) - 1) / f;
742  if (is_rectangle)
743  {
744  retval(2) /= f;
745  retval(3) /= f;
746  }
747  else if (! is_2d)
748  retval(2) = 0;
749  }
750  }
751  }
752  else if (! is_rectangle && ! is_2d)
753  retval(2) = 0;
754 
755  return retval;
756 }
757 
758 static Matrix
760  const caseless_str& from_units,
761  const caseless_str& to_units)
762 {
764  graphics_object ax = go.get_ancestor ("axes");
765 
766  Matrix retval;
767 
768  if (ax.valid_object ())
769  {
770  const axes::properties& ax_props =
771  dynamic_cast<const axes::properties&> (ax.get_properties ());
772  graphics_xform ax_xform = ax_props.get_transform ();
773  bool is_rectangle = (pos.numel () == 4);
774  Matrix ax_bbox = ax_props.get_boundingbox (true),
775  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
776 
777  if (from_units.compare ("data"))
778  {
779  if (is_rectangle)
780  {
781  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
782  v2 = ax_xform.transform (pos(0) + pos(2),
783  pos(1) + pos(3), 0);
784 
785  retval.resize (1, 4);
786 
787  retval(0) = v1(0) - ax_bbox(0) + 1;
788  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
789  retval(2) = v2(0) - v1(0);
790  retval(3) = v1(1) - v2(1);
791  }
792  else
793  {
794  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
795 
796  retval.resize (1, 3);
797 
798  retval(0) = v(0) - ax_bbox(0) + 1;
799  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
800  retval(2) = 0;
801  }
802  }
803  else
804  retval = convert_position (pos, from_units, "pixels", ax_size);
805 
806  if (! to_units.compare ("pixels"))
807  {
808  if (to_units.compare ("data"))
809  {
810  if (is_rectangle)
811  {
812  ColumnVector v1, v2;
813  v1 = ax_xform.untransform (
814  retval(0) + ax_bbox(0) - 1,
815  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
816  v2 = ax_xform.untransform (
817  retval(0) + retval(2) + ax_bbox(0) - 1,
818  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
819 
820  retval.resize (1, 4);
821 
822  retval(0) = v1(0);
823  retval(1) = v1(1);
824  retval(2) = v2(0) - v1(0);
825  retval(3) = v2(1) - v1(1);
826  }
827  else
828  {
829  ColumnVector v;
830  v = ax_xform.untransform (
831  retval(0) + ax_bbox(0) - 1,
832  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
833 
834  retval.resize (1, 3);
835 
836  retval(0) = v(0);
837  retval(1) = v(1);
838  retval(2) = v(2);
839  }
840  }
841  else
842  retval = convert_position (retval, "pixels", to_units, ax_size);
843  }
844  }
845 
846  return retval;
847 }
848 
849 // This function always returns the screensize in pixels
850 static Matrix
852 {
854  Matrix sz = obj.get ("screensize").matrix_value ();
855  return convert_position (sz, obj.get ("units").string_value (), "pixels",
856  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
857 }
858 
859 static void
860 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
861  const double *cmapv, double x, octave_idx_type lda,
862  octave_idx_type nc, octave_idx_type i, double *av)
863 {
864  if (is_scaled)
865  x = octave::math::fix (nc * (x - clim_0) / (clim_1 - clim_0));
866  else if (is_real)
867  x = octave::math::fix (x - 1);
868 
869  if (octave::math::isnan (x))
870  {
871  av[i] = x;
872  av[i+lda] = x;
873  av[i+2*lda] = x;
874  }
875  else
876  {
877  if (x < 0)
878  x = 0;
879  else if (x >= nc)
880  x = (nc - 1);
881 
882  octave_idx_type idx = static_cast<octave_idx_type> (x);
883 
884  av[i] = cmapv[idx];
885  av[i+lda] = cmapv[idx+nc];
886  av[i+2*lda] = cmapv[idx+2*nc];
887  }
888 }
889 
890 template <typename T>
891 void
892 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
893  const double *cmapv, const T *cv, octave_idx_type lda,
894  octave_idx_type nc, double *av)
895 {
896  for (octave_idx_type i = 0; i < lda; i++)
897  convert_cdata_2 (is_scaled, is_real,
898  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
899 }
900 
901 static octave_value
902 convert_cdata (const base_properties& props, const octave_value& cdata,
903  bool is_scaled, int cdim)
904 {
905  dim_vector dv (cdata.dims ());
906 
907  // TrueColor data doesn't require conversion
908  if (dv.ndims () == cdim && dv(cdim-1) == 3)
909  return cdata;
910 
911  Matrix cmap (1, 3, 0.0);
912  Matrix clim (1, 2, 0.0);
913 
915  graphics_object fig = go.get_ancestor ("figure");
916 
917  if (fig.valid_object ())
918  {
919  Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
920 
921  cmap = _cmap;
922  }
923 
924  if (is_scaled)
925  {
926  graphics_object ax = go.get_ancestor ("axes");
927 
928  if (ax.valid_object ())
929  {
930  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
931 
932  clim = _clim;
933  }
934  }
935 
936  dv.resize (cdim);
937  dv(cdim-1) = 3;
938 
939  NDArray a (dv);
940 
941  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
942  octave_idx_type nc = cmap.rows ();
943 
944  double *av = a.fortran_vec ();
945  const double *cmapv = cmap.data ();
946 
947  double clim_0 = clim(0);
948  double clim_1 = clim(1);
949 
950  // FIXME: There is a lot of processing time spent just on data conversion
951  // both here in graphics.cc and again in gl-render.cc. There must
952  // be room for improvement! Here a macro expands to a templated
953  // function which in turn calls another function (covert_cdata_2).
954  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
955  // is supported anyways so there is another double for loop across
956  // height and width to convert all of the input data to GLfloat.
957 
958 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
959  do \
960  { \
961  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
962  \
963  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
964  tmp.data (), lda, nc, av); \
965  } \
966  while (0)
967 
968  if (cdata.is_uint8_type ())
969  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
970  else if (cdata.is_uint16_type ())
971  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
972  else if (cdata.is_double_type ())
973  CONVERT_CDATA_1 (NDArray, , true);
974  else if (cdata.is_single_type ())
975  CONVERT_CDATA_1 (FloatNDArray, float_, true);
976  else if (cdata.is_bool_type ())
977  CONVERT_CDATA_1 (boolNDArray, bool_, false);
978  else
979  {
980  // Don't throw an error; leads to an incomplete FLTK object (bug #46933).
981  warning ("unsupported type for cdata (= %s). "
982  "Valid types are uint8, uint16, double, single, and bool.",
983  cdata.type_name ().c_str ());
984  a = NDArray (dv, 0); // return 0 instead
985  }
986 
987 #undef CONVERT_CDATA_1
988 
989  return octave_value (a);
990 }
991 
992 template <typename T>
993 static void
994 get_array_limits (const Array<T>& m, double& emin, double& emax,
995  double& eminp, double& emaxp)
996 {
997  const T *data = m.data ();
998  octave_idx_type n = m.numel ();
999 
1000  for (octave_idx_type i = 0; i < n; i++)
1001  {
1002  double e = double (data[i]);
1003 
1004  // Don't need to test for NaN here as NaN>x and NaN<x is always false
1005  if (! octave::math::isinf (e))
1006  {
1007  if (e < emin)
1008  emin = e;
1009 
1010  if (e > emax)
1011  emax = e;
1012 
1013  if (e > 0 && e < eminp)
1014  eminp = e;
1015 
1016  if (e < 0 && e > emaxp)
1017  emaxp = e;
1018  }
1019  }
1020 }
1021 
1022 static bool
1024  caseless_str& rest)
1025 {
1026  int len = name.length ();
1027  int offset = 0;
1028  bool result = false;
1029 
1030  if (len >= 4)
1031  {
1032  caseless_str pfx = name.substr (0, 4);
1033 
1034  if (pfx.compare ("axes") || pfx.compare ("line")
1035  || pfx.compare ("text"))
1036  offset = 4;
1037  else if (len >= 5)
1038  {
1039  pfx = name.substr (0, 5);
1040 
1041  if (pfx.compare ("image") || pfx.compare ("patch"))
1042  offset = 5;
1043  else if (len >= 6)
1044  {
1045  pfx = name.substr (0, 6);
1046 
1047  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1048  offset = 6;
1049  else if (len >= 7)
1050  {
1051  pfx = name.substr (0, 7);
1052 
1053  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1054  || pfx.compare ("uipanel"))
1055  offset = 7;
1056  else if (len >= 9)
1057  {
1058  pfx = name.substr (0, 9);
1059 
1060  if (pfx.compare ("uicontrol")
1061  || pfx.compare ("uitoolbar"))
1062  offset = 9;
1063  else if (len >= 10)
1064  {
1065  pfx = name.substr (0, 10);
1066 
1067  if (pfx.compare ("uipushtool"))
1068  offset = 10;
1069  else if (len >= 12)
1070  {
1071  pfx = name.substr (0, 12);
1072 
1073  if (pfx.compare ("uitoggletool"))
1074  offset = 12;
1075  else if (len >= 13)
1076  {
1077  pfx = name.substr (0, 13);
1078 
1079  if (pfx.compare ("uicontextmenu") ||
1080  pfx.compare ("uibuttongroup"))
1081  offset = 13;
1082  }
1083  }
1084  }
1085  }
1086  }
1087  }
1088  }
1089 
1090  if (offset > 0)
1091  {
1092  go_name = pfx;
1093  rest = name.substr (offset);
1094  result = true;
1095  }
1096  }
1097 
1098  return result;
1099 }
1100 
1101 static base_graphics_object*
1103  const graphics_handle& h = graphics_handle (),
1104  const graphics_handle& p = graphics_handle ())
1105 {
1106  base_graphics_object *go = 0;
1107 
1108  if (type.compare ("figure"))
1109  go = new figure (h, p);
1110  else if (type.compare ("axes"))
1111  go = new axes (h, p);
1112  else if (type.compare ("line"))
1113  go = new line (h, p);
1114  else if (type.compare ("text"))
1115  go = new text (h, p);
1116  else if (type.compare ("image"))
1117  go = new image (h, p);
1118  else if (type.compare ("light"))
1119  go = new light (h, p);
1120  else if (type.compare ("patch"))
1121  go = new patch (h, p);
1122  else if (type.compare ("surface"))
1123  go = new surface (h, p);
1124  else if (type.compare ("hggroup"))
1125  go = new hggroup (h, p);
1126  else if (type.compare ("uimenu"))
1127  go = new uimenu (h, p);
1128  else if (type.compare ("uicontrol"))
1129  go = new uicontrol (h, p);
1130  else if (type.compare ("uipanel"))
1131  go = new uipanel (h, p);
1132  else if (type.compare ("uibuttongroup"))
1133  go = new uibuttongroup (h, p);
1134  else if (type.compare ("uicontextmenu"))
1135  go = new uicontextmenu (h, p);
1136  else if (type.compare ("uitoolbar"))
1137  go = new uitoolbar (h, p);
1138  else if (type.compare ("uipushtool"))
1139  go = new uipushtool (h, p);
1140  else if (type.compare ("uitoggletool"))
1141  go = new uitoggletool (h, p);
1142  return go;
1143 }
1144 
1145 // ---------------------------------------------------------------------
1146 
1147 bool
1148 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1149 {
1150  if (do_set (v))
1151  {
1152  // Notify graphics toolkit.
1153  if (id >= 0 && do_notify_toolkit)
1154  {
1156  if (go)
1157  go.update (id);
1158  }
1159 
1160  // run listeners
1161  if (do_run)
1163 
1164  return true;
1165  }
1166 
1167  return false;
1168 }
1169 
1170 void
1172 {
1173  const octave_value_list& l = listeners[mode];
1174 
1175  for (int i = 0; i < l.length (); i++)
1177 }
1178 
1180  : default_val (), possible_vals ()
1181 {
1182  size_t beg = 0;
1183  size_t len = opt_string.length ();
1184  bool done = len == 0;
1185 
1186  while (! done)
1187  {
1188  size_t end = opt_string.find ('|', beg);
1189 
1190  if (end == std::string::npos)
1191  {
1192  end = len;
1193  done = true;
1194  }
1195 
1196  std::string t = opt_string.substr (beg, end-beg);
1197 
1198  // Might want more error checking here...
1199  if (t[0] == '{')
1200  {
1201  t = t.substr (1, t.length () - 2);
1202  default_val = t;
1203  }
1204  else if (beg == 0) // ensure default value
1205  default_val = t;
1206 
1207  possible_vals.insert (t);
1208 
1209  beg = end + 1;
1210  }
1211 }
1212 
1215 {
1217  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1218  it != possible_vals.end (); it++)
1219  {
1220  if (retval.empty ())
1221  {
1222  if (*it == default_value ())
1223  retval = "{" + *it + "}";
1224  else
1225  retval = *it;
1226  }
1227  else
1228  {
1229  if (*it == default_value ())
1230  retval += " | {" + *it + "}";
1231  else
1232  retval += " | " + *it;
1233  }
1234  }
1235 
1236  if (! retval.empty ())
1237  retval = "[ " + retval + " ]";
1238 
1239  return retval;
1240 }
1241 
1242 Cell
1244 {
1245  octave_idx_type i = 0;
1246  Cell retval (nelem (), 1);
1247  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1248  it != possible_vals.end (); it++)
1249  retval(i++) = std::string (*it);
1250  return retval;
1251 }
1252 
1253 bool
1255 {
1256  bool retval = true;
1257 
1258  double tmp_rgb[3] = {0, 0, 0};
1259 
1260  std::string str = str_arg;
1261  unsigned int len = str.length ();
1262 
1263  std::transform (str.begin (), str.end (), str.begin (), tolower);
1264 
1265  if (str.compare (0, len, "blue", 0, len) == 0)
1266  tmp_rgb[2] = 1;
1267  else if (str.compare (0, len, "black", 0, len) == 0
1268  || str.compare (0, len, "k", 0, len) == 0)
1269  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1270  else if (str.compare (0, len, "red", 0, len) == 0)
1271  tmp_rgb[0] = 1;
1272  else if (str.compare (0, len, "green", 0, len) == 0)
1273  tmp_rgb[1] = 1;
1274  else if (str.compare (0, len, "yellow", 0, len) == 0)
1275  tmp_rgb[0] = tmp_rgb[1] = 1;
1276  else if (str.compare (0, len, "magenta", 0, len) == 0)
1277  tmp_rgb[0] = tmp_rgb[2] = 1;
1278  else if (str.compare (0, len, "cyan", 0, len) == 0)
1279  tmp_rgb[1] = tmp_rgb[2] = 1;
1280  else if (str.compare (0, len, "white", 0, len) == 0
1281  || str.compare (0, len, "w", 0, len) == 0)
1282  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1283  else
1284  retval = false;
1285 
1286  if (retval)
1287  {
1288  for (int i = 0; i < 3; i++)
1289  xrgb(i) = tmp_rgb[i];
1290  }
1291 
1292  return retval;
1293 }
1294 
1295 bool
1297 {
1298  if (val.is_string ())
1299  {
1300  std::string s = val.string_value ();
1301 
1302  if (s.empty ())
1303  error ("invalid value for color property \"%s\"",
1304  get_name ().c_str ());
1305 
1306  std::string match;
1307 
1308  if (radio_val.contains (s, match))
1309  {
1310  if (current_type != radio_t || match != current_val)
1311  {
1312  if (s.length () != match.length ())
1313  warning_with_id ("Octave:abbreviated-property-match",
1314  "%s: allowing %s to match %s value %s",
1315  "set", s.c_str (), get_name ().c_str (),
1316  match.c_str ());
1317  current_val = match;
1319  return true;
1320  }
1321  }
1322  else
1323  {
1324  try
1325  {
1326  color_values col (s);
1327 
1328  if (current_type != color_t || col != color_val)
1329  {
1330  color_val = col;
1332  return true;
1333  }
1334  }
1335  catch (octave::execution_exception& e)
1336  {
1337  error (e, "invalid value for color property \"%s\" (value = %s)",
1338  get_name ().c_str (), s.c_str ());
1339  }
1340  }
1341  }
1342  else if (val.is_numeric_type ())
1343  {
1344  Matrix m = val.matrix_value ();
1345 
1346  if (m.numel () != 3)
1347  error ("invalid value for color property \"%s\"",
1348  get_name ().c_str ());
1349 
1350  color_values col (m(0), m(1), m(2));
1351 
1352  if (current_type != color_t || col != color_val)
1353  {
1354  color_val = col;
1356  return true;
1357  }
1358  }
1359  else
1360  error ("invalid value for color property \"%s\"",
1361  get_name ().c_str ());
1362 
1363  return false;
1364 }
1365 
1366 bool
1368 {
1369  if (val.is_string ())
1370  {
1371  std::string s = val.string_value ();
1372  std::string match;
1373 
1374  if (s.empty () || ! radio_val.contains (s, match))
1375  error ("invalid value for double_radio property \"%s\"",
1376  get_name ().c_str ());
1377 
1378  if (current_type != radio_t || match != current_val)
1379  {
1380  if (s.length () != match.length ())
1381  warning_with_id ("Octave:abbreviated-property-match",
1382  "%s: allowing %s to match %s value %s",
1383  "set", s.c_str (), get_name ().c_str (),
1384  match.c_str ());
1385  current_val = match;
1387  return true;
1388  }
1389  }
1390  else if (val.is_scalar_type () && val.is_real_type ())
1391  {
1392  double new_dval = val.double_value ();
1393 
1394  if (current_type != double_t || new_dval != dval)
1395  {
1396  dval = new_dval;
1398  return true;
1399  }
1400  }
1401  else
1402  error ("invalid value for double_radio property \"%s\"",
1403  get_name ().c_str ());
1404 
1405  return false;
1406 }
1407 
1408 bool
1410 {
1411  bool xok = false;
1412 
1413  // check value type
1414  if (type_constraints.size () > 0)
1415  {
1416  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1417  xok = true;
1418 
1419  // check if complex is allowed (it's also of class "double", so
1420  // checking that alone is not enough to ensure real type)
1421  if (type_constraints.find ("real") != type_constraints.end ()
1422  && v.is_complex_type ())
1423  xok = false;
1424  }
1425  else
1426  xok = v.is_numeric_type () || v.is_bool_scalar ();
1427 
1428  if (xok)
1429  {
1430  if (size_constraints.size () == 0)
1431  return true;
1432 
1433  dim_vector vdims = v.dims ();
1434  int vlen = vdims.ndims ();
1435 
1436  xok = false;
1437 
1438  // check dimensional size constraints until a match is found
1439  for (auto it = size_constraints.cbegin ();
1440  ! xok && it != size_constraints.cend ();
1441  ++it)
1442  {
1443  dim_vector itdims = (*it);
1444 
1445  if (itdims.ndims () == vlen)
1446  {
1447  xok = true;
1448 
1449  for (int i = 0; xok && i < vlen; i++)
1450  {
1451  if (itdims(i) > 0)
1452  {
1453  if (itdims(i) != vdims(i))
1454  xok = false;
1455  }
1456  else if (v.is_empty ())
1457  break;
1458  }
1459  }
1460  }
1461  }
1462 
1463  return xok;
1464 }
1465 
1466 bool
1468 {
1469  if (data.type_name () == v.type_name ())
1470  {
1471  if (data.dims () == v.dims ())
1472  {
1473 
1474 #define CHECK_ARRAY_EQUAL(T, F, A) \
1475  { \
1476  if (data.numel () == 1) \
1477  return data.F ## scalar_value () == \
1478  v.F ## scalar_value (); \
1479  else \
1480  { \
1481  /* Keep copy of array_value to allow */ \
1482  /* sparse/bool arrays that are converted, to */ \
1483  /* not be deallocated early */ \
1484  const A m1 = data.F ## array_value (); \
1485  const T* d1 = m1.data (); \
1486  const A m2 = v.F ## array_value (); \
1487  const T* d2 = m2.data (); \
1488  \
1489  bool flag = true; \
1490  \
1491  for (int i = 0; flag && i < data.numel (); i++) \
1492  if (d1[i] != d2[i]) \
1493  flag = false; \
1494  \
1495  return flag; \
1496  } \
1497  }
1498 
1499  if (data.is_double_type () || data.is_bool_type ())
1500  CHECK_ARRAY_EQUAL (double, , NDArray)
1501  else if (data.is_single_type ())
1502  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1503  else if (data.is_int8_type ())
1505  else if (data.is_int16_type ())
1507  else if (data.is_int32_type ())
1509  else if (data.is_int64_type ())
1511  else if (data.is_uint8_type ())
1513  else if (data.is_uint16_type ())
1515  else if (data.is_uint32_type ())
1517  else if (data.is_uint64_type ())
1519  }
1520  }
1521 
1522  return false;
1523 }
1524 
1525 void
1527 {
1530 
1531  if (! data.is_empty ())
1532  {
1533  if (data.is_integer_type ())
1534  {
1535  if (data.is_int8_type ())
1537  xmin, xmax, xminp, xmaxp);
1538  else if (data.is_uint8_type ())
1540  xmin, xmax, xminp, xmaxp);
1541  else if (data.is_int16_type ())
1543  xmin, xmax, xminp, xmaxp);
1544  else if (data.is_uint16_type ())
1546  xmin, xmax, xminp, xmaxp);
1547  else if (data.is_int32_type ())
1549  xmin, xmax, xminp, xmaxp);
1550  else if (data.is_uint32_type ())
1552  xmin, xmax, xminp, xmaxp);
1553  else if (data.is_int64_type ())
1555  xmin, xmax, xminp, xmaxp);
1556  else if (data.is_uint64_type ())
1558  xmin, xmax, xminp, xmaxp);
1559  }
1560  else
1562  }
1563 }
1564 
1565 bool
1567 {
1568  // Users may want to use empty matrix to reset a handle property
1569  if (v.is_empty ())
1570  {
1571  if (! get ().is_empty ())
1572  {
1574  return true;
1575  }
1576  else
1577  return false;
1578  }
1579 
1580  double dv = v.xdouble_value ("set: invalid graphics handle for property \"%s\"",
1581  get_name ().c_str ());
1582 
1584 
1585  if (! (octave::math::isnan (gh.value ()) || gh.ok ()))
1586  error ("set: invalid graphics handle (= %g) for property \"%s\"",
1587  dv, get_name ().c_str ());
1588 
1589  if (current_val != gh)
1590  {
1591  current_val = gh;
1592  return true;
1593  }
1594 
1595  return false;
1596 }
1597 
1598 Matrix
1599 children_property::do_get_children (bool return_hidden) const
1600 {
1601  Matrix retval (children_list.size (), 1);
1602  octave_idx_type k = 0;
1603 
1605 
1606  root_figure::properties& props =
1607  dynamic_cast<root_figure::properties&> (go.get_properties ());
1608 
1609  if (! props.is_showhiddenhandles ())
1610  {
1611  for (const auto& hchild : children_list)
1612  {
1613  graphics_handle kid = hchild;
1614 
1616  {
1617  if (! return_hidden)
1618  retval(k++) = hchild;
1619  }
1620  else if (return_hidden)
1621  retval(k++) = hchild;
1622  }
1623 
1624  retval.resize (k, 1);
1625  }
1626  else
1627  {
1628  for (const auto& hchild : children_list)
1629  retval(k++) = hchild;
1630  }
1631 
1632  return retval;
1633 }
1634 
1635 void
1637 {
1638  for (auto& hchild : children_list)
1639  {
1641 
1642  if (go.valid_object ())
1643  gh_manager::free (hchild);
1644  }
1645 
1646  if (clear)
1647  children_list.clear ();
1648 }
1649 
1650 bool
1652 {
1653  // case 1: empty matrix
1654  // case 2: function handle
1655  // case 3: string corresponding to known function name
1656  // case 4: string that can be eval()'ed
1657  // case 5: cell array with first element being a function handle
1658 
1659  if (v.is_empty ())
1660  return true;
1661  else if (v.is_function_handle ())
1662  return true;
1663  else if (v.is_string ())
1664  // complete validation will be done at execution-time
1665  return true;
1666  else if (v.is_cell () && (v.rows () == 1 || v.columns () == 1)
1667  && v.cell_value ()(0).is_function_handle ())
1668  return true;
1669 
1670  return false;
1671 }
1672 
1673 // If TRUE, we are executing any callback function, or the functions it calls.
1674 // Used to determine handle visibility inside callback functions.
1675 static bool executing_callback = false;
1676 
1677 void
1679 {
1681 
1682  // We are executing the callback function associated with this
1683  // callback property. When set to true, we avoid recursive calls to
1684  // callback routines.
1685  frame.protect_var (executing);
1686 
1687  // We are executing a callback function, so allow handles that have
1688  // their handlevisibility property set to "callback" to be visible.
1689  frame.protect_var (executing_callback);
1690 
1691  if (! executing)
1692  {
1693  executing = true;
1694  executing_callback = true;
1695 
1696  if (callback.is_defined () && ! callback.is_empty ())
1698  }
1699 }
1700 
1701 // Used to cache dummy graphics objects from which dynamic properties can be
1702 // cloned.
1703 static std::map<caseless_str, graphics_object> dprop_obj_map;
1704 
1705 property
1707  const caseless_str& type, const octave_value_list& args)
1708 {
1709  property retval;
1710 
1711  if (type.compare ("string"))
1712  {
1713  std::string sv = (args.length () > 0 ? args(0).string_value () : "");
1714 
1715  retval = property (new string_property (name, h, sv));
1716  }
1717  else if (type.compare ("any"))
1718  {
1719  octave_value ov = (args.length () > 0 ? args(0)
1720  : octave_value (Matrix ()));
1721 
1722  retval = property (new any_property (name, h, ov));
1723  }
1724  else if (type.compare ("radio"))
1725  {
1726  if (args.length () < 1)
1727  error ("addproperty: missing possible values for radio property");
1728 
1729  std::string sv = args(0).xstring_value ("addproperty: argument for radio property must be a string");
1730 
1731  retval = property (new radio_property (name, h, sv));
1732 
1733  if (args.length () > 1)
1734  retval.set (args(1));
1735  }
1736  else if (type.compare ("double"))
1737  {
1738  double dv = (args.length () > 0 ? args(0).double_value () : 0.0);
1739 
1740  retval = property (new double_property (name, h, dv));
1741  }
1742  else if (type.compare ("handle"))
1743  {
1744  double hv = args.length () > 0 ? args(0).double_value ()
1746 
1747  graphics_handle gh (hv);
1748 
1749  retval = property (new handle_property (name, h, gh));
1750  }
1751  else if (type.compare ("boolean"))
1752  {
1753  retval = property (new bool_property (name, h, false));
1754 
1755  if (args.length () > 0)
1756  retval.set (args(0));
1757  }
1758  else if (type.compare ("data"))
1759  {
1760  retval = property (new array_property (name, h, Matrix ()));
1761 
1762  if (args.length () > 0)
1763  {
1764  retval.set (args(0));
1765  // FIXME: additional argument could define constraints,
1766  // but is this really useful?
1767  }
1768  }
1769  else if (type.compare ("color"))
1770  {
1771  color_values cv (0, 0, 0);
1772  radio_values rv;
1773 
1774  if (args.length () > 1)
1775  rv = radio_values (args(1).string_value ());
1776 
1777  retval = property (new color_property (name, h, cv, rv));
1778 
1779  if (args.length () > 0 && ! args(0).is_empty ())
1780  retval.set (args(0));
1781  else
1782  retval.set (rv.default_value ());
1783  }
1784  else
1785  {
1786  caseless_str go_name, go_rest;
1787 
1788  if (! lookup_object_name (type, go_name, go_rest))
1789  error ("addproperty: unsupported type for dynamic property (= %s)",
1790  type.c_str ());
1791 
1792  graphics_object go;
1793 
1794  std::map<caseless_str, graphics_object>::const_iterator it =
1795  dprop_obj_map.find (go_name);
1796 
1797  if (it == dprop_obj_map.end ())
1798  {
1799  base_graphics_object *bgo =
1801 
1802  if (bgo)
1803  {
1804  go = graphics_object (bgo);
1805 
1806  dprop_obj_map[go_name] = go;
1807  }
1808  }
1809  else
1810  go = it->second;
1811 
1812  if (! go.valid_object ())
1813  error ("addproperty: invalid object type (= %s)",
1814  go_name.c_str ());
1815 
1816  property prop = go.get_properties ().get_property (go_rest);
1817 
1818  retval = prop.clone ();
1819 
1820  retval.set_parent (h);
1821  retval.set_name (name);
1822 
1823  if (args.length () > 0)
1824  retval.set (args(0));
1825  }
1826 
1827  return retval;
1828 }
1829 
1830 static void
1832 {
1834 
1835  if (go)
1836  {
1837  Matrix children = go.get_properties ().get_all_children ();
1838 
1839  for (int k = 0; k < children.numel (); k++)
1840  finalize_r (children(k));
1841 
1842  go.finalize ();
1843  }
1844 }
1845 
1846 static void
1848 {
1850 
1851  if (go)
1852  {
1853  Matrix children = go.get_properties ().get_all_children ();
1854 
1855  go.initialize ();
1856 
1857  for (int k = 0; k < children.numel (); k++)
1858  initialize_r (children(k));
1859  }
1860 }
1861 
1862 void
1864 {
1865  if (toolkit)
1867 
1868  toolkit = b;
1870  __plot_stream__ = Matrix ();
1871 
1872  if (toolkit)
1874 
1875  mark_modified ();
1876 }
1877 
1878 void
1880 {
1881  std::string direction = "in";
1882 
1883  octave_value val = val_arg;
1884 
1885  if (val.is_string ())
1886  {
1887  std::string modestr = val.string_value ();
1888 
1889  if (modestr == "zoom in")
1890  {
1891  val = modestr = "zoom";
1892  direction = "in";
1893  }
1894  else if (modestr == "zoom out")
1895  {
1896  val = modestr = "zoom";
1897  direction = "out";
1898  }
1899 
1900  if (__mouse_mode__.set (val, true))
1901  {
1902  std::string mode = __mouse_mode__.current_value ();
1903 
1904  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
1905  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
1906  set___pan_mode__ (pm);
1907 
1908  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
1909  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
1910  set___rotate_mode__ (rm);
1911 
1912  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
1913  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
1914  zm.setfield ("Direction", direction);
1915  set___zoom_mode__ (zm);
1916 
1917  mark_modified ();
1918  }
1919  else if (modestr == "zoom")
1920  {
1921  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
1922  std::string curr_direction
1923  = zm.getfield ("Direction").string_value ();
1924 
1925  if (direction != curr_direction)
1926  {
1927  zm.setfield ("Direction", direction);
1928  set___zoom_mode__ (zm);
1929 
1930  mark_modified ();
1931  }
1932  }
1933  }
1934 }
1935 
1936 // ---------------------------------------------------------------------
1937 
1938 void
1940 {
1941  size_t offset = 0;
1942 
1943  size_t len = name.length ();
1944 
1945  if (len > 4)
1946  {
1947  caseless_str pfx = name.substr (0, 4);
1948 
1949  if (pfx.compare ("axes") || pfx.compare ("line")
1950  || pfx.compare ("text"))
1951  offset = 4;
1952  else if (len > 5)
1953  {
1954  pfx = name.substr (0, 5);
1955 
1956  if (pfx.compare ("image") || pfx.compare ("patch"))
1957  offset = 5;
1958  else if (len > 6)
1959  {
1960  pfx = name.substr (0, 6);
1961 
1962  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1963  offset = 6;
1964  else if (len > 7)
1965  {
1966  pfx = name.substr (0, 7);
1967 
1968  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1969  || pfx.compare ("uipanel"))
1970  offset = 7;
1971  else if (len > 9)
1972  {
1973  pfx = name.substr (0, 9);
1974 
1975  if (pfx.compare ("uicontrol")
1976  || pfx.compare ("uitoolbar"))
1977  offset = 9;
1978  else if (len > 10)
1979  {
1980  pfx = name.substr (0, 10);
1981 
1982  if (pfx.compare ("uipushtool"))
1983  offset = 10;
1984  else if (len > 12)
1985  {
1986  pfx = name.substr (0, 12);
1987 
1988  if (pfx.compare ("uitoogletool"))
1989  offset = 12;
1990  else if (len > 13)
1991  {
1992  pfx = name.substr (0, 13);
1993 
1994  if (pfx.compare ("uicontextmenu")
1995  || pfx.compare ("uibuttongroup"))
1996  offset = 13;
1997  }
1998  }
1999  }
2000  }
2001  }
2002  }
2003  }
2004 
2005  if (offset > 0)
2006  {
2007  // FIXME: should we validate property names and values here?
2008 
2009  std::string pname = name.substr (offset);
2010 
2011  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2012  std::transform (pname.begin (), pname.end (), pname.begin (),
2013  tolower);
2014 
2015  bool has_property = false;
2016  if (pfx == "axes")
2017  has_property = axes::properties::has_core_property (pname);
2018  else if (pfx == "figure")
2019  has_property = figure::properties::has_core_property (pname);
2020  else if (pfx == "line")
2021  has_property = line::properties::has_core_property (pname);
2022  else if (pfx == "text")
2023  has_property = text::properties::has_core_property (pname);
2024  else if (pfx == "image")
2025  has_property = image::properties::has_core_property (pname);
2026  else if (pfx == "patch")
2027  has_property = patch::properties::has_core_property (pname);
2028  else if (pfx == "surface")
2029  has_property = surface::properties::has_core_property (pname);
2030  else if (pfx == "hggroup")
2031  has_property = hggroup::properties::has_core_property (pname);
2032  else if (pfx == "uimenu")
2033  has_property = uimenu::properties::has_core_property (pname);
2034  else if (pfx == "uicontrol")
2035  has_property = uicontrol::properties::has_core_property (pname);
2036  else if (pfx == "uibuttongroup")
2037  has_property = uibuttongroup::properties::has_core_property (pname);
2038  else if (pfx == "uipanel")
2039  has_property = uipanel::properties::has_core_property (pname);
2040  else if (pfx == "uicontextmenu")
2041  has_property = uicontextmenu::properties::has_core_property (pname);
2042  else if (pfx == "uitoolbar")
2043  has_property = uitoolbar::properties::has_core_property (pname);
2044  else if (pfx == "uipushtool")
2045  has_property = uipushtool::properties::has_core_property (pname);
2046 
2047  if (! has_property)
2048  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2049 
2050  bool remove = false;
2051  if (val.is_string ())
2052  {
2053  std::string sval = val.string_value ();
2054 
2055  remove = (sval == "remove");
2056  }
2057 
2058  pval_map_type& pval_map = plist_map[pfx];
2059 
2060  if (remove)
2061  {
2062  pval_map_iterator p = pval_map.find (pname);
2063 
2064  if (p != pval_map.end ())
2065  pval_map.erase (p);
2066  }
2067  else
2068  pval_map[pname] = val;
2069  }
2070  }
2071 
2072  if (offset == 0)
2073  error ("invalid default property specification");
2074 }
2075 
2078 {
2080 
2081  size_t offset = 0;
2082 
2083  size_t len = name.length ();
2084 
2085  if (len > 4)
2086  {
2087  caseless_str pfx = name.substr (0, 4);
2088 
2089  if (pfx.compare ("axes") || pfx.compare ("line")
2090  || pfx.compare ("text"))
2091  offset = 4;
2092  else if (len > 5)
2093  {
2094  pfx = name.substr (0, 5);
2095 
2096  if (pfx.compare ("image") || pfx.compare ("patch"))
2097  offset = 5;
2098  else if (len > 6)
2099  {
2100  pfx = name.substr (0, 6);
2101 
2102  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2103  offset = 6;
2104  else if (len > 7)
2105  {
2106  pfx = name.substr (0, 7);
2107 
2108  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2109  || pfx.compare ("uipanel"))
2110  offset = 7;
2111  else if (len > 9)
2112  {
2113  pfx = name.substr (0, 9);
2114 
2115  if (pfx.compare ("uicontrol")
2116  || pfx.compare ("uitoolbar"))
2117  offset = 9;
2118  else if (len > 10)
2119  {
2120  pfx = name.substr (0, 10);
2121 
2122  if (pfx.compare ("uipushtool"))
2123  offset = 10;
2124  else if (len > 12)
2125  {
2126  pfx = name.substr (0, 12);
2127 
2128  if (pfx.compare ("uitoggletool"))
2129  offset = 12;
2130  else if (len > 13)
2131  {
2132  pfx = name.substr (0, 13);
2133 
2134  if (pfx.compare ("uicontextmenu")
2135  || pfx.compare ("uibuttongroup"))
2136  offset = 13;
2137  }
2138  }
2139  }
2140  }
2141  }
2142  }
2143  }
2144 
2145  if (offset > 0)
2146  {
2147  std::string pname = name.substr (offset);
2148 
2149  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2150  std::transform (pname.begin (), pname.end (), pname.begin (),
2151  tolower);
2152 
2154 
2155  if (p != end ())
2156  {
2157  const pval_map_type& pval_map = p->second;
2158 
2159  pval_map_const_iterator q = pval_map.find (pname);
2160 
2161  if (q != pval_map.end ())
2162  retval = q->second;
2163  }
2164  }
2165  }
2166 
2167  return retval;
2168 }
2169 
2171 property_list::as_struct (const std::string& prefix_arg) const
2172 {
2174 
2175  for (plist_map_const_iterator p = begin (); p != end (); p++)
2176  {
2177  std::string prefix = prefix_arg + p->first;
2178 
2179  const pval_map_type pval_map = p->second;
2180 
2181  for (const auto& prop_val_p : pval_map)
2182  m.assign (prefix + prop_val_p.first, prop_val_p.second);
2183  }
2184 
2185  return m;
2186 }
2187 
2188 // Set properties given as a cs-list of name, value pairs.
2189 
2190 void
2192 {
2193  int nargin = args.length ();
2194 
2195  if (nargin == 0)
2196  error ("graphics_object::set: Nothing to set");
2197 
2198  for (int i = 0; i < nargin; )
2199  {
2200  if (args(i).is_map () )
2201  {
2202  set (args(i).map_value ());
2203  i++;
2204  }
2205  else if (i < nargin - 1)
2206  {
2207  caseless_str pname = args(i).xstring_value ("set: argument %d must be a property name", i);
2208  octave_value val = args(i+1);
2209  set_value_or_default (pname, val);
2210  i += 2;
2211  }
2212  else
2213  error ("set: invalid number of arguments");
2214  }
2215 }
2216 
2217 /*
2218 ## test set with name, value pairs
2219 %!test
2220 %! hf = figure ("visible", "off");
2221 %! h = plot (1:10, 10:-1:1);
2222 %! set (h, "linewidth", 10, "marker", "x");
2223 %! lw = get (h, "linewidth");
2224 %! mk = get (h, "marker");
2225 %! close (hf);
2226 %! assert (lw, 10);
2227 %! assert (mk, "x");
2228 */
2229 
2230 // Set properties given in two cell arrays containing names and values.
2231 void
2233  const Cell& values, octave_idx_type row)
2234 {
2235  if (pnames.numel () != values.columns ())
2236  error ("set: number of names must match number of value columns (%d != %d)",
2237  pnames.numel (), values.columns ());
2238 
2239  octave_idx_type k = pnames.columns ();
2240 
2241  for (octave_idx_type column = 0; column < k; column++)
2242  {
2243  caseless_str pname = pnames(column);
2244  octave_value val = values(row, column);
2245 
2246  set_value_or_default (pname, val);
2247  }
2248 }
2249 
2250 /*
2251 ## test set with cell array arguments
2252 %!test
2253 %! hf = figure ("visible", "off");
2254 %! h = plot (1:10, 10:-1:1);
2255 %! set (h, {"linewidth", "marker"}, {10, "x"});
2256 %! lw = get (h, "linewidth");
2257 %! mk = get (h, "marker");
2258 %! close (hf);
2259 %! assert (lw, 10);
2260 %! assert (mk, "x");
2261 
2262 ## test set with multiple handles and cell array arguments
2263 %!test
2264 %! hf = figure ("visible", "off");
2265 %! unwind_protect
2266 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2267 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2268 %! assert (get (h, "linewidth"), {10; 5});
2269 %! assert (get (h, "marker"), {"x"; "o"});
2270 %! set (h, {"linewidth", "marker"}, {10, "x"});
2271 %! assert (get (h, "linewidth"), {10; 10});
2272 %! assert (get (h, "marker"), {"x"; "x"});
2273 %! unwind_protect_cleanup
2274 %! close (hf);
2275 %! end_unwind_protect;
2276 
2277 %!error <set: number of graphics handles must match number of value rows>
2278 %! hf = figure ("visible", "off");
2279 %! unwind_protect
2280 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2281 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2282 %! unwind_protect_cleanup
2283 %! close (hf);
2284 %! end_unwind_protect
2285 
2286 %!error <set: number of names must match number of value columns>
2287 %! hf = figure ("visible", "off");
2288 %! unwind_protect
2289 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2290 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2291 %! unwind_protect_cleanup
2292 %! close (hf);
2293 %! end_unwind_protect
2294 */
2295 
2296 // Set properties given in a struct array
2297 void
2299 {
2300  for (octave_idx_type p = 0; p < m.nfields (); p++)
2301  {
2302  // FIXME: Would it be better to extract all the keys at once rather than
2303  // repeatedly call keys() inside a for loop?
2304  caseless_str pname = m.keys ()[p];
2305 
2306  octave_value val = octave_value (m.contents (pname).elem (m.numel () - 1));
2307 
2308  set_value_or_default (pname, val);
2309  }
2310 }
2311 
2312 /*
2313 ## test set ticklabels for compatibility
2314 %!test
2315 %! hf = figure ("visible", "off");
2316 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2317 %! xticklabel = get (gca (), "xticklabel");
2318 %! close (hf);
2319 %! assert (class (xticklabel), "char");
2320 %! assert (size (xticklabel), [6, 3]);
2321 
2322 %!test
2323 %! hf = figure ("visible", "off");
2324 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2325 %! xticklabel = get (gca (), "xticklabel");
2326 %! close (hf);
2327 %! assert (class (xticklabel), "char");
2328 %! assert (size (xticklabel), [6, 3]);
2329 
2330 %!test
2331 %! hf = figure ("visible", "off");
2332 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2333 %! xticklabel = get (gca (), "xticklabel");
2334 %! close (hf);
2335 %! assert (class (xticklabel), "char");
2336 %! assert (size (xticklabel), [6, 3]);
2337 
2338 %!test
2339 %! hf = figure ("visible", "off");
2340 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2341 %! xticklabel = get (gca (), "xticklabel");
2342 %! close (hf);
2343 %! assert (class (xticklabel), "cell");
2344 %! assert (size (xticklabel), [6, 1]);
2345 */
2346 
2347 /*
2348 ## test set with struct arguments
2349 %!test
2350 %! hf = figure ("visible", "off");
2351 %! unwind_protect
2352 %! h = plot (1:10, 10:-1:1);
2353 %! set (h, struct ("linewidth", 10, "marker", "x"));
2354 %! assert (get (h, "linewidth"), 10);
2355 %! assert (get (h, "marker"), "x");
2356 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2357 %! set (h, struct ("linewidth", {5, 10}));
2358 %! assert (get (h, "linewidth"), {10; 10});
2359 %! unwind_protect_cleanup
2360 %! close (hf);
2361 %! end_unwind_protect
2362 
2363 ## test ordering
2364 %!test
2365 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2366 %! hf = figure ("visible", "off");
2367 %! unwind_protect
2368 %! h = line ();
2369 %! set (h, "userdata", {});
2370 %! addlistener (h, "color", {markchanged, "color"});
2371 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2372 %! ## "linewidth" first
2373 %! props.linewidth = 2;
2374 %! props.color = "r";
2375 %! set (h, props);
2376 %! assert (get (h, "userdata"), fieldnames (props));
2377 %! clear props;
2378 %! clf ();
2379 %! h = line ();
2380 %! set (h, "userdata", {});
2381 %! addlistener (h, "color", {markchanged, "color"});
2382 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2383 %! ## "color" first
2384 %! props.color = "r";
2385 %! props.linewidth = 2;
2386 %! set (h, props);
2387 %! assert (get (h, "userdata"), fieldnames (props));
2388 %! unwind_protect_cleanup
2389 %! close (hf);
2390 %! end_unwind_protect
2391 */
2392 
2393 // Set a property to a value or to its (factory) default value.
2394 
2395 void
2397  const octave_value& val)
2398 {
2399  if (val.is_string ())
2400  {
2401  std::string sval = val.string_value ();
2402 
2403  octave_value default_val;
2404 
2405  if (sval == "default")
2406  {
2407  default_val = get_default (pname);
2408 
2409  rep->set (pname, default_val);
2410  }
2411  else if (sval == "factory")
2412  {
2413  default_val = get_factory_default (pname);
2414 
2415  rep->set (pname, default_val);
2416  }
2417  else
2418  {
2419  // Matlab specifically uses "\default" to escape string setting
2420  if (sval == "\\default")
2421  rep->set (pname, "default");
2422  else if (sval == "\\factory")
2423  rep->set (pname, "factory");
2424  else
2425  rep->set (pname, val);
2426  }
2427  }
2428  else
2429  rep->set (pname, val);
2430 }
2431 
2432 /*
2433 ## test setting of default values
2434 %!test
2435 %! old_lw = get (0, "defaultlinelinewidth");
2436 %! unwind_protect
2437 %! hf = figure ("visible", "off");
2438 %! h = plot (1:10, 10:-1:1);
2439 %! set (0, "defaultlinelinewidth", 20);
2440 %! set (h, "linewidth", "default");
2441 %! assert (get (h, "linewidth"), 20);
2442 %! set (h, "linewidth", "factory");
2443 %! assert (get (h, "linewidth"), 0.5);
2444 %! unwind_protect_cleanup
2445 %! close (hf);
2446 %! set (0, "defaultlinelinewidth", old_lw);
2447 %! end_unwind_protect
2448 */
2449 
2450 static double
2452 {
2453  static double maxrand = RAND_MAX + 2.0;
2454 
2455  return (rand () + 1.0) / maxrand;
2456 }
2457 
2459 gh_manager::do_get_handle (bool integer_figure_handle)
2460 {
2462 
2463  if (integer_figure_handle)
2464  {
2465  // Figure handles are positive integers corresponding
2466  // to the figure number.
2467 
2468  // We always want the lowest unused figure number.
2469 
2470  retval = 1;
2471 
2472  while (handle_map.find (retval) != handle_map.end ())
2473  retval++;
2474  }
2475  else
2476  {
2477  // Other graphics handles are negative integers plus some random
2478  // fractional part. To avoid running out of integers, we recycle the
2479  // integer part but tack on a new random part each time.
2480 
2481  free_list_iterator p = handle_free_list.begin ();
2482 
2483  if (p != handle_free_list.end ())
2484  {
2485  retval = *p;
2486  handle_free_list.erase (p);
2487  }
2488  else
2489  {
2490  retval = graphics_handle (next_handle);
2491 
2492  next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2493  }
2494  }
2495 
2496  return retval;
2497 }
2498 
2499 void
2501 {
2502  if (h.ok ())
2503  {
2504  if (h.value () == 0)
2505  error ("graphics_handle::free: can't delete root figure");
2506 
2507  iterator p = handle_map.find (h);
2508 
2509  if (p == handle_map.end ())
2510  error ("graphics_handle::free: invalid object %g", h.value ());
2511 
2512  base_properties& bp = p->second.get_properties ();
2513 
2514  bp.set_beingdeleted (true);
2515 
2516  bp.delete_children ();
2517 
2518  octave_value val = bp.get_deletefcn ();
2519 
2520  bp.execute_deletefcn ();
2521 
2522  // Notify graphics toolkit.
2523  p->second.finalize ();
2524 
2525  // Note: this will be valid only for first explicitly deleted
2526  // object. All its children will then have an
2527  // unknown graphics toolkit.
2528 
2529  // Graphics handles for non-figure objects are negative
2530  // integers plus some random fractional part. To avoid
2531  // running out of integers, we recycle the integer part
2532  // but tack on a new random part each time.
2533 
2534  handle_map.erase (p);
2535 
2536  if (h.value () < 0)
2537  handle_free_list.insert
2538  (std::ceil (h.value ()) - make_handle_fraction ());
2539  }
2540 }
2541 
2542 void
2544  const graphics_handle& new_gh)
2545 {
2546  iterator p = handle_map.find (old_gh);
2547 
2548  if (p == handle_map.end ())
2549  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2550 
2551  graphics_object go = p->second;
2552 
2553  handle_map.erase (p);
2554 
2555  handle_map[new_gh] = go;
2556 
2557  if (old_gh.value () < 0)
2558  handle_free_list.insert (std::ceil (old_gh.value ())
2559  - make_handle_fraction ());
2560 
2561  for (auto& hfig : figure_list)
2562  {
2563  if (hfig == old_gh)
2564  {
2565  hfig = new_gh;
2566  break;
2567  }
2568  }
2569 }
2570 
2572 
2573 static void
2575  const octave_value& val)
2576 {
2578  go.set (pname, val);
2579 }
2580 
2581 static void
2583 {
2584  if (args.length () > 0)
2585  {
2587  go.set (args);
2588  }
2589 }
2590 
2591 static octave_value
2593 {
2595  return go.get (pname);
2596 }
2597 
2598 static graphics_handle
2599 reparent (const octave_value& ov, const std::string& who,
2600  const std::string& pname, const graphics_handle& new_parent,
2601  bool adopt = true)
2602 {
2604 
2605  double hv = ov.xdouble_value ("%s: %s must be a graphics handle",
2606  who.c_str (), pname.c_str ());
2607 
2608  h = gh_manager::lookup (hv);
2609 
2610  if (! h.ok ())
2611  error ("%s: invalid graphics handle (= %g) for %s",
2612  who.c_str (), hv, pname.c_str ());
2613 
2615 
2616  graphics_handle parent_h = go.get_parent ();
2617 
2618  graphics_object parent_go = gh_manager::get_object (parent_h);
2619 
2620  parent_go.remove_child (h);
2621 
2622  if (adopt)
2623  go.set ("parent", new_parent.value ());
2624  else
2625  go.reparent (new_parent);
2626 
2627  return h;
2628 }
2629 
2630 // This function is NOT equivalent to the scripting language function gcf.
2632 gcf (void)
2633 {
2634  octave_value val = xget (0, "currentfigure");
2635 
2637  : val.double_value ();
2638 }
2639 
2640 // This function is NOT equivalent to the scripting language function gca.
2642 gca (void)
2643 {
2644  octave_value val = xget (gcf (), "currentaxes");
2645 
2647  : val.double_value ();
2648 }
2649 
2650 static void
2652 {
2653  if (h.ok ())
2654  {
2656 
2657  // Don't do recursive deleting, due to callbacks
2658  if (! go.get_properties ().is_beingdeleted ())
2659  {
2660  graphics_handle parent_h = go.get_parent ();
2661 
2662  graphics_object parent_go = gh_manager::get_object (parent_h);
2663 
2664  // NOTE: free the handle before removing it from its parent's
2665  // children, such that the object's state is correct when the
2666  // deletefcn callback is executed
2667 
2668  gh_manager::free (h);
2669 
2670  // A callback function might have already deleted the parent
2671  if (parent_go.valid_object ())
2672  parent_go.remove_child (h);
2673 
2674  Vdrawnow_requested = true;
2675  }
2676  }
2677 }
2678 
2679 static void
2681 {
2683 }
2684 
2685 // Flag to stop redraws due to callbacks while deletion is in progress.
2686 static bool delete_executing = false;
2687 
2688 static void
2690 {
2691  // Prevent redraw of partially deleted objects.
2693  frame.protect_var (delete_executing);
2694  delete_executing = true;
2695 
2696  for (octave_idx_type i = 0; i < vals.numel (); i++)
2697  delete_graphics_object (vals.elem (i));
2698 }
2699 
2700 static void
2702 {
2703  octave_value closerequestfcn = xget (h, "closerequestfcn");
2704 
2705  OCTAVE_SAFE_CALL (gh_manager::execute_callback, (h, closerequestfcn));
2706 }
2707 
2708 static void
2710 {
2711  // Remove the deletefcn and closerequestfcn callbacks
2712  // and delete the object directly.
2713 
2714  xset (h, "deletefcn", Matrix ());
2715  xset (h, "closerequestfcn", Matrix ());
2716 
2718 }
2719 
2720 void
2722 {
2723  // FIXME: should we process or discard pending events?
2724 
2725  event_queue.clear ();
2726 
2727  // Don't use figure_list_iterator because we'll be removing elements
2728  // from the list elsewhere.
2729 
2730  Matrix hlist = do_figure_handle_list (true);
2731 
2732  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2733  {
2735 
2736  if (h.ok ())
2737  close_figure (h);
2738  }
2739 
2740  // They should all be closed now. If not, force them to close.
2741 
2742  hlist = do_figure_handle_list (true);
2743 
2744  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2745  {
2747 
2748  if (h.ok ())
2749  force_close_figure (h);
2750  }
2751 
2752  // None left now, right?
2753 
2754  hlist = do_figure_handle_list (true);
2755 
2756  if (hlist.numel () != 0)
2757  warning ("gh_manager::do_close_all_figures: some graphics elements failed to close.");
2758 
2759  // Clear all callback objects from our list.
2760 
2761  callback_objects.clear ();
2762 }
2763 
2764 static void
2765 adopt (const graphics_handle& parent_h, const graphics_handle& h)
2766 {
2767  graphics_object parent_go = gh_manager::get_object (parent_h);
2768  parent_go.adopt (h);
2769 }
2770 
2771 static bool
2773 {
2774  return h.ok ();
2775 }
2776 
2777 static bool
2778 is_handle (double val)
2779 {
2781 
2782  return h.ok ();
2783 }
2784 
2785 static octave_value
2787 {
2788  octave_value retval = false;
2789 
2790  if (val.is_real_scalar () && is_handle (val.double_value ()))
2791  retval = true;
2792  else if (val.is_numeric_type () && val.is_real_type ())
2793  {
2794  const NDArray handles = val.array_value ();
2795 
2796  boolNDArray result (handles.dims ());
2797 
2798  for (octave_idx_type i = 0; i < handles.numel (); i++)
2799  result.xelem (i) = is_handle (handles(i));
2800 
2801  retval = result;
2802  }
2803 
2804  return retval;
2805 }
2806 
2807 static bool
2808 is_figure (double val)
2809 {
2811 
2812  return go && go.isa ("figure");
2813 }
2814 
2815 static void
2817 {
2820 }
2821 
2822 static void
2824 {
2826 
2827  if (go)
2828  go.initialize ();
2829 }
2830 
2831 // ---------------------------------------------------------------------
2832 
2833 void
2835 {
2837 
2838  update (go, id);
2839 }
2840 
2841 bool
2843 {
2845 
2846  return initialize (go);
2847 }
2848 
2849 void
2851 {
2853 
2854  finalize (go);
2855 }
2856 
2857 static void
2859  property_list::pval_map_type factory_pval)
2860 {
2862 
2863  // Replace factory defaults by user defined ones
2864  std::string go_name = go.get_properties ().graphics_object_name ();
2866  go.build_user_defaults_map (pval, go_name);
2867 
2868  for (property_list::pval_map_const_iterator p = pval.begin ();
2869  p != pval.end (); p++)
2870  {
2871  factory_pval[p->first] = p->second;
2872  }
2873 
2874  // Reset defaults
2875  for (property_list::pval_map_const_iterator it = factory_pval.begin ();
2876  it != factory_pval.end (); it++)
2877  {
2878  std::string pname = it->first;
2879 
2880  // Don't reset internal properties and handle_properties
2881  if (! go.has_readonly_property (pname)
2882  && pname.find ("__") != 0 && pname.find ("current") != 0
2883  && pname != "uicontextmenu" && pname != "parent")
2884  {
2885  // Store *mode prop/val in order to set them last
2886  if (pname.find ("mode") == (pname.length () - 4))
2887  pval[pname] = it->second;
2888  else
2889  go.set (pname, it->second);
2890  }
2891  }
2892 
2893  // set *mode properties
2894  for (property_list::pval_map_const_iterator it = pval.begin ();
2895  it != pval.end (); it++)
2896  go.set (it->first, it->second);
2897 }
2898 
2899 // ---------------------------------------------------------------------
2900 
2901 void
2904 {
2905  std::string go_name = graphics_object_name ();
2906 
2907  property_list::plist_map_const_iterator plist = defaults.find (go_name);
2908 
2909  if (plist != defaults.end ())
2910  {
2911  const property_list::pval_map_type pval_map = plist->second;
2912 
2913  for (const auto& prop_val_p : pval_map)
2914  {
2915  std::string pname = prop_val_p.first;
2916 
2917  try
2918  {
2919  bgo.set (pname, prop_val_p.second);
2920  }
2921  catch (octave::execution_exception& e)
2922  {
2923  error (e, "error setting default property %s", pname.c_str ());
2924  }
2925  }
2926  }
2927 }
2928 
2929 /*
2930 ## test defaults are set in the order they were stored
2931 %!test
2932 %! set(0, "defaultfigureunits", "normalized");
2933 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
2934 %! hf = figure ("visible", "off");
2935 %! tol = 20 * eps;
2936 %! unwind_protect
2937 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
2938 %! unwind_protect_cleanup
2939 %! close (hf);
2940 %! set(0, "defaultfigureunits", "remove");
2941 %! set(0, "defaultfigureposition", "remove");
2942 %! end_unwind_protect
2943 */
2944 
2947 {
2948  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2949  all_props.find (pname);
2950 
2951  if (it == all_props.end ())
2952  error ("get: unknown property \"%s\"", pname.c_str ());
2953 
2954  return it->second.get ();
2955 }
2956 
2959 {
2961 
2962  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
2963  it = all_props.begin (); it != all_props.end (); ++it)
2964  if (all || ! it->second.is_hidden ())
2965  m.assign (it->second.get_name (), it->second.get ());
2966 
2967  return m;
2968 }
2969 
2970 std::set<std::string>
2972 {
2973  return dynamic_properties;
2974 }
2975 
2976 bool
2978 {
2979  const std::set<std::string>& dynprops = dynamic_property_names ();
2980 
2981  if (dynprops.find (pname) != dynprops.end ())
2982  return true;
2983  else
2984  return all_props.find (pname) != all_props.end ();
2985 }
2986 
2987 void
2989  const octave_value& val)
2990 {
2991  std::map<caseless_str, property, cmp_caseless_str>::iterator it =
2992  all_props.find (pname);
2993 
2994  if (it == all_props.end ())
2995  error ("set: unknown property \"%s\"", pname.c_str ());
2996 
2997  it->second.set (val);
2998 
2999  dynamic_properties.insert (pname);
3000 
3001  mark_modified ();
3002 }
3003 
3004 property
3006 {
3007  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
3008  all_props.find (pname);
3009 
3010  if (it == all_props.end ())
3011  error ("get_property: unknown property \"%s\"", pname.c_str ());
3012 
3013  return it->second;
3014 }
3015 
3016 void
3018 {
3019  double hp = val.xdouble_value ("set: parent must be a graphics handle");
3020 
3022 
3023  if (hp == __myhandle__)
3024  error ("set: can not set object parent to be object itself");
3025 
3026  new_parent = gh_manager::lookup (hp);
3027 
3028  if (! new_parent.ok ())
3029  error ("set: invalid graphics handle (= %g) for parent", hp);
3030 
3031  // Remove child from current parent
3032  graphics_object old_parent_go;
3033  old_parent_go = gh_manager::get_object (get_parent ());
3034 
3035  if (old_parent_go.get_handle () != hp)
3036  old_parent_go.remove_child (__myhandle__);
3037  else
3038  return; // Do nothing more
3039 
3040  // Check new parent's parent is not this child to avoid recursion
3041  graphics_object new_parent_go;
3042  new_parent_go = gh_manager::get_object (new_parent);
3043  if (new_parent_go.get_parent () == __myhandle__)
3044  {
3045  // new parent's parent gets child's original parent
3046  new_parent_go.get_properties ().set_parent (get_parent ().as_octave_value ());
3047  }
3048 
3049  // Set parent property to new_parent and do adoption
3050  parent = new_parent.as_octave_value ();
3051  ::adopt (parent.handle_value (), __myhandle__);
3052 }
3053 
3054 /*
3055 %!test
3056 %! hf = figure ("visible", "off");
3057 %! unwind_protect
3058 %! hax = gca ();
3059 %! set (hax, "parent", gcf ());
3060 %! assert (gca (), hax);
3061 %! unwind_protect_cleanup
3062 %! close (hf);
3063 %! end_unwind_protect
3064 */
3065 
3066 void
3068 {
3069  // Mark existing object as modified
3070  __modified__ = "on";
3071  // Attempt to mark parent object as modified if it exists
3073  if (parent_go)
3074  parent_go.mark_modified ();
3075 }
3076 
3077 void
3079 {
3081 
3082  if (parent_go)
3083  parent_go.override_defaults (obj);
3084 }
3085 
3086 void
3088 {
3089  graphics_object go = gh_manager::get_object (__myhandle__);
3090 
3091  if (go)
3092  go.update_axis_limits (axis_type);
3093 }
3094 
3095 void
3097  const graphics_handle& h) const
3098 {
3099  graphics_object go = gh_manager::get_object (__myhandle__);
3100 
3101  if (go)
3102  go.update_axis_limits (axis_type, h);
3103 }
3104 
3105 void
3107 {
3108  if (uicontextmenu.get ().is_empty ())
3109  return;
3110 
3112  if (go && go.isa ("uicontextmenu"))
3113  {
3114  uicontextmenu::properties& props =
3115  reinterpret_cast<uicontextmenu::properties&> (go.get_properties ());
3116  props.add_dependent_obj (__myhandle__);
3117  }
3118 }
3119 
3120 bool
3122 {
3123  return (handlevisibility.is ("on")
3124  || (executing_callback && ! handlevisibility.is ("off")));
3125 }
3126 
3129 {
3131 
3132  if (go)
3133  return go.get_toolkit ();
3134  else
3135  return graphics_toolkit ();
3136 }
3137 
3138 void
3140 {
3141  Matrix kids = get_children ();
3142 
3143  for (int i = 0; i < kids.numel (); i++)
3144  {
3146 
3147  if (go.valid_object ())
3149  }
3150 }
3151 
3152 void
3154 {
3156 
3157  if (parent_go.valid_object ())
3158  parent_go.get_properties ().update_autopos (elem_type);
3159 }
3160 
3161 void
3163  const octave_value& val,
3165 {
3166  property p = get_property (pname);
3167 
3168  if (p.ok ())
3169  p.add_listener (val, mode);
3170 }
3171 
3172 void
3174  const octave_value& val,
3176 {
3177  property p = get_property (pname);
3178 
3179  if (p.ok ())
3180  p.delete_listener (val, mode);
3181 }
3182 
3183 // ---------------------------------------------------------------------
3184 
3185 void
3187 {
3188  if (! valid_object ())
3189  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3190 
3192 
3193  if (parent_go)
3194  parent_go.update_axis_limits (axis_type);
3195 }
3196 
3197 void
3199  const graphics_handle& h)
3200 {
3201  if (! valid_object ())
3202  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3203 
3205 
3206  if (parent_go)
3207  parent_go.update_axis_limits (axis_type, h);
3208 }
3209 
3210 void
3212 {
3213  octave_map m = get (true).map_value ();
3214 
3215  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
3216  {
3217  // FIXME: there has to be a better way. I think we want to
3218  // ask whether it is OK to delete the listener for the given
3219  // property. How can we know in advance that it will be OK?
3220 
3222 
3224  frame.protect_var (Vdebug_on_error);
3226 
3227  discard_error_messages = true;
3228  Vdebug_on_error = false;
3229  Vdebug_on_warning = false;
3230 
3231  try
3232  {
3233  property p = get_properties ().get_property (pa->first);
3234 
3235  if (p.ok ())
3236  p.delete_listener ();
3237  }
3238  catch (const octave::execution_exception&)
3239  {
3241  }
3242  }
3243 }
3244 
3245 void
3247 {
3248  property_list local_defaults = get_defaults_list ();
3249  const auto it = local_defaults.find (go_name);
3250 
3251  if (it != local_defaults.end ())
3252  {
3253  property_list::pval_map_type pval = it->second;
3254  for (const auto& prop_val_p : pval)
3255  {
3256  std::string pname = prop_val_p.first;
3257  if (def.find (pname) == def.end ())
3258  def[pname] = prop_val_p.second;
3259  }
3260  }
3261 
3263 
3264  if (parent_go)
3265  parent_go.build_user_defaults_map (def, go_name);
3266 }
3267 
3268 void
3270 {
3271  if (valid_object ())
3272  {
3273  property_list::pval_map_type factory_pval =
3275  .find (type ())->second;
3276 
3277  // save warning state of "Octave:deprecated-property"
3278  int old_dep_prop = warning_enabled ("Octave:deprecated-property");
3279  disable_warning ("Octave:deprecated-property");
3280 
3281  xreset_default_properties (get_handle (), factory_pval);
3282 
3283  // re-enable warning state of "Octave:deprecated-property"
3284  if (old_dep_prop == 1)
3285  set_warning_state ("Octave:deprecated-property", "on");
3286  else if (old_dep_prop == 2)
3287  set_warning_state ("Octave:deprecated-property", "error");
3288 
3289  }
3290 }
3291 
3294 {
3296 
3297  if (! valid_object ())
3298  error ("base_graphics_object::values_as_string: invalid graphics object");
3299 
3300  octave_map m = get ().map_value ();
3302 
3303  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
3304  {
3305  if (pa->first != "children" && ! go.has_readonly_property (pa->first))
3306  {
3307  property p = get_properties ().get_property (pa->first);
3308 
3309  if (p.ok () && ! p.is_hidden ())
3310  {
3311  retval += "\n\t" + std::string (pa->first) + ": ";
3312  if (p.is_radio ())
3313  retval += p.values_as_string ();
3314  }
3315  }
3316  }
3317 
3318  if (! retval.empty ())
3319  retval += "\n";
3320 
3321  return retval;
3322 }
3323 
3326 {
3328 
3329  if (! valid_object ())
3330  error ("base_graphics_object::value_as_string: invalid graphics object");
3331 
3333 
3334  if (prop != "children" && ! go.has_readonly_property (prop))
3335  {
3336  property p = get_properties ().get_property (prop);
3337 
3338  if (p.ok () && ! p.is_hidden ())
3339  {
3340  if (p.is_radio ())
3341  retval += p.values_as_string ();
3342  }
3343  }
3344 
3345  if (! retval.empty ())
3346  retval += "\n";
3347 
3348  return retval;
3349 }
3350 
3353 {
3355 
3356  if (! valid_object ())
3357  error ("base_graphics_object::values_as_struct: invalid graphics object");
3358 
3359  octave_scalar_map m = get ().scalar_map_value ();
3361 
3363  pa != m.end (); pa++)
3364  {
3365  if (pa->first != "children"
3366  && ! go.has_readonly_property (pa->first))
3367  {
3368  property p = get_properties ().get_property (pa->first);
3369 
3370  if (p.ok () && ! p.is_hidden ())
3371  {
3372  if (p.is_radio ())
3373  retval.assign (p.get_name (), p.values_as_cell ());
3374  else
3375  retval.assign (p.get_name (), Cell ());
3376  }
3377  }
3378  }
3379 
3380  return retval;
3381 }
3382 
3383 /*
3384 %!test
3385 %! hfig = figure ("visible", "off");
3386 %! unwind_protect
3387 %! hax = axes ();
3388 %! ret = set (hax, "tightinset");
3389 %! assert (isempty (ret));
3390 %! ret = set (hax, "type");
3391 %! assert (isempty (ret));
3392 %! ret = set (hfig, "tag");
3393 %! assert (isempty (ret));
3394 %! ret = set (0, "commandwindowsize");
3395 %! assert (isempty (ret));
3396 %! ret = set (0);
3397 %! assert (! isfield (ret, "commandwindowsize"));
3398 %! unwind_protect_cleanup
3399 %! close (hfig);
3400 %! end_unwind_protect
3401 */
3402 
3405 {
3406  if (valid_object ())
3407  {
3408  if (isa (obj_type))
3409  return *this;
3410  else
3411  return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
3412  }
3413  else
3414  return graphics_object ();
3415 }
3416 
3417 // ---------------------------------------------------------------------
3418 
3419 #include "graphics-props.cc"
3420 
3421 // ---------------------------------------------------------------------
3422 
3423 void
3425 {
3426  graphics_handle val (v);
3427 
3428  if (octave::math::isnan (val.value ()))
3429  {
3430  if (! cbo_stack.empty ())
3431  {
3432  val = cbo_stack.front ();
3433 
3434  cbo_stack.pop_front ();
3435  }
3436 
3437  callbackobject = val;
3438  }
3439  else if (is_handle (val))
3440  {
3441  if (get_callbackobject ().ok ())
3442  cbo_stack.push_front (get_callbackobject ());
3443 
3444  callbackobject = val;
3445  }
3446  else
3447  err_set_invalid ("callbackobject");
3448 }
3449 
3450 void
3452 {
3453  graphics_handle val (v);
3454 
3455  if (octave::math::isnan (val.value ()) || is_handle (val))
3456  {
3457  currentfigure = val;
3458 
3459  if (val.ok ())
3461  }
3462  else
3463  err_set_invalid ("currentfigure");
3464 }
3465 
3466 void
3468 {
3469  if (integerhandle.set (val, true))
3470  {
3471  bool int_fig_handle = integerhandle.is_on ();
3472 
3473  graphics_object this_go = gh_manager::get_object (__myhandle__);
3474 
3475  graphics_handle old_myhandle = __myhandle__;
3476 
3477  __myhandle__ = gh_manager::get_handle (int_fig_handle);
3478 
3479  gh_manager::renumber_figure (old_myhandle, __myhandle__);
3480 
3482 
3483  base_properties& props = parent_go.get_properties ();
3484 
3485  props.renumber_child (old_myhandle, __myhandle__);
3486 
3487  Matrix kids = get_children ();
3488 
3489  for (octave_idx_type i = 0; i < kids.numel (); i++)
3490  {
3492 
3493  kid.get_properties ().renumber_parent (__myhandle__);
3494  }
3495 
3497 
3498  if (__myhandle__ == cf)
3499  xset (0, "currentfigure", __myhandle__.value ());
3500 
3501  this_go.update (integerhandle.get_id ());
3502 
3503  mark_modified ();
3504  }
3505 }
3506 
3507 // FIXME: This should update monitorpositions and pointerlocation, but as these
3508 // properties aren't yet used, it doesn't matter that they aren't set either.
3509 void
3511 {
3512  std::string xunits = get_units ();
3513 
3514  Matrix scrn_sz = default_screensize ();
3515 
3516  double dpi = get_screenpixelsperinch ();
3517 
3518  if (xunits == "inches")
3519  {
3520  scrn_sz(0) = 0;
3521  scrn_sz(1) = 0;
3522  scrn_sz(2) /= dpi;
3523  scrn_sz(3) /= dpi;
3524  }
3525  else if (xunits == "centimeters")
3526  {
3527  scrn_sz(0) = 0;
3528  scrn_sz(1) = 0;
3529  scrn_sz(2) *= 2.54 / dpi;
3530  scrn_sz(3) *= 2.54 / dpi;
3531  }
3532  else if (xunits == "normalized")
3533  {
3534  scrn_sz = Matrix (1, 4, 1.0);
3535  scrn_sz(0) = 0;
3536  scrn_sz(1) = 0;
3537  }
3538  else if (xunits == "points")
3539  {
3540  scrn_sz(0) = 0;
3541  scrn_sz(1) = 0;
3542  scrn_sz(2) *= 72 / dpi;
3543  scrn_sz(3) *= 72 / dpi;
3544  }
3545 
3546  set_screensize (scrn_sz);
3547 }
3548 
3549 Matrix
3551 {
3552  Matrix screen_size = screen_size_pixels ();
3553  Matrix pos = Matrix (1, 4, 0.0);
3554 
3555  pos(2) = screen_size(0);
3556  pos(3) = screen_size(1);
3557 
3558  return pos;
3559 }
3560 
3561 /*
3562 %!test
3563 %! old_units = get (0, "units");
3564 %! unwind_protect
3565 %! set (0, "units", "pixels");
3566 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3567 %! dpi = get (0, "screenpixelsperinch");
3568 %! set (0, "units", "inches");
3569 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3570 %! set (0, "units", "centimeters");
3571 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3572 %! set (0, "units", "points");
3573 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3574 %! set (0, "units", "normalized");
3575 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3576 %! set (0, "units", "pixels");
3577 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3578 %! unwind_protect_cleanup
3579 %! set (0, "units", old_units);
3580 %! end_unwind_protect
3581 */
3582 
3583 void
3585 {
3587 
3589 
3590  xset (0, "currentfigure", cf.value ());
3591 
3593 }
3594 
3597 
3598 void
3600 {
3601  // empty list of local defaults
3603 
3606 }
3607 
3608 // ---------------------------------------------------------------------
3609 
3610 void
3612 {
3613  graphics_handle hax (val);
3614 
3615  if (octave::math::isnan (hax.value ()) || is_handle (hax))
3616  currentaxes = hax;
3617  else
3618  err_set_invalid ("currentaxes");
3619 }
3620 
3621 void
3623 {
3625 
3626  if (h == currentaxes.handle_value ())
3627  {
3628  graphics_handle new_currentaxes;
3629 
3630  Matrix kids = get_children ();
3631 
3632  for (octave_idx_type i = 0; i < kids.numel (); i++)
3633  {
3634  graphics_handle kid = kids(i);
3635 
3637 
3638  if (go.isa ("axes"))
3639  {
3640  new_currentaxes = kid;
3641  break;
3642  }
3643  }
3644 
3645  currentaxes = new_currentaxes;
3646  }
3647 }
3648 
3649 void
3651 {
3653 
3654  if (! get_currentaxes ().ok ())
3655  {
3657 
3658  if (go.type () == "axes")
3659  set_currentaxes (h.as_octave_value ());
3660  }
3661 }
3662 
3663 /*
3664 %!test
3665 %! hf1 = figure ("visible", "off");
3666 %! ax1 = subplot (1,2,1);
3667 %! ax2 = subplot (1,2,2);
3668 %! hf2 = figure ("visible", "off");
3669 %! unwind_protect
3670 %! set (ax2, "parent", hf2);
3671 %! assert (get (hf2, "currentaxes"), ax2);
3672 %! assert (get (hf1, "currentaxes"), ax1);
3673 %! set (ax1, "parent", hf2);
3674 %! assert (get (hf2, "currentaxes"), ax2);
3675 %! unwind_protect_cleanup
3676 %! close (hf1);
3677 %! close (hf2);
3678 %! end_unwind_protect
3679 */
3680 
3681 void
3683 {
3684  std::string sval = val.string_value ();
3685 
3686  if (sval == "on")
3687  xset (0, "currentfigure", __myhandle__.value ());
3688 
3689  visible = val;
3690 }
3691 
3692 Matrix
3693 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3694 {
3695  Matrix screen_size = screen_size_pixels ();
3696  Matrix pos = (internal ?
3697  get_position ().matrix_value () :
3698  get_outerposition ().matrix_value ());
3699 
3700  pos = convert_position (pos, get_units (), "pixels", screen_size);
3701 
3702  pos(0)--;
3703  pos(1)--;
3704  pos(1) = screen_size(1) - pos(1) - pos(3);
3705 
3706  return pos;
3707 }
3708 
3709 void
3711  bool do_notify_toolkit)
3712 {
3713  Matrix screen_size = screen_size_pixels ();
3714  Matrix pos = bb;
3715 
3716  pos(1) = screen_size(1) - pos(1) - pos(3);
3717  pos(1)++;
3718  pos(0)++;
3719  pos = convert_position (pos, "pixels", get_units (), screen_size);
3720 
3721  if (internal)
3722  set_position (pos, do_notify_toolkit);
3723  else
3724  set_outerposition (pos, do_notify_toolkit);
3725 }
3726 
3727 Matrix
3729 {
3730  Matrix bb = get_boundingbox (true);
3731  Matrix pos (1, 2, 0.0);
3732 
3733  pos(0) = x;
3734  pos(1) = y;
3735 
3736  pos(1) = bb(3) - pos(1);
3737  pos(0)++;
3738  pos = convert_position (pos, "pixels", get_units (),
3739  bb.extract_n (0, 2, 1, 2));
3740 
3741  return pos;
3742 }
3743 
3744 Matrix
3746 {
3747  Matrix bb = get_boundingbox (true);
3748  Matrix pos (1, 2, 0.0);
3749 
3750  pos(0) = x;
3751  pos(1) = y;
3752 
3753  pos = convert_position (pos, get_units (), "pixels",
3754  bb.extract_n (0, 2, 1, 2));
3755  pos(0)--;
3756  pos(1) = bb(3) - pos(1);
3757 
3758  return pos;
3759 }
3760 
3761 void
3763  bool do_notify_toolkit)
3764 {
3765  Matrix old_bb, new_bb;
3766  bool modified = false;
3767 
3768  old_bb = get_boundingbox (true);
3769  modified = position.set (v, false, do_notify_toolkit);
3770  new_bb = get_boundingbox (true);
3771 
3772  if (old_bb != new_bb)
3773  {
3774  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
3775  {
3776  execute_resizefcn ();
3777  update_boundingbox ();
3778  }
3779  }
3780 
3781  if (modified)
3782  {
3783  position.run_listeners (POSTSET);
3784  mark_modified ();
3785  }
3786 
3787  if (paperpositionmode.is ("auto"))
3788  paperposition.set (get_auto_paperposition ());
3789 }
3790 
3791 void
3793  bool do_notify_toolkit)
3794 {
3795  if (outerposition.set (v, true, do_notify_toolkit))
3796  mark_modified ();
3797 }
3798 
3799 void
3801 {
3802  caseless_str punits = val.string_value ();
3803  caseless_str ptype = get_papertype ();
3804 
3805  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
3806  error ("set: can't set paperunits to normalized when papertype is custom");
3807 
3808  caseless_str old_paperunits = get_paperunits ();
3809  if (paperunits.set (val, true))
3810  {
3811  update_paperunits (old_paperunits);
3812  mark_modified ();
3813  }
3814 }
3815 
3816 void
3818 {
3819  caseless_str ptype = val.string_value ();
3820  caseless_str punits = get_paperunits ();
3821 
3822  if (punits.compare ("normalized") && ptype.compare ("<custom>"))
3823  error ("set: can't set paperunits to normalized when papertype is custom");
3824 
3825  if (papertype.set (val, true))
3826  {
3827  update_papertype ();
3828  mark_modified ();
3829  }
3830 }
3831 
3832 static Matrix
3833 papersize_from_type (const caseless_str punits, const caseless_str ptype)
3834 {
3835  Matrix retval (1, 2, 1.0);
3836 
3837  if (! punits.compare ("normalized"))
3838  {
3839  double in2units;
3840  double mm2units;
3841 
3842  if (punits.compare ("inches"))
3843  {
3844  in2units = 1.0;
3845  mm2units = 1 / 25.4;
3846  }
3847  else if (punits.compare ("centimeters"))
3848  {
3849  in2units = 2.54;
3850  mm2units = 1 / 10.0;
3851  }
3852  else // points
3853  {
3854  in2units = 72.0;
3855  mm2units = 72.0 / 25.4;
3856  }
3857 
3858  if (ptype.compare ("usletter"))
3859  {
3860  retval(0) = 8.5 * in2units;
3861  retval(1) = 11.0 * in2units;
3862  }
3863  else if (ptype.compare ("uslegal"))
3864  {
3865  retval(0) = 8.5 * in2units;
3866  retval(1) = 14.0 * in2units;
3867  }
3868  else if (ptype.compare ("tabloid"))
3869  {
3870  retval(0) = 11.0 * in2units;
3871  retval(1) = 17.0 * in2units;
3872  }
3873  else if (ptype.compare ("a0"))
3874  {
3875  retval(0) = 841.0 * mm2units;
3876  retval(1) = 1189.0 * mm2units;
3877  }
3878  else if (ptype.compare ("a1"))
3879  {
3880  retval(0) = 594.0 * mm2units;
3881  retval(1) = 841.0 * mm2units;
3882  }
3883  else if (ptype.compare ("a2"))
3884  {
3885  retval(0) = 420.0 * mm2units;
3886  retval(1) = 594.0 * mm2units;
3887  }
3888  else if (ptype.compare ("a3"))
3889  {
3890  retval(0) = 297.0 * mm2units;
3891  retval(1) = 420.0 * mm2units;
3892  }
3893  else if (ptype.compare ("a4"))
3894  {
3895  retval(0) = 210.0 * mm2units;
3896  retval(1) = 297.0 * mm2units;
3897  }
3898  else if (ptype.compare ("a5"))
3899  {
3900  retval(0) = 148.0 * mm2units;
3901  retval(1) = 210.0 * mm2units;
3902  }
3903  else if (ptype.compare ("b0"))
3904  {
3905  retval(0) = 1029.0 * mm2units;
3906  retval(1) = 1456.0 * mm2units;
3907  }
3908  else if (ptype.compare ("b1"))
3909  {
3910  retval(0) = 728.0 * mm2units;
3911  retval(1) = 1028.0 * mm2units;
3912  }
3913  else if (ptype.compare ("b2"))
3914  {
3915  retval(0) = 514.0 * mm2units;
3916  retval(1) = 728.0 * mm2units;
3917  }
3918  else if (ptype.compare ("b3"))
3919  {
3920  retval(0) = 364.0 * mm2units;
3921  retval(1) = 514.0 * mm2units;
3922  }
3923  else if (ptype.compare ("b4"))
3924  {
3925  retval(0) = 257.0 * mm2units;
3926  retval(1) = 364.0 * mm2units;
3927  }
3928  else if (ptype.compare ("b5"))
3929  {
3930  retval(0) = 182.0 * mm2units;
3931  retval(1) = 257.0 * mm2units;
3932  }
3933  else if (ptype.compare ("arch-a"))
3934  {
3935  retval(0) = 9.0 * in2units;
3936  retval(1) = 12.0 * in2units;
3937  }
3938  else if (ptype.compare ("arch-b"))
3939  {
3940  retval(0) = 12.0 * in2units;
3941  retval(1) = 18.0 * in2units;
3942  }
3943  else if (ptype.compare ("arch-c"))
3944  {
3945  retval(0) = 18.0 * in2units;
3946  retval(1) = 24.0 * in2units;
3947  }
3948  else if (ptype.compare ("arch-d"))
3949  {
3950  retval(0) = 24.0 * in2units;
3951  retval(1) = 36.0 * in2units;
3952  }
3953  else if (ptype.compare ("arch-e"))
3954  {
3955  retval(0) = 36.0 * in2units;
3956  retval(1) = 48.0 * in2units;
3957  }
3958  else if (ptype.compare ("a"))
3959  {
3960  retval(0) = 8.5 * in2units;
3961  retval(1) = 11.0 * in2units;
3962  }
3963  else if (ptype.compare ("b"))
3964  {
3965  retval(0) = 11.0 * in2units;
3966  retval(1) = 17.0 * in2units;
3967  }
3968  else if (ptype.compare ("c"))
3969  {
3970  retval(0) = 17.0 * in2units;
3971  retval(1) = 22.0 * in2units;
3972  }
3973  else if (ptype.compare ("d"))
3974  {
3975  retval(0) = 22.0 * in2units;
3976  retval(1) = 34.0 * in2units;
3977  }
3978  else if (ptype.compare ("e"))
3979  {
3980  retval(0) = 34.0 * in2units;
3981  retval(1) = 43.0 * in2units;
3982  }
3983  }
3984 
3985  return retval;
3986 }
3987 
3988 Matrix
3990 {
3991  Matrix pos = get_position ().matrix_value ();
3992  Matrix sz;
3993 
3994  caseless_str funits = get_units ();
3995  caseless_str punits = get_paperunits ();
3996 
3997  // Convert position from figure units to paperunits
3998  if (funits == "normalized" || punits == "normalized")
3999  {
4000  sz = screen_size_pixels ();
4001  pos = convert_position (pos, funits, "inches", sz);
4002 
4003  if (punits == "normalized")
4004  sz = papersize_from_type ("points", get_papertype ());
4005 
4006  pos = convert_position (pos, "inches", punits, sz);
4007  }
4008  else
4009  pos = convert_position (pos, funits, punits, sz);
4010 
4011  // Center the figure on the page
4012  sz = get_papersize ().matrix_value ();
4013 
4014  pos(0) = sz(0)/2 - pos(2)/2;
4015  pos(1) = sz(1)/2 - pos(3)/2;
4016 
4017  return pos;
4018 }
4019 
4020 /*
4021 %!test
4022 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4023 %! in_pos = [0 0 4 5];
4024 %! tol = 20 * eps ();
4025 %! unwind_protect
4026 %! ## paperpositionmode "auto" converts figure size to paper units
4027 %! set (hf, "units", "inches");
4028 %! set (hf, "position", in_pos);
4029 %! set (hf, "paperunits", "centimeters");
4030 %! psz = get (hf, "papersize");
4031 %! fsz = in_pos(3:4) * 2.54;
4032 %! pos = [(psz/2 .- fsz/2) fsz];
4033 %! set (hf, "paperpositionmode", "auto");
4034 %! assert (get (hf, "paperposition"), pos, tol);
4035 %! unwind_protect_cleanup
4036 %! close (hf);
4037 %! end_unwind_protect
4038 
4039 %!test
4040 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4041 %! in_pos = [0 0 4 5];
4042 %! tol = 20 * eps ();
4043 %! unwind_protect
4044 %! ## likewise with normalized units
4045 %! set (hf, "units", "inches");
4046 %! set (hf, "position", in_pos);
4047 %! psz = get (hf, "papersize");
4048 %! set (hf, "paperunits", "normalized");
4049 %! fsz = in_pos(3:4) ./ psz;
4050 %! pos = [([0.5 0.5] .- fsz/2) fsz];
4051 %! assert (get (hf, "paperposition"), pos, tol);
4052 %! unwind_protect_cleanup
4053 %! close (hf);
4054 %! end_unwind_protect
4055 
4056 %!test
4057 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4058 %! in_pos = [0 0 4 5];
4059 %! tol = 20 * eps ();
4060 %! unwind_protect
4061 %! ## changing papertype updates paperposition
4062 %! set (hf, "units", "inches");
4063 %! set (hf, "position", in_pos);
4064 %! set (hf, "papertype", "a4");
4065 %! psz = get (hf, "papersize");
4066 %! fsz = in_pos(3:4);
4067 %! pos = [(psz/2 .- fsz/2) fsz];
4068 %! assert (get (hf, "paperposition"), pos, tol);
4069 %! unwind_protect_cleanup
4070 %! close (hf);
4071 %! end_unwind_protect
4072 
4073 %!test
4074 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4075 %! in_pos = [0 0 4 5];
4076 %! tol = 20 * eps ();
4077 %! unwind_protect
4078 %! ## lanscape updates paperposition
4079 %! set (hf, "units", "inches");
4080 %! set (hf, "position", in_pos);
4081 %! set (hf, "paperorientation", "landscape");
4082 %! psz = get (hf, "papersize");
4083 %! fsz = in_pos(3:4);
4084 %! pos = [(psz/2 .- fsz/2) fsz];
4085 %! assert (get (hf, "paperposition"), pos, tol);
4086 %! unwind_protect_cleanup
4087 %! close (hf);
4088 %! end_unwind_protect
4089 
4090 %!test
4091 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4092 %! in_pos = [0 0 4 5];
4093 %! unwind_protect
4094 %! ## back to manual mode
4095 %! set (hf, "paperposition", in_pos * 1.1);
4096 %! assert (get (hf, "paperpositionmode"), "manual");
4097 %! assert (get (hf, "paperposition"), in_pos * 1.1);
4098 %! unwind_protect_cleanup
4099 %! close (hf);
4100 %! end_unwind_protect
4101 */
4102 
4103 void
4105 {
4106  Matrix pos = get_paperposition ().matrix_value ();
4107  Matrix sz = get_papersize ().matrix_value ();
4108 
4109  pos(0) /= sz(0);
4110  pos(1) /= sz(1);
4111  pos(2) /= sz(0);
4112  pos(3) /= sz(1);
4113 
4114  std::string porient = get_paperorientation ();
4115  caseless_str punits = get_paperunits ();
4116  caseless_str ptype = get_papertype ();
4117 
4118  if (ptype.compare ("<custom>"))
4119  {
4120  if (old_paperunits.compare ("centimeters"))
4121  {
4122  sz(0) /= 2.54;
4123  sz(1) /= 2.54;
4124  }
4125  else if (old_paperunits.compare ("points"))
4126  {
4127  sz(0) /= 72.0;
4128  sz(1) /= 72.0;
4129  }
4130 
4131  if (punits.compare ("centimeters"))
4132  {
4133  sz(0) *= 2.54;
4134  sz(1) *= 2.54;
4135  }
4136  else if (punits.compare ("points"))
4137  {
4138  sz(0) *= 72.0;
4139  sz(1) *= 72.0;
4140  }
4141  }
4142  else
4143  {
4144  sz = papersize_from_type (punits, ptype);
4145  if (porient == "landscape")
4146  std::swap (sz(0), sz(1));
4147  }
4148 
4149  pos(0) *= sz(0);
4150  pos(1) *= sz(1);
4151  pos(2) *= sz(0);
4152  pos(3) *= sz(1);
4153 
4154  papersize.set (octave_value (sz));
4155  paperposition.set (octave_value (pos));
4156 }
4157 
4158 void
4160 {
4161  std::string typ = get_papertype ();
4162  if (typ != "<custom>")
4163  {
4164  Matrix sz = papersize_from_type (get_paperunits (), typ);
4165  if (get_paperorientation () == "landscape")
4166  std::swap (sz(0), sz(1));
4167  // Call papersize.set rather than set_papersize to avoid loops
4168  // between update_papersize and update_papertype.
4169  papersize.set (octave_value (sz));
4170  }
4171 
4172  if (paperpositionmode.is ("auto"))
4173  paperposition.set (get_auto_paperposition ());
4174 }
4175 
4176 void
4178 {
4179  Matrix sz = get_papersize ().matrix_value ();
4180  if (sz(0) > sz(1))
4181  {
4182  std::swap (sz(0), sz(1));
4183  papersize.set (octave_value (sz));
4184  paperorientation.set (octave_value ("landscape"));
4185  }
4186  else
4187  {
4188  paperorientation.set ("portrait");
4189  }
4190 
4191  std::string punits = get_paperunits ();
4192  if (punits == "centimeters")
4193  {
4194  sz(0) /= 2.54;
4195  sz(1) /= 2.54;
4196  }
4197  else if (punits == "points")
4198  {
4199  sz(0) /= 72.0;
4200  sz(1) /= 72.0;
4201  }
4202  if (punits == "normalized")
4203  {
4204  if (get_papertype () == "<custom>")
4205  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4206  }
4207  else
4208  {
4209  // FIXME: The papersizes info is also in papersize_from_type().
4210  // Both should be rewritten to avoid the duplication.
4211  // Don't Repeat Yourself (DRY) principle.
4212  std::string ptype = "<custom>";
4213  const double mm2in = 1.0 / 25.4;
4214  const double tol = 0.01;
4215 
4216  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4217  ptype = "usletter";
4218  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4219  ptype = "uslegal";
4220  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4221  ptype = "tabloid";
4222  else if (std::abs (sz(0) - 841.0 * mm2in)
4223  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4224  ptype = "a0";
4225  else if (std::abs (sz(0) - 594.0 * mm2in)
4226  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4227  ptype = "a1";
4228  else if (std::abs (sz(0) - 420.0 * mm2in)
4229  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4230  ptype = "a2";
4231  else if (std::abs (sz(0) - 297.0 * mm2in)
4232  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4233  ptype = "a3";
4234  else if (std::abs (sz(0) - 210.0 * mm2in)
4235  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4236  ptype = "a4";
4237  else if (std::abs (sz(0) - 148.0 * mm2in)
4238  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4239  ptype = "a5";
4240  else if (std::abs (sz(0) - 1029.0 * mm2in)
4241  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4242  ptype = "b0";
4243  else if (std::abs (sz(0) - 728.0 * mm2in)
4244  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4245  ptype = "b1";
4246  else if (std::abs (sz(0) - 514.0 * mm2in)
4247  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4248  ptype = "b2";
4249  else if (std::abs (sz(0) - 364.0 * mm2in)
4250  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4251  ptype = "b3";
4252  else if (std::abs (sz(0) - 257.0 * mm2in)
4253  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4254  ptype = "b4";
4255  else if (std::abs (sz(0) - 182.0 * mm2in)
4256  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4257  ptype = "b5";
4258  else if (std::abs (sz(0) - 9.0)
4259  + std::abs (sz(1) - 12.0) < tol)
4260  ptype = "arch-a";
4261  else if (std::abs (sz(0) - 12.0)
4262  + std::abs (sz(1) - 18.0) < tol)
4263  ptype = "arch-b";
4264  else if (std::abs (sz(0) - 18.0)
4265  + std::abs (sz(1) - 24.0) < tol)
4266  ptype = "arch-c";
4267  else if (std::abs (sz(0) - 24.0)
4268  + std::abs (sz(1) - 36.0) < tol)
4269  ptype = "arch-d";
4270  else if (std::abs (sz(0) - 36.0)
4271  + std::abs (sz(1) - 48.0) < tol)
4272  ptype = "arch-e";
4273  else if (std::abs (sz(0) - 8.5)
4274  + std::abs (sz(1) - 11.0) < tol)
4275  ptype = "a";
4276  else if (std::abs (sz(0) - 11.0)
4277  + std::abs (sz(1) - 17.0) < tol)
4278  ptype = "b";
4279  else if (std::abs (sz(0) - 17.0)
4280  + std::abs (sz(1) - 22.0) < tol)
4281  ptype = "c";
4282  else if (std::abs (sz(0) - 22.0)
4283  + std::abs (sz(1) - 34.0) < tol)
4284  ptype = "d";
4285  else if (std::abs (sz(0) - 34.0)
4286  + std::abs (sz(1) - 43.0) < tol)
4287  ptype = "e";
4288  // Call papertype.set rather than set_papertype to avoid loops between
4289  // update_papersize and update_papertype
4290  papertype.set (ptype);
4291  }
4292  if (punits == "centimeters")
4293  {
4294  sz(0) *= 2.54;
4295  sz(1) *= 2.54;
4296  }
4297  else if (punits == "points")
4298  {
4299  sz(0) *= 72.0;
4300  sz(1) *= 72.0;
4301  }
4302  if (get_paperorientation () == "landscape")
4303  {
4304  std::swap (sz(0), sz(1));
4305  papersize.set (octave_value (sz));
4306  }
4307 
4308  if (paperpositionmode.is ("auto"))
4309  paperposition.set (get_auto_paperposition ());
4310 }
4311 
4312 /*
4313 %!test
4314 %! hf = figure ("visible", "off");
4315 %! unwind_protect
4316 %! set (hf, "paperunits", "inches");
4317 %! set (hf, "papersize", [5, 4]);
4318 %! set (hf, "paperunits", "points");
4319 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4320 %! papersize = get (hf, "papersize");
4321 %! set (hf, "papersize", papersize + 1);
4322 %! set (hf, "papersize", papersize);
4323 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4324 %! unwind_protect_cleanup
4325 %! close (hf);
4326 %! end_unwind_protect
4327 
4328 %!test
4329 %! hf = figure ("visible", "off");
4330 %! unwind_protect
4331 %! set (hf, "paperunits", "inches");
4332 %! set (hf, "papersize", [5, 4]);
4333 %! set (hf, "paperunits", "centimeters");
4334 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4335 %! papersize = get (hf, "papersize");
4336 %! set (hf, "papersize", papersize + 1);
4337 %! set (hf, "papersize", papersize);
4338 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4339 %! unwind_protect_cleanup
4340 %! close (hf);
4341 %! end_unwind_protect
4342 */
4343 
4344 void
4346 {
4347  std::string porient = get_paperorientation ();
4348  Matrix sz = get_papersize ().matrix_value ();
4349  if ((sz(0) > sz(1) && porient == "portrait")
4350  || (sz(0) < sz(1) && porient == "landscape"))
4351  {
4352  std::swap (sz(0), sz(1));
4353  // Call papertype.set rather than set_papertype to avoid loops
4354  // between update_papersize and update_papertype
4355  papersize.set (octave_value (sz));
4356  }
4357 
4358  if (paperpositionmode.is ("auto"))
4359  paperposition.set (get_auto_paperposition ());
4360 }
4361 
4362 /*
4363 %!test
4364 %! hf = figure ("visible", "off");
4365 %! unwind_protect
4366 %! tol = 100 * eps ();
4367 %! ## UPPER case and MiXed case is part of test and should not be changed.
4368 %! set (hf, "paperorientation", "PORTRAIT");
4369 %! set (hf, "paperunits", "inches");
4370 %! set (hf, "papertype", "USletter");
4371 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4372 %! set (hf, "paperorientation", "Landscape");
4373 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4374 %! set (hf, "paperunits", "centimeters");
4375 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4376 %! set (hf, "papertype", "a4");
4377 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4378 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4379 %! assert (get (hf, "papertype"), "usletter");
4380 %! assert (get (hf, "paperorientation"), "portrait");
4381 %! set (hf, "papersize", [11.0, 8.5]);
4382 %! assert (get (hf, "papertype"), "usletter");
4383 %! assert (get (hf, "paperorientation"), "landscape");
4384 %! unwind_protect_cleanup
4385 %! close (hf);
4386 %! end_unwind_protect
4387 */
4388 
4389 void
4391 {
4392  caseless_str old_units = get_units ();
4393 
4394  if (units.set (val, true))
4395  {
4396  update_units (old_units);
4397  mark_modified ();
4398  }
4399 }
4400 
4401 void
4403 {
4404  position.set (convert_position (get_position ().matrix_value (), old_units,
4405  get_units (), screen_size_pixels ()), false);
4406 }
4407 
4408 /*
4409 %!test
4410 %! hf = figure ("visible", "off");
4411 %! old_units = get (0, "units");
4412 %! unwind_protect
4413 %! set (0, "units", "pixels");
4414 %! rsz = get (0, "screensize");
4415 %! set (gcf (), "units", "pixels");
4416 %! fsz = get (gcf (), "position");
4417 %! set (gcf (), "units", "normalized");
4418 %! pos = get (gcf (), "position");
4419 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
4420 %! unwind_protect_cleanup
4421 %! close (hf);
4422 %! set (0, "units", old_units);
4423 %! end_unwind_protect
4424 */
4425 
4428 {
4429  if (is_numbertitle ())
4430  {
4431  std::ostringstream os;
4432  std::string nm = get_name ();
4433 
4434  os << "Figure " << __myhandle__.value ();
4435  if (! nm.empty ())
4436  os << ": " << get_name ();
4437 
4438  return os.str ();
4439  }
4440  else
4441  return get_name ();
4442 }
4443 
4446 {
4448 
4449  if (retval.is_undefined ())
4450  {
4451  graphics_handle parent_h = get_parent ();
4452  graphics_object parent_go = gh_manager::get_object (parent_h);
4453 
4454  retval = parent_go.get_default (name);
4455  }
4456 
4457  return retval;
4458 }
4459 
4460 void
4462 {
4463  // empty list of local defaults
4465 
4467  plist.erase ("units");
4468  plist.erase ("position");
4469  plist.erase ("outerposition");
4470  plist.erase ("paperunits");
4471  plist.erase ("paperposition");
4472  plist.erase ("windowstyle");
4473 
4475 }
4476 
4477 // ---------------------------------------------------------------------
4478 
4479 void
4481 {
4482  position.add_constraint (dim_vector (1, 4));
4483  outerposition.add_constraint (dim_vector (1, 4));
4484  tightinset.add_constraint (dim_vector (1, 4));
4485  looseinset.add_constraint (dim_vector (1, 4));
4486  colororder.add_constraint (dim_vector (-1, 3));
4487  dataaspectratio.add_constraint (dim_vector (1, 3));
4488  plotboxaspectratio.add_constraint (dim_vector (1, 3));
4489  // FIXME: Should these use dimension vectors? Currently can set 'xlim' to
4490  // any matrix size, but only first two elements are used.
4491  alim.add_constraint (2);
4492  clim.add_constraint (2);
4493  xlim.add_constraint (2);
4494  ylim.add_constraint (2);
4495  zlim.add_constraint (2);
4496  xtick.add_constraint (dim_vector (1, -1));
4497  ytick.add_constraint (dim_vector (1, -1));
4498  ztick.add_constraint (dim_vector (1, -1));
4499  ticklength.add_constraint (dim_vector (1, 2));
4500  Matrix vw (1, 2, 0);
4501  vw(1) = 90;
4502  view = vw;
4503  view.add_constraint (dim_vector (1, 2));
4504  cameraposition.add_constraint (dim_vector (1, 3));
4505  cameratarget.add_constraint (dim_vector (1, 3));
4506  Matrix upv (1, 3, 0.0);
4507  upv(2) = 1.0;
4508  cameraupvector = upv;
4509  cameraupvector.add_constraint (dim_vector (1, 3));
4510  currentpoint.add_constraint (dim_vector (2, 3));
4511  // No constraints for hidden transform properties
4512  update_font ();
4513 
4514  x_zlim.resize (1, 2);
4515 
4516  sx = "linear";
4517  sy = "linear";
4518  sz = "linear";
4519 
4520  calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
4521  calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
4522  calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
4523 
4524  xset (xlabel.handle_value (), "handlevisibility", "off");
4525  xset (ylabel.handle_value (), "handlevisibility", "off");
4526  xset (zlabel.handle_value (), "handlevisibility", "off");
4527  xset (title.handle_value (), "handlevisibility", "off");
4528 
4529  xset (xlabel.handle_value (), "horizontalalignment", "center");
4530  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4531  xset (ylabel.handle_value (), "horizontalalignment", "center");
4532  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4533  xset (zlabel.handle_value (), "horizontalalignment", "right");
4534  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4535  xset (title.handle_value (), "horizontalalignment", "center");
4536  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4537 
4538  xset (xlabel.handle_value (), "verticalalignment", "top");
4539  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4540  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4541  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4542  xset (title.handle_value (), "verticalalignment", "bottom");
4543  xset (title.handle_value (), "verticalalignmentmode", "auto");
4544 
4545  xset (ylabel.handle_value (), "rotation", 90.0);
4546  xset (ylabel.handle_value (), "rotationmode", "auto");
4547 
4548  xset (zlabel.handle_value (), "visible", "off");
4549 
4550  xset (xlabel.handle_value (), "clipping", "off");
4551  xset (ylabel.handle_value (), "clipping", "off");
4552  xset (zlabel.handle_value (), "clipping", "off");
4553  xset (title.handle_value (), "clipping", "off");
4554 
4555  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4556  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4557  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4558  xset (title.handle_value (), "autopos_tag", "title");
4559 
4560  adopt (xlabel.handle_value ());
4561  adopt (ylabel.handle_value ());
4562  adopt (zlabel.handle_value ());
4563  adopt (title.handle_value ());
4564 
4565  Matrix tlooseinset = default_axes_position ();
4566  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4567  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4568  looseinset = tlooseinset;
4569 }
4570 
4571 Matrix
4573 {
4574  Matrix pos = init_pos;
4576  Matrix parent_bb = go.get_properties ().get_boundingbox (true);
4577  Matrix ext = get_extent (true, true);
4578  ext(1) = parent_bb(3) - ext(1) - ext(3);
4579  ext(0)++;
4580  ext(1)++;
4581  ext = convert_position (ext, "pixels", get_units (),
4582  parent_bb.extract_n (0, 2, 1, 2));
4583  if (ext(0) < pos(0))
4584  {
4585  pos(2) += pos(0)-ext(0);
4586  pos(0) = ext(0);
4587  }
4588  if (ext(0)+ext(2) > pos(0)+pos(2))
4589  pos(2) = ext(0)+ext(2)-pos(0);
4590 
4591  if (ext(1) < pos(1))
4592  {
4593  pos(3) += pos(1)-ext(1);
4594  pos(1) = ext(1);
4595  }
4596  if (ext(1)+ext(3) > pos(1)+pos(3))
4597  pos(3) = ext(1)+ext(3)-pos(1);
4598 
4599  return pos;
4600 }
4601 
4602 void
4604 {
4605  // First part is equivalent to `update_tightinset ()'
4606  if (activepositionproperty.is ("position"))
4607  update_position ();
4608  else
4609  update_outerposition ();
4610  caseless_str old_units = get_units ();
4611  set_units ("normalized");
4612  Matrix pos = position.get ().matrix_value ();
4613  Matrix outpos = outerposition.get ().matrix_value ();
4614  Matrix tightpos = calc_tightbox (pos);
4615  Matrix tinset (1, 4, 1.0);
4616  tinset(0) = pos(0)-tightpos(0);
4617  tinset(1) = pos(1)-tightpos(1);
4618  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4619  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4620  tightinset = tinset;
4621  set_units (old_units);
4622  update_transform ();
4623  if (activepositionproperty.is ("position"))
4624  update_position ();
4625  else
4626  update_outerposition ();
4627 }
4628 
4629 /*
4630 %!testif HAVE_OPENGL, HAVE_FLTK
4631 %! if (! have_window_system)
4632 %! return;
4633 %! endif
4634 %! hf = figure ("visible", "off");
4635 %! graphics_toolkit (hf, "fltk");
4636 %! unwind_protect
4637 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4638 %! hax = findall (gcf (), "type", "axes");
4639 %! positions = cell2mat (get (hax, "position"));
4640 %! outerpositions = cell2mat (get (hax, "outerposition"));
4641 %! looseinsets = cell2mat (get (hax, "looseinset"));
4642 %! tightinsets = cell2mat (get (hax, "tightinset"));
4643 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4644 %! hax = findall (gcf (), "type", "axes");
4645 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4646 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4647 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4648 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4649 %! unwind_protect_cleanup
4650 %! close (hf);
4651 %! end_unwind_protect
4652 
4653 %!testif HAVE_OPENGL, HAVE_FLTK
4654 %! if (! have_window_system)
4655 %! return;
4656 %! endif
4657 %! hf = figure ("visible", "off");
4658 %! graphics_toolkit (hf, "fltk");
4659 %! fpos = get (hf, "position");
4660 %! unwind_protect
4661 %! plot (rand (3));
4662 %! position = get (gca, "position");
4663 %! outerposition = get (gca, "outerposition");
4664 %! looseinset = get (gca, "looseinset");
4665 %! tightinset = get (gca, "tightinset");
4666 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4667 %! set (hf, "position", fpos);
4668 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4669 %! assert (get (gca, "position"), position, 0.001);
4670 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4671 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4672 %! unwind_protect_cleanup
4673 %! close (hf);
4674 %! end_unwind_protect
4675 
4676 %!testif HAVE_OPENGL, HAVE_FLTK
4677 %! if (! have_window_system)
4678 %! return;
4679 %! endif
4680 %! hf = figure ("visible", "off");
4681 %! graphics_toolkit (hf, "fltk");
4682 %! fpos = get (hf, "position");
4683 %! set (gca, "activepositionproperty", "position");
4684 %! unwind_protect
4685 %! plot (rand (3));
4686 %! position = get (gca, "position");
4687 %! outerposition = get (gca, "outerposition");
4688 %! looseinset = get (gca, "looseinset");
4689 %! tightinset = get (gca, "tightinset");
4690 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)]);
4691 %! set (hf, "position", fpos);
4692 %! assert (get (gca, "position"), position, 0.001);
4693 %! assert (get (gca, "outerposition"), outerposition, 0.001);
4694 %! assert (get (gca, "looseinset"), looseinset, 0.001);
4695 %! assert (get (gca, "tightinset"), tightinset, 0.001);
4696 %! unwind_protect_cleanup
4697 %! close (hf);
4698 %! end_unwind_protect
4699 */
4700 
4701 void
4703  const std::string& who,
4704  const octave_value& v)
4705 {
4706  if (v.is_string ())
4707  {
4708  xset (hp.handle_value (), "string", v);
4709  return;
4710  }
4711 
4714 
4715  if (go.isa ("text"))
4716  val = ::reparent (v, "set", who, __myhandle__, false);
4717  else
4718  {
4719  std::string cname = v.class_name ();
4720 
4721  error ("set: expecting text graphics object or character string for %s property, found %s",
4722  who.c_str (), cname.c_str ());
4723  }
4724 
4725  xset (val, "handlevisibility", "off");
4726 
4728 
4730 
4731  hp = val;
4732 
4733  adopt (hp.handle_value ());
4734 }
4735 
4736 void
4738 {
4739  set_text_child (xlabel, "xlabel", v);
4740  xset (xlabel.handle_value (), "positionmode", "auto");
4741  xset (xlabel.handle_value (), "rotationmode", "auto");
4742  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4743  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4744  xset (xlabel.handle_value (), "clipping", "off");
4745  xset (xlabel.handle_value (), "color", get_xcolor ());
4746  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4747  update_xlabel_position ();
4748 }
4749 
4750 void
4752 {
4753  set_text_child (ylabel, "ylabel", v);
4754  xset (ylabel.handle_value (), "positionmode", "auto");
4755  xset (ylabel.handle_value (), "rotationmode", "auto");
4756  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4757  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4758  xset (ylabel.handle_value (), "clipping", "off");
4759  xset (ylabel.handle_value (), "color", get_ycolor ());
4760  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4761  update_ylabel_position ();
4762 }
4763 
4764 void
4766 {
4767  set_text_child (zlabel, "zlabel", v);
4768  xset (zlabel.handle_value (), "positionmode", "auto");
4769  xset (zlabel.handle_value (), "rotationmode", "auto");
4770  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4771  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
4772  xset (zlabel.handle_value (), "clipping", "off");
4773  xset (zlabel.handle_value (), "color", get_zcolor ());
4774  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4775  update_zlabel_position ();
4776 }
4777 
4778 void
4780 {
4781  set_text_child (title, "title", v);
4782  xset (title.handle_value (), "positionmode", "auto");
4783  xset (title.handle_value (), "horizontalalignment", "center");
4784  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4785  xset (title.handle_value (), "verticalalignment", "bottom");
4786  xset (title.handle_value (), "verticalalignmentmode", "auto");
4787  xset (title.handle_value (), "clipping", "off");
4788  xset (title.handle_value (), "autopos_tag", "title");
4789  update_title_position ();
4790 }
4791 
4792 void
4794  const std::string& mode)
4795 {
4796  // FIXME: Should this have all properties in it?
4797  // Including ones we do don't implement?
4798 
4799  Matrix tlim (1, 2, 0.0);
4800  tlim(1) = 1;
4801  alim = tlim;
4802  xlim = tlim;
4803  ylim = tlim;
4804  zlim = tlim;
4805 
4806  alimmode = "auto";
4807  climmode = "auto";
4808  xlimmode = "auto";
4809  ylimmode = "auto";
4810  zlimmode = "auto";
4811 
4812  ambientlightcolor = Matrix (1, 3, 1.0);
4813 
4814  box = "off";
4815  boxstyle = "back";
4816 
4817  // Note: camera properties (not mode) will be set in update_transform
4818  camerapositionmode = "auto";
4819  cameratargetmode = "auto";
4820  cameraupvectormode = "auto";
4821  cameraviewanglemode = "auto";
4822 
4823  Matrix cl (1, 2, 0.0);
4824  cl(1) = 1;
4825  clim = cl;
4826 
4827  clippingstyle = "3dbox";
4828 
4829  color = color_values ("white");
4830  colororder = default_colororder ();
4831  colororderindex = 1.0;
4832 
4833  // Note: dataspectratio (not mode) will be set through update_aspectratios
4834  dataaspectratiomode = "auto";
4835 
4836  drawmode = "normal";
4837 
4838  fontangle = "normal";
4839  fontname = OCTAVE_DEFAULT_FONTNAME;
4840  fontsize = 10;
4841  fontunits = "points";
4842  fontsmoothing = "on";
4843  fontweight = "normal";
4844 
4845  gridalpha = 0.15;
4846  gridalphamode = "auto";
4847  gridcolor = color_values (0.15, 0.15, 0.15);
4848  gridcolormode = "auto";
4849  gridlinestyle = "-";
4850 
4851  labelfontsizemultiplier = 1.1;
4852 
4853  layer = "bottom";
4854 
4855  linestyleorder = "-";
4856  linestyleorderindex = 1.0;
4857 
4858  linewidth = 0.5;
4859 
4860  minorgridalpha = 0.25;
4861  minorgridalphamode = "auto";
4862  minorgridcolor = color_values (0.1, 0.1, 0.1);
4863  minorgridcolormode = "auto";
4864  minorgridlinestyle = ":";
4865 
4866  nextplot = "replace";
4867 
4868  // Note: plotboxaspectratio will be set through update_aspectratios
4869  plotboxaspectratiomode = "auto";
4870  projection = "orthographic";
4871 
4872  sortmethod = "depth";
4873 
4874  tickdir = "in";
4875  tickdirmode = "auto";
4876  ticklabelinterpreter = "tex";
4877  ticklength = default_axes_ticklength ();
4878 
4879  tightinset = Matrix (1, 4, 0.0);
4880 
4881  titlefontsizemultiplier = 1.1;
4882  titlefontweight = "bold";
4883 
4884  Matrix tview (1, 2, 0.0);
4885  tview(1) = 90;
4886  view = tview;
4887 
4888  xaxislocation = "bottom";
4889 
4890  xcolor = color_values (0.15, 0.15, 0.15);
4891  xcolormode = "auto";
4892  xdir = "normal";
4893  xgrid = "off";
4894  xminorgrid = "off";
4895  xminortick = "off";
4896  xscale = "linear";
4897  xtick = Matrix ();
4898  xticklabel = "";
4899  xticklabelmode = "auto";
4900  xticklabelrotation = 0.0;
4901  xtickmode = "auto";
4902 
4903  yaxislocation = "left";
4904 
4905  ycolor = color_values (0.15, 0.15, 0.15);
4906  ycolormode = "auto";
4907  ydir = "normal";
4908  ygrid = "off";
4909  yminorgrid = "off";
4910  yminortick = "off";
4911  yscale = "linear";
4912  ytick = Matrix ();
4913  yticklabel = "";
4914  yticklabelmode = "auto";
4915  yticklabelrotation = 0.0;
4916  ytickmode = "auto";
4917 
4918  zcolor = color_values (0.15, 0.15, 0.15);
4919  zcolormode = "auto";
4920  zdir = "normal";
4921  zgrid = "off";
4922  zminorgrid = "off";
4923  zminortick = "off";
4924  zscale = "linear";
4925  ztick = Matrix ();
4926  zticklabel = "";
4927  zticklabelmode = "auto";
4928  zticklabelrotation = 0.0;
4929  ztickmode = "auto";
4930 
4931  sx = "linear";
4932  sy = "linear";
4933  sz = "linear";
4934 
4935  visible = "on";
4936 
4937  // Replace/Reset preserves Position and Units properties
4938  if (mode != "replace" && mode != "reset")
4939  {
4940  outerposition = default_axes_outerposition ();
4941  position = default_axes_position ();
4942  activepositionproperty = "outerposition";
4943  }
4944 
4945  if (mode != "reset")
4946  {
4947  delete_children (true);
4948 
4949  xlabel.invalidate ();
4950  ylabel.invalidate ();
4951  zlabel.invalidate ();
4952  title.invalidate ();
4953 
4954  xlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4955  false, false, false);
4956  ylabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4957  false, false, false);
4958  zlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4959  false, false, false);
4960  title = gh_manager::make_graphics_handle ("text", __myhandle__,
4961  false, false, false);
4962 
4963  adopt (xlabel.handle_value ());
4964  adopt (ylabel.handle_value ());
4965  adopt (zlabel.handle_value ());
4966  adopt (title.handle_value ());
4967 
4968  update_xlabel_position ();
4969  update_ylabel_position ();
4970  update_zlabel_position ();
4971  update_title_position ();
4972  }
4973  else
4974  {
4975  graphics_object go = gh_manager::get_object (xlabel.handle_value ());
4977  go = gh_manager::get_object (ylabel.handle_value ());
4979  go = gh_manager::get_object (zlabel.handle_value ());
4981  go = gh_manager::get_object (title.handle_value ());
4983  }
4984 
4985  xset (xlabel.handle_value (), "handlevisibility", "off");
4986  xset (ylabel.handle_value (), "handlevisibility", "off");
4987  xset (zlabel.handle_value (), "handlevisibility", "off");
4988  xset (title.handle_value (), "handlevisibility", "off");
4989 
4990  xset (xlabel.handle_value (), "horizontalalignment", "center");
4991  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4992  xset (ylabel.handle_value (), "horizontalalignment", "center");
4993  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4994  xset (zlabel.handle_value (), "horizontalalignment", "right");
4995  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4996  xset (title.handle_value (), "horizontalalignment", "center");
4997  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4998 
4999  xset (xlabel.handle_value (), "verticalalignment", "top");
5000  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5001  xset (ylabel.handle_value (), "verticalalignment", "bottom");
5002  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5003  xset (title.handle_value (), "verticalalignment", "bottom");
5004  xset (title.handle_value (), "verticalalignmentmode", "auto");
5005 
5006  xset (ylabel.handle_value (), "rotation", 90.0);
5007  xset (ylabel.handle_value (), "rotationmode", "auto");
5008 
5009  xset (zlabel.handle_value (), "visible", "off");
5010 
5011  xset (xlabel.handle_value (), "clipping", "off");
5012  xset (ylabel.handle_value (), "clipping", "off");
5013  xset (zlabel.handle_value (), "clipping", "off");
5014  xset (title.handle_value (), "clipping", "off");
5015 
5016  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
5017  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
5018  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
5019  xset (title.handle_value (), "autopos_tag", "title");
5020 
5021  update_transform ();
5022  sync_positions ();
5023  override_defaults (bgo);
5024 }
5025 
5026 void
5028 {
5029  graphics_handle h = hp.handle_value ();
5030 
5031  if (h.ok ())
5032  {
5034 
5035  if (go.valid_object ())
5036  gh_manager::free (h);
5037 
5039  }
5040 
5041  // FIXME: is it necessary to check whether the axes object is
5042  // being deleted now? I think this function is only called when an
5043  // individual child object is delete and not when the parent axes
5044  // object is deleted.
5045 
5046  if (! is_beingdeleted ())
5047  {
5048  hp = gh_manager::make_graphics_handle ("text", __myhandle__,
5049  false, false);
5050 
5051  xset (hp.handle_value (), "handlevisibility", "off");
5052 
5053  adopt (hp.handle_value ());
5054  }
5055 }
5056 
5057 void
5059 {
5060  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
5061  {
5062  delete_text_child (xlabel);
5063  update_xlabel_position ();
5064  }
5065  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
5066  {
5067  delete_text_child (ylabel);
5068  update_ylabel_position ();
5069  }
5070  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
5071  {
5072  delete_text_child (zlabel);
5073  update_zlabel_position ();
5074  }
5075  else if (title.handle_value ().ok () && h == title.handle_value ())
5076  {
5077  delete_text_child (title);
5078  update_title_position ();
5079  }
5080  else
5082 }
5083 
5084 inline Matrix
5086 {
5087  Matrix m (4, 4, 0.0);
5088 
5089  for (int i = 0; i < 4; i++)
5090  m(i,i) = 1;
5091 
5092  return m;
5093 }
5094 
5095 inline ColumnVector
5097 {
5098  ColumnVector v (4, 0.0);
5099 
5100  v(3) = 1;
5101 
5102  return v;
5103 }
5104 
5105 inline ColumnVector
5106 xform_vector (double x, double y, double z)
5107 {
5108  ColumnVector v (4, 1.0);
5109 
5110  v(0) = x;
5111  v(1) = y;
5112  v(2) = z;
5113 
5114  return v;
5115 }
5116 
5117 inline ColumnVector
5118 transform (const Matrix& m, double x, double y, double z)
5119 {
5120  return (m * xform_vector (x, y, z));
5121 }
5122 
5123 inline Matrix
5124 xform_scale (double x, double y, double z)
5125 {
5126  Matrix m (4, 4, 0.0);
5127 
5128  m(0,0) = x;
5129  m(1,1) = y;
5130  m(2,2) = z;
5131  m(3,3) = 1;
5132 
5133  return m;
5134 }
5135 
5136 inline Matrix
5137 xform_translate (double x, double y, double z)
5138 {
5139  Matrix m = xform_matrix ();
5140 
5141  m(0,3) = x;
5142  m(1,3) = y;
5143  m(2,3) = z;
5144  m(3,3) = 1;
5145 
5146  return m;
5147 }
5148 
5149 inline void
5150 scale (Matrix& m, double x, double y, double z)
5151 {
5152  m = m * xform_scale (x, y, z);
5153 }
5154 
5155 inline void
5156 translate (Matrix& m, double x, double y, double z)
5157 {
5158  m = m * xform_translate (x, y, z);
5159 }
5160 
5161 inline void
5163 {
5164  v = m * v;
5165 }
5166 
5167 inline void
5168 scale (ColumnVector& v, double x, double y, double z)
5169 {
5170  v(0) *= x;
5171  v(1) *= y;
5172  v(2) *= z;
5173 }
5174 
5175 inline void
5176 translate (ColumnVector& v, double x, double y, double z)
5177 {
5178  v(0) += x;
5179  v(1) += y;
5180  v(2) += z;
5181 }
5182 
5183 inline void
5185 {
5186  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5187  scale (v, fact, fact, fact);
5188 }
5189 
5190 inline double
5191 dot (const ColumnVector& v1, const ColumnVector& v2)
5192 {
5193  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5194 }
5195 
5196 inline double
5197 norm (const ColumnVector& v)
5198 {
5199  return sqrt (dot (v, v));
5200 }
5201 
5202 inline ColumnVector
5203 cross (const ColumnVector& v1, const ColumnVector& v2)
5204 {
5205  ColumnVector r = xform_vector ();
5206 
5207  r(0) = v1(1)*v2(2) - v1(2)*v2(1);
5208  r(1) = v1(2)*v2(0) - v1(0)*v2(2);
5209  r(2) = v1(0)*v2(1) - v1(1)*v2(0);
5210 
5211  return r;
5212 }
5213 
5214 inline Matrix
5216 {
5217  static double data[32] =
5218  {
5219  0,0,0,1,
5220  1,0,0,1,
5221  0,1,0,1,
5222  0,0,1,1,
5223  1,1,0,1,
5224  1,0,1,1,
5225  0,1,1,1,
5226  1,1,1,1
5227  };
5228  Matrix m (4, 8);
5229 
5230  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5231 
5232  return m;
5233 }
5234 
5235 inline ColumnVector
5237 {
5238  ColumnVector retval (4, 1.0);
5239 
5240  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
5241 
5242  return retval;
5243 }
5244 
5245 inline RowVector
5247 {
5248  return v.extract_n (0, 3).transpose ();
5249 }
5250 
5251 void
5253 {
5254  double xd = (xdir_is ("normal") ? 1 : -1);
5255  double yd = (ydir_is ("normal") ? 1 : -1);
5256  double zd = (zdir_is ("normal") ? 1 : -1);
5257 
5258  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
5259  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
5260  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
5261 
5262  double xo = xlimits(xd > 0 ? 0 : 1);
5263  double yo = ylimits(yd > 0 ? 0 : 1);
5264  double zo = zlimits(zd > 0 ? 0 : 1);
5265 
5266  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5267 
5268  bool autocam = (camerapositionmode_is ("auto")
5269  && cameratargetmode_is ("auto")
5270  && cameraupvectormode_is ("auto")
5271  && cameraviewanglemode_is ("auto"));
5272  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5273  && plotboxaspectratiomode_is ("auto"));
5274 
5275  ColumnVector c_eye (xform_vector ());
5276  ColumnVector c_center (xform_vector ());
5277  ColumnVector c_upv (xform_vector ());
5278 
5279  if (cameratargetmode_is ("auto"))
5280  {
5281  c_center(0) = (xlimits(0) + xlimits(1)) / 2;
5282  c_center(1) = (ylimits(0) + ylimits(1)) / 2;
5283  c_center(2) = (zlimits(0) + zlimits(1)) / 2;
5284 
5285  cameratarget = xform2cam (c_center);
5286  }
5287  else
5288  c_center = cam2xform (get_cameratarget ().matrix_value ());
5289 
5290  if (camerapositionmode_is ("auto"))
5291  {
5292  Matrix tview = get_view ().matrix_value ();
5293  double az = tview(0);
5294  double el = tview(1);
5295  double d = 5 * sqrt (pb(0)*pb(0) + pb(1)*pb(1) + pb(2)*pb(2));
5296 
5297  if (el == 90 || el == -90)
5298  c_eye(2) = d*octave::math::signum (el);
5299  else
5300  {
5301  az *= M_PI/180.0;
5302  el *= M_PI/180.0;
5303  c_eye(0) = d * cos (el) * sin (az);
5304  c_eye(1) = -d* cos (el) * cos (az);
5305  c_eye(2) = d * sin (el);
5306  }
5307  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
5308  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
5309  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
5310 
5311  cameraposition = xform2cam (c_eye);
5312  }
5313  else
5314  c_eye = cam2xform (get_cameraposition ().matrix_value ());
5315 
5316  if (cameraupvectormode_is ("auto"))
5317  {
5318  Matrix tview = get_view ().matrix_value ();
5319  double az = tview(0);
5320  double el = tview(1);
5321 
5322  if (el == 90 || el == -90)
5323  {
5324  c_upv(0) = -octave::math::signum (el)
5325  * sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
5326  c_upv(1) = octave::math::signum (el)
5327  * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
5328  }
5329  else
5330  c_upv(2) = 1;
5331 
5332  cameraupvector = xform2cam (c_upv);
5333  }
5334  else
5335  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
5336 
5337  Matrix x_view = xform_matrix ();
5338  Matrix x_projection = xform_matrix ();
5339  Matrix x_viewport = xform_matrix ();
5340  Matrix x_normrender = xform_matrix ();
5341  Matrix x_pre = xform_matrix ();
5342 
5343  x_render = xform_matrix ();
5344  x_render_inv = xform_matrix ();
5345 
5346  scale (x_pre, pb(0), pb(1), pb(2));
5347  translate (x_pre, -0.5, -0.5, -0.5);
5348  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5349  zd/(zlimits(1)-zlimits(0)));
5350  translate (x_pre, -xo, -yo, -zo);
5351 
5352  xform (c_eye, x_pre);
5353  xform (c_center, x_pre);
5354  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
5355  pb(2)/(zlimits(1)-zlimits(0)));
5356  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
5357 
5358  ColumnVector F (c_center), f (F), UP (c_upv);
5359  normalize (f);
5360  normalize (UP);
5361 
5362  if (std::abs (dot (f, UP)) > 1e-15)
5363  {
5364  double fa = 1 / sqrt (1 - f(2)*f(2));
5365  scale (UP, fa, fa, fa);
5366  }
5367 
5368  ColumnVector s = cross (f, UP);
5369  ColumnVector u = cross (s, f);
5370 
5371  scale (x_view, 1, 1, -1);
5372  Matrix l = xform_matrix ();
5373  l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
5374  l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
5375  l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
5376  x_view = x_view * l;
5377  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
5378  scale (x_view, pb(0), pb(1), pb(2));
5379  translate (x_view, -0.5, -0.5, -0.5);
5380 
5381  Matrix x_cube = x_view * unit_cube ();
5382  ColumnVector cmin = x_cube.row_min ();
5383  ColumnVector cmax = x_cube.row_max ();
5384  double xM = cmax(0) - cmin(0);
5385  double yM = cmax(1) - cmin(1);
5386 
5387  Matrix bb = get_boundingbox (true);
5388 
5389  double v_angle;
5390 
5391  if (cameraviewanglemode_is ("auto"))
5392  {
5393  double af;
5394 
5395  // FIXME: was this really needed? When compared to Matlab, it
5396  // does not seem to be required. Need investigation with concrete
5397  // graphics toolkit to see results visually.
5398  if (false && dowarp)
5399  af = 1.0 / (xM > yM ? xM : yM);
5400  else
5401  {
5402  if ((bb(2)/bb(3)) > (xM/yM))
5403  af = 1.0 / yM;
5404  else
5405  af = 1.0 / xM;
5406  }
5407  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
5408 
5409  cameraviewangle = v_angle;
5410  }
5411  else
5412  v_angle = get_cameraviewangle ();
5413 
5414  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
5415  scale (x_projection, pf, pf, 1);
5416 
5417  if (dowarp)
5418  {
5419  xM *= pf;
5420  yM *= pf;
5421  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5422  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
5423  }
5424  else
5425  {
5426  double pix = 1;
5427  if (autocam)
5428  {
5429  if ((bb(2)/bb(3)) > (xM/yM))
5430  pix = bb(3);
5431  else
5432  pix = bb(2);
5433  }
5434  else
5435  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
5436  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5437  scale (x_viewport, pix, -pix, 1);
5438  }
5439 
5440  x_normrender = x_viewport * x_projection * x_view;
5441 
5442  x_cube = x_normrender * unit_cube ();
5443  cmin = x_cube.row_min ();
5444  cmax = x_cube.row_max ();
5445  x_zlim.resize (1, 2);
5446  x_zlim(0) = cmin(2);
5447  x_zlim(1) = cmax(2);
5448 
5449  x_render = x_normrender;
5450  scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5451  zd/(zlimits(1)-zlimits(0)));
5452  translate (x_render, -xo, -yo, -zo);
5453 
5454  x_viewtransform = x_view;
5455  x_projectiontransform = x_projection;
5456  x_viewporttransform = x_viewport;
5457  x_normrendertransform = x_normrender;
5458  x_rendertransform = x_render;
5459 
5460  x_render_inv = x_render.inverse ();
5461 
5462  // Note: these matrices are a slight modified version of the regular matrices,
5463  // more suited for OpenGL rendering (x_gl_mat1 => light => x_gl_mat2)
5464  x_gl_mat1 = x_view;
5465  scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5466  zd/(zlimits(1)-zlimits(0)));
5467  translate (x_gl_mat1, -xo, -yo, -zo);
5468  x_gl_mat2 = x_viewport * x_projection;
5469 }
5470 
5471 static bool updating_axes_layout = false;
5472 
5473 void
5475 {
5476  if (updating_axes_layout)
5477  return;
5478 
5479  graphics_xform xform = get_transform ();
5480 
5481  double xd = (xdir_is ("normal") ? 1 : -1);
5482  double yd = (ydir_is ("normal") ? 1 : -1);
5483  double zd = (zdir_is ("normal") ? 1 : -1);
5484 
5485  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
5486  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
5487  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
5488 
5489  double x_min, x_max, y_min, y_max, z_min, z_max;
5490  x_min = xlims(0), x_max = xlims(1);
5491  y_min = ylims(0), y_max = ylims(1);
5492  z_min = zlims(0), z_max = zlims(1);
5493 
5494  ColumnVector p1, p2, dir (3);
5495 
5496  xstate = ystate = zstate = AXE_ANY_DIR;
5497 
5498  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
5499  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
5500  dir(0) = octave::math::round (p2(0) - p1(0));
5501  dir(1) = octave::math::round (p2(1) - p1(1));
5502  dir(2) = (p2(2) - p1(2));
5503  if (dir(0) == 0 && dir(1) == 0)
5504  xstate = AXE_DEPTH_DIR;
5505  else if (dir(2) == 0)
5506  {
5507  if (dir(0) == 0)
5508  xstate = AXE_VERT_DIR;
5509  else if (dir(1) == 0)
5510  xstate = AXE_HORZ_DIR;
5511  }
5512 
5513  if (dir(2) == 0)
5514  {
5515  if (dir(1) == 0)
5516  xPlane = (dir(0) > 0 ? x_max : x_min);
5517  else
5518  xPlane = (dir(1) < 0 ? x_max : x_min);
5519  }
5520  else
5521  xPlane = (dir(2) < 0 ? x_min : x_max);
5522 
5523  xPlaneN = (xPlane == x_min ? x_max : x_min);
5524  fx = (x_max - x_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5525 
5526  p1 = xform.transform ((x_min + x_max)/2, y_min, (z_min + z_max)/2, false);
5527  p2 = xform.transform ((x_min + x_max)/2, y_max, (z_min + z_max)/2, false);
5528  dir(0) = octave::math::round (p2(0) - p1(0));
5529  dir(1) = octave::math::round (p2(1) - p1(1));
5530  dir(2) = (p2(2) - p1(2));
5531  if (dir(0) == 0 && dir(1) == 0)
5532  ystate = AXE_DEPTH_DIR;
5533  else if (dir(2) == 0)
5534  {
5535  if (dir(0) == 0)
5536  ystate = AXE_VERT_DIR;
5537  else if (dir(1) == 0)
5538  ystate = AXE_HORZ_DIR;
5539  }
5540 
5541  if (dir(2) == 0)
5542  {
5543  if (dir(1) == 0)
5544  yPlane = (dir(0) > 0 ? y_max : y_min);
5545  else
5546  yPlane = (dir(1) < 0 ? y_max : y_min);
5547  }
5548  else
5549  yPlane = (dir(2) < 0 ? y_min : y_max);
5550 
5551  yPlaneN = (yPlane == y_min ? y_max : y_min);
5552  fy = (y_max - y_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5553 
5554  p1 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_min, false);
5555  p2 = xform.transform ((x_min + x_max)/2, (y_min + y_max)/2, z_max, false);
5556  dir(0) = octave::math::round (p2(0) - p1(0));
5557  dir(1) = octave::math::round (p2(1) - p1(1));
5558  dir(2) = (p2(2) - p1(2));
5559  if (dir(0) == 0 && dir(1) == 0)
5560  zstate = AXE_DEPTH_DIR;
5561  else if (dir(2) == 0)
5562  {
5563  if (dir(0) == 0)
5564  zstate = AXE_VERT_DIR;
5565  else if (dir(1) == 0)
5566  zstate = AXE_HORZ_DIR;
5567  }
5568 
5569  if (dir(2) == 0)
5570  {
5571  if (dir(1) == 0)
5572  zPlane = (dir(0) > 0 ? z_min : z_max);
5573  else
5574  zPlane = (dir(1) < 0 ? z_min : z_max);
5575  }
5576  else
5577  zPlane = (dir(2) < 0 ? z_min : z_max);
5578 
5579  zPlaneN = (zPlane == z_min ? z_max : z_min);
5580  fz = (z_max - z_min) / sqrt (dir(0)*dir(0) + dir(1)*dir(1));
5581 
5583  frame.protect_var (updating_axes_layout);
5584  updating_axes_layout = true;
5585 
5586  xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
5587  zSign = (zd*(zPlane-zPlaneN) <= 0);
5588  xyzSym = zSign ? xySym : ! xySym;
5589  xpTick = (zSign ? xPlaneN : xPlane);
5590  ypTick = (zSign ? yPlaneN : yPlane);
5591  zpTick = (zSign ? zPlane : zPlaneN);
5592  xpTickN = (zSign ? xPlane : xPlaneN);
5593  ypTickN = (zSign ? yPlane : yPlaneN);
5594  zpTickN = (zSign ? zPlaneN : zPlane);
5595 
5596  // 2D mode
5597  x2Dtop = false;
5598  y2Dright = false;
5599  layer2Dtop = false;
5600  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
5601  {
5602  if (xaxislocation_is ("top"))
5603  {
5604  std::swap (yPlane, yPlaneN);
5605  x2Dtop = true;
5606  }
5607  ypTick = yPlaneN;
5608  ypTickN = yPlane;
5609  if (yaxislocation_is ("right"))
5610  {
5611  std::swap (xPlane, xPlaneN);
5612  y2Dright = true;
5613  }
5614  xpTick = xPlaneN;
5615  xpTickN = xPlane;
5616  if (layer_is ("top"))
5617  {
5618  zpTick = zPlaneN;
5619  layer2Dtop = true;
5620  }
5621  else
5622  zpTick = zPlane;
5623  }
5624 
5625  Matrix viewmat = get_view ().matrix_value ();
5626  nearhoriz = std::abs (viewmat(1)) <= 5;
5627  is2D = viewmat(1) == 90;
5628 
5629  update_ticklength ();
5630 }
5631 
5632 void
5634 {
5635  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
5636  (ystate > AXE_DEPTH_DIR ? 1 : 0) +
5637  (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5638 
5639  if (tickdirmode_is ("auto"))
5640  tickdir.set (mode2d ? "in" : "out", true);
5641 
5642  double ticksign = (tickdir_is ("in") ? -1 : 1);
5643 
5644  Matrix bbox = get_boundingbox (true);
5645  Matrix ticklen = get_ticklength ().matrix_value ();
5646  ticklen(0) *= std::max (bbox(2), bbox(3));
5647  ticklen(1) *= std::max (bbox(2), bbox(3));
5648 
5649  xticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5650  yticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5651  zticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5652 
5653  xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
5654  ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
5655  ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
5656 
5657  update_xlabel_position ();
5658  update_ylabel_position ();
5659  update_zlabel_position ();
5660  update_title_position ();
5661 }
5662 
5663 /*
5664 ## FIXME: A demo can't be called in a C++ file. This should be made a test
5665 ## or moved to a .m file where it can be called.
5666 %!demo
5667 %! clf;
5668 %! subplot (2,1,1);
5669 %! plot (rand (3));
5670 %! xlabel xlabel;
5671 %! ylabel ylabel;
5672 %! title title;
5673 %! subplot (2,1,2);
5674 %! plot (rand (3));
5675 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
5676 %! xlabel xlabel;
5677 %! ylabel ylabel;
5678 %! title title;
5679 */
5680 
5681 static ColumnVector
5683  const text::properties& props,
5684  const graphics_xform& xform,
5685  const Matrix& bbox)
5686 {
5688 
5689  std::string to_units = props.get_units ();
5690 
5691  if (to_units != "data")
5692  {
5693  ColumnVector v = xform.transform (p(0), p(1), p(2));
5694 
5695  retval.resize (3);
5696 
5697  retval(0) = v(0) - bbox(0) + 1;
5698  retval(1) = bbox(1) + bbox(3) - v(1) + 1;
5699  retval(2) = 0;
5700 
5701  retval = convert_position (retval, "pixels", to_units,
5702  bbox.extract_n (0, 2, 1, 2));
5703  }
5704  else
5705  retval = p;
5706 
5707  return retval;
5708 }
5709 
5710 static bool updating_xlabel_position = false;
5711 
5712 void
5714 {
5715  if (updating_xlabel_position)
5716  return;
5717 
5718  graphics_object go = gh_manager::get_object (get_xlabel ());
5719 
5720  if (! go.valid_object ())
5721  return;
5722 
5723  text::properties& xlabel_props
5724  = reinterpret_cast<text::properties&> (go.get_properties ());</