GNU Octave  3.8.0
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-2013 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 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <cctype>
28 #include <cfloat>
29 #include <cstdlib>
30 #include <ctime>
31 
32 #include <algorithm>
33 #include <list>
34 #include <map>
35 #include <set>
36 #include <string>
37 #include <sstream>
38 
39 #include "cmd-edit.h"
40 #include "file-ops.h"
41 #include "file-stat.h"
42 #include "oct-locbuf.h"
43 #include "singleton-cleanup.h"
44 
45 #include "builtins.h"
46 #include "cutils.h"
47 #include "defun.h"
48 #include "display.h"
49 #include "error.h"
50 #include "graphics.h"
51 #include "input.h"
52 #include "ov.h"
53 #include "oct-obj.h"
54 #include "oct-map.h"
55 #include "ov-fcn-handle.h"
56 #include "pager.h"
57 #include "parse.h"
58 #include "toplev.h"
59 #include "txt-eng-ft.h"
60 #include "unwind-prot.h"
61 
62 // forward declarations
63 static octave_value xget (const graphics_handle& h, const caseless_str& name);
64 
65 static void
66 gripe_set_invalid (const std::string& pname)
67 {
68  error ("set: invalid value for %s property", pname.c_str ());
69 }
70 
71 // Check to see that PNAME matches just one of PNAMES uniquely.
72 // Return the full name of the match, or an empty caseless_str object
73 // if there is no match, or the match is ambiguous.
74 
75 static caseless_str
76 validate_property_name (const std::string& who, const std::string& what,
77  const std::set<std::string>& pnames,
78  const caseless_str& pname)
79 {
80  size_t len = pname.length ();
81  std::set<std::string> matches;
82 
83  for (std::set<std::string>::const_iterator p = pnames.begin ();
84  p != pnames.end (); p++)
85  {
86  if (pname.compare (*p, len))
87  {
88  if (len == p->length ())
89  {
90  // Exact match.
91  return pname;
92  }
93 
94  matches.insert (*p);
95  }
96  }
97 
98  size_t num_matches = matches.size ();
99 
100  if (num_matches == 0)
101  {
102  error ("%s: unknown %s property %s",
103  who.c_str (), what.c_str (), pname.c_str ());
104  }
105  else if (num_matches > 1)
106  {
107  string_vector sv (matches);
108 
109  std::ostringstream os;
110 
111  sv.list_in_columns (os);
112 
113  std::string match_list = os.str ();
114 
115  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
116  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
117  }
118  else if (num_matches == 1)
119  {
120  // Exact match was handled above.
121 
122  std::string possible_match = *(matches.begin ());
123 
124  warning_with_id ("Octave:abbreviated-property-match",
125  "%s: allowing %s to match %s property %s",
126  who.c_str (), pname.c_str (), what.c_str (),
127  possible_match.c_str ());
128 
129  return possible_match;
130  }
131 
132  return caseless_str ();
133 }
134 
135 static Matrix
137 {
138  Matrix cmap (64, 3, 0.0);
139 
140  // Produce X in the same manner as linspace so that
141  // jet_colormap and jet.m produce *exactly* the same result.
142  double delta = 1.0 / 63.0;
143 
144  for (octave_idx_type i = 0; i < 64; i++)
145  {
146  // This is the jet colormap. It would be nice to be able
147  // to feval the jet function but since there is a static
148  // property object that includes a colormap_property
149  // object, we need to initialize this before main is even
150  // called, so calling an interpreted function is not
151  // possible.
152 
153  double x = i*delta;
154 
155  if (x >= 3.0/8.0 && x < 5.0/8.0)
156  cmap(i,0) = 4.0 * x - 3.0/2.0;
157  else if (x >= 5.0/8.0 && x < 7.0/8.0)
158  cmap(i,0) = 1.0;
159  else if (x >= 7.0/8.0)
160  cmap(i,0) = -4.0 * x + 9.0/2.0;
161 
162  if (x >= 1.0/8.0 && x < 3.0/8.0)
163  cmap(i,1) = 4.0 * x - 1.0/2.0;
164  else if (x >= 3.0/8.0 && x < 5.0/8.0)
165  cmap(i,1) = 1.0;
166  else if (x >= 5.0/8.0 && x < 7.0/8.0)
167  cmap(i,1) = -4.0 * x + 7.0/2.0;
168 
169  if (x < 1.0/8.0)
170  cmap(i,2) = 4.0 * x + 1.0/2.0;
171  else if (x >= 1.0/8.0 && x < 3.0/8.0)
172  cmap(i,2) = 1.0;
173  else if (x >= 3.0/8.0 && x < 5.0/8.0)
174  cmap(i,2) = -4.0 * x + 5.0/2.0;
175  }
176 
177  return cmap;
178 }
179 
180 static double
182 {
183  return display_info::depth ();
184 }
185 
186 static Matrix
188 {
189  Matrix retval (1, 4, 1.0);
190 
191  retval(2) = display_info::width ();
192  retval(3) = display_info::height ();
193 
194  return retval;
195 }
196 
197 static double
199 {
200  return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
201 }
202 
203 static Matrix
205 {
206  Matrix retval (7, 3, 0.0);
207 
208  retval(0,2) = 1.0;
209 
210  retval(1,1) = 0.5;
211 
212  retval(2,0) = 1.0;
213 
214  retval(3,1) = 0.75;
215  retval(3,2) = 0.75;
216 
217  retval(4,0) = 0.75;
218  retval(4,2) = 0.75;
219 
220  retval(5,0) = 0.75;
221  retval(5,1) = 0.75;
222 
223  retval(6,0) = 0.25;
224  retval(6,1) = 0.25;
225  retval(6,2) = 0.25;
226 
227  return retval;
228 }
229 
230 static Matrix
231 default_lim (bool logscale = false)
232 {
233  Matrix m (1, 2, 0);
234 
235  if (logscale)
236  {
237  m(0) = 0.1;
238  m(1) = 1.0;
239  }
240  else
241  m(1) = 1;
242 
243  return m;
244 }
245 
246 static Matrix
248 {
249  Matrix retval (1, 2);
250 
251  retval(0) = 0;
252  retval(1) = 1;
253 
254  return retval;
255 }
256 
257 static Matrix
259 {
260  Matrix m (1, 4, 0.0);
261  m(0) = 0.13;
262  m(1) = 0.11;
263  m(2) = 0.775;
264  m(3) = 0.815;
265  return m;
266 }
267 
268 static Matrix
270 {
271  Matrix m (1, 4, 0.0);
272  m(2) = m(3) = 1.0;
273  return m;
274 }
275 
276 static Matrix
278 {
279  Matrix m (1, 6, 0.0);
280  m(0) = 0.0;
281  m(1) = 0.2;
282  m(2) = 0.4;
283  m(3) = 0.6;
284  m(4) = 0.8;
285  m(5) = 1.0;
286  return m;
287 }
288 
289 static Matrix
291 {
292  Matrix m (1, 2, 0.0);
293  m(0) = 0.01;
294  m(1) = 0.025;
295  return m;
296 }
297 
298 static Matrix
300 {
301  Matrix m (1, 4, 0.0);
302  m(0) = 300;
303  m(1) = 200;
304  m(2) = 560;
305  m(3) = 420;
306  return m;
307 }
308 
309 static Matrix
311 {
312  Matrix m (1, 2, 0.0);
313  m(0) = 8.5;
314  m(1) = 11.0;
315  return m;
316 }
317 
318 static Matrix
320 {
321  Matrix m (1, 4, 0.0);
322  m(0) = 0.25;
323  m(1) = 2.50;
324  m(2) = 8.00;
325  m(3) = 6.00;
326  return m;
327 }
328 
329 static Matrix
331 {
332  Matrix retval (1, 4, 0.0);
333 
334  retval(0) = 0;
335  retval(1) = 0;
336  retval(2) = 80;
337  retval(3) = 30;
338 
339  return retval;
340 }
341 
342 static Matrix
344 {
345  Matrix retval (1, 2, 0.0);
346 
347  retval(0) = 0.01;
348  retval(1) = 0.1;
349 
350  return retval;
351 }
352 
353 static Matrix
355 {
356  Matrix retval (1, 4, 0.0);
357 
358  retval(0) = 0;
359  retval(1) = 0;
360  retval(2) = 0.5;
361  retval(3) = 0.5;
362 
363  return retval;
364 }
365 
366 static double
367 convert_font_size (double font_size, const caseless_str& from_units,
368  const caseless_str& to_units, double parent_height = 0)
369 {
370  // Simple case where from_units == to_units
371 
372  if (from_units.compare (to_units))
373  return font_size;
374 
375  // Converts the given fontsize using the following transformation:
376  // <old_font_size> => points => <new_font_size>
377 
378  double points_size = 0;
379  double res = 0;
380 
381  if (from_units.compare ("points"))
382  points_size = font_size;
383  else
384  {
385  res = xget (0, "screenpixelsperinch").double_value ();
386 
387  if (from_units.compare ("pixels"))
388  points_size = font_size * 72.0 / res;
389  else if (from_units.compare ("inches"))
390  points_size = font_size * 72.0;
391  else if (from_units.compare ("centimeters"))
392  points_size = font_size * 72.0 / 2.54;
393  else if (from_units.compare ("normalized"))
394  points_size = font_size * parent_height * 72.0 / res;
395  }
396 
397  double new_font_size = 0;
398 
399  if (to_units.compare ("points"))
400  new_font_size = points_size;
401  else
402  {
403  if (res <= 0)
404  res = xget (0, "screenpixelsperinch").double_value ();
405 
406  if (to_units.compare ("pixels"))
407  new_font_size = points_size * res / 72.0;
408  else if (to_units.compare ("inches"))
409  new_font_size = points_size / 72.0;
410  else if (to_units.compare ("centimeters"))
411  new_font_size = points_size * 2.54 / 72.0;
412  else if (to_units.compare ("normalized"))
413  {
414  // Avoid setting font size to (0/0) = NaN
415 
416  if (parent_height > 0)
417  new_font_size = points_size * res / (parent_height * 72.0);
418  }
419  }
420 
421  return new_font_size;
422 }
423 
424 static Matrix
425 convert_position (const Matrix& pos, const caseless_str& from_units,
426  const caseless_str& to_units, const Matrix& parent_dim)
427 {
428  Matrix retval (1, pos.numel ());
429  double res = 0;
430  bool is_rectangle = (pos.numel () == 4);
431  bool is_2d = (pos.numel () == 2);
432 
433  if (from_units.compare ("pixels"))
434  retval = pos;
435  else if (from_units.compare ("normalized"))
436  {
437  retval(0) = pos(0) * parent_dim(0) + 1;
438  retval(1) = pos(1) * parent_dim(1) + 1;
439  if (is_rectangle)
440  {
441  retval(2) = pos(2) * parent_dim(0);
442  retval(3) = pos(3) * parent_dim(1);
443  }
444  else if (! is_2d)
445  retval(2) = 0;
446  }
447  else if (from_units.compare ("characters"))
448  {
449  if (res <= 0)
450  res = xget (0, "screenpixelsperinch").double_value ();
451 
452  double f = 0.0;
453 
454  // FIXME: this assumes the system font is Helvetica 10pt
455  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
456  f = 12.0 * res / 74.951;
457 
458  if (f > 0)
459  {
460  retval(0) = 0.5 * pos(0) * f;
461  retval(1) = pos(1) * f;
462  if (is_rectangle)
463  {
464  retval(2) = 0.5 * pos(2) * f;
465  retval(3) = pos(3) * f;
466  }
467  else if (! is_2d)
468  retval(2) = 0;
469  }
470  }
471  else
472  {
473  if (res <= 0)
474  res = xget (0, "screenpixelsperinch").double_value ();
475 
476  double f = 0.0;
477 
478  if (from_units.compare ("points"))
479  f = res / 72.0;
480  else if (from_units.compare ("inches"))
481  f = res;
482  else if (from_units.compare ("centimeters"))
483  f = res / 2.54;
484 
485  if (f > 0)
486  {
487  retval(0) = pos(0) * f + 1;
488  retval(1) = pos(1) * f + 1;
489  if (is_rectangle)
490  {
491  retval(2) = pos(2) * f;
492  retval(3) = pos(3) * f;
493  }
494  else if (! is_2d)
495  retval(2) = 0;
496  }
497  }
498 
499  if (! to_units.compare ("pixels"))
500  {
501  if (to_units.compare ("normalized"))
502  {
503  retval(0) = (retval(0) - 1) / parent_dim(0);
504  retval(1) = (retval(1) - 1) / parent_dim(1);
505  if (is_rectangle)
506  {
507  retval(2) /= parent_dim(0);
508  retval(3) /= parent_dim(1);
509  }
510  else if (! is_2d)
511  retval(2) = 0;
512  }
513  else if (to_units.compare ("characters"))
514  {
515  if (res <= 0)
516  res = xget (0, "screenpixelsperinch").double_value ();
517 
518  double f = 0.0;
519 
520  f = 12.0 * res / 74.951;
521 
522  if (f > 0)
523  {
524  retval(0) = 2 * retval(0) / f;
525  retval(1) = retval(1) / f;
526  if (is_rectangle)
527  {
528  retval(2) = 2 * retval(2) / f;
529  retval(3) = retval(3) / f;
530  }
531  else if (! is_2d)
532  retval(2) = 0;
533  }
534  }
535  else
536  {
537  if (res <= 0)
538  res = xget (0, "screenpixelsperinch").double_value ();
539 
540  double f = 0.0;
541 
542  if (to_units.compare ("points"))
543  f = res / 72.0;
544  else if (to_units.compare ("inches"))
545  f = res;
546  else if (to_units.compare ("centimeters"))
547  f = res / 2.54;
548 
549  if (f > 0)
550  {
551  retval(0) = (retval(0) - 1) / f;
552  retval(1) = (retval(1) - 1) / f;
553  if (is_rectangle)
554  {
555  retval(2) /= f;
556  retval(3) /= f;
557  }
558  else if (! is_2d)
559  retval(2) = 0;
560  }
561  }
562  }
563  else if (! is_rectangle && ! is_2d)
564  retval(2) = 0;
565 
566  return retval;
567 }
568 
569 static Matrix
571  const caseless_str& from_units,
572  const caseless_str& to_units)
573 {
575  graphics_object ax = go.get_ancestor ("axes");
576 
577  Matrix retval;
578 
579  if (ax.valid_object ())
580  {
581  const axes::properties& ax_props =
582  dynamic_cast<const axes::properties&> (ax.get_properties ());
583  graphics_xform ax_xform = ax_props.get_transform ();
584  bool is_rectangle = (pos.numel () == 4);
585  Matrix ax_bbox = ax_props.get_boundingbox (true),
586  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
587 
588  if (from_units.compare ("data"))
589  {
590  if (is_rectangle)
591  {
592  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
593  v2 = ax_xform.transform (pos(0) + pos(2),
594  pos(1) + pos(3), 0);
595 
596  retval.resize (1, 4);
597 
598  retval(0) = v1(0) - ax_bbox(0) + 1;
599  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
600  retval(2) = v2(0) - v1(0);
601  retval(3) = v1(1) - v2(1);
602  }
603  else
604  {
605  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
606 
607  retval.resize (1, 3);
608 
609  retval(0) = v(0) - ax_bbox(0) + 1;
610  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
611  retval(2) = 0;
612  }
613  }
614  else
615  retval = convert_position (pos, from_units, "pixels", ax_size);
616 
617  if (! to_units.compare ("pixels"))
618  {
619  if (to_units.compare ("data"))
620  {
621  if (is_rectangle)
622  {
623  ColumnVector v1, v2;
624  v1 = ax_xform.untransform (
625  retval(0) + ax_bbox(0) - 1,
626  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
627  v2 = ax_xform.untransform (
628  retval(0) + retval(2) + ax_bbox(0) - 1,
629  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
630 
631  retval.resize (1, 4);
632 
633  retval(0) = v1(0);
634  retval(1) = v1(1);
635  retval(2) = v2(0) - v1(0);
636  retval(3) = v2(1) - v1(1);
637  }
638  else
639  {
640  ColumnVector v;
641  v = ax_xform.untransform (
642  retval(0) + ax_bbox(0) - 1,
643  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
644 
645  retval.resize (1, 3);
646 
647  retval(0) = v(0);
648  retval(1) = v(1);
649  retval(2) = v(2);
650  }
651  }
652  else
653  retval = convert_position (retval, "pixels", to_units, ax_size);
654  }
655  }
656 
657  return retval;
658 }
659 
660 // This function always returns the screensize in pixels
661 static Matrix
663 {
665  Matrix sz = obj.get ("screensize").matrix_value ();
666  return convert_position (sz, obj.get ("units").string_value (), "pixels",
667  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
668 }
669 
670 static void
671 convert_cdata_2 (bool is_scaled, double clim_0, double clim_1,
672  const double *cmapv, double x, octave_idx_type lda,
673  octave_idx_type nc, octave_idx_type i, double *av)
674 {
675  if (is_scaled)
676  x = xround ((nc - 1) * (x - clim_0) / (clim_1 - clim_0));
677  else
678  x = xround (x - 1);
679 
680  if (xisnan (x))
681  {
682  av[i] = x;
683  av[i+lda] = x;
684  av[i+2*lda] = x;
685  }
686  else
687  {
688  if (x < 0)
689  x = 0;
690  else if (x >= nc)
691  x = (nc - 1);
692 
693  octave_idx_type idx = static_cast<octave_idx_type> (x);
694 
695  av[i] = cmapv[idx];
696  av[i+lda] = cmapv[idx+nc];
697  av[i+2*lda] = cmapv[idx+2*nc];
698  }
699 }
700 
701 template <class T>
702 void
703 convert_cdata_1 (bool is_scaled, double clim_0, double clim_1,
704  const double *cmapv, const T *cv, octave_idx_type lda,
705  octave_idx_type nc, double *av)
706 {
707  for (octave_idx_type i = 0; i < lda; i++)
708  convert_cdata_2 (is_scaled, clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
709 }
710 
711 static octave_value
712 convert_cdata (const base_properties& props, const octave_value& cdata,
713  bool is_scaled, int cdim)
714 {
715  dim_vector dv (cdata.dims ());
716 
717  if (dv.length () == cdim && dv(cdim-1) == 3)
718  return cdata;
719 
720  Matrix cmap (1, 3, 0.0);
721  Matrix clim (1, 2, 0.0);
722 
724  graphics_object fig = go.get_ancestor ("figure");
725 
726  if (fig.valid_object ())
727  {
728  Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
729 
730  if (! error_state)
731  cmap = _cmap;
732  }
733 
734  if (is_scaled)
735  {
736  graphics_object ax = go.get_ancestor ("axes");
737 
738  if (ax.valid_object ())
739  {
740  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
741 
742  if (! error_state)
743  clim = _clim;
744  }
745  }
746 
747  dv.resize (cdim);
748  dv(cdim-1) = 3;
749 
750  NDArray a (dv);
751 
752  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
753  octave_idx_type nc = cmap.rows ();
754 
755  double *av = a.fortran_vec ();
756  const double *cmapv = cmap.data ();
757 
758  double clim_0 = clim(0);
759  double clim_1 = clim(1);
760 
761 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN) \
762  do \
763  { \
764  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
765  \
766  convert_cdata_1 (is_scaled, clim_0, clim_1, cmapv, \
767  tmp.data (), lda, nc, av); \
768  } \
769  while (0)
770 
771  if (cdata.is_uint8_type ())
772  CONVERT_CDATA_1 (uint8NDArray, uint8_);
773  else if (cdata.is_single_type ())
774  CONVERT_CDATA_1 (FloatNDArray, float_);
775  else if (cdata.is_double_type ())
777  else
778  error ("unsupported type for cdata (= %s)", cdata.type_name ().c_str ());
779 
780 #undef CONVERT_CDATA_1
781 
782  return octave_value (a);
783 }
784 
785 template<class T>
786 static void
787 get_array_limits (const Array<T>& m, double& emin, double& emax,
788  double& eminp, double& emaxp)
789 {
790  const T *data = m.data ();
791  octave_idx_type n = m.numel ();
792 
793  for (octave_idx_type i = 0; i < n; i++)
794  {
795  double e = double (data[i]);
796 
797  // Don't need to test for NaN here as NaN>x and NaN<x is always false
798  if (! xisinf (e))
799  {
800  if (e < emin)
801  emin = e;
802 
803  if (e > emax)
804  emax = e;
805 
806  if (e > 0 && e < eminp)
807  eminp = e;
808 
809  if (e < 0 && e > emaxp)
810  emaxp = e;
811  }
812  }
813 }
814 
815 static bool
817  caseless_str& rest)
818 {
819  int len = name.length ();
820  int offset = 0;
821  bool result = false;
822 
823  if (len >= 4)
824  {
825  caseless_str pfx = name.substr (0, 4);
826 
827  if (pfx.compare ("axes") || pfx.compare ("line")
828  || pfx.compare ("text"))
829  offset = 4;
830  else if (len >= 5)
831  {
832  pfx = name.substr (0, 5);
833 
834  if (pfx.compare ("image") || pfx.compare ("patch"))
835  offset = 5;
836  else if (len >= 6)
837  {
838  pfx = name.substr (0, 6);
839 
840  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
841  offset = 6;
842  else if (len >= 7)
843  {
844  pfx = name.substr (0, 7);
845 
846  if (pfx.compare ("surface") || pfx.compare ("hggroup")
847  || pfx.compare ("uipanel"))
848  offset = 7;
849  else if (len >= 9)
850  {
851  pfx = name.substr (0, 9);
852 
853  if (pfx.compare ("uicontrol")
854  || pfx.compare ("uitoolbar"))
855  offset = 9;
856  else if (len >= 10)
857  {
858  pfx = name.substr (0, 10);
859 
860  if (pfx.compare ("uipushtool"))
861  offset = 10;
862  else if (len >= 12)
863  {
864  pfx = name.substr (0, 12);
865 
866  if (pfx.compare ("uitoggletool"))
867  offset = 12;
868  else if (len >= 13)
869  {
870  pfx = name.substr (0, 13);
871 
872  if (pfx.compare ("uicontextmenu"))
873  offset = 13;
874  }
875  }
876  }
877  }
878  }
879  }
880  }
881 
882  if (offset > 0)
883  {
884  go_name = pfx;
885  rest = name.substr (offset);
886  result = true;
887  }
888  }
889 
890  return result;
891 }
892 
893 static base_graphics_object*
895  const graphics_handle& h = graphics_handle (),
896  const graphics_handle& p = graphics_handle ())
897 {
898  base_graphics_object *go = 0;
899 
900  if (type.compare ("figure"))
901  go = new figure (h, p);
902  else if (type.compare ("axes"))
903  go = new axes (h, p);
904  else if (type.compare ("line"))
905  go = new line (h, p);
906  else if (type.compare ("text"))
907  go = new text (h, p);
908  else if (type.compare ("image"))
909  go = new image (h, p);
910  else if (type.compare ("patch"))
911  go = new patch (h, p);
912  else if (type.compare ("surface"))
913  go = new surface (h, p);
914  else if (type.compare ("hggroup"))
915  go = new hggroup (h, p);
916  else if (type.compare ("uimenu"))
917  go = new uimenu (h, p);
918  else if (type.compare ("uicontrol"))
919  go = new uicontrol (h, p);
920  else if (type.compare ("uipanel"))
921  go = new uipanel (h, p);
922  else if (type.compare ("uicontextmenu"))
923  go = new uicontextmenu (h, p);
924  else if (type.compare ("uitoolbar"))
925  go = new uitoolbar (h, p);
926  else if (type.compare ("uipushtool"))
927  go = new uipushtool (h, p);
928  else if (type.compare ("uitoggletool"))
929  go = new uitoggletool (h, p);
930  return go;
931 }
932 
933 // ---------------------------------------------------------------------
934 
935 bool
936 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
937 {
938  if (do_set (v))
939  {
940 
941  // Notify graphics toolkit.
942  if (id >= 0 && do_notify_toolkit)
943  {
945  if (go)
946  go.update (id);
947  }
948 
949  // run listeners
950  if (do_run && ! error_state)
952 
953  return true;
954  }
955 
956  return false;
957 }
958 
959 
960 void
962 {
963  const octave_value_list& l = listeners[mode];
964 
965  for (int i = 0; i < l.length (); i++)
966  {
968 
969  if (error_state)
970  break;
971  }
972 }
973 
974 radio_values::radio_values (const std::string& opt_string)
975  : default_val (), possible_vals ()
976 {
977  size_t beg = 0;
978  size_t len = opt_string.length ();
979  bool done = len == 0;
980 
981  while (! done)
982  {
983  size_t end = opt_string.find ('|', beg);
984 
985  if (end == std::string::npos)
986  {
987  end = len;
988  done = true;
989  }
990 
991  std::string t = opt_string.substr (beg, end-beg);
992 
993  // Might want more error checking here...
994  if (t[0] == '{')
995  {
996  t = t.substr (1, t.length () - 2);
997  default_val = t;
998  }
999  else if (beg == 0) // ensure default value
1000  default_val = t;
1001 
1002  possible_vals.insert (t);
1003 
1004  beg = end + 1;
1005  }
1006 }
1007 
1008 std::string
1010 {
1011  std::string retval;
1012  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1013  it != possible_vals.end (); it++)
1014  {
1015  if (retval == "")
1016  {
1017  if (*it == default_value ())
1018  retval = "{" + *it + "}";
1019  else
1020  retval = *it;
1021  }
1022  else
1023  {
1024  if (*it == default_value ())
1025  retval += " | {" + *it + "}";
1026  else
1027  retval += " | " + *it;
1028  }
1029  }
1030  if (retval != "")
1031  retval = "[ " + retval + " ]";
1032  return retval;
1033 }
1034 
1035 Cell
1037 {
1038  octave_idx_type i = 0;
1039  Cell retval (nelem (), 1);
1040  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1041  it != possible_vals.end (); it++)
1042  retval(i++) = std::string (*it);
1043  return retval;
1044 }
1045 
1046 bool
1047 color_values::str2rgb (std::string str)
1048 {
1049  double tmp_rgb[3] = {0, 0, 0};
1050  bool retval = true;
1051  unsigned int len = str.length ();
1052 
1053  std::transform (str.begin (), str.end (), str.begin (), tolower);
1054 
1055  if (str.compare (0, len, "blue", 0, len) == 0)
1056  tmp_rgb[2] = 1;
1057  else if (str.compare (0, len, "black", 0, len) == 0
1058  || str.compare (0, len, "k", 0, len) == 0)
1059  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1060  else if (str.compare (0, len, "red", 0, len) == 0)
1061  tmp_rgb[0] = 1;
1062  else if (str.compare (0, len, "green", 0, len) == 0)
1063  tmp_rgb[1] = 1;
1064  else if (str.compare (0, len, "yellow", 0, len) == 0)
1065  tmp_rgb[0] = tmp_rgb[1] = 1;
1066  else if (str.compare (0, len, "magenta", 0, len) == 0)
1067  tmp_rgb[0] = tmp_rgb[2] = 1;
1068  else if (str.compare (0, len, "cyan", 0, len) == 0)
1069  tmp_rgb[1] = tmp_rgb[2] = 1;
1070  else if (str.compare (0, len, "white", 0, len) == 0
1071  || str.compare (0, len, "w", 0, len) == 0)
1072  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1073  else
1074  retval = false;
1075 
1076  if (retval)
1077  {
1078  for (int i = 0; i < 3; i++)
1079  xrgb(i) = tmp_rgb[i];
1080  }
1081 
1082  return retval;
1083 }
1084 
1085 bool
1087 {
1088  if (val.is_string ())
1089  {
1090  std::string s = val.string_value ();
1091 
1092  if (! s.empty ())
1093  {
1094  std::string match;
1095 
1096  if (radio_val.contains (s, match))
1097  {
1098  if (current_type != radio_t || match != current_val)
1099  {
1100  if (s.length () != match.length ())
1101  warning_with_id ("Octave:abbreviated-property-match",
1102  "%s: allowing %s to match %s value %s",
1103  "set", s.c_str (), get_name ().c_str (),
1104  match.c_str ());
1105  current_val = match;
1107  return true;
1108  }
1109  }
1110  else
1111  {
1112  color_values col (s);
1113  if (! error_state)
1114  {
1115  if (current_type != color_t || col != color_val)
1116  {
1117  color_val = col;
1119  return true;
1120  }
1121  }
1122  else
1123  error ("invalid value for color property \"%s\" (value = %s)",
1124  get_name ().c_str (), s.c_str ());
1125  }
1126  }
1127  else
1128  error ("invalid value for color property \"%s\"",
1129  get_name ().c_str ());
1130  }
1131  else if (val.is_numeric_type ())
1132  {
1133  Matrix m = val.matrix_value ();
1134 
1135  if (m.numel () == 3)
1136  {
1137  color_values col (m(0), m(1), m(2));
1138  if (! error_state)
1139  {
1140  if (current_type != color_t || col != color_val)
1141  {
1142  color_val = col;
1144  return true;
1145  }
1146  }
1147  }
1148  else
1149  error ("invalid value for color property \"%s\"",
1150  get_name ().c_str ());
1151  }
1152  else
1153  error ("invalid value for color property \"%s\"",
1154  get_name ().c_str ());
1155 
1156  return false;
1157 }
1158 
1159 bool
1161 {
1162  if (val.is_string ())
1163  {
1164  std::string s = val.string_value ();
1165  std::string match;
1166 
1167  if (! s.empty () && radio_val.contains (s, match))
1168  {
1169  if (current_type != radio_t || match != current_val)
1170  {
1171  if (s.length () != match.length ())
1172  warning_with_id ("Octave:abbreviated-property-match",
1173  "%s: allowing %s to match %s value %s",
1174  "set", s.c_str (), get_name ().c_str (),
1175  match.c_str ());
1176  current_val = match;
1178  return true;
1179  }
1180  }
1181  else
1182  error ("invalid value for double_radio property \"%s\"",
1183  get_name ().c_str ());
1184  }
1185  else if (val.is_scalar_type () && val.is_real_type ())
1186  {
1187  double new_dval = val.double_value ();
1188 
1189  if (current_type != double_t || new_dval != dval)
1190  {
1191  dval = new_dval;
1193  return true;
1194  }
1195  }
1196  else
1197  error ("invalid value for double_radio property \"%s\"",
1198  get_name ().c_str ());
1199 
1200  return false;
1201 }
1202 
1203 bool
1205 {
1206  bool xok = false;
1207 
1208  // check value type
1209  if (type_constraints.size () > 0)
1210  {
1211  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1212  xok = true;
1213 
1214  // check if complex is allowed (it's also of class "double", so
1215  // checking that alone is not enough to ensure real type)
1216  if (type_constraints.find ("real") != type_constraints.end ()
1217  && v.is_complex_type ())
1218  xok = false;
1219  }
1220  else
1221  xok = v.is_numeric_type ();
1222 
1223  if (xok)
1224  {
1225  if (size_constraints.size () == 0)
1226  return true;
1227 
1228  dim_vector vdims = v.dims ();
1229  int vlen = vdims.length ();
1230 
1231  xok = false;
1232 
1233  // check dimensional size constraints until a match is found
1234  for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
1235  ! xok && it != size_constraints.end (); ++it)
1236  {
1237  dim_vector itdims = (*it);
1238 
1239  if (itdims.length () == vlen)
1240  {
1241  xok = true;
1242 
1243  for (int i = 0; xok && i < vlen; i++)
1244  {
1245  if (itdims(i) > 0)
1246  {
1247  if (itdims(i) != vdims(i))
1248  xok = false;
1249  }
1250  else if (v.is_empty ())
1251  break;
1252  }
1253  }
1254  }
1255  }
1256 
1257  return xok;
1258 }
1259 
1260 bool
1262 {
1263  if (data.type_name () == v.type_name ())
1264  {
1265  if (data.dims () == v.dims ())
1266  {
1267 
1268 #define CHECK_ARRAY_EQUAL(T,F,A) \
1269  { \
1270  if (data.numel () == 1) \
1271  return data.F ## scalar_value () == \
1272  v.F ## scalar_value (); \
1273  else \
1274  { \
1275  /* Keep copy of array_value to allow sparse/bool arrays */ \
1276  /* that are converted, to not be deallocated early */ \
1277  const A m1 = data.F ## array_value (); \
1278  const T* d1 = m1.data (); \
1279  const A m2 = v.F ## array_value (); \
1280  const T* d2 = m2.data ();\
1281  \
1282  bool flag = true; \
1283  \
1284  for (int i = 0; flag && i < data.numel (); i++) \
1285  if (d1[i] != d2[i]) \
1286  flag = false; \
1287  \
1288  return flag; \
1289  } \
1290  }
1291 
1292  if (data.is_double_type () || data.is_bool_type ())
1293  CHECK_ARRAY_EQUAL (double, , NDArray)
1294  else if (data.is_single_type ())
1295  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1296  else if (data.is_int8_type ())
1298  else if (data.is_int16_type ())
1300  else if (data.is_int32_type ())
1302  else if (data.is_int64_type ())
1304  else if (data.is_uint8_type ())
1306  else if (data.is_uint16_type ())
1308  else if (data.is_uint32_type ())
1310  else if (data.is_uint64_type ())
1312  }
1313  }
1314 
1315  return false;
1316 }
1317 
1318 void
1320 {
1321  xmin = xminp = octave_Inf;
1322  xmax = xmaxp = -octave_Inf;
1323 
1324  if (! data.is_empty ())
1325  {
1326  if (data.is_integer_type ())
1327  {
1328  if (data.is_int8_type ())
1330  xmin, xmax, xminp, xmaxp);
1331  else if (data.is_uint8_type ())
1333  xmin, xmax, xminp, xmaxp);
1334  else if (data.is_int16_type ())
1336  xmin, xmax, xminp, xmaxp);
1337  else if (data.is_uint16_type ())
1339  xmin, xmax, xminp, xmaxp);
1340  else if (data.is_int32_type ())
1342  xmin, xmax, xminp, xmaxp);
1343  else if (data.is_uint32_type ())
1345  xmin, xmax, xminp, xmaxp);
1346  else if (data.is_int64_type ())
1348  xmin, xmax, xminp, xmaxp);
1349  else if (data.is_uint64_type ())
1351  xmin, xmax, xminp, xmaxp);
1352  }
1353  else
1355  }
1356 }
1357 
1358 bool
1360 {
1361  double dv = v.double_value ();
1362 
1363  if (! error_state)
1364  {
1366 
1367  if (xisnan (gh.value ()) || gh.ok ())
1368  {
1369  if (current_val != gh)
1370  {
1371  current_val = gh;
1372  return true;
1373  }
1374  }
1375  else
1376  error ("set: invalid graphics handle (= %g) for property \"%s\"",
1377  dv, get_name ().c_str ());
1378  }
1379  else
1380  error ("set: invalid graphics handle for property \"%s\"",
1381  get_name ().c_str ());
1382 
1383  return false;
1384 }
1385 
1386 Matrix
1387 children_property::do_get_children (bool return_hidden) const
1388 {
1389  Matrix retval (children_list.size (), 1);
1390  octave_idx_type k = 0;
1391 
1393 
1394  root_figure::properties& props =
1395  dynamic_cast<root_figure::properties&> (go.get_properties ());
1396 
1397  if (! props.is_showhiddenhandles ())
1398  {
1399  for (const_children_list_iterator p = children_list.begin ();
1400  p != children_list.end (); p++)
1401  {
1402  graphics_handle kid = *p;
1403 
1405  {
1406  if (! return_hidden)
1407  retval(k++) = *p;
1408  }
1409  else if (return_hidden)
1410  retval(k++) = *p;
1411  }
1412 
1413  retval.resize (k, 1);
1414  }
1415  else
1416  {
1417  for (const_children_list_iterator p = children_list.begin ();
1418  p != children_list.end (); p++)
1419  retval(k++) = *p;
1420  }
1421 
1422  return retval;
1423 }
1424 
1425 void
1427 {
1428  for (children_list_iterator p = children_list.begin ();
1429  p != children_list.end (); p++)
1430  {
1432 
1433  if (go.valid_object ())
1434  gh_manager::free (*p);
1435 
1436  }
1437 
1438  if (clear)
1439  children_list.clear ();
1440 }
1441 
1442 bool
1444 {
1445  // case 1: function handle
1446  // case 2: cell array with first element being a function handle
1447  // case 3: string corresponding to known function name
1448  // case 4: evaluatable string
1449  // case 5: empty matrix
1450 
1451  if (v.is_function_handle ())
1452  return true;
1453  else if (v.is_string ())
1454  // complete validation will be done at execution-time
1455  return true;
1456  else if (v.is_cell () && v.length () > 0
1457  && (v.rows () == 1 || v.columns () == 1)
1458  && v.cell_value ()(0).is_function_handle ())
1459  return true;
1460  else if (v.is_empty ())
1461  return true;
1462 
1463  return false;
1464 }
1465 
1466 // If TRUE, we are executing any callback function, or the functions it
1467 // calls. Used to determine handle visibility inside callback
1468 // functions.
1469 static bool executing_callback = false;
1470 
1471 void
1473 {
1474  unwind_protect frame;
1475 
1476  // We are executing the callback function associated with this
1477  // callback property. When set to true, we avoid recursive calls to
1478  // callback routines.
1479  frame.protect_var (executing);
1480 
1481  // We are executing a callback function, so allow handles that have
1482  // their handlevisibility property set to "callback" to be visible.
1483  frame.protect_var (executing_callback);
1484 
1485  if (! executing)
1486  {
1487  executing = true;
1488  executing_callback = true;
1489 
1490  if (callback.is_defined () && ! callback.is_empty ())
1492  }
1493 }
1494 
1495 // Used to cache dummy graphics objects from which dynamic
1496 // properties can be cloned.
1497 static std::map<caseless_str, graphics_object> dprop_obj_map;
1498 
1499 property
1500 property::create (const std::string& name, const graphics_handle& h,
1501  const caseless_str& type, const octave_value_list& args)
1502 {
1503  property retval;
1504 
1505  if (type.compare ("string"))
1506  {
1507  std::string val = (args.length () > 0 ? args(0).string_value () : "");
1508 
1509  if (! error_state)
1510  retval = property (new string_property (name, h, val));
1511  }
1512  else if (type.compare ("any"))
1513  {
1514  octave_value val = args.length () > 0 ? args(0)
1515  : octave_value (Matrix ());
1516 
1517  retval = property (new any_property (name, h, val));
1518  }
1519  else if (type.compare ("radio"))
1520  {
1521  if (args.length () > 0)
1522  {
1523  std::string vals = args(0).string_value ();
1524 
1525  if (! error_state)
1526  {
1527  retval = property (new radio_property (name, h, vals));
1528 
1529  if (args.length () > 1)
1530  retval.set (args(1));
1531  }
1532  else
1533  error ("addproperty: invalid argument for radio property, expected a string value");
1534  }
1535  else
1536  error ("addproperty: missing possible values for radio property");
1537  }
1538  else if (type.compare ("double"))
1539  {
1540  double d = (args.length () > 0 ? args(0).double_value () : 0);
1541 
1542  if (! error_state)
1543  retval = property (new double_property (name, h, d));
1544  }
1545  else if (type.compare ("handle"))
1546  {
1547  double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
1548 
1549  if (! error_state)
1550  {
1551  graphics_handle gh (hh);
1552 
1553  retval = property (new handle_property (name, h, gh));
1554  }
1555  }
1556  else if (type.compare ("boolean"))
1557  {
1558  retval = property (new bool_property (name, h, false));
1559 
1560  if (args.length () > 0)
1561  retval.set (args(0));
1562  }
1563  else if (type.compare ("data"))
1564  {
1565  retval = property (new array_property (name, h, Matrix ()));
1566 
1567  if (args.length () > 0)
1568  {
1569  retval.set (args(0));
1570 
1571  // FIXME: additional argument could define constraints,
1572  // but is this really useful?
1573  }
1574  }
1575  else if (type.compare ("color"))
1576  {
1577  color_values cv (0, 0, 0);
1578  radio_values rv;
1579 
1580  if (args.length () > 1)
1581  rv = radio_values (args(1).string_value ());
1582 
1583  if (! error_state)
1584  {
1585  retval = property (new color_property (name, h, cv, rv));
1586 
1587  if (! error_state)
1588  {
1589  if (args.length () > 0 && ! args(0).is_empty ())
1590  retval.set (args(0));
1591  else
1592  retval.set (rv.default_value ());
1593  }
1594  }
1595  }
1596  else
1597  {
1598  caseless_str go_name, go_rest;
1599 
1600  if (lookup_object_name (type, go_name, go_rest))
1601  {
1602  graphics_object go;
1603 
1604  std::map<caseless_str, graphics_object>::const_iterator it =
1605  dprop_obj_map.find (go_name);
1606 
1607  if (it == dprop_obj_map.end ())
1608  {
1609  base_graphics_object *bgo =
1611 
1612  if (bgo)
1613  {
1614  go = graphics_object (bgo);
1615 
1616  dprop_obj_map[go_name] = go;
1617  }
1618  }
1619  else
1620  go = it->second;
1621 
1622  if (go.valid_object ())
1623  {
1624  property prop = go.get_properties ().get_property (go_rest);
1625 
1626  if (! error_state)
1627  {
1628  retval = prop.clone ();
1629 
1630  retval.set_parent (h);
1631  retval.set_name (name);
1632 
1633  if (args.length () > 0)
1634  retval.set (args(0));
1635  }
1636  }
1637  else
1638  error ("addproperty: invalid object type (= %s)",
1639  go_name.c_str ());
1640  }
1641  else
1642  error ("addproperty: unsupported type for dynamic property (= %s)",
1643  type.c_str ());
1644  }
1645 
1646  return retval;
1647 }
1648 
1649 static void
1651 {
1653 
1654  if (go)
1655  {
1656  Matrix children = go.get_properties ().get_all_children ();
1657 
1658  for (int k = 0; k < children.numel (); k++)
1659  finalize_r (children(k));
1660 
1661  go.finalize ();
1662  }
1663 }
1664 
1665 static void
1667 {
1669 
1670  if (go)
1671  {
1672  Matrix children = go.get_properties ().get_all_children ();
1673 
1674  go.initialize ();
1675 
1676  for (int k = 0; k < children.numel (); k++)
1677  initialize_r (children(k));
1678  }
1679 }
1680 
1681 void
1683 {
1684  if (toolkit)
1686 
1687  toolkit = b;
1689  __plot_stream__ = Matrix ();
1690 
1691  if (toolkit)
1693 
1694  mark_modified ();
1695 }
1696 
1697 // ---------------------------------------------------------------------
1698 
1699 void
1701 {
1702  size_t offset = 0;
1703 
1704  size_t len = name.length ();
1705 
1706  if (len > 4)
1707  {
1708  caseless_str pfx = name.substr (0, 4);
1709 
1710  if (pfx.compare ("axes") || pfx.compare ("line")
1711  || pfx.compare ("text"))
1712  offset = 4;
1713  else if (len > 5)
1714  {
1715  pfx = name.substr (0, 5);
1716 
1717  if (pfx.compare ("image") || pfx.compare ("patch"))
1718  offset = 5;
1719  else if (len > 6)
1720  {
1721  pfx = name.substr (0, 6);
1722 
1723  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1724  offset = 6;
1725  else if (len > 7)
1726  {
1727  pfx = name.substr (0, 7);
1728 
1729  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1730  || pfx.compare ("uipanel"))
1731  offset = 7;
1732  else if (len > 9)
1733  {
1734  pfx = name.substr (0, 9);
1735 
1736  if (pfx.compare ("uicontrol")
1737  || pfx.compare ("uitoolbar"))
1738  offset = 9;
1739  else if (len > 10)
1740  {
1741  pfx = name.substr (0, 10);
1742 
1743  if (pfx.compare ("uipushtool"))
1744  offset = 10;
1745  else if (len > 12)
1746  {
1747  pfx = name.substr (0, 12);
1748 
1749  if (pfx.compare ("uitoogletool"))
1750  offset = 12;
1751  else if (len > 13)
1752  {
1753  pfx = name.substr (0, 13);
1754 
1755  if (pfx.compare ("uicontextmenu"))
1756  offset = 13;
1757  }
1758  }
1759  }
1760  }
1761  }
1762  }
1763  }
1764 
1765  if (offset > 0)
1766  {
1767  // FIXME: should we validate property names and values here?
1768 
1769  std::string pname = name.substr (offset);
1770 
1771  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1772  std::transform (pname.begin (), pname.end (), pname.begin (),
1773  tolower);
1774 
1775  bool has_property = false;
1776  if (pfx == "axes")
1777  has_property = axes::properties::has_core_property (pname);
1778  else if (pfx == "line")
1779  has_property = line::properties::has_core_property (pname);
1780  else if (pfx == "text")
1781  has_property = text::properties::has_core_property (pname);
1782  else if (pfx == "image")
1783  has_property = image::properties::has_core_property (pname);
1784  else if (pfx == "patch")
1785  has_property = patch::properties::has_core_property (pname);
1786  else if (pfx == "figure")
1787  has_property = figure::properties::has_core_property (pname);
1788  else if (pfx == "surface")
1789  has_property = surface::properties::has_core_property (pname);
1790  else if (pfx == "hggroup")
1791  has_property = hggroup::properties::has_core_property (pname);
1792  else if (pfx == "uimenu")
1793  has_property = uimenu::properties::has_core_property (pname);
1794  else if (pfx == "uicontrol")
1795  has_property = uicontrol::properties::has_core_property (pname);
1796  else if (pfx == "uipanel")
1797  has_property = uipanel::properties::has_core_property (pname);
1798  else if (pfx == "uicontextmenu")
1799  has_property = uicontextmenu::properties::has_core_property (pname);
1800  else if (pfx == "uitoolbar")
1801  has_property = uitoolbar::properties::has_core_property (pname);
1802  else if (pfx == "uipushtool")
1803  has_property = uipushtool::properties::has_core_property (pname);
1804 
1805  if (has_property)
1806  {
1807  bool remove = false;
1808  if (val.is_string ())
1809  {
1810  std::string tval = val.string_value ();
1811 
1812  remove = (tval.compare ("remove") == 0);
1813  }
1814 
1815  pval_map_type& pval_map = plist_map[pfx];
1816 
1817  if (remove)
1818  {
1819  pval_map_iterator p = pval_map.find (pname);
1820 
1821  if (p != pval_map.end ())
1822  pval_map.erase (p);
1823  }
1824  else
1825  pval_map[pname] = val;
1826  }
1827  else
1828  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
1829  }
1830  }
1831 
1832  if (! error_state && offset == 0)
1833  error ("invalid default property specification");
1834 }
1835 
1838 {
1839  octave_value retval;
1840 
1841  size_t offset = 0;
1842 
1843  size_t len = name.length ();
1844 
1845  if (len > 4)
1846  {
1847  caseless_str pfx = name.substr (0, 4);
1848 
1849  if (pfx.compare ("axes") || pfx.compare ("line")
1850  || pfx.compare ("text"))
1851  offset = 4;
1852  else if (len > 5)
1853  {
1854  pfx = name.substr (0, 5);
1855 
1856  if (pfx.compare ("image") || pfx.compare ("patch"))
1857  offset = 5;
1858  else if (len > 6)
1859  {
1860  pfx = name.substr (0, 6);
1861 
1862  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1863  offset = 6;
1864  else if (len > 7)
1865  {
1866  pfx = name.substr (0, 7);
1867 
1868  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1869  || pfx.compare ("uipanel"))
1870  offset = 7;
1871  else if (len > 9)
1872  {
1873  pfx = name.substr (0, 9);
1874 
1875  if (pfx.compare ("uicontrol")
1876  || pfx.compare ("uitoolbar"))
1877  offset = 9;
1878  else if (len > 10)
1879  {
1880  pfx = name.substr (0, 10);
1881 
1882  if (pfx.compare ("uipushtool"))
1883  offset = 10;
1884  else if (len > 12)
1885  {
1886  pfx = name.substr (0, 12);
1887 
1888  if (pfx.compare ("uitoggletool"))
1889  offset = 12;
1890  else if (len > 13)
1891  {
1892  pfx = name.substr (0, 13);
1893 
1894  if (pfx.compare ("uicontextmenu"))
1895  offset = 13;
1896  }
1897  }
1898  }
1899  }
1900  }
1901  }
1902  }
1903 
1904  if (offset > 0)
1905  {
1906  std::string pname = name.substr (offset);
1907 
1908  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1909  std::transform (pname.begin (), pname.end (), pname.begin (),
1910  tolower);
1911 
1912  plist_map_const_iterator p = find (pfx);
1913 
1914  if (p != end ())
1915  {
1916  const pval_map_type& pval_map = p->second;
1917 
1918  pval_map_const_iterator q = pval_map.find (pname);
1919 
1920  if (q != pval_map.end ())
1921  retval = q->second;
1922  }
1923  }
1924  }
1925 
1926  return retval;
1927 }
1928 
1930 property_list::as_struct (const std::string& prefix_arg) const
1931 {
1933 
1934  for (plist_map_const_iterator p = begin (); p != end (); p++)
1935  {
1936  std::string prefix = prefix_arg + p->first;
1937 
1938  const pval_map_type pval_map = p->second;
1939 
1940  for (pval_map_const_iterator q = pval_map.begin ();
1941  q != pval_map.end ();
1942  q++)
1943  m.assign (prefix + q->first, q->second);
1944  }
1945 
1946  return m;
1947 }
1948 
1949 // Set properties given as a cs-list of name, value pairs.
1950 
1951 void
1953 {
1954  int nargin = args.length ();
1955 
1956  if (nargin == 0)
1957  error ("graphics_object::set: Nothing to set");
1958  else if (nargin % 2 == 0)
1959  {
1960  for (int i = 0; i < nargin; i += 2)
1961  {
1962  caseless_str name = args(i).string_value ();
1963 
1964  if (! error_state)
1965  {
1966  octave_value val = args(i+1);
1967 
1968  set_value_or_default (name, val);
1969 
1970  if (error_state)
1971  break;
1972  }
1973  else
1974  error ("set: expecting argument %d to be a property name", i);
1975  }
1976  }
1977  else
1978  error ("set: invalid number of arguments");
1979 }
1980 
1981 /*
1982 ## test set with name, value pairs
1983 %!test
1984 %! hf = figure ("visible", "off");
1985 %! h = plot (1:10, 10:-1:1);
1986 %! set (h, "linewidth", 10, "marker", "x");
1987 %! lw = get (h, "linewidth");
1988 %! mk = get (h, "marker");
1989 %! close (hf);
1990 %! assert (lw, 10);
1991 %! assert (mk, "x");
1992 */
1993 
1994 // Set properties given in two cell arrays containing names and values.
1995 void
1997  const Cell& values, octave_idx_type row)
1998 {
1999  if (names.numel () != values.columns ())
2000  {
2001  error ("set: number of names must match number of value columns (%d != %d)",
2002  names.numel (), values.columns ());
2003  }
2004 
2005  octave_idx_type k = names.columns ();
2006 
2007  for (octave_idx_type column = 0; column < k; column++)
2008  {
2009  caseless_str name = names(column);
2010  octave_value val = values(row, column);
2011 
2012  set_value_or_default (name, val);
2013 
2014  if (error_state)
2015  break;
2016  }
2017 }
2018 
2019 /*
2020 ## test set with cell array arguments
2021 %!test
2022 %! hf = figure ("visible", "off");
2023 %! h = plot (1:10, 10:-1:1);
2024 %! set (h, {"linewidth", "marker"}, {10, "x"});
2025 %! lw = get (h, "linewidth");
2026 %! mk = get (h, "marker");
2027 %! close (hf);
2028 %! assert (lw, 10);
2029 %! assert (mk, "x");
2030 
2031 ## test set with multiple handles and cell array arguments
2032 %!test
2033 %! hf = figure ("visible", "off");
2034 %! unwind_protect
2035 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2036 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2037 %! assert (get (h, "linewidth"), {10; 5});
2038 %! assert (get (h, "marker"), {"x"; "o"});
2039 %! set (h, {"linewidth", "marker"}, {10, "x"});
2040 %! assert (get (h, "linewidth"), {10; 10});
2041 %! assert (get (h, "marker"), {"x"; "x"});
2042 %! unwind_protect_cleanup
2043 %! close (hf);
2044 %! end_unwind_protect;
2045 
2046 %!error <set: number of graphics handles must match number of value rows>
2047 %! hf = figure ("visible", "off");
2048 %! unwind_protect
2049 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2050 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2051 %! unwind_protect_cleanup
2052 %! close (hf);
2053 %! end_unwind_protect
2054 
2055 %!error <set: number of names must match number of value columns>
2056 %! hf = figure ("visible", "off");
2057 %! unwind_protect
2058 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2059 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2060 %! unwind_protect_cleanup
2061 %! close (hf);
2062 %! end_unwind_protect
2063 */
2064 
2065 // Set properties given in a struct array
2066 void
2068 {
2069  for (octave_idx_type p = 0; p < m.nfields (); p++)
2070  {
2071  caseless_str name = m.keys ()[p];
2072 
2073  octave_value val = octave_value (m.contents (name).elem (m.numel () - 1));
2074 
2075  set_value_or_default (name, val);
2076 
2077  if (error_state)
2078  break;
2079  }
2080 }
2081 
2082 /*
2083 ## test set ticklabels for compatibility
2084 %!test
2085 %! hf = figure ("visible", "off");
2086 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2087 %! xticklabel = get (gca (), "xticklabel");
2088 %! close (hf);
2089 %! assert (class (xticklabel), "char");
2090 %! assert (size (xticklabel), [6, 3]);
2091 
2092 %!test
2093 %! hf = figure ("visible", "off");
2094 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2095 %! xticklabel = get (gca (), "xticklabel");
2096 %! close (hf);
2097 %! assert (class (xticklabel), "char");
2098 %! assert (size (xticklabel), [6, 3]);
2099 
2100 %!test
2101 %! hf = figure ("visible", "off");
2102 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2103 %! xticklabel = get (gca (), "xticklabel");
2104 %! close (hf);
2105 %! assert (class (xticklabel), "char");
2106 %! assert (size (xticklabel), [6, 3]);
2107 
2108 %!test
2109 %! hf = figure ("visible", "off");
2110 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2111 %! xticklabel = get (gca (), "xticklabel");
2112 %! close (hf);
2113 %! assert (class (xticklabel), "cell");
2114 %! assert (size (xticklabel), [6, 1]);
2115 */
2116 
2117 /*
2118 ## test set with struct arguments
2119 %!test
2120 %! hf = figure ("visible", "off");
2121 %! unwind_protect
2122 %! h = plot (1:10, 10:-1:1);
2123 %! set (h, struct ("linewidth", 10, "marker", "x"));
2124 %! assert (get (h, "linewidth"), 10);
2125 %! assert (get (h, "marker"), "x");
2126 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2127 %! set (h, struct ("linewidth", {5, 10}));
2128 %! assert (get (h, "linewidth"), {10; 10});
2129 %! unwind_protect_cleanup
2130 %! close (hf);
2131 %! end_unwind_protect
2132 
2133 ## test ordering
2134 %!test
2135 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2136 %! hf = figure ("visible", "off");
2137 %! unwind_protect
2138 %! h = line ();
2139 %! set (h, "userdata", {});
2140 %! addlistener (h, "color", {markchanged, "color"});
2141 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2142 %! ## "linewidth" first
2143 %! props.linewidth = 2;
2144 %! props.color = "r";
2145 %! set (h, props);
2146 %! assert (get (h, "userdata"), fieldnames (props));
2147 %! clear props;
2148 %! clf ();
2149 %! h = line ();
2150 %! set (h, "userdata", {});
2151 %! addlistener (h, "color", {markchanged, "color"});
2152 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2153 %! ## "color" first
2154 %! props.color = "r";
2155 %! props.linewidth = 2;
2156 %! set (h, props);
2157 %! assert (get (h, "userdata"), fieldnames (props));
2158 %! unwind_protect_cleanup
2159 %! close (hf);
2160 %! end_unwind_protect
2161 */
2162 
2163 // Set a property to a value or to its (factory) default value.
2164 
2165 void
2167  const octave_value& val)
2168 {
2169  if (val.is_string ())
2170  {
2171  std::string tval = val.string_value ();
2172 
2173  octave_value default_val;
2174 
2175  if (tval.compare ("default") == 0)
2176  {
2177  default_val = get_default (name);
2178 
2179  if (error_state)
2180  return;
2181 
2182  rep->set (name, default_val);
2183  }
2184  else if (tval.compare ("factory") == 0)
2185  {
2186  default_val = get_factory_default (name);
2187 
2188  if (error_state)
2189  return;
2190 
2191  rep->set (name, default_val);
2192  }
2193  else
2194  {
2195  // Matlab specifically uses "\default" to escape string setting
2196  if (tval.compare ("\\default") == 0)
2197  rep->set (name, "default");
2198  else if (tval.compare ("\\factory") == 0)
2199  rep->set (name, "factory");
2200  else
2201  rep->set (name, val);
2202  }
2203  }
2204  else
2205  rep->set (name, val);
2206 }
2207 
2208 /*
2209 ## test setting of default values
2210 %!test
2211 %! old_lw = get (0, "defaultlinelinewidth");
2212 %! unwind_protect
2213 %! hf = figure ("visible", "off");
2214 %! h = plot (1:10, 10:-1:1);
2215 %! set (0, "defaultlinelinewidth", 20);
2216 %! set (h, "linewidth", "default");
2217 %! assert (get (h, "linewidth"), 20);
2218 %! set (h, "linewidth", "factory");
2219 %! assert (get (h, "linewidth"), 0.5);
2220 %! unwind_protect_cleanup
2221 %! close (hf);
2222 %! set (0, "defaultlinelinewidth", old_lw);
2223 %! end_unwind_protect
2224 */
2225 
2226 static double
2228 {
2229  static double maxrand = RAND_MAX + 2.0;
2230 
2231  return (rand () + 1.0) / maxrand;
2232 }
2233 
2235 gh_manager::do_get_handle (bool integer_figure_handle)
2236 {
2237  graphics_handle retval;
2238 
2239  if (integer_figure_handle)
2240  {
2241  // Figure handles are positive integers corresponding to the
2242  // figure number.
2243 
2244  // We always want the lowest unused figure number.
2245 
2246  retval = 1;
2247 
2248  while (handle_map.find (retval) != handle_map.end ())
2249  retval++;
2250  }
2251  else
2252  {
2253  // Other graphics handles are negative integers plus some random
2254  // fractional part. To avoid running out of integers, we
2255  // recycle the integer part but tack on a new random part each
2256  // time.
2257 
2258  free_list_iterator p = handle_free_list.begin ();
2259 
2260  if (p != handle_free_list.end ())
2261  {
2262  retval = *p;
2263  handle_free_list.erase (p);
2264  }
2265  else
2266  {
2267  retval = graphics_handle (next_handle);
2268 
2269  next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2270  }
2271  }
2272 
2273  return retval;
2274 }
2275 
2276 void
2278 {
2279  if (h.ok ())
2280  {
2281  if (h.value () != 0)
2282  {
2283  iterator p = handle_map.find (h);
2284 
2285  if (p != handle_map.end ())
2286  {
2287  base_properties& bp = p->second.get_properties ();
2288 
2289  bp.set_beingdeleted (true);
2290 
2291  bp.delete_children ();
2292 
2293  octave_value val = bp.get_deletefcn ();
2294 
2295  bp.execute_deletefcn ();
2296 
2297  // Notify graphics toolkit.
2298  p->second.finalize ();
2299 
2300  // Note: this will be valid only for first explicitly
2301  // deleted object. All its children will then have an
2302  // unknown graphics toolkit.
2303 
2304  // Graphics handles for non-figure objects are negative
2305  // integers plus some random fractional part. To avoid
2306  // running out of integers, we recycle the integer part
2307  // but tack on a new random part each time.
2308 
2309  handle_map.erase (p);
2310 
2311  if (h.value () < 0)
2312  handle_free_list.insert
2313  (std::ceil (h.value ()) - make_handle_fraction ());
2314  }
2315  else
2316  error ("graphics_handle::free: invalid object %g", h.value ());
2317  }
2318  else
2319  error ("graphics_handle::free: can't delete root figure");
2320  }
2321 }
2322 
2323 void
2325  const graphics_handle& new_gh)
2326 {
2327  iterator p = handle_map.find (old_gh);
2328 
2329  if (p != handle_map.end ())
2330  {
2331  graphics_object go = p->second;
2332 
2333  handle_map.erase (p);
2334 
2335  handle_map[new_gh] = go;
2336 
2337  if (old_gh.value () < 0)
2338  handle_free_list.insert (std::ceil (old_gh.value ())
2339  - make_handle_fraction ());
2340  }
2341  else
2342  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2343 
2344  for (figure_list_iterator q = figure_list.begin ();
2345  q != figure_list.end (); q++)
2346  {
2347  if (*q == old_gh)
2348  {
2349  *q = new_gh;
2350  break;
2351  }
2352  }
2353 }
2354 
2356 
2357 static void
2358 xset (const graphics_handle& h, const caseless_str& name,
2359  const octave_value& val)
2360 {
2362  obj.set (name, val);
2363 }
2364 
2365 static void
2366 xset (const graphics_handle& h, const octave_value_list& args)
2367 {
2368  if (args.length () > 0)
2369  {
2371  obj.set (args);
2372  }
2373 }
2374 
2375 static octave_value
2376 xget (const graphics_handle& h, const caseless_str& name)
2377 {
2379  return obj.get (name);
2380 }
2381 
2382 static graphics_handle
2383 reparent (const octave_value& ov, const std::string& who,
2384  const std::string& property, const graphics_handle& new_parent,
2385  bool adopt = true)
2386 {
2388 
2389  double val = ov.double_value ();
2390 
2391  if (! error_state)
2392  {
2393  h = gh_manager::lookup (val);
2394 
2395  if (h.ok ())
2396  {
2398 
2399  graphics_handle parent_h = obj.get_parent ();
2400 
2401  graphics_object parent_obj = gh_manager::get_object (parent_h);
2402 
2403  parent_obj.remove_child (h);
2404 
2405  if (adopt)
2406  obj.set ("parent", new_parent.value ());
2407  else
2408  obj.reparent (new_parent);
2409  }
2410  else
2411  error ("%s: invalid graphics handle (= %g) for %s",
2412  who.c_str (), val, property.c_str ());
2413  }
2414  else
2415  error ("%s: expecting %s to be a graphics handle",
2416  who.c_str (), property.c_str ());
2417 
2418  return h;
2419 }
2420 
2421 // This function is NOT equivalent to the scripting language function gcf.
2423 gcf (void)
2424 {
2425  octave_value val = xget (0, "currentfigure");
2426 
2427  return val.is_empty () ? octave_NaN : val.double_value ();
2428 }
2429 
2430 // This function is NOT equivalent to the scripting language function gca.
2432 gca (void)
2433 {
2434  octave_value val = xget (gcf (), "currentaxes");
2435 
2436  return val.is_empty () ? octave_NaN : val.double_value ();
2437 }
2438 
2439 static void
2441 {
2442  if (h.ok ())
2443  {
2445 
2446  // Don't do recursive deleting, due to callbacks
2447  if (! obj.get_properties ().is_beingdeleted ())
2448  {
2449  graphics_handle parent_h = obj.get_parent ();
2450 
2451  graphics_object parent_obj =
2452  gh_manager::get_object (parent_h);
2453 
2454  // NOTE: free the handle before removing it from its
2455  // parent's children, such that the object's
2456  // state is correct when the deletefcn callback
2457  // is executed
2458 
2459  gh_manager::free (h);
2460 
2461  // A callback function might have already deleted
2462  // the parent
2463  if (parent_obj.valid_object ())
2464  parent_obj.remove_child (h);
2465 
2466  Vdrawnow_requested = true;
2467  }
2468  }
2469 }
2470 
2471 static void
2473 {
2475 }
2476 
2477 static void
2479 {
2480  for (octave_idx_type i = 0; i < vals.numel (); i++)
2481  delete_graphics_object (vals.elem (i));
2482 }
2483 
2484 static void
2486 {
2487  octave_value closerequestfcn = xget (handle, "closerequestfcn");
2488 
2489  OCTAVE_SAFE_CALL (gh_manager::execute_callback, (handle, closerequestfcn));
2490 }
2491 
2492 static void
2494 {
2495  // Remove the deletefcn and closerequestfcn callbacks and delete the
2496  // object directly.
2497 
2498  xset (handle, "deletefcn", Matrix ());
2499  xset (handle, "closerequestfcn", Matrix ());
2500 
2501  delete_graphics_object (handle);
2502 }
2503 
2504 void
2506 {
2507  // FIXME: should we process or discard pending events?
2508 
2509  event_queue.clear ();
2510 
2511  // Don't use figure_list_iterator because we'll be removing elements
2512  // from the list elsewhere.
2513 
2514  Matrix hlist = do_figure_handle_list (true);
2515 
2516  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2517  {
2518  graphics_handle h = gh_manager::lookup (hlist(i));
2519 
2520  if (h.ok ())
2521  close_figure (h);
2522  }
2523 
2524  // They should all be closed now. If not, force them to close.
2525 
2526  hlist = do_figure_handle_list (true);
2527 
2528  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2529  {
2530  graphics_handle h = gh_manager::lookup (hlist(i));
2531 
2532  if (h.ok ())
2533  force_close_figure (h);
2534  }
2535 
2536  // None left now, right?
2537 
2538  hlist = do_figure_handle_list (true);
2539 
2540  assert (hlist.numel () == 0);
2541 
2542  // Clear all callback objects from our list.
2543 
2544  callback_objects.clear ();
2545 }
2546 
2547 static void
2549 {
2550  graphics_object parent_obj = gh_manager::get_object (p);
2551  parent_obj.adopt (h);
2552 }
2553 
2554 static bool
2556 {
2557  return h.ok ();
2558 }
2559 
2560 static bool
2561 is_handle (double val)
2562 {
2564 
2565  return h.ok ();
2566 }
2567 
2568 static octave_value
2570 {
2571  octave_value retval = false;
2572 
2573  if (val.is_real_scalar () && is_handle (val.double_value ()))
2574  retval = true;
2575  else if (val.is_numeric_type () && val.is_real_type ())
2576  {
2577  const NDArray handles = val.array_value ();
2578 
2579  if (! error_state)
2580  {
2581  boolNDArray result (handles.dims ());
2582 
2583  for (octave_idx_type i = 0; i < handles.numel (); i++)
2584  result.xelem (i) = is_handle (handles (i));
2585 
2586  retval = result;
2587  }
2588  }
2589 
2590  return retval;
2591 }
2592 
2593 static bool
2594 is_figure (double val)
2595 {
2597 
2598  return obj && obj.isa ("figure");
2599 }
2600 
2601 static void
2603 {
2606 }
2607 
2608 static void
2610 {
2612 
2613  if (go)
2614  go.initialize ();
2615 }
2616 
2617 // ---------------------------------------------------------------------
2618 
2619 void
2621 {
2623 
2624  update (go, id);
2625 }
2626 
2627 bool
2629 {
2631 
2632  return initialize (go);
2633 }
2634 
2635 void
2637 {
2639 
2640  finalize (go);
2641 }
2642 
2643 // ---------------------------------------------------------------------
2644 
2645 void
2648 {
2649  std::string go_name = graphics_object_name ();
2650 
2651  property_list::plist_map_const_iterator p = defaults.find (go_name);
2652 
2653  if (p != defaults.end ())
2654  {
2655  const property_list::pval_map_type pval_map = p->second;
2656 
2657  for (property_list::pval_map_const_iterator q = pval_map.begin ();
2658  q != pval_map.end ();
2659  q++)
2660  {
2661  std::string pname = q->first;
2662 
2663  obj.set (pname, q->second);
2664 
2665  if (error_state)
2666  {
2667  error ("error setting default property %s", pname.c_str ());
2668  break;
2669  }
2670  }
2671  }
2672 }
2673 
2676 {
2677  octave_value retval;
2678 
2679  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2680  all_props.find (name);
2681 
2682  if (it != all_props.end ())
2683  retval = it->second.get ();
2684  else
2685  error ("get: unknown property \"%s\"", name.c_str ());
2686 
2687  return retval;
2688 }
2689 
2692 {
2694 
2695  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
2696  it = all_props.begin (); it != all_props.end (); ++it)
2697  if (all || ! it->second.is_hidden ())
2698  m.assign (it->second.get_name (), it->second.get ());
2699 
2700  return m;
2701 }
2702 
2703 std::set<std::string>
2705 {
2706  return dynamic_properties;
2707 }
2708 
2709 bool
2710 base_properties::has_dynamic_property (const std::string& pname)
2711 {
2712  const std::set<std::string>& dynprops = dynamic_property_names ();
2713 
2714  if (dynprops.find (pname) != dynprops.end ())
2715  return true;
2716  else
2717  return all_props.find (pname) != all_props.end ();
2718 }
2719 
2720 void
2722  const octave_value& val)
2723 {
2724  std::map<caseless_str, property, cmp_caseless_str>::iterator it =
2725  all_props.find (pname);
2726 
2727  if (it != all_props.end ())
2728  it->second.set (val);
2729  else
2730  error ("set: unknown property \"%s\"", pname.c_str ());
2731 
2732  if (! error_state)
2733  {
2734  dynamic_properties.insert (pname);
2735 
2736  mark_modified ();
2737  }
2738 }
2739 
2740 property
2742 {
2743  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2744  all_props.find (name);
2745 
2746  if (it == all_props.end ())
2747  {
2748  error ("get_property: unknown property \"%s\"", name.c_str ());
2749  return property ();
2750  }
2751  else
2752  return it->second;
2753 }
2754 
2755 void
2757 {
2758  double hnp = val.double_value ();
2759 
2760  graphics_handle new_parent = octave_NaN;
2761 
2762  if (! error_state)
2763  {
2764  if (hnp == __myhandle__)
2765  error ("set: can not set object parent to be object itself");
2766  else
2767  {
2768  new_parent = gh_manager::lookup (hnp);
2769 
2770  if (new_parent.ok ())
2771  {
2772  // Remove child from current parent
2773  graphics_object old_parent_obj;
2774  old_parent_obj = gh_manager::get_object (get_parent ());
2775  old_parent_obj.remove_child (__myhandle__);
2776 
2777  // Check new parent's parent is not this child to avoid recursion
2778  graphics_object new_parent_obj;
2779  new_parent_obj = gh_manager::get_object (new_parent);
2780  if (new_parent_obj.get_parent () == __myhandle__)
2781  {
2782  // new parent's parent gets child's original parent
2783  new_parent_obj.get_properties ().set_parent (get_parent ().as_octave_value ());
2784  }
2785 
2786  // Set parent property to new_parent and do adoption
2787  parent = new_parent.as_octave_value ();
2788  ::adopt (parent.handle_value (), __myhandle__);
2789  }
2790  else
2791  error ("set: invalid graphics handle (= %g) for parent", hnp);
2792  }
2793  }
2794  else
2795  error ("set: expecting parent to be a graphics handle");
2796 }
2797 
2798 void
2800 {
2801  __modified__ = "on";
2803  if (parent_obj)
2804  parent_obj.mark_modified ();
2805 }
2806 
2807 void
2809 {
2811 
2812  if (parent_obj)
2813  parent_obj.override_defaults (obj);
2814 }
2815 
2816 void
2817 base_properties::update_axis_limits (const std::string& axis_type) const
2818 {
2819  graphics_object obj = gh_manager::get_object (__myhandle__);
2820 
2821  if (obj)
2822  obj.update_axis_limits (axis_type);
2823 }
2824 
2825 void
2826 base_properties::update_axis_limits (const std::string& axis_type,
2827  const graphics_handle& h) const
2828 {
2829  graphics_object obj = gh_manager::get_object (__myhandle__);
2830 
2831  if (obj)
2832  obj.update_axis_limits (axis_type, h);
2833 }
2834 
2835 bool
2837 {
2838  return (handlevisibility.is ("on")
2839  || (executing_callback && ! handlevisibility.is ("off")));
2840 }
2841 
2844 {
2846 
2847  if (go)
2848  return go.get_toolkit ();
2849  else
2850  return graphics_toolkit ();
2851 }
2852 
2853 void
2855 {
2856  Matrix kids = get_children ();
2857 
2858  for (int i = 0; i < kids.numel (); i++)
2859  {
2860  graphics_object go = gh_manager::get_object (kids(i));
2861 
2862  if (go.valid_object ())
2864  }
2865 }
2866 
2867 void
2868 base_properties::update_autopos (const std::string& elem_type)
2869 {
2871 
2872  if (parent_obj.valid_object ())
2873  parent_obj.get_properties ().update_autopos (elem_type);
2874 }
2875 
2876 void
2878  listener_mode mode)
2879 {
2880  property p = get_property (nm);
2881 
2882  if (! error_state && p.ok ())
2883  p.add_listener (v, mode);
2884 }
2885 
2886 void
2888  const octave_value& v, listener_mode mode)
2889 {
2890  property p = get_property (nm);
2891 
2892  if (! error_state && p.ok ())
2893  p.delete_listener (v, mode);
2894 }
2895 
2896 // ---------------------------------------------------------------------
2897 
2898 void
2899 base_graphics_object::update_axis_limits (const std::string& axis_type)
2900 {
2901  if (valid_object ())
2902  {
2904 
2905  if (parent_obj)
2906  parent_obj.update_axis_limits (axis_type);
2907  }
2908  else
2909  error ("base_graphics_object::update_axis_limits: invalid graphics object");
2910 }
2911 
2912 void
2913 base_graphics_object::update_axis_limits (const std::string& axis_type,
2914  const graphics_handle& h)
2915 {
2916  if (valid_object ())
2917  {
2919 
2920  if (parent_obj)
2921  parent_obj.update_axis_limits (axis_type, h);
2922  }
2923  else
2924  error ("base_graphics_object::update_axis_limits: invalid graphics object");
2925 }
2926 
2927 void
2929 {
2930  octave_map m = get (true).map_value ();
2931 
2932  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2933  {
2934  // FIXME: there has to be a better way. I think we want to
2935  // ask whether it is OK to delete the listener for the given
2936  // property. How can we know in advance that it will be OK?
2937 
2938  unwind_protect frame;
2939 
2940  frame.protect_var (error_state);
2942  frame.protect_var (Vdebug_on_error);
2944 
2945  discard_error_messages = true;
2946  Vdebug_on_error = false;
2947  Vdebug_on_warning = false;
2948 
2949  property p = get_properties ().get_property (pa->first);
2950 
2951  if (! error_state && p.ok ())
2952  p.delete_listener ();
2953  }
2954 }
2955 
2956 std::string
2958 {
2959  std::string retval;
2960 
2961  if (valid_object ())
2962  {
2963  octave_map m = get ().map_value ();
2964 
2965  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2966  {
2967  if (pa->first != "children")
2968  {
2969  property p = get_properties ().get_property (pa->first);
2970 
2971  if (p.ok () && ! p.is_hidden ())
2972  {
2973  retval += "\n\t" + std::string (pa->first) + ": ";
2974  if (p.is_radio ())
2975  retval += p.values_as_string ();
2976  }
2977  }
2978  }
2979  if (retval != "")
2980  retval += "\n";
2981  }
2982  else
2983  error ("base_graphics_object::values_as_string: invalid graphics object");
2984 
2985  return retval;
2986 }
2987 
2990 {
2991  octave_scalar_map retval;
2992 
2993  if (valid_object ())
2994  {
2995  octave_scalar_map m = get ().scalar_map_value ();
2996 
2998  pa != m.end (); pa++)
2999  {
3000  if (pa->first != "children")
3001  {
3002  property p = get_properties ().get_property (pa->first);
3003 
3004  if (p.ok () && ! p.is_hidden ())
3005  {
3006  if (p.is_radio ())
3007  retval.assign (p.get_name (), p.values_as_cell ());
3008  else
3009  retval.assign (p.get_name (), Cell ());
3010  }
3011  }
3012  }
3013  }
3014  else
3015  error ("base_graphics_object::values_as_struct: invalid graphics object");
3016 
3017  return retval;
3018 }
3019 
3021 graphics_object::get_ancestor (const std::string& obj_type) const
3022 {
3023  if (valid_object ())
3024  {
3025  if (isa (obj_type))
3026  return *this;
3027  else
3028  return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
3029  }
3030  else
3031  return graphics_object ();
3032 }
3033 
3034 // ---------------------------------------------------------------------
3035 
3036 #include "graphics-props.cc"
3037 
3038 // ---------------------------------------------------------------------
3039 
3040 void
3042 {
3043  graphics_handle val (v);
3044 
3045  if (error_state)
3046  return;
3047 
3048  if (xisnan (val.value ()) || is_handle (val))
3049  {
3050  currentfigure = val;
3051 
3052  if (val.ok ())
3054  }
3055  else
3056  gripe_set_invalid ("currentfigure");
3057 }
3058 
3059 void
3061 {
3062  graphics_handle val (v);
3063 
3064  if (error_state)
3065  return;
3066 
3067  if (xisnan (val.value ()))
3068  {
3069  if (! cbo_stack.empty ())
3070  {
3071  val = cbo_stack.front ();
3072 
3073  cbo_stack.pop_front ();
3074  }
3075 
3076  callbackobject = val;
3077  }
3078  else if (is_handle (val))
3079  {
3080  if (get_callbackobject ().ok ())
3081  cbo_stack.push_front (get_callbackobject ());
3082 
3083  callbackobject = val;
3084  }
3085  else
3086  gripe_set_invalid ("callbackobject");
3087 }
3088 
3089 void
3091 {
3092  if (! error_state)
3093  {
3094  if (integerhandle.set (val, true))
3095  {
3096  bool int_fig_handle = integerhandle.is_on ();
3097 
3098  graphics_object this_go = gh_manager::get_object (__myhandle__);
3099 
3100  graphics_handle old_myhandle = __myhandle__;
3101 
3102  __myhandle__ = gh_manager::get_handle (int_fig_handle);
3103 
3104  gh_manager::renumber_figure (old_myhandle, __myhandle__);
3105 
3107 
3108  base_properties& props = parent_go.get_properties ();
3109 
3110  props.renumber_child (old_myhandle, __myhandle__);
3111 
3112  Matrix kids = get_children ();
3113 
3114  for (octave_idx_type i = 0; i < kids.numel (); i++)
3115  {
3116  graphics_object kid = gh_manager::get_object (kids(i));
3117 
3118  kid.get_properties ().renumber_parent (__myhandle__);
3119  }
3120 
3122 
3123  if (__myhandle__ == cf)
3124  xset (0, "currentfigure", __myhandle__.value ());
3125 
3126  this_go.update (integerhandle.get_id ());
3127 
3128  mark_modified ();
3129  }
3130  }
3131 }
3132 
3133 // FIXME: This should update monitorpositions and pointerlocation, but
3134 // as these properties are yet used, and so it doesn't matter that they
3135 // aren't set yet.
3136 void
3138 {
3139  caseless_str xunits = get_units ();
3140 
3141  Matrix ss = default_screensize ();
3142 
3143  double dpi = get_screenpixelsperinch ();
3144 
3145  if (xunits.compare ("inches"))
3146  {
3147  ss(0) = 0;
3148  ss(1) = 0;
3149  ss(2) /= dpi;
3150  ss(3) /= dpi;
3151  }
3152  else if (xunits.compare ("centimeters"))
3153  {
3154  ss(0) = 0;
3155  ss(1) = 0;
3156  ss(2) *= 2.54 / dpi;
3157  ss(3) *= 2.54 / dpi;
3158  }
3159  else if (xunits.compare ("normalized"))
3160  {
3161  ss = Matrix (1, 4, 1.0);
3162  ss(0) = 0;
3163  ss(1) = 0;
3164  }
3165  else if (xunits.compare ("points"))
3166  {
3167  ss(0) = 0;
3168  ss(1) = 0;
3169  ss(2) *= 72 / dpi;
3170  ss(3) *= 72 / dpi;
3171  }
3172 
3173  set_screensize (ss);
3174 }
3175 
3176 Matrix
3178 {
3179  Matrix screen_size = screen_size_pixels ();
3180  Matrix pos = Matrix (1, 4, 0);
3181  pos(2) = screen_size(0);
3182  pos(3) = screen_size(1);
3183  return pos;
3184 }
3185 
3186 /*
3187 %!test
3188 %! old_units = get (0, "units");
3189 %! unwind_protect
3190 %! set (0, "units", "pixels");
3191 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3192 %! dpi = get (0, "screenpixelsperinch");
3193 %! set (0, "units", "inches");
3194 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3195 %! set (0, "units", "centimeters");
3196 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3197 %! set (0, "units", "points");
3198 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3199 %! set (0, "units", "normalized");
3200 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3201 %! set (0, "units", "pixels");
3202 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3203 %! unwind_protect_cleanup
3204 %! set (0, "units", old_units);
3205 %! end_unwind_protect
3206 */
3207 
3208 void
3210 {
3212 
3214 
3215  xset (0, "currentfigure", cf.value ());
3216 
3218 }
3219 
3222 
3223 static void
3225 {
3226  property_list new_defaults;
3227 
3228  for (property_list::plist_map_const_iterator p = default_properties.begin ();
3229  p != default_properties.end (); p++)
3230  {
3231  const property_list::pval_map_type pval_map = p->second;
3232  std::string prefix = p->first;
3233 
3234  for (property_list::pval_map_const_iterator q = pval_map.begin ();
3235  q != pval_map.end ();
3236  q++)
3237  {
3238  std::string s = q->first;
3239 
3240  if (prefix == "axes" && (s == "position" || s == "units"))
3241  new_defaults.set (prefix + s, q->second);
3242  else if (prefix == "figure" && (s == "position" || s == "units"
3243  || s == "windowstyle"
3244  || s == "paperunits"))
3245  new_defaults.set (prefix + s, q->second);
3246  }
3247  }
3248 
3249  default_properties = new_defaults;
3250 }
3251 
3252 void
3254 {
3256 }
3257 
3258 // ---------------------------------------------------------------------
3259 
3260 void
3262 {
3263  graphics_handle val (v);
3264 
3265  if (error_state)
3266  return;
3267 
3268  if (xisnan (val.value ()) || is_handle (val))
3269  currentaxes = val;
3270  else
3271  gripe_set_invalid ("currentaxes");
3272 }
3273 
3274 void
3276 {
3278 
3279  if (gh == currentaxes.handle_value ())
3280  {
3281  graphics_handle new_currentaxes;
3282 
3283  Matrix kids = get_children ();
3284 
3285  for (octave_idx_type i = 0; i < kids.numel (); i++)
3286  {
3287  graphics_handle kid = kids(i);
3288 
3290 
3291  if (go.isa ("axes"))
3292  {
3293  new_currentaxes = kid;
3294  break;
3295  }
3296  }
3297 
3298  currentaxes = new_currentaxes;
3299  }
3300 }
3301 
3302 void
3304 {
3305  std::string s = val.string_value ();
3306 
3307  if (! error_state)
3308  {
3309  if (s == "on")
3310  xset (0, "currentfigure", __myhandle__.value ());
3311 
3312  visible = val;
3313  }
3314 }
3315 
3316 Matrix
3317 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3318 {
3319  Matrix screen_size = screen_size_pixels ();
3320  Matrix pos = (internal ?
3321  get_position ().matrix_value () :
3322  get_outerposition ().matrix_value ());
3323 
3324  pos = convert_position (pos, get_units (), "pixels", screen_size);
3325 
3326  pos(0)--;
3327  pos(1)--;
3328  pos(1) = screen_size(1) - pos(1) - pos(3);
3329 
3330  return pos;
3331 }
3332 
3333 void
3335  bool do_notify_toolkit)
3336 {
3337  Matrix screen_size = screen_size_pixels ();
3338  Matrix pos = bb;
3339 
3340  pos(1) = screen_size(1) - pos(1) - pos(3);
3341  pos(1)++;
3342  pos(0)++;
3343  pos = convert_position (pos, "pixels", get_units (), screen_size);
3344 
3345  if (internal)
3346  set_position (pos, do_notify_toolkit);
3347  else
3348  set_outerposition (pos, do_notify_toolkit);
3349 }
3350 
3351 Matrix
3353 {
3354  Matrix bb = get_boundingbox (true);
3355  Matrix pos (1, 2, 0);
3356 
3357  pos(0) = x;
3358  pos(1) = y;
3359 
3360  pos(1) = bb(3) - pos(1);
3361  pos(0)++;
3362  pos = convert_position (pos, "pixels", get_units (),
3363  bb.extract_n (0, 2, 1, 2));
3364 
3365  return pos;
3366 }
3367 
3368 Matrix
3370 {
3371  Matrix bb = get_boundingbox (true);
3372  Matrix pos (1, 2, 0);
3373 
3374  pos(0) = x;
3375  pos(1) = y;
3376 
3377  pos = convert_position (pos, get_units (), "pixels",
3378  bb.extract_n (0, 2, 1, 2));
3379  pos(0)--;
3380  pos(1) = bb(3) - pos(1);
3381 
3382  return pos;
3383 }
3384 
3385 void
3387  bool do_notify_toolkit)
3388 {
3389  if (! error_state)
3390  {
3391  Matrix old_bb, new_bb;
3392  bool modified = false;
3393 
3394  old_bb = get_boundingbox (true);
3395  modified = position.set (v, false, do_notify_toolkit);
3396  new_bb = get_boundingbox (true);
3397 
3398  if (old_bb != new_bb)
3399  {
3400  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
3401  {
3402  execute_resizefcn ();
3403  update_boundingbox ();
3404  }
3405  }
3406 
3407  if (modified)
3408  {
3409  position.run_listeners (POSTSET);
3410  mark_modified ();
3411  }
3412  }
3413 }
3414 
3415 void
3417  bool do_notify_toolkit)
3418 {
3419  if (! error_state)
3420  {
3421  if (outerposition.set (v, true, do_notify_toolkit))
3422  {
3423  mark_modified ();
3424  }
3425  }
3426 }
3427 
3428 void
3430 {
3431  if (! error_state)
3432  {
3433  caseless_str typ = get_papertype ();
3434  caseless_str punits = v.string_value ();
3435  if (! error_state)
3436  {
3437  if (punits.compare ("normalized") && typ.compare ("<custom>"))
3438  error ("set: can't set the paperunits to normalized when the papertype is custom");
3439  else
3440  {
3441  caseless_str old_paperunits = get_paperunits ();
3442  if (paperunits.set (v, true))
3443  {
3444  update_paperunits (old_paperunits);
3445  mark_modified ();
3446  }
3447  }
3448  }
3449  }
3450 }
3451 
3452 void
3454 {
3455  if (! error_state)
3456  {
3457  caseless_str typ = v.string_value ();
3458  caseless_str punits = get_paperunits ();
3459  if (! error_state)
3460  {
3461  if (punits.compare ("normalized") && typ.compare ("<custom>"))
3462  error ("set: can't set the paperunits to normalized when the papertype is custom");
3463  else
3464  {
3465  if (papertype.set (v, true))
3466  {
3467  update_papertype ();
3468  mark_modified ();
3469  }
3470  }
3471  }
3472  }
3473 }
3474 
3475 static Matrix
3477 {
3478  Matrix ret (1, 2, 1.0);
3479 
3480  if (! punits.compare ("normalized"))
3481  {
3482  double in2units;
3483  double mm2units;
3484 
3485  if (punits.compare ("inches"))
3486  {
3487  in2units = 1.0;
3488  mm2units = 1 / 25.4 ;
3489  }
3490  else if (punits.compare ("centimeters"))
3491  {
3492  in2units = 2.54;
3493  mm2units = 1 / 10.0;
3494  }
3495  else // points
3496  {
3497  in2units = 72.0;
3498  mm2units = 72.0 / 25.4;
3499  }
3500 
3501  if (typ.compare ("usletter"))
3502  {
3503  ret (0) = 8.5 * in2units;
3504  ret (1) = 11.0 * in2units;
3505  }
3506  else if (typ.compare ("uslegal"))
3507  {
3508  ret (0) = 8.5 * in2units;
3509  ret (1) = 14.0 * in2units;
3510  }
3511  else if (typ.compare ("tabloid"))
3512  {
3513  ret (0) = 11.0 * in2units;
3514  ret (1) = 17.0 * in2units;
3515  }
3516  else if (typ.compare ("a0"))
3517  {
3518  ret (0) = 841.0 * mm2units;
3519  ret (1) = 1189.0 * mm2units;
3520  }
3521  else if (typ.compare ("a1"))
3522  {
3523  ret (0) = 594.0 * mm2units;
3524  ret (1) = 841.0 * mm2units;
3525  }
3526  else if (typ.compare ("a2"))
3527  {
3528  ret (0) = 420.0 * mm2units;
3529  ret (1) = 594.0 * mm2units;
3530  }
3531  else if (typ.compare ("a3"))
3532  {
3533  ret (0) = 297.0 * mm2units;
3534  ret (1) = 420.0 * mm2units;
3535  }
3536  else if (typ.compare ("a4"))
3537  {
3538  ret (0) = 210.0 * mm2units;
3539  ret (1) = 297.0 * mm2units;
3540  }
3541  else if (typ.compare ("a5"))
3542  {
3543  ret (0) = 148.0 * mm2units;
3544  ret (1) = 210.0 * mm2units;
3545  }
3546  else if (typ.compare ("b0"))
3547  {
3548  ret (0) = 1029.0 * mm2units;
3549  ret (1) = 1456.0 * mm2units;
3550  }
3551  else if (typ.compare ("b1"))
3552  {
3553  ret (0) = 728.0 * mm2units;
3554  ret (1) = 1028.0 * mm2units;
3555  }
3556  else if (typ.compare ("b2"))
3557  {
3558  ret (0) = 514.0 * mm2units;
3559  ret (1) = 728.0 * mm2units;
3560  }
3561  else if (typ.compare ("b3"))
3562  {
3563  ret (0) = 364.0 * mm2units;
3564  ret (1) = 514.0 * mm2units;
3565  }
3566  else if (typ.compare ("b4"))
3567  {
3568  ret (0) = 257.0 * mm2units;
3569  ret (1) = 364.0 * mm2units;
3570  }
3571  else if (typ.compare ("b5"))
3572  {
3573  ret (0) = 182.0 * mm2units;
3574  ret (1) = 257.0 * mm2units;
3575  }
3576  else if (typ.compare ("arch-a"))
3577  {
3578  ret (0) = 9.0 * in2units;
3579  ret (1) = 12.0 * in2units;
3580  }
3581  else if (typ.compare ("arch-b"))
3582  {
3583  ret (0) = 12.0 * in2units;
3584  ret (1) = 18.0 * in2units;
3585  }
3586  else if (typ.compare ("arch-c"))
3587  {
3588  ret (0) = 18.0 * in2units;
3589  ret (1) = 24.0 * in2units;
3590  }
3591  else if (typ.compare ("arch-d"))
3592  {
3593  ret (0) = 24.0 * in2units;
3594  ret (1) = 36.0 * in2units;
3595  }
3596  else if (typ.compare ("arch-e"))
3597  {
3598  ret (0) = 36.0 * in2units;
3599  ret (1) = 48.0 * in2units;
3600  }
3601  else if (typ.compare ("a"))
3602  {
3603  ret (0) = 8.5 * in2units;
3604  ret (1) = 11.0 * in2units;
3605  }
3606  else if (typ.compare ("b"))
3607  {
3608  ret (0) = 11.0 * in2units;
3609  ret (1) = 17.0 * in2units;
3610  }
3611  else if (typ.compare ("c"))
3612  {
3613  ret (0) = 17.0 * in2units;
3614  ret (1) = 22.0 * in2units;
3615  }
3616  else if (typ.compare ("d"))
3617  {
3618  ret (0) = 22.0 * in2units;
3619  ret (1) = 34.0 * in2units;
3620  }
3621  else if (typ.compare ("e"))
3622  {
3623  ret (0) = 34.0 * in2units;
3624  ret (1) = 43.0 * in2units;
3625  }
3626  }
3627 
3628  return ret;
3629 }
3630 
3631 void
3633 {
3634  Matrix pos = get_paperposition ().matrix_value ();
3635  Matrix sz = get_papersize ().matrix_value ();
3636 
3637  pos(0) /= sz(0);
3638  pos(1) /= sz(1);
3639  pos(2) /= sz(0);
3640  pos(3) /= sz(1);
3641 
3642  std::string porient = get_paperorientation ();
3643  caseless_str punits = get_paperunits ();
3644  caseless_str typ = get_papertype ();
3645 
3646  if (typ.compare ("<custom>"))
3647  {
3648  if (old_paperunits.compare ("centimeters"))
3649  {
3650  sz(0) /= 2.54;
3651  sz(1) /= 2.54;
3652  }
3653  else if (old_paperunits.compare ("points"))
3654  {
3655  sz(0) /= 72.0;
3656  sz(1) /= 72.0;
3657  }
3658 
3659  if (punits.compare ("centimeters"))
3660  {
3661  sz(0) *= 2.54;
3662  sz(1) *= 2.54;
3663  }
3664  else if (punits.compare ("points"))
3665  {
3666  sz(0) *= 72.0;
3667  sz(1) *= 72.0;
3668  }
3669  }
3670  else
3671  {
3672  sz = papersize_from_type (punits, typ);
3673  if (porient == "landscape")
3674  std::swap (sz(0), sz(1));
3675  }
3676 
3677  pos(0) *= sz(0);
3678  pos(1) *= sz(1);
3679  pos(2) *= sz(0);
3680  pos(3) *= sz(1);
3681 
3682  papersize.set (octave_value (sz));
3683  paperposition.set (octave_value (pos));
3684 }
3685 
3686 void
3688 {
3689  caseless_str typ = get_papertype ();
3690  if (! typ.compare ("<custom>"))
3691  {
3692  Matrix sz = papersize_from_type (get_paperunits (), typ);
3693  if (get_paperorientation () == "landscape")
3694  std::swap (sz(0), sz(1));
3695  // Call papersize.set rather than set_papersize to avoid loops
3696  // between update_papersize and update_papertype
3697  papersize.set (octave_value (sz));
3698  }
3699 }
3700 
3701 void
3703 {
3704  Matrix sz = get_papersize ().matrix_value ();
3705  if (sz(0) > sz(1))
3706  {
3707  std::swap (sz(0), sz(1));
3708  papersize.set (octave_value (sz));
3709  paperorientation.set (octave_value ("landscape"));
3710  }
3711  else
3712  {
3713  paperorientation.set ("portrait");
3714  }
3715  std::string punits = get_paperunits ();
3716  if (punits == "centimeters")
3717  {
3718  sz(0) /= 2.54;
3719  sz(1) /= 2.54;
3720  }
3721  else if (punits == "points")
3722  {
3723  sz(0) /= 72.0;
3724  sz(1) /= 72.0;
3725  }
3726  if (punits == "normalized")
3727  {
3728  caseless_str typ = get_papertype ();
3729  if (get_papertype () == "<custom>")
3730  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
3731  }
3732  else
3733  {
3734  // TODO - the papersizes info is also in papersize_from_type().
3735  // Both should be rewritten to avoid the duplication.
3736  std::string typ = "<custom>";
3737  const double mm2in = 1.0 / 25.4;
3738  const double tol = 0.01;
3739 
3740  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
3741  typ = "usletter";
3742  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
3743  typ = "uslegal";
3744  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
3745  typ = "tabloid";
3746  else if (std::abs (sz(0) - 841.0 * mm2in)
3747  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
3748  typ = "a0";
3749  else if (std::abs (sz(0) - 594.0 * mm2in)
3750  + std::abs (sz(1) - 841.0 * mm2in) < tol)
3751  typ = "a1";
3752  else if (std::abs (sz(0) - 420.0 * mm2in)
3753  + std::abs (sz(1) - 594.0 * mm2in) < tol)
3754  typ = "a2";
3755  else if (std::abs (sz(0) - 297.0 * mm2in)
3756  + std::abs (sz(1) - 420.0 * mm2in) < tol)
3757  typ = "a3";
3758  else if (std::abs (sz(0) - 210.0 * mm2in)
3759  + std::abs (sz(1) - 297.0 * mm2in) < tol)
3760  typ = "a4";
3761  else if (std::abs (sz(0) - 148.0 * mm2in)
3762  + std::abs (sz(1) - 210.0 * mm2in) < tol)
3763  typ = "a5";
3764  else if (std::abs (sz(0) - 1029.0 * mm2in)
3765  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
3766  typ = "b0";
3767  else if (std::abs (sz(0) - 728.0 * mm2in)
3768  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
3769  typ = "b1";
3770  else if (std::abs (sz(0) - 514.0 * mm2in)
3771  + std::abs (sz(1) - 728.0 * mm2in) < tol)
3772  typ = "b2";
3773  else if (std::abs (sz(0) - 364.0 * mm2in)
3774  + std::abs (sz(1) - 514.0 * mm2in) < tol)
3775  typ = "b3";
3776  else if (std::abs (sz(0) - 257.0 * mm2in)
3777  + std::abs (sz(1) - 364.0 * mm2in) < tol)
3778  typ = "b4";
3779  else if (std::abs (sz(0) - 182.0 * mm2in)
3780  + std::abs (sz(1) - 257.0 * mm2in) < tol)
3781  typ = "b5";
3782  else if (std::abs (sz(0) - 9.0)
3783  + std::abs (sz(1) - 12.0) < tol)
3784  typ = "arch-a";
3785  else if (std::abs (sz(0) - 12.0)
3786  + std::abs (sz(1) - 18.0) < tol)
3787  typ = "arch-b";
3788  else if (std::abs (sz(0) - 18.0)
3789  + std::abs (sz(1) - 24.0) < tol)
3790  typ = "arch-c";
3791  else if (std::abs (sz(0) - 24.0)
3792  + std::abs (sz(1) - 36.0) < tol)
3793  typ = "arch-d";
3794  else if (std::abs (sz(0) - 36.0)
3795  + std::abs (sz(1) - 48.0) < tol)
3796  typ = "arch-e";
3797  else if (std::abs (sz(0) - 8.5)
3798  + std::abs (sz(1) - 11.0) < tol)
3799  typ = "a";
3800  else if (std::abs (sz(0) - 11.0)
3801  + std::abs (sz(1) - 17.0) < tol)
3802  typ = "b";
3803  else if (std::abs (sz(0) - 17.0)
3804  + std::abs (sz(1) - 22.0) < tol)
3805  typ = "c";
3806  else if (std::abs (sz(0) - 22.0)
3807  + std::abs (sz(1) - 34.0) < tol)
3808  typ = "d";
3809  else if (std::abs (sz(0) - 34.0)
3810  + std::abs (sz(1) - 43.0) < tol)
3811  typ = "e";
3812  // Call papertype.set rather than set_papertype to avoid loops between
3813  // update_papersize and update_papertype
3814  papertype.set (typ);
3815  }
3816  if (punits == "centimeters")
3817  {
3818  sz(0) *= 2.54;
3819  sz(1) *= 2.54;
3820  }
3821  else if (punits == "points")
3822  {
3823  sz(0) *= 72.0;
3824  sz(1) *= 72.0;
3825  }
3826  if (get_paperorientation () == "landscape")
3827  {
3828  std::swap (sz(0), sz(1));
3829  papersize.set (octave_value (sz));
3830  }
3831 }
3832 
3833 /*
3834 %!test
3835 %! hf = figure ("visible", "off");
3836 %! unwind_protect
3837 %! set (hf, "paperunits", "inches");
3838 %! set (hf, "papersize", [5, 4]);
3839 %! set (hf, "paperunits", "points");
3840 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
3841 %! papersize = get (hf, "papersize");
3842 %! set (hf, "papersize", papersize + 1);
3843 %! set (hf, "papersize", papersize);
3844 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
3845 %! unwind_protect_cleanup
3846 %! close (hf);
3847 %! end_unwind_protect
3848 
3849 %!test
3850 %! hf = figure ("visible", "off");
3851 %! unwind_protect
3852 %! set (hf, "paperunits", "inches");
3853 %! set (hf, "papersize", [5, 4]);
3854 %! set (hf, "paperunits", "centimeters");
3855 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
3856 %! papersize = get (hf, "papersize");
3857 %! set (hf, "papersize", papersize + 1);
3858 %! set (hf, "papersize", papersize);
3859 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
3860 %! unwind_protect_cleanup
3861 %! close (hf);
3862 %! end_unwind_protect
3863 */
3864 
3865 void
3867 {
3868  std::string porient = get_paperorientation ();
3869  Matrix sz = get_papersize ().matrix_value ();
3870  Matrix pos = get_paperposition ().matrix_value ();
3871  if ((sz(0) > sz(1) && porient == "portrait")
3872  || (sz(0) < sz(1) && porient == "landscape"))
3873  {
3874  std::swap (sz(0), sz(1));
3875  std::swap (pos(0), pos(1));
3876  std::swap (pos(2), pos(3));
3877  // Call papertype.set rather than set_papertype to avoid loops
3878  // between update_papersize and update_papertype
3879  papersize.set (octave_value (sz));
3880  paperposition.set (octave_value (pos));
3881  }
3882 }
3883 
3884 /*
3885 %!test
3886 %! hf = figure ("visible", "off");
3887 %! unwind_protect
3888 %! tol = 100 * eps ();
3889 %! ## UPPER case and MiXed case is part of test and should not be changed.
3890 %! set (hf, "paperorientation", "PORTRAIT");
3891 %! set (hf, "paperunits", "inches");
3892 %! set (hf, "papertype", "USletter");
3893 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
3894 %! set (hf, "paperorientation", "Landscape");
3895 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
3896 %! set (hf, "paperunits", "centimeters");
3897 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
3898 %! set (hf, "papertype", "a4");
3899 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
3900 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
3901 %! assert (get (hf, "papertype"), "usletter");
3902 %! assert (get (hf, "paperorientation"), "portrait");
3903 %! set (hf, "papersize", [11.0, 8.5]);
3904 %! assert (get (hf, "papertype"), "usletter");
3905 %! assert (get (hf, "paperorientation"), "landscape");
3906 %! unwind_protect_cleanup
3907 %! close (hf);
3908 %! end_unwind_protect
3909 */
3910 
3911 void
3913 {
3914  if (! error_state)
3915  {
3916  caseless_str old_units = get_units ();
3917  if (units.set (v, true))
3918  {
3919  update_units (old_units);
3920  mark_modified ();
3921  }
3922  }
3923 }
3924 
3925 void
3927 {
3928  position.set (convert_position (get_position ().matrix_value (), old_units,
3929  get_units (), screen_size_pixels ()), false);
3930 }
3931 
3932 /*
3933 %!test
3934 %! hf = figure ("visible", "off");
3935 %! old_units = get (0, "units");
3936 %! unwind_protect
3937 %! set (0, "units", "pixels");
3938 %! rsz = get (0, "screensize");
3939 %! set (gcf (), "units", "pixels");
3940 %! fsz = get (gcf (), "position");
3941 %! set (gcf (), "units", "normalized");
3942 %! pos = get (gcf (), "position");
3943 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
3944 %! unwind_protect_cleanup
3945 %! close (hf);
3946 %! set (0, "units", old_units);
3947 %! end_unwind_protect
3948 */
3949 
3950 std::string
3952 {
3953  if (is_numbertitle ())
3954  {
3955  std::ostringstream os;
3956  std::string nm = get_name ();
3957 
3958  os << "Figure " << __myhandle__.value ();
3959  if (! nm.empty ())
3960  os << ": " << get_name ();
3961 
3962  return os.str ();
3963  }
3964  else
3965  return get_name ();
3966 }
3967 
3970 {
3971  octave_value retval = default_properties.lookup (name);
3972 
3973  if (retval.is_undefined ())
3974  {
3975  graphics_handle parent = get_parent ();
3976  graphics_object parent_obj = gh_manager::get_object (parent);
3977 
3978  retval = parent_obj.get_default (name);
3979  }
3980 
3981  return retval;
3982 }
3983 
3984 void
3986 {
3988 }
3989 
3990 // ---------------------------------------------------------------------
3991 
3992 void
3994 {
3995  position.add_constraint (dim_vector (1, 4));
3996  outerposition.add_constraint (dim_vector (1, 4));
3997  tightinset.add_constraint (dim_vector (1, 4));
3998  looseinset.add_constraint (dim_vector (1, 4));
3999  colororder.add_constraint (dim_vector (-1, 3));
4000  dataaspectratio.add_constraint (dim_vector (1, 3));
4001  plotboxaspectratio.add_constraint (dim_vector (1, 3));
4002  alim.add_constraint (2);
4003  clim.add_constraint (2);
4004  xlim.add_constraint (2);
4005  ylim.add_constraint (2);
4006  zlim.add_constraint (2);
4007  xtick.add_constraint (dim_vector (1, -1));
4008  ytick.add_constraint (dim_vector (1, -1));
4009  ztick.add_constraint (dim_vector (1, -1));
4010  ticklength.add_constraint (dim_vector (1, 2));
4011  Matrix vw (1, 2, 0);
4012  vw(1) = 90;
4013  view = vw;
4014  view.add_constraint (dim_vector (1, 2));
4015  cameraposition.add_constraint (dim_vector (1, 3));
4016  cameratarget.add_constraint (dim_vector (1, 3));
4017  Matrix upv (1, 3, 0.0);
4018  upv(2) = 1.0;
4019  cameraupvector = upv;
4020  cameraupvector.add_constraint (dim_vector (1, 3));
4021  currentpoint.add_constraint (dim_vector (2, 3));
4022  // No constraints for hidden transform properties
4023  update_font ();
4024 
4025  x_zlim.resize (1, 2);
4026 
4027  sx = "linear";
4028  sy = "linear";
4029  sz = "linear";
4030 
4031  calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
4032  calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
4033  calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
4034 
4035  xset (xlabel.handle_value (), "handlevisibility", "off");
4036  xset (ylabel.handle_value (), "handlevisibility", "off");
4037  xset (zlabel.handle_value (), "handlevisibility", "off");
4038  xset (title.handle_value (), "handlevisibility", "off");
4039 
4040  xset (xlabel.handle_value (), "horizontalalignment", "center");
4041  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4042  xset (ylabel.handle_value (), "horizontalalignment", "center");
4043  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4044  xset (zlabel.handle_value (), "horizontalalignment", "right");
4045  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4046  xset (title.handle_value (), "horizontalalignment", "center");
4047  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4048 
4049  xset (xlabel.handle_value (), "verticalalignment", "top");
4050  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4051  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4052  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4053  xset (title.handle_value (), "verticalalignment", "bottom");
4054  xset (title.handle_value (), "verticalalignmentmode", "auto");
4055 
4056  xset (ylabel.handle_value (), "rotation", 90.0);
4057  xset (ylabel.handle_value (), "rotationmode", "auto");
4058 
4059  xset (zlabel.handle_value (), "visible", "off");
4060 
4061  xset (xlabel.handle_value (), "clipping", "off");
4062  xset (ylabel.handle_value (), "clipping", "off");
4063  xset (zlabel.handle_value (), "clipping", "off");
4064  xset (title.handle_value (), "clipping", "off");
4065 
4066  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4067  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4068  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4069  xset (title.handle_value (), "autopos_tag", "title");
4070 
4071  adopt (xlabel.handle_value ());
4072  adopt (ylabel.handle_value ());
4073  adopt (zlabel.handle_value ());
4074  adopt (title.handle_value ());
4075 
4076  Matrix tlooseinset = default_axes_position ();
4077  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4078  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4079  looseinset = tlooseinset;
4080 }
4081 
4082 Matrix
4084 {
4085  Matrix pos = init_pos;
4087  Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
4088  Matrix ext = get_extent (true, true);
4089  ext(1) = parent_bb(3) - ext(1) - ext(3);
4090  ext(0)++;
4091  ext(1)++;
4092  ext = convert_position (ext, "pixels", get_units (),
4093  parent_bb.extract_n (0, 2, 1, 2));
4094  if (ext(0) < pos(0))
4095  {
4096  pos(2) += pos(0)-ext(0);
4097  pos(0) = ext(0);
4098  }
4099  if (ext(0)+ext(2) > pos(0)+pos(2))
4100  pos(2) = ext(0)+ext(2)-pos(0);
4101 
4102  if (ext(1) < pos(1))
4103  {
4104  pos(3) += pos(1)-ext(1);
4105  pos(1) = ext(1);
4106  }
4107  if (ext(1)+ext(3) > pos(1)+pos(3))
4108  pos(3) = ext(1)+ext(3)-pos(1);
4109  return pos;
4110 }
4111 
4112 void
4114 {
4115  // First part is equivalent to `update_tightinset ()'
4116  if (activepositionproperty.is ("position"))
4117  update_position ();
4118  else
4119  update_outerposition ();
4120  caseless_str old_units = get_units ();
4121  set_units ("normalized");
4122  Matrix pos = position.get ().matrix_value ();
4123  Matrix outpos = outerposition.get ().matrix_value ();
4124  Matrix tightpos = calc_tightbox (pos);
4125  Matrix tinset (1, 4, 1.0);
4126  tinset(0) = pos(0)-tightpos(0);
4127  tinset(1) = pos(1)-tightpos(1);
4128  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4129  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4130  tightinset = tinset;
4131  set_units (old_units);
4132  update_transform ();
4133  if (activepositionproperty.is ("position"))
4134  update_position ();
4135  else
4136  update_outerposition ();
4137 }
4138 
4139 /*
4140 %!testif HAVE_FLTK
4141 %! hf = figure ("visible", "off");
4142 %! graphics_toolkit (hf, "fltk");
4143 %! unwind_protect
4144 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4145 %! hax = findall (gcf (), "type", "axes");
4146 %! positions = cell2mat (get (hax, "position"));
4147 %! outerpositions = cell2mat (get (hax, "outerposition"));
4148 %! looseinsets = cell2mat (get (hax, "looseinset"));
4149 %! tightinsets = cell2mat (get (hax, "tightinset"));
4150 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4151 %! hax = findall (gcf (), "type", "axes");
4152 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4153 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4154 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4155 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4156 %! unwind_protect_cleanup
4157 %! close (hf);
4158 %! end_unwind_protect
4159 %!testif HAVE_FLTK
4160 %! hf = figure ("visible", "off");
4161 %! graphics_toolkit (hf, "fltk");
4162 %! fpos = get (hf, "position");
4163 %! unwind_protect
4164 %! plot (rand (3))
4165 %! position = get (gca, "position");
4166 %! outerposition = get (gca, "outerposition");
4167 %! looseinset = get (gca, "looseinset");
4168 %! tightinset = get (gca, "tightinset");
4169 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)])
4170 %! set (hf, "position", fpos);
4171 %! assert (get (gca, "outerposition"), outerposition, 0.001)
4172 %! assert (get (gca, "position"), position, 0.001)
4173 %! assert (get (gca, "looseinset"), looseinset, 0.001)
4174 %! assert (get (gca, "tightinset"), tightinset, 0.001)
4175 %! unwind_protect_cleanup
4176 %! close (hf);
4177 %! end_unwind_protect
4178 %!testif HAVE_FLTK
4179 %! hf = figure ("visible", "off");
4180 %! graphics_toolkit (hf, "fltk");
4181 %! fpos = get (hf, "position");
4182 %! set (gca, "activepositionproperty", "position")
4183 %! unwind_protect
4184 %! plot (rand (3))
4185 %! position = get (gca, "position");
4186 %! outerposition = get (gca, "outerposition");
4187 %! looseinset = get (gca, "looseinset");
4188 %! tightinset = get (gca, "tightinset");
4189 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)])
4190 %! set (hf, "position", fpos);
4191 %! assert (get (gca, "position"), position, 0.001)
4192 %! assert (get (gca, "outerposition"), outerposition, 0.001)
4193 %! assert (get (gca, "looseinset"), looseinset, 0.001)
4194 %! assert (get (gca, "tightinset"), tightinset, 0.001)
4195 %! unwind_protect_cleanup
4196 %! close (hf);
4197 %! end_unwind_protect
4198 */
4199 
4200 void
4202  const std::string& who,
4203  const octave_value& v)
4204 {
4205  graphics_handle val;
4206 
4207  if (v.is_string ())
4208  {
4209  val = gh_manager::make_graphics_handle ("text", __myhandle__,
4210  false, false);
4211 
4212  xset (val, "string", v);
4213  }
4214  else
4215  {
4217 
4218  if (go.isa ("text"))
4219  val = ::reparent (v, "set", who, __myhandle__, false);
4220  else
4221  {
4222  std::string cname = v.class_name ();
4223 
4224  error ("set: expecting text graphics object or character string for %s property, found %s",
4225  who.c_str (), cname.c_str ());
4226  }
4227  }
4228 
4229  if (! error_state)
4230  {
4231  xset (val, "handlevisibility", "off");
4232 
4234 
4236 
4237  hp = val;
4238 
4239  adopt (hp.handle_value ());
4240  }
4241 }
4242 
4243 void
4245 {
4246  set_text_child (xlabel, "xlabel", v);
4247  xset (xlabel.handle_value (), "positionmode", "auto");
4248  xset (xlabel.handle_value (), "rotationmode", "auto");
4249  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4250  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4251  xset (xlabel.handle_value (), "clipping", "off");
4252  xset (xlabel.handle_value (), "color", get_xcolor ());
4253  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4254  update_xlabel_position ();
4255 }
4256 
4257 void
4259 {
4260  set_text_child (ylabel, "ylabel", v);
4261  xset (ylabel.handle_value (), "positionmode", "auto");
4262  xset (ylabel.handle_value (), "rotationmode", "auto");
4263  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4264  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4265  xset (ylabel.handle_value (), "clipping", "off");
4266  xset (ylabel.handle_value (), "color", get_ycolor ());
4267  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4268  update_ylabel_position ();
4269 }
4270 
4271 void
4273 {
4274  set_text_child (zlabel, "zlabel", v);
4275  xset (zlabel.handle_value (), "positionmode", "auto");
4276  xset (zlabel.handle_value (), "rotationmode", "auto");
4277  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4278  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
4279  xset (zlabel.handle_value (), "clipping", "off");
4280  xset (zlabel.handle_value (), "color", get_zcolor ());
4281  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4282  update_zlabel_position ();
4283 }
4284 
4285 void
4287 {
4288  set_text_child (title, "title", v);
4289  xset (title.handle_value (), "positionmode", "auto");
4290  xset (title.handle_value (), "horizontalalignment", "center");
4291  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4292  xset (title.handle_value (), "verticalalignment", "bottom");
4293  xset (title.handle_value (), "verticalalignmentmode", "auto");
4294  xset (title.handle_value (), "clipping", "off");
4295  xset (title.handle_value (), "autopos_tag", "title");
4296  update_title_position ();
4297 }
4298 
4299 void
4301  const std::string& mode)
4302 {
4303  box = "on";
4304  colororder = default_colororder ();
4305  // Note: dataspectratio will be set through update_aspectratios
4306  dataaspectratiomode = "auto";
4307  layer = "bottom";
4308 
4309  Matrix tlim (1, 2, 0.0);
4310  tlim(1) = 1;
4311  xlim = tlim;
4312  ylim = tlim;
4313  zlim = tlim;
4314 
4315  Matrix cl (1, 2, 0);
4316  cl(1) = 1;
4317  clim = cl;
4318 
4319  alim = tlim;
4320 
4321  xlimmode = "auto";
4322  ylimmode = "auto";
4323  zlimmode = "auto";
4324  climmode = "auto";
4325  alimmode = "auto";
4326 
4327  xgrid = "off";
4328  ygrid = "off";
4329  zgrid = "off";
4330  xminorgrid = "off";
4331  yminorgrid = "off";
4332  zminorgrid = "off";
4333  xtick = Matrix ();
4334  ytick = Matrix ();
4335  ztick = Matrix ();
4336  xtickmode = "auto";
4337  ytickmode = "auto";
4338  ztickmode = "auto";
4339  xminortick = "off";
4340  yminortick = "off";
4341  zminortick = "off";
4342  xticklabel = "";
4343  yticklabel = "";
4344  zticklabel = "";
4345  xticklabelmode = "auto";
4346  yticklabelmode = "auto";
4347  zticklabelmode = "auto";
4348 
4349  interpreter = "none";
4350 
4351  color = color_values ("white");
4352  xcolor = color_values ("black");
4353  ycolor = color_values ("black");
4354  zcolor = color_values ("black");
4355  xscale = "linear";
4356  yscale = "linear";
4357  zscale = "linear";
4358  xdir = "normal";
4359  ydir = "normal";
4360  zdir = "normal";
4361  yaxislocation = "left";
4362  xaxislocation = "bottom";
4363 
4364  Matrix tview (1, 2, 0.0);
4365  tview(1) = 90;
4366  view = tview;
4367 
4368  __hold_all__ = "off";
4369  nextplot = "replace";
4370 
4371  ambientlightcolor = Matrix (1, 3, 1.0);
4372 
4373  // Note: camera properties (not mode) will be set in update_transform
4374  camerapositionmode = "auto";
4375  cameratargetmode = "auto";
4376  cameraupvectormode = "auto";
4377  cameraviewanglemode = "auto";
4378 
4379  drawmode = "normal";
4380 
4381  fontangle = "normal";
4382  fontname = OCTAVE_DEFAULT_FONTNAME;
4383  fontsize = 10;
4384  fontunits = "points";
4385  fontweight = "normal";
4386 
4387  gridlinestyle = ":";
4388  linestyleorder = "-";
4389  linewidth = 0.5;
4390  minorgridlinestyle = ":";
4391 
4392  // Note: plotboxaspectratio will be set through update_aspectratios
4393  plotboxaspectratiomode = "auto";
4394  projection = "orthographic";
4395 
4396  tickdir = "in";
4397  tickdirmode = "auto";
4398  ticklength = default_axes_ticklength ();
4399 
4400  tightinset = Matrix (1, 4, 0.0);
4401 
4402  sx = "linear";
4403  sy = "linear";
4404  sz = "linear";
4405 
4406  visible = "on";
4407 
4408  // Replace preserves Position and Units properties
4409  if (mode != "replace")
4410  {
4411  outerposition = default_axes_outerposition ();
4412  position = default_axes_position ();
4413  activepositionproperty = "outerposition";
4414  }
4415 
4416  delete_children (true);
4417 
4418  xlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4419  false, false);
4420 
4421  ylabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4422  false, false);
4423 
4424  zlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4425  false, false);
4426 
4427  title = gh_manager::make_graphics_handle ("text", __myhandle__,
4428  false, false);
4429 
4430  xset (xlabel.handle_value (), "handlevisibility", "off");
4431  xset (ylabel.handle_value (), "handlevisibility", "off");
4432  xset (zlabel.handle_value (), "handlevisibility", "off");
4433  xset (title.handle_value (), "handlevisibility", "off");
4434 
4435  xset (xlabel.handle_value (), "horizontalalignment", "center");
4436  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4437  xset (ylabel.handle_value (), "horizontalalignment", "center");
4438  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4439  xset (zlabel.handle_value (), "horizontalalignment", "right");
4440  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4441  xset (title.handle_value (), "horizontalalignment", "center");
4442  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4443 
4444  xset (xlabel.handle_value (), "verticalalignment", "top");
4445  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4446  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4447  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4448  xset (title.handle_value (), "verticalalignment", "bottom");
4449  xset (title.handle_value (), "verticalalignmentmode", "auto");
4450 
4451  xset (ylabel.handle_value (), "rotation", 90.0);
4452  xset (ylabel.handle_value (), "rotationmode", "auto");
4453 
4454  xset (zlabel.handle_value (), "visible", "off");
4455 
4456  xset (xlabel.handle_value (), "clipping", "off");
4457  xset (ylabel.handle_value (), "clipping", "off");
4458  xset (zlabel.handle_value (), "clipping", "off");
4459  xset (title.handle_value (), "clipping", "off");
4460 
4461  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4462  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4463  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4464  xset (title.handle_value (), "autopos_tag", "title");
4465 
4466  adopt (xlabel.handle_value ());
4467  adopt (ylabel.handle_value ());
4468  adopt (zlabel.handle_value ());
4469  adopt (title.handle_value ());
4470 
4471  update_transform ();
4472  sync_positions ();
4473  override_defaults (obj);
4474 }
4475 
4476 void
4478 {
4479  graphics_handle h = hp.handle_value ();
4480 
4481  if (h.ok ())
4482  {
4484 
4485  if (go.valid_object ())
4486  gh_manager::free (h);
4487 
4489  }
4490 
4491  // FIXME: is it necessary to check whether the axes object is
4492  // being deleted now? I think this function is only called when an
4493  // individual child object is delete and not when the parent axes
4494  // object is deleted.
4495 
4496  if (! is_beingdeleted ())
4497  {
4498  hp = gh_manager::make_graphics_handle ("text", __myhandle__,
4499  false, false);
4500 
4501  xset (hp.handle_value (), "handlevisibility", "off");
4502 
4503  adopt (hp.handle_value ());
4504  }
4505 }
4506 
4507 void
4509 {
4510  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
4511  delete_text_child (xlabel);
4512  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
4513  delete_text_child (ylabel);
4514  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
4515  delete_text_child (zlabel);
4516  else if (title.handle_value ().ok () && h == title.handle_value ())
4517  delete_text_child (title);
4518  else
4520 }
4521 
4522 inline Matrix
4524 {
4525  Matrix m (4, 4, 0.0);
4526  for (int i = 0; i < 4; i++)
4527  m(i,i) = 1;
4528  return m;
4529 }
4530 
4531 inline ColumnVector
4533 {
4534  ColumnVector v (4, 0.0);
4535  v(3) = 1;
4536  return v;
4537 }
4538 
4539 inline ColumnVector
4540 xform_vector (double x, double y, double z)
4541 {
4542  ColumnVector v (4, 1.0);
4543  v(0) = x; v(1) = y; v(2) = z;
4544  return v;
4545 }
4546 
4547 inline ColumnVector
4548 transform (const Matrix& m, double x, double y, double z)
4549 {
4550  return (m * xform_vector (x, y, z));
4551 }
4552 
4553 inline Matrix
4554 xform_scale (double x, double y, double z)
4555 {
4556  Matrix m (4, 4, 0.0);
4557  m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
4558  return m;
4559 }
4560 
4561 inline Matrix
4562 xform_translate (double x, double y, double z)
4563 {
4564  Matrix m = xform_matrix ();
4565  m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
4566  return m;
4567 }
4568 
4569 inline void
4570 scale (Matrix& m, double x, double y, double z)
4571 {
4572  m = m * xform_scale (x, y, z);
4573 }
4574 
4575 inline void
4576 translate (Matrix& m, double x, double y, double z)
4577 {
4578  m = m * xform_translate (x, y, z);
4579 }
4580 
4581 inline void
4582 xform (ColumnVector& v, const Matrix& m)
4583 {
4584  v = m*v;
4585 }
4586 
4587 inline void
4588 scale (ColumnVector& v, double x, double y, double z)
4589 {
4590  v(0) *= x;
4591  v(1) *= y;
4592  v(2) *= z;
4593 }
4594 
4595 inline void
4596 translate (ColumnVector& v, double x, double y, double z)
4597 {
4598  v(0) += x;
4599  v(1) += y;
4600  v(2) += z;
4601 }
4602 
4603 inline void
4605 {
4606  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
4607  scale (v, fact, fact, fact);
4608 }
4609 
4610 inline double
4612 {
4613  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
4614 }
4615 
4616 inline double
4617 norm (const ColumnVector& v)
4618 {
4619  return sqrt (dot (v, v));
4620 }
4621 
4622 inline ColumnVector
4624 {
4625  ColumnVector r = xform_vector ();
4626  r(0) = v1(1)*v2(2)-v1(2)*v2(1);
4627  r(1) = v1(2)*v2(0)-v1(0)*v2(2);
4628  r(2) = v1(0)*v2(1)-v1(1)*v2(0);
4629  return r;
4630 }
4631 
4632 inline Matrix
4634 {
4635  static double data[32] =
4636  {
4637  0,0,0,1,
4638  1,0,0,1,
4639  0,1,0,1,
4640  0,0,1,1,
4641  1,1,0,1,
4642  1,0,1,1,
4643  0,1,1,1,
4644  1,1,1,1
4645  };
4646  Matrix m (4, 8);
4647  memcpy (m.fortran_vec (), data, sizeof (double)*32);
4648  return m;
4649 }
4650 
4651 inline ColumnVector
4653 {
4654  ColumnVector retval (4, 1.0);
4655  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
4656  return retval;
4657 }
4658 
4659 inline RowVector
4661 {
4662  return v.extract_n (0, 3).transpose ();
4663 }
4664 
4665 void
4667 {
4668  double xd = (xdir_is ("normal") ? 1 : -1);
4669  double yd = (ydir_is ("normal") ? 1 : -1);
4670  double zd = (zdir_is ("normal") ? 1 : -1);
4671 
4672  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
4673  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
4674  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
4675 
4676  double xo = xlimits(xd > 0 ? 0 : 1);
4677  double yo = ylimits(yd > 0 ? 0 : 1);
4678  double zo = zlimits(zd > 0 ? 0 : 1);
4679 
4680  Matrix pb = get_plotboxaspectratio ().matrix_value ();
4681 
4682  bool autocam = (camerapositionmode_is ("auto")
4683  && cameratargetmode_is ("auto")
4684  && cameraupvectormode_is ("auto")
4685  && cameraviewanglemode_is ("auto"));
4686  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
4687  && plotboxaspectratiomode_is ("auto"));
4688 
4689  ColumnVector c_eye (xform_vector ());
4690  ColumnVector c_center (xform_vector ());
4691  ColumnVector c_upv (xform_vector ());
4692 
4693  if (cameratargetmode_is ("auto"))
4694  {
4695  c_center(0) = (xlimits(0)+xlimits(1))/2;
4696  c_center(1) = (ylimits(0)+ylimits(1))/2;
4697  c_center(2) = (zlimits(0)+zlimits(1))/2;
4698 
4699  cameratarget = xform2cam (c_center);
4700  }
4701  else
4702  c_center = cam2xform (get_cameratarget ().matrix_value ());
4703 
4704  if (camerapositionmode_is ("auto"))
4705  {
4706  Matrix tview = get_view ().matrix_value ();
4707  double az = tview(0), el = tview(1);
4708  double d = 5 * sqrt (pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
4709 
4710  if (el == 90 || el == -90)
4711  c_eye(2) = d*signum (el);
4712  else
4713  {
4714  az *= M_PI/180.0;
4715  el *= M_PI/180.0;
4716  c_eye(0) = d * cos (el) * sin (az);
4717  c_eye(1) = -d* cos (el) * cos (az);
4718  c_eye(2) = d * sin (el);
4719  }
4720  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
4721  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
4722  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
4723 
4724  cameraposition = xform2cam (c_eye);
4725  }
4726  else
4727  c_eye = cam2xform (get_cameraposition ().matrix_value ());
4728 
4729  if (cameraupvectormode_is ("auto"))
4730  {
4731  Matrix tview = get_view ().matrix_value ();
4732  double az = tview(0), el = tview(1);
4733 
4734  if (el == 90 || el == -90)
4735  {
4736  c_upv(0) =
4737  -signum (el) *sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
4738  c_upv(1) =
4739  signum (el) * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
4740  }
4741  else
4742  c_upv(2) = 1;
4743 
4744  cameraupvector = xform2cam (c_upv);
4745  }
4746  else
4747  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
4748 
4749  Matrix x_view = xform_matrix ();
4750  Matrix x_projection = xform_matrix ();
4751  Matrix x_viewport = xform_matrix ();
4752  Matrix x_normrender = xform_matrix ();
4753  Matrix x_pre = xform_matrix ();
4754 
4755  x_render = xform_matrix ();
4756  x_render_inv = xform_matrix ();
4757 
4758  scale (x_pre, pb(0), pb(1), pb(2));
4759  translate (x_pre, -0.5, -0.5, -0.5);
4760  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4761  zd/(zlimits(1)-zlimits(0)));
4762  translate (x_pre, -xo, -yo, -zo);
4763 
4764  xform (c_eye, x_pre);
4765  xform (c_center, x_pre);
4766  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
4767  pb(2)/(zlimits(1)-zlimits(0)));
4768  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
4769 
4770  ColumnVector F (c_center), f (F), UP (c_upv);
4771  normalize (f);
4772  normalize (UP);
4773 
4774  if (std::abs (dot (f, UP)) > 1e-15)
4775  {
4776  double fa = 1 / sqrt(1-f(2)*f(2));
4777  scale (UP, fa, fa, fa);
4778  }
4779 
4780  ColumnVector s = cross (f, UP);
4781  ColumnVector u = cross (s, f);
4782 
4783  scale (x_view, 1, 1, -1);
4784  Matrix l = xform_matrix ();
4785  l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
4786  l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
4787  l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
4788  x_view = x_view * l;
4789  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
4790  scale (x_view, pb(0), pb(1), pb(2));
4791  translate (x_view, -0.5, -0.5, -0.5);
4792 
4793  Matrix x_cube = x_view * unit_cube ();
4794  ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
4795  double xM = cmax(0)-cmin(0);
4796  double yM = cmax(1)-cmin(1);
4797 
4798  Matrix bb = get_boundingbox (true);
4799 
4800  double v_angle;
4801 
4802  if (cameraviewanglemode_is ("auto"))
4803  {
4804  double af;
4805 
4806  // FIXME: was this really needed? When compared to Matlab, it
4807  // does not seem to be required. Need investigation with concrete
4808  // graphics toolkit to see results visually.
4809  if (false && dowarp)
4810  af = 1.0 / (xM > yM ? xM : yM);
4811  else
4812  {
4813  if ((bb(2)/bb(3)) > (xM/yM))
4814  af = 1.0 / yM;
4815  else
4816  af = 1.0 / xM;
4817  }
4818  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
4819 
4820  cameraviewangle = v_angle;
4821  }
4822  else
4823  v_angle = get_cameraviewangle ();
4824 
4825  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
4826  scale (x_projection, pf, pf, 1);
4827 
4828  if (dowarp)
4829  {
4830  xM *= pf;
4831  yM *= pf;
4832  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
4833  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
4834  }
4835  else
4836  {
4837  double pix = 1;
4838  if (autocam)
4839  {
4840  if ((bb(2)/bb(3)) > (xM/yM))
4841  pix = bb(3);
4842  else
4843  pix = bb(2);
4844  }
4845  else
4846  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
4847  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
4848  scale (x_viewport, pix, -pix, 1);
4849  }
4850 
4851  x_normrender = x_viewport * x_projection * x_view;
4852 
4853  x_cube = x_normrender * unit_cube ();
4854  cmin = x_cube.row_min ();
4855  cmax = x_cube.row_max ();
4856  x_zlim.resize (1, 2);
4857  x_zlim(0) = cmin(2);
4858  x_zlim(1) = cmax(2);
4859 
4860  x_render = x_normrender;
4861  scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4862  zd/(zlimits(1)-zlimits(0)));
4863  translate (x_render, -xo, -yo, -zo);
4864 
4865  x_viewtransform = x_view;
4866  x_projectiontransform = x_projection;
4867  x_viewporttransform = x_viewport;
4868  x_normrendertransform = x_normrender;
4869  x_rendertransform = x_render;
4870 
4871  x_render_inv = x_render.inverse ();
4872 
4873  // Note: these matrices are a slight modified version of the regular
4874  // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
4875  // => x_gl_mat2)
4876  x_gl_mat1 = x_view;
4877  scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4878  zd/(zlimits(1)-zlimits(0)));
4879  translate (x_gl_mat1, -xo, -yo, -zo);
4880  x_gl_mat2 = x_viewport * x_projection;
4881 }
4882 
4883 static bool updating_axes_layout = false;
4884 
4885 void
4887 {
4888  if (updating_axes_layout)
4889  return;
4890 
4891  graphics_xform xform = get_transform ();
4892 
4893  double xd = (xdir_is ("normal") ? 1 : -1);
4894  double yd = (ydir_is ("normal") ? 1 : -1);
4895  double zd = (zdir_is ("normal") ? 1 : -1);
4896 
4897  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
4898  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
4899  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
4900  double x_min = xlims(0), x_max = xlims(1);
4901  double y_min = ylims(0), y_max = ylims(1);
4902  double z_min = zlims(0), z_max = zlims(1);
4903 
4904  ColumnVector p1, p2, dir (3);
4905 
4906  xstate = ystate = zstate = AXE_ANY_DIR;
4907 
4908  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
4909  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
4910  dir(0) = xround (p2(0)-p1(0));
4911  dir(1) = xround (p2(1)-p1(1));
4912  dir(2) = (p2(2)-p1(2));
4913  if (dir(0) == 0 && dir(1) == 0)
4914  xstate = AXE_DEPTH_DIR;
4915  else if (dir(2) == 0)
4916  {
4917  if (dir(0) == 0)
4918  xstate = AXE_VERT_DIR;
4919  else if (dir(1) == 0)
4920  xstate = AXE_HORZ_DIR;
4921  }
4922 
4923  if (dir(2) == 0)
4924  {
4925  if (dir(1) == 0)
4926  xPlane = (dir(0) > 0 ? x_max : x_min);
4927  else
4928  xPlane = (dir(1) < 0 ? x_max : x_min);
4929  }
4930  else
4931  xPlane = (dir(2) < 0 ? x_min : x_max);
4932 
4933  xPlaneN = (xPlane == x_min ? x_max : x_min);
4934  fx = (x_max-x_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4935 
4936  p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
4937  p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
4938  dir(0) = xround (p2(0)-p1(0));
4939  dir(1) = xround (p2(1)-p1(1));
4940  dir(2) = (p2(2)-p1(2));
4941  if (dir(0) == 0 && dir(1) == 0)
4942  ystate = AXE_DEPTH_DIR;
4943  else if (dir(2) == 0)
4944  {
4945  if (dir(0) == 0)
4946  ystate = AXE_VERT_DIR;
4947  else if (dir(1) == 0)
4948  ystate = AXE_HORZ_DIR;
4949  }
4950 
4951  if (dir(2) == 0)
4952  {
4953  if (dir(1) == 0)
4954  yPlane = (dir(0) > 0 ? y_max : y_min);
4955  else
4956  yPlane = (dir(1) < 0 ? y_max : y_min);
4957  }
4958  else
4959  yPlane = (dir(2) < 0 ? y_min : y_max);
4960 
4961  yPlaneN = (yPlane == y_min ? y_max : y_min);
4962  fy = (y_max-y_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4963 
4964  p1 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
4965  p2 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
4966  dir(0) = xround (p2(0)-p1(0));
4967  dir(1) = xround (p2(1)-p1(1));
4968  dir(2) = (p2(2)-p1(2));
4969  if (dir(0) == 0 && dir(1) == 0)
4970  zstate = AXE_DEPTH_DIR;
4971  else if (dir(2) == 0)
4972  {
4973  if (dir(0) == 0)
4974  zstate = AXE_VERT_DIR;
4975  else if (dir(1) == 0)
4976  zstate = AXE_HORZ_DIR;
4977  }
4978 
4979  if (dir(2) == 0)
4980  {
4981  if (dir(1) == 0)
4982  zPlane = (dir(0) > 0 ? z_min : z_max);
4983  else
4984  zPlane = (dir(1) < 0 ? z_min : z_max);
4985  }
4986  else
4987  zPlane = (dir(2) < 0 ? z_min : z_max);
4988 
4989  zPlaneN = (zPlane == z_min ? z_max : z_min);
4990  fz = (z_max-z_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4991 
4992  unwind_protect frame;
4993  frame.protect_var (updating_axes_layout);
4994  updating_axes_layout = true;
4995 
4996  xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
4997  zSign = (zd*(zPlane-zPlaneN) <= 0);
4998  xyzSym = zSign ? xySym : !xySym;
4999  xpTick = (zSign ? xPlaneN : xPlane);
5000  ypTick = (zSign ? yPlaneN : yPlane);
5001  zpTick = (zSign ? zPlane : zPlaneN);
5002  xpTickN = (zSign ? xPlane : xPlaneN);
5003  ypTickN = (zSign ? yPlane : yPlaneN);
5004  zpTickN = (zSign ? zPlaneN : zPlane);
5005 
5006  /* 2D mode */
5007  x2Dtop = false;
5008  y2Dright = false;
5009  layer2Dtop = false;
5010  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
5011  {
5012  if (xaxislocation_is ("top"))
5013  {
5014  double tmp = yPlane;
5015  yPlane = yPlaneN;
5016  yPlaneN = tmp;
5017  x2Dtop = true;
5018  }
5019  ypTick = yPlaneN;
5020  ypTickN = yPlane;
5021  if (yaxislocation_is ("right"))
5022  {
5023  double tmp = xPlane;
5024  xPlane = xPlaneN;
5025  xPlaneN = tmp;
5026  y2Dright = true;
5027  }
5028  xpTick = xPlaneN;
5029  xpTickN = xPlane;
5030  if (layer_is ("top"))
5031  {
5032  zpTick = zPlaneN;
5033  layer2Dtop = true;
5034  }
5035  else
5036  zpTick = zPlane;
5037  }
5038 
5039  Matrix viewmat = get_view ().matrix_value ();
5040  nearhoriz = std::abs (viewmat(1)) <= 5;
5041 
5042  update_ticklength ();
5043 }
5044 
5045 void
5047 {
5048  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
5049  (ystate > AXE_DEPTH_DIR ? 1 : 0) +
5050  (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5051 
5052  if (tickdirmode_is ("auto"))
5053  tickdir.set (mode2d ? "in" : "out", true);
5054 
5055  double ticksign = (tickdir_is ("in") ? -1 : 1);
5056 
5057  Matrix bbox = get_boundingbox (true);
5058  Matrix ticklen = get_ticklength ().matrix_value ();
5059  ticklen(0) = ticklen(0) * std::max (bbox(2), bbox(3));
5060  ticklen(1) = ticklen(1) * std::max (bbox(2), bbox(3));
5061 
5062  xticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5063  yticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5064  zticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5065 
5066  xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
5067  ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
5068  ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
5069 
5070  update_xlabel_position ();
5071  update_ylabel_position ();
5072  update_zlabel_position ();
5073  update_title_position ();
5074 }
5075 
5076 /*
5077 ## FIXME: A demo can't be called in a C++ file. This should be made a test
5078 ## or moved to a .m file where it can be called.
5079 %!demo
5080 %! clf;
5081 %! subplot (2,1,1);
5082 %! plot (rand (3));
5083 %! xlabel xlabel;
5084 %! ylabel ylabel;
5085 %! title title;
5086 %! subplot (2,1,2);
5087 %! plot (rand (3));
5088 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
5089 %! xlabel xlabel;
5090 %! ylabel ylabel;
5091 %! title title;
5092 */
5093 
5094 static bool updating_xlabel_position = false;
5095 
5096 void
5098 {
5099  if (updating_xlabel_position)
5100  return;
5101 
5102  text::properties& xlabel_props
5103  = reinterpret_cast<text::properties&>
5104  (gh_manager::get_object (get_xlabel ()).get_properties ());
5105 
5106  bool is_empty = xlabel_props.get_string ().is_empty ();
5107 
5108  unwind_protect frame;
5109  frame.protect_var (updating_xlabel_position);
5110  updating_xlabel_position = true;
5111 
5112  if (! is_empty)
5113  {
5114  if (xlabel_props.horizontalalignmentmode_is ("auto"))
5115  {
5116  xlabel_props.set_horizontalalignment
5117  (xstate > AXE_DEPTH_DIR
5118  ? "center" : (xyzSym ? "left" : "right"));
5119 
5120  xlabel_props.set_horizontalalignmentmode ("auto");
5121  }
5122 
5123  if (xlabel_props.verticalalignmentmode_is ("auto"))
5124  {
5125  xlabel_props.set_verticalalignment
5126  (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
5127 
5128  xlabel_props.set_verticalalignmentmode ("auto");
5129  }
5130  }
5131 
5132  if (xlabel_props.positionmode_is ("auto")
5133  || xlabel_props.rotationmode_is ("auto"))
5134  {
5135  graphics_xform xform = get_transform ();
5136 
5137  Matrix ext (1, 2, 0.0);
5138  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5139  get_xticklabel ().all_strings (),
5140  get_xlim ().matrix_value ());
5141 
5142  double wmax = ext(0), hmax = ext(1), angle = 0;
5143  ColumnVector p =
5144  graphics_xform::xform_vector ((xpTickN+xpTick)/2, ypTick, zpTick);
5145 
5146  bool tick_along_z = nearhoriz || xisinf (fy);
5147  if (tick_along_z)
5148  p(2) += (signum (zpTick-zpTickN)*fz*xtickoffset);
5149  else
5150  p(1) += (signum (ypTick-ypTickN)*fy*xtickoffset);
5151 
5152  p = xform.transform (p(0), p(1), p(2), false);
5153 
5154  switch (xstate)
5155  {
5156  case AXE_ANY_DIR:
5157  p(0) += (xyzSym ? wmax : -wmax);
5158  p(1) += hmax;
5159  break;
5160 
5161  case AXE_VERT_DIR:
5162  p(0) -= wmax;
5163  angle = 90;
5164  break;
5165 
5166  case AXE_HORZ_DIR:
5167  p(1) += (x2Dtop ? -hmax : hmax);
5168  break;
5169  }
5170 
5171  if (xlabel_props.positionmode_is ("auto"))
5172  {
5173  p = xform.untransform (p(0), p(1), p(2), true);
5174  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
5175  xlabel_props.set_positionmode ("auto");
5176  }
5177 
5178  if (! is_empty && xlabel_props.rotationmode_is ("auto"))
5179  {
5180  xlabel_props.set_rotation (angle);
5181  xlabel_props.set_rotationmode ("auto");
5182  }
5183  }
5184 }
5185 
5186 static bool updating_ylabel_position = false;
5187 
5188 void
5190 {
5191  if (updating_ylabel_position)
5192  return;
5193 
5194  text::properties& ylabel_props
5195  = reinterpret_cast<text::properties&>
5196  (gh_manager::get_object (get_ylabel ()).get_properties ());
5197 
5198  bool is_empty = ylabel_props.get_string ().is_empty ();
5199 
5200  unwind_protect frame;
5201  frame.protect_var (updating_ylabel_position);
5202  updating_ylabel_position = true;
5203 
5204  if (! is_empty)
5205  {
5206  if (ylabel_props.horizontalalignmentmode_is ("auto"))
5207  {
5208  ylabel_props.set_horizontalalignment
5209  (ystate > AXE_DEPTH_DIR
5210  ? "center" : (!xyzSym ? "left" : "right"));
5211 
5212  ylabel_props.set_horizontalalignmentmode ("auto");
5213  }
5214 
5215  if (ylabel_props.verticalalignmentmode_is ("auto"))
5216  {
5217  ylabel_props.set_verticalalignment
5218  (ystate == AXE_VERT_DIR && !y2Dright ? "bottom" : "top");
5219 
5220  ylabel_props.set_verticalalignmentmode ("auto");
5221  }
5222  }
5223 
5224  if (ylabel_props.positionmode_is ("auto")
5225  || ylabel_props.rotationmode_is ("auto"))
5226  {
5227  graphics_xform xform = get_transform ();
5228 
5229  Matrix ext (1, 2, 0.0);
5230 
5231  // The underlying get_extents() from FreeType produces mismatched values.
5232  // x-extent accurately measures the width of the glyphs.
5233  // y-extent instead measures from baseline-to-baseline.
5234  // Pad x-extent (+4) so that it approximately matches y-extent.
5235  // This keeps ylabels about the same distance from y-axis as
5236  // xlabels are from x-axis.
5237  // ALWAYS use an even number for padding or horizontal alignment
5238  // will be off.
5239  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
5240  get_yticklabel ().all_strings (),
5241  get_ylim ().matrix_value ());
5242 
5243  double wmax = ext(0)+4, hmax = ext(1), angle = 0;
5244  ColumnVector p =
5245  graphics_xform::xform_vector (xpTick, (ypTickN+ypTick)/2, zpTick);
5246 
5247  bool tick_along_z = nearhoriz || xisinf (fx);
5248  if (tick_along_z)
5249  p(2) += (signum (zpTick-zpTickN)*fz*ytickoffset);
5250  else
5251  p(0) += (signum (xpTick-xpTickN)*fx*ytickoffset);
5252 
5253  p = xform.transform (p(0), p(1), p(2), false);
5254 
5255  switch (ystate)
5256  {
5257  case AXE_ANY_DIR:
5258  p(0) += (!xyzSym ? wmax : -wmax);
5259  p(1) += hmax;
5260  break;
5261 
5262  case AXE_VERT_DIR:
5263  p(0) += (y2Dright ? wmax : -wmax);
5264  angle = 90;
5265  break;
5266 
5267  case AXE_HORZ_DIR:
5268  p(1) += hmax;
5269  break;
5270  }
5271 
5272  if (ylabel_props.positionmode_is ("auto"))
5273  {
5274  p = xform.untransform (p(0), p(1), p(2), true);
5275  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
5276  ylabel_props.set_positionmode ("auto");
5277  }
5278 
5279  if (! is_empty && ylabel_props.rotationmode_is ("auto"))
5280  {
5281  ylabel_props.set_rotation (angle);
5282  ylabel_props.set_rotationmode ("auto");
5283  }
5284  }
5285 }
5286 
5287 static bool updating_zlabel_position = false;
5288 
5289 void
5291 {
5292  if (updating_zlabel_position)
5293  return;
5294 
5295  text::properties& zlabel_props
5296  = reinterpret_cast<text::properties&>
5297  (gh_manager::get_object (get_zlabel ()).get_properties ());
5298 
5299  bool camAuto = cameraupvectormode_is ("auto");
5300  bool is_empty = zlabel_props.get_string ().is_empty ();
5301 
5302  unwind_protect frame;
5303  frame.protect_var (updating_zlabel_position);
5304  updating_zlabel_position = true;
5305 
5306  if (! is_empty)
5307  {
5308  if (zlabel_props.horizontalalignmentmode_is ("auto"))
5309  {
5310  zlabel_props.set_horizontalalignment
5311  ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
5312 
5313  zlabel_props.set_horizontalalignmentmode ("auto");
5314  }
5315 
5316  if (zlabel_props.verticalalignmentmode_is ("auto"))
5317  {
5318  zlabel_props.set_verticalalignment
5319  (zstate == AXE_VERT_DIR
5320  ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
5321 
5322  zlabel_props.set_verticalalignmentmode ("auto");
5323  }
5324  }
5325 
5326  if (zlabel_props.positionmode_is ("auto")
5327  || zlabel_props.rotationmode_is ("auto"))
5328  {
5329  graphics_xform xform = get_transform ();
5330 
5331  Matrix ext (1, 2, 0.0);
5332  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
5333  get_zticklabel ().all_strings (),
5334  get_zlim ().matrix_value ());
5335 
5336  double wmax = ext(0), hmax = ext(1), angle = 0;
5337  ColumnVector p;
5338 
5339  if (xySym)
5340  {
5341  p = graphics_xform::xform_vector (xPlaneN, yPlane,
5342  (zpTickN+zpTick)/2);
5343  if (xisinf (fy))
5344  p(0) += (signum (xPlaneN-xPlane)*fx*ztickoffset);
5345  else
5346  p(1) += (signum (yPlane-yPlaneN)*fy*ztickoffset);
5347  }
5348  else
5349  {
5350  p = graphics_xform::xform_vector (xPlane, yPlaneN,
5351  (zpTickN+zpTick)/2);
5352  if (xisinf (fx))
5353  p(1) += (signum (yPlaneN-yPlane)*fy*ztickoffset);
5354  else
5355  p(0) += (signum (xPlane-xPlaneN)*fx*ztickoffset);
5356  }
5357 
5358  p = xform.transform (p(0), p(1), p(2), false);
5359 
5360  switch (zstate)
5361  {
5362  case AXE_ANY_DIR:
5363  if (camAuto)
5364  {
5365  p(0) -= wmax;
5366  angle = 90;
5367  }
5368 
5369  // FIXME: what's the correct offset?
5370  //
5371  // p[0] += (!xySym ? wmax : -wmax);
5372  // p[1] += (zSign ? hmax : -hmax);
5373 
5374  break;
5375 
5376  case AXE_VERT_DIR:
5377  p(0) -= wmax;
5378  angle = 90;
5379  break;
5380 
5381  case AXE_HORZ_DIR:
5382  p(1) += hmax;
5383  break;
5384  }
5385 
5386  if (zlabel_props.positionmode_is ("auto"))
5387  {
5388  p = xform.untransform (p(0), p(1), p(2), true);
5389  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
5390  zlabel_props.set_positionmode ("auto");
5391  }
5392 
5393  if (! is_empty && zlabel_props.rotationmode_is ("auto"))
5394  {
5395  zlabel_props.set_rotation (angle);
5396  zlabel_props.set_rotationmode ("auto");
5397  }
5398  }
5399 }
5400 
5401 static bool updating_title_position = false;
5402 
5403 void
5405 {
5406  if (updating_title_position)
5407  return;
5408 
5409  text::properties& title_props
5410  = reinterpret_cast<text::properties&>
5411  (gh_manager::get_object (get_title ()).get_properties ());
5412 
5413  unwind_protect frame;
5414  frame.protect_var (updating_title_position);
5415  updating_title_position = true;
5416 
5417  if (title_props.positionmode_is ("auto"))
5418  {
5419  graphics_xform xform = get_transform ();
5420 
5421  // FIXME: bbox should be stored in axes::properties
5422  Matrix bbox = get_extent (false);
5423 
5424  ColumnVector p =
5425  graphics_xform::xform_vector (bbox(0)+bbox(2)/2,
5426  bbox(1)-10,
5427  (x_zlim(0)+x_zlim(1))/2);
5428 
5429  if (x2Dtop)
5430  {
5431  Matrix ext (1, 2, 0.0);
5432  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5433  get_xticklabel ().all_strings (),
5434  get_xlim ().matrix_value ());
5435  p(1) -= ext(1);
5436  }
5437 
5438  p = xform.untransform (p(0), p(1), p(2), true);
5439 
5440  title_props.set_position (p.extract_n (0, 3).transpose ());
5441  title_props.set_positionmode ("auto");
5442  }
5443 }
5444 
5445 void
5446 axes::properties::update_autopos (const std::string& elem_type)
5447 {
5448  if (elem_type == "xlabel")
5449  update_xlabel_position ();
5450  else if (elem_type == "ylabel")
5451  update_ylabel_position ();
5452  else if (elem_type == "zlabel")
5453  update_zlabel_position ();
5454  else if (elem_type == "title")
5455  update_title_position ();
5456  else if (elem_type == "sync")
5457  sync_positions ();
5458 }
5459 
5460 static void
5461 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
5462  double xlength, double ylength, double zlength)
5463 {
5464  double xval = xlength/scalefactors(0);
5465  double yval = ylength/scalefactors(1);
5466  double zval = zlength/scalefactors(2);
5467 
5468  double minval = xmin (xmin (xval, yval), zval);
5469 
5470  aspectratios(0) = xval/minval;
5471  aspectratios(1) = yval/minval;
5472  aspectratios(2) = zval/minval;
5473 }
5474 
5475 static void
5476 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
5477  double pbfactor, double dafactor, char limit_type, bool tight)
5478 {
5479  if (tight)
5480  {
5481  double minval = octave_Inf;
5482  double maxval = -octave_Inf;
5483  double min_pos = octave_Inf;
5484  double max_neg = -octave_Inf;
5485  get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
5486  if (xfinite (minval) && xfinite (maxval))
5487  {
5488  limits(0) = minval;
5489  limits(1) = maxval;
5490  s = xmax(s, (maxval - minval) / (pbfactor * dafactor));
5491  }
5492  }
5493  else
5494  s = xmax(s, (limits(1) - limits(0)) / (pbfactor * dafactor));
5495 }
5496 
5497 static bool updating_aspectratios = false;
5498 
5499 void
5501 {
5502  if (updating_aspectratios)
5503  return;
5504 
5505  Matrix xlimits = get_xlim ().matrix_value ();
5506  Matrix ylimits = get_ylim ().matrix_value ();
5507  Matrix zlimits = get_zlim ().matrix_value ();
5508 
5509  double dx = (xlimits(1)-xlimits(0));
5510  double dy = (ylimits(1)-ylimits(0));
5511  double dz = (zlimits(1)-zlimits(0));
5512 
5513  Matrix da = get_dataaspectratio ().matrix_value ();
5514  Matrix pba = get_plotboxaspectratio ().matrix_value ();
5515 
5516  if (dataaspectratiomode_is ("auto"))
5517  {
5518  if (plotboxaspectratiomode_is ("auto"))
5519  {
5520  pba = Matrix (1, 3, 1.0);
5521  plotboxaspectratio.set (pba, false);
5522  }
5523 
5524  normalized_aspectratios (da, pba, dx, dy, dz);
5525  dataaspectratio.set (da, false);
5526  }
5527  else if (plotboxaspectratiomode_is ("auto"))
5528  {
5529  normalized_aspectratios (pba, da, dx, dy, dz);
5530  plotboxaspectratio.set (pba, false);
5531  }
5532  else
5533  {
5534  double s = -octave_Inf;
5535  bool modified_limits = false;
5536  Matrix kids;
5537 
5538  if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
5539  {
5540  modified_limits = true;
5541  kids = get_children ();
5542  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
5543  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
5544  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
5545  }
5546  else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
5547  {
5548  modified_limits = true;
5549  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
5550  }
5551  else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
5552  {
5553  modified_limits = true;
5554  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
5555  }
5556  else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
5557  {
5558  modified_limits = true;
5559  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
5560  }
5561 
5562  if (modified_limits)
5563  {
5564 
5565  unwind_protect frame;
5566  frame.protect_var (updating_aspectratios);
5567 
5568  updating_aspectratios = true;
5569 
5570  dx = pba(0) *da(0);
5571  dy = pba(1) *da(1);
5572  dz = pba(2) *da(2);
5573  if (xisinf (s))
5574  s = 1 / xmin (xmin (dx, dy), dz);
5575 
5576  if (xlimmode_is ("auto"))
5577  {
5578  dx = s * dx;
5579  xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
5580  xlimits(1) = xlimits(0) + dx;
5581  set_xlim (xlimits);
5582  set_xlimmode ("auto");
5583  }
5584 
5585  if (ylimmode_is ("auto"))
5586  {
5587  dy = s * dy;
5588  ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
5589  ylimits(1) = ylimits(0) + dy;
5590  set_ylim (ylimits);
5591  set_ylimmode ("auto");
5592  }
5593 
5594  if (zlimmode_is ("auto"))
5595  {
5596  dz = s * dz;
5597  zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
5598  zlimits(1) = zlimits(0) + dz;
5599  set_zlim (zlimits);
5600  set_zlimmode ("auto");
5601  }
5602  }
5603  else
5604  {
5605  normalized_aspectratios (pba, da, dx, dy, dz);
5606  plotboxaspectratio.set (pba, false);
5607  }
5608  }
5609 }
5610 
5611 void
5613 {
5614 #ifdef HAVE_FREETYPE
5615 #ifdef HAVE_FONTCONFIG
5616  text_renderer.set_font (get ("fontname").string_value (),
5617  get ("fontweight").string_value (),
5618  get ("fontangle").string_value (),
5619  get ("fontsize").double_value ());
5620 #endif
5621 #endif
5622 }
5623 
5624 // The INTERNAL flag defines whether position or outerposition is used.
5625 
5626 Matrix
5628  const Matrix& parent_pix_size) const
5629 {
5630  Matrix pos = internal ? get_position ().matrix_value ()
5631  : get_outerposition ().matrix_value ();
5632  Matrix parent_size (parent_pix_size);
5633 
5634  if (parent_size.numel () == 0)
5635  {
5637 
5638  if (obj.valid_object ())
5639  parent_size =
5640  obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
5641  else
5642  parent_size = default_figure_position ();
5643  }
5644 
5645  pos = convert_position (pos, get_units (), "pixels", parent_size);
5646 
5647  pos(0)--;
5648  pos(1)--;
5649  pos(1) = parent_size(1) - pos(1) - pos(3);
5650 
5651  return pos;
5652 }
5653 
5654 Matrix
5655 axes::properties::get_extent (bool with_text, bool only_text_height) const
5656 {
5657  graphics_xform xform = get_transform ();
5658 
5659  Matrix ext (1, 4, 0.0);
5660  ext(0) = octave_Inf;
5661  ext(1) = octave_Inf;
5662  ext(2) = -octave_Inf;
5663  ext(3) = -octave_Inf;
5664  for (int i = 0; i <= 1; i++)
5665  for (int j = 0; j <= 1; j++)
5666  for (int k = 0; k <= 1; k++)
5667  {
5668  ColumnVector p = xform.transform (i ? xPlaneN : xPlane,
5669  j ? yPlaneN : yPlane,
5670  k ? zPlaneN : zPlane, false);
5671  ext(0) = std::min (ext(0), p(0));
5672  ext(1) = std::min (ext(1), p(1));
5673  ext(2) = std::max (ext(2), p(0));
5674  ext(3) = std::max (ext(3), p(1));
5675  }
5676 
5677  if (with_text)
5678  {
5679  for (int i = 0; i < 4; i++)
5680  {
5681  graphics_handle text_handle;
5682  if (i == 0)
5683  text_handle = get_title ();
5684  else if (i == 1)
5685  text_handle = get_xlabel ();
5686  else if (i == 2)
5687  text_handle = get_ylabel ();
5688  else if (i == 3)
5689  text_handle = get_zlabel ();
5690 
5691  text::properties& text_props
5692  = reinterpret_cast<text::properties&>
5693  (gh_manager::get_object (text_handle).get_properties ());
5694 
5695  Matrix text_pos = text_props.get_data_position ();
5696  text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
5697  if (text_props.get_string ().is_empty ())
5698  {
5699  ext(0) = std::min (ext(0), text_pos(0));
5700  ext(1) = std::min (ext(1), text_pos(1));
5701  ext(2) = std::max (ext(2), text_pos(0));
5702  ext(3) = std::max (ext(3), text_pos(1));
5703  }
5704  else
5705  {
5706  Matrix text_ext = text_props.get_extent_matrix ();
5707 
5708  bool ignore_horizontal = false;
5709  bool ignore_vertical = false;
5710  if (only_text_height)
5711  {
5712  double text_rotation = text_props.get_rotation ();
5713  if (text_rotation == 0. || text_rotation == 180.)
5714  ignore_horizontal = true;
5715  else if (text_rotation == 90. || text_rotation == 270.)
5716  ignore_vertical = true;
5717  }
5718 
5719  if (! ignore_horizontal)
5720  {
5721  ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
5722  ext(2) = std::max (ext(2),
5723  text_pos(0)+text_ext(0)+text_ext(2));
5724  }
5725 
5726  if (! ignore_vertical)
5727  {
5728  ext(1) = std::min (ext(1),
5729  text_pos(1)-text_ext(1)-text_ext(3));
5730  ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
5731  }
5732  }
5733  }
5734  }
5735 
5736  ext(2) = ext(2)-ext(0);
5737  ext(3) = ext(3)-ext(1);
5738 
5739  return ext;
5740 }
5741 
5742 static octave_value
5744 {
5745  octave_value retval = val;
5746 
5747  if (val.is_cellstr ())
5748  {
5749  // Always return a column vector for Matlab Compatibility
5750  if (val.columns () > 1)
5751  retval = val.reshape (dim_vector (val.numel (), 1));
5752  }
5753  else
5754  {
5755  string_vector sv;
5756  if (val.is_numeric_type ())
5757  {
5758  NDArray data = val.array_value ();
5759  std::ostringstream oss;
5760  oss.precision (5);
5761  for (octave_idx_type i = 0; i < val.numel (); i++)
5762  {
5763  oss.str ("");
5764  oss << data(i);
5765  sv.append (oss.str ());
5766  }
5767  }
5768  else if (val.is_string () && val.rows () == 1)
5769  {
5770  std::string valstr = val.string_value ();
5771  std::istringstream iss (valstr);
5772  std::string tmpstr;
5773 
5774  // Split string with delimiter '|'
5775  while (std::getline (iss, tmpstr, '|'))
5776  sv.append (tmpstr);
5777 
5778  // If string ends with '|' Matlab appends a null string
5779  if (*valstr.rbegin () == '|')
5780  sv.append (std::string (""));
5781  }
5782  else
5783  return retval;
5784 
5785  charMatrix chmat (sv, ' ');
5786 
5787  retval = octave_value (chmat);
5788  }
5789 
5790  return retval;
5791 }
5792 
5793 void
5795 {
5796  if (!error_state)
5797  {
5798  if (xticklabel.set (convert_ticklabel_string (v), false))
5799  {
5800  set_xticklabelmode ("manual");
5801  xticklabel.run_listeners (POSTSET);
5802  mark_modified ();
5803  }
5804  else
5805  set_xticklabelmode ("manual");
5806  }
5807 }
5808 
5809 void
5811 {
5812  if (!error_state)
5813  {
5814  if (yticklabel.set (convert_ticklabel_string (v), false))
5815  {
5816  set_yticklabelmode ("manual");
5817  yticklabel.run_listeners (POSTSET);
5818  mark_modified ();
5819  }
5820  else
5821  set_yticklabelmode ("manual");
5822  }
5823 }
5824 
5825 void
5827 {
5828  if (!error_state)
5829  {
5830  if (zticklabel.set (convert_ticklabel_string (v), false))
5831  {
5832  set_zticklabelmode ("manual");
5833  zticklabel.run_listeners (POSTSET);
5834  mark_modified ();
5835  }
5836  else
5837  set_zticklabelmode ("manual");
5838  }
5839 }
5840 
5841 // Almost identical to convert_ticklabel_string but it only accepts
5842 // cellstr or string, not numeric input.
5843 static octave_value
5845 {
5846  octave_value retval = val;
5847 
5848  if (val.is_cellstr ())
5849  {
5850  // Always return a column vector for Matlab Compatibility
5851  if (val.columns () > 1)
5852  retval = val.reshape (dim_vector (val.numel (), 1));
5853  }
5854  else
5855  {
5856  string_vector sv;
5857  if (val.is_string () && val.rows () == 1)
5858  {
5859  std::string valstr = val.string_value ();
5860  std::istringstream iss (valstr);
5861  std::string tmpstr;
5862 
5863  // Split string with delimiter '|'
5864  while (std::getline (iss, tmpstr, '|'))
5865  sv.append (tmpstr);
5866 
5867  // If string ends with '|' Matlab appends a null string
5868  if (*valstr.rbegin () == '|')
5869  sv.append (std::string (""));
5870  }
5871  else
5872  return retval;
5873 
5874  charMatrix chmat (sv, ' ');
5875 
5876  retval = octave_value (chmat);
5877  }
5878 
5879  return retval;
5880 }
5881 
5882 void
5884 {
5885  if (!error_state)
5886  {
5887  linestyleorder.set (convert_linestyleorder_string (v), false);
5888  }
5889 }
5890 
5891 void
5893 {
5894  if (! error_state)
5895  {
5896  caseless_str old_units = get_units ();
5897  if (units.set (v, true))
5898  {
5899  update_units (old_units);
5900  mark_modified ();
5901  }
5902  }
5903 }
5904 
5905 void
5907 {
5909  Matrix parent_bb
5910  = obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
5911  caseless_str new_units = get_units ();
5912  position.set (octave_value (convert_position (get_position ().matrix_value (),
5913  old_units, new_units,
5914  parent_bb)),
5915  false);
5916  outerposition.set (octave_value (convert_position (get_outerposition ().matrix_value (),
5917  old_units, new_units,
5918  parent_bb)),
5919  false);
5920  tightinset.set (octave_value (convert_position (get_tightinset ().matrix_value (),
5921  old_units, new_units,
5922  parent_bb)),
5923  false);
5924  looseinset.set (octave_value (convert_position (get_looseinset ().matrix_value (),
5925  old_units, new_units,
5926  parent_bb)),
5927  false);
5928 }
5929 
5930 void
5932 {
5933  if (! error_state)
5934  {
5935  caseless_str old_fontunits = get_fontunits ();
5936  if (fontunits.set (v, true))
5937  {
5938  update_fontunits (old_fontunits);
5939  mark_modified ();
5940  }
5941  }
5942 }
5943 
5944 void
5946 {
5947  caseless_str new_units = get_fontunits ();
5948  double parent_height = get_boundingbox (true).elem (3);
5949  double fsz = get_fontsize ();
5950 
5951  fsz = convert_font_size (fsz, old_units, new_units, parent_height);
5952 
5953  set_fontsize (octave_value (fsz));
5954 }
5955 
5956 double
5957 axes::properties::get_fontsize_points (double box_pix_height) const
5958 {
5959  double fs = get_fontsize ();
5960  double parent_height = box_pix_height;
5961 
5962  if (fontunits_is ("normalized") && parent_height <= 0)
5963  parent_height = get_boundingbox (true).elem (3);
5964 
5965  return convert_font_size (fs, get_fontunits (), "points", parent_height);
5966 }
5967 
5969 graphics_xform::xform_vector (double x, double y, double z)
5970 {
5971  return ::xform_vector (x, y, z);
5972 }
5973 
5974 Matrix
5976 {
5978 }
5979 
5981 graphics_xform::transform (double x, double y, double z,
5982  bool use_scale) const
5983 {
5984  if (use_scale)
5985  {
5986  x = sx.scale (x);
5987  y = sy.scale (y);
5988  z = sz.scale (z);
5989  }
5990 
5991  return ::transform (xform, x, y, z);
5992 }
5993 
5995 graphics_xform::untransform (double x, double y, double z,
5996  bool use_scale) const
5997 {
5998  ColumnVector v = ::transform (xform_inv, x, y, z);
5999 
6000  if (use_scale)
6001  {
6002  v(0) = sx.unscale (v(0));
6003  v(1) = sy.unscale (v(1));
6004  v(2) = sz.unscale (v(2));
6005  }
6006 
6007  return v;
6008 }
6009 
6011 axes::get_default (const caseless_str& name) const
6012 {
6013  octave_value retval = default_properties.lookup (name);
6014 
6015  if (retval.is_undefined ())
6016  {
6017  graphics_handle parent = get_parent ();
6018  graphics_object parent_obj = gh_manager::get_object (parent);
6019 
6020  retval = parent_obj.get_default (name);
6021  }
6022 
6023  return retval;
6024 }
6025 
6026 // FIXME: remove.
6027 // FIXME: maybe this should go into array_property class?
6028 /*
6029 static void
6030 check_limit_vals (double& min_val, double& max_val,
6031  double& min_pos, double& max_neg,
6032  const array_property& data)
6033 {
6034  double val = data.min_val ();
6035  if (xfinite (val) && val < min_val)
6036  min_val = val;
6037  val = data.max_val ();
6038  if (xfinite (val) && val > max_val)
6039  max_val = val;
6040  val = data.min_pos ();
6041  if (xfinite (val) && val > 0 && val < min_pos)
6042  min_pos = val;
6043  val = data.max_neg ();
6044  if (xfinite (val) && val < 0 && val > max_neg)
6045  max_neg = val;
6046 }
6047 */
6048 
6049 static void
6050 check_limit_vals (double& min_val, double& max_val,
6051  double& min_pos, double& max_neg,
6052  const octave_value& data)
6053 {
6054  if (data.is_matrix_type ())
6055  {
6056  Matrix m = data.matrix_value ();
6057 
6058  if (! error_state && m.numel () == 4)
6059  {
6060  double val;
6061 
6062  val = m(0);
6063  if (xfinite (val) && val < min_val)
6064  min_val = val;
6065 
6066  val = m(1);
6067  if (xfinite (val) && val > max_val)
6068  max_val = val;
6069 
6070  val = m(2);
6071  if (xfinite (val) && val > 0 && val < min_pos)
6072  min_pos = val;
6073 
6074  val = m(3);
6075  if (xfinite (val) && val < 0 && val > max_neg)
6076  max_neg = val;
6077  }
6078  }
6079 }
6080 
6081 // magform(x) Returns (a, b), where x = a * 10^b, abs (a) >= 1., and b is
6082 // integer.
6083 
6084 static void
6085 magform (double x, double& a, int& b)
6086 {
6087  if (x == 0)
6088  {
6089  a = 0;
6090  b = 0;
6091  }
6092  else
6093  {
6094  b = static_cast<int> (gnulib::floor (std::log10 (std::abs (x))));
6095  a = x / std::pow (10.0, b);
6096  }
6097 }
6098 
6099 // A translation from Tom Holoryd's python code at
6100 // http://kurage.nimh.nih.gov/tomh/tics.py
6101 // FIXME: add log ticks
6102 
6103 double
6104 axes::properties::calc_tick_sep (double lo, double hi)
6105 {
6106  int ticint = 5;
6107 
6108  // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
6109  // SCALE3 for Determination of Scales on Computer Generated
6110  // Plots", Communications of the ACM, 10 (1973), 639-640.
6111  // Also cited as ACM Algorithm 463.
6112 
6113  double a;
6114  int b, x;
6115 
6116  magform ((hi-lo)/ticint, a, b);
6117 
6118  static const double sqrt_2 = sqrt (2.0);
6119  static const double sqrt_10 = sqrt (10.0);
6120  static const double sqrt_50 = sqrt (50.0);
6121 
6122  if (a < sqrt_2)
6123  x = 1;
6124  else if (a < sqrt_10)
6125  x = 2;
6126  else if (a < sqrt_50)
6127  x = 5;
6128  else
6129  x = 10;
6130 
6131  return x * std::pow (10., b);
6132 
6133 }
6134 
6135 // Attempt to make "nice" limits from the actual max and min of the
6136 // data. For log plots, we will also use the smallest strictly positive
6137 // value.
6138 
6139 Matrix
6141  double min_pos, double max_neg,
6142  bool logscale)
6143 {
6144  Matrix retval;
6145 
6146  double min_val = xmin;
6147  double max_val = xmax;
6148 
6149  if (xisinf (min_val) && min_val > 0 && xisinf (max_val) && max_val < 0)
6150  {
6