changelog shortlog tags changeset files revisions annotate raw

src/graphics.cc

changeset 10289: 4b124317dc38
parent:8c583af2cf64
author: John W. Eaton <jwe@octave.org>
date: Tue Feb 09 20:58:55 2010 -0500 (28 minutes ago)
permissions: -rw-r--r--
description: base_properties::set_children: account for hidden children
1/*
2
3Copyright (C) 2007, 2008, 2009 John W. Eaton
4
5This file is part of Octave.
6
7Octave is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by the
9Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
11
12Octave is distributed in the hope that it will be useful, but WITHOUT
13ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
31#include <algorithm>
32#include <list>
33#include <map>
34#include <set>
35#include <string>
36#include <sstream>
37
38#include "file-ops.h"
39#include "file-stat.h"
40
41#include "cmd-edit.h"
42#include "defun.h"
43#include "display.h"
44#include "error.h"
45#include "graphics.h"
46#include "input.h"
47#include "ov.h"
48#include "oct-obj.h"
49#include "oct-map.h"
50#include "ov-fcn-handle.h"
51#include "parse.h"
52#include "toplev.h"
53#include "unwind-prot.h"
54
55// forward declaration
56static octave_value xget (const graphics_handle& h, const caseless_str& name);
57
58static void
59gripe_set_invalid (const std::string& pname)
60{
61 error ("set: invalid value for %s property", pname.c_str ());
62}
63
64// Check to see that PNAME matches just one of PNAMES uniquely.
65// Return the full name of the match, or an empty caseless_str object
66// if there is no match, or the match is ambiguous.
67
68static caseless_str
69validate_property_name (const std::string& who, const std::string& what,
70 const std::set<std::string>& pnames,
71 const caseless_str& pname)
72{
73 size_t len = pname.length ();
74 std::set<std::string> matches;
75
76 for (std::set<std::string>::const_iterator p = pnames.begin ();
77 p != pnames.end (); p++)
78 {
79 if (pname.compare (*p, len))
80 {
81 if (len == p->length ())
82 {
83 // Exact match.
84 return pname;
85 }
86
87 matches.insert (*p);
88 }
89 }
90
91 size_t num_matches = matches.size ();
92
93 if (num_matches == 0)
94 {
95 error ("%s: unknown %s property %s",
96 who.c_str (), what.c_str (), pname.c_str ());
97 }
98 else if (num_matches > 1)
99 {
100 string_vector sv (matches);
101
102 std::ostringstream os;
103
104 sv.list_in_columns (os);
105
106 std::string match_list = os.str ();
107
108 error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
109 who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
110 }
111 else if (num_matches == 1)
112 {
113 // Exact match was handled above.
114
115 std::string possible_match = *(matches.begin ());
116
117 warning_with_id ("Octave:abbreviated-property-match",
118 "%s: allowing %s to match %s property %s",
119 who.c_str (), pname.c_str (), what.c_str (),
120 possible_match.c_str ());
121
122 return possible_match;
123 }
124
125 return caseless_str ();
126}
127
128static Matrix
129jet_colormap (void)
130{
131 Matrix cmap (64, 3, 0.0);
132
133 for (octave_idx_type i = 0; i < 64; i++)
134 {
135 // This is the jet colormap. It would be nice to be able
136 // to feval the jet function but since there is a static
137 // property object that includes a colormap_property
138 // object, we need to initialize this before main is even
139 // called, so calling an interpreted function is not
140 // possible.
141
142 double x = i / 63.0;
143
144 if (x >= 3.0/8.0 && x < 5.0/8.0)
145 cmap(i,0) = 4.0 * x - 3.0/2.0;
146 else if (x >= 5.0/8.0 && x < 7.0/8.0)
147 cmap(i,0) = 1.0;
148 else if (x >= 7.0/8.0)
149 cmap(i,0) = -4.0 * x + 9.0/2.0;
150
151 if (x >= 1.0/8.0 && x < 3.0/8.0)
152 cmap(i,1) = 4.0 * x - 1.0/2.0;
153 else if (x >= 3.0/8.0 && x < 5.0/8.0)
154 cmap(i,1) = 1.0;
155 else if (x >= 5.0/8.0 && x < 7.0/8.0)
156 cmap(i,1) = -4.0 * x + 7.0/2.0;
157
158 if (x < 1.0/8.0)
159 cmap(i,2) = 4.0 * x + 1.0/2.0;
160 else if (x >= 1.0/8.0 && x < 3.0/8.0)
161 cmap(i,2) = 1.0;
162 else if (x >= 3.0/8.0 && x < 5.0/8.0)
163 cmap(i,2) = -4.0 * x + 5.0/2.0;
164 }
165
166 return cmap;
167}
168
169static double
170default_screendepth (void)
171{
172 return display_info::depth ();
173}
174
175static Matrix
176default_screensize (void)
177{
178 Matrix retval (1, 4, 1.0);
179
180 retval(2) = display_info::width ();
181 retval(3) = display_info::height ();
182
183 return retval;
184}
185
186static double
187default_screenpixelsperinch (void)
188{
189 return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
190}
191
192static Matrix
193default_colororder (void)
194{
195 Matrix retval (7, 3, 0.0);
196
197 retval(0,2) = 1.0;
198
199 retval(1,1) = 0.5;
200
201 retval(2,0) = 1.0;
202
203 retval(3,1) = 0.75;
204 retval(3,2) = 0.75;
205
206 retval(4,0) = 0.75;
207 retval(4,2) = 0.75;
208
209 retval(5,0) = 0.75;
210 retval(5,1) = 0.75;
211
212 retval(6,0) = 0.25;
213 retval(6,1) = 0.25;
214 retval(6,2) = 0.25;
215
216 return retval;
217}
218
219static Matrix
220default_lim (void)
221{
222 Matrix m (1, 2, 0);
223 m(1) = 1;
224 return m;
225}
226
227static Matrix
228default_data (void)
229{
230 Matrix retval (1, 2);
231
232 retval(0) = 0;
233 retval(1) = 1;
234
235 return retval;
236}
237
238static Matrix
239default_axes_position (void)
240{
241 Matrix m (1, 4, 0.0);
242 m(0) = 0.13;
243 m(1) = 0.11;
244 m(2) = 0.775;
245 m(3) = 0.815;
246 return m;
247}
248
249static Matrix
250default_axes_outerposition (void)
251{
252 Matrix m (1, 4, 0.0);
253 m(2) = m(3) = 1.0;
254 return m;
255}
256
257static Matrix
258default_axes_tick (void)
259{
260 Matrix m (1, 6, 0.0);
261 m(0) = 0.0;
262 m(1) = 0.2;
263 m(2) = 0.4;
264 m(3) = 0.6;
265 m(4) = 0.8;
266 m(5) = 1.0;
267 return m;
268}
269
270static Matrix
271default_axes_ticklength (void)
272{
273 Matrix m (1, 2, 0.01);
274 m(1) = 0.025;
275 return m;
276}
277
278static Matrix
279default_figure_position (void)
280{
281 Matrix m (1, 4, 0.0);
282 m(0) = 300;
283 m(1) = 200;
284 m(2) = 560;
285 m(3) = 420;
286 return m;
287}
288
289static Matrix
290default_figure_papersize (void)
291{
292 Matrix m (1, 2, 0.0);
293 m(0) = 8.5;
294 m(1) = 11.0;
295 return m;
296}
297
298static Matrix
299default_figure_paperposition (void)
300{
301 Matrix m (1, 4, 0.0);
302 m(0) = 0.25;
303 m(1) = 2.50;
304 m(2) = 8.00;
305 m(3) = 6.00;
306 return m;
307}
308
309static Matrix
310convert_position (const Matrix& pos, const caseless_str& from_units,
311 const caseless_str& to_units,
312 const Matrix& parent_dim = Matrix (1, 2, 0.0))
313{
314 Matrix retval (1, 4);
315 double res = 0;
316
317 if (from_units.compare ("pixels"))
318 retval = pos;
319 else if (from_units.compare ("normalized"))
320 {
321 retval(0) = pos(0) * parent_dim(0) + 1;
322 retval(1) = pos(1) * parent_dim(1) + 1;
323 retval(2) = pos(2) * parent_dim(0);
324 retval(3) = pos(3) * parent_dim(1);
325 }
326 else if (from_units.compare ("characters"))
327 {
328 if (res <= 0)
329 res = xget (0, "screenpixelsperinch").double_value ();
330
331 double f = 0.0;
332
333 // FIXME -- this assumes the system font is Helvetica 10pt
334 // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
335 f = 12.0 * res / 74.951;
336
337 if (f > 0)
338 {
339 retval(0) = 0.5 * pos(0) * f;
340 retval(1) = pos(1) * f;
341 retval(2) = 0.5 * pos(2) * f;
342 retval(3) = pos(3) * f;
343 }
344 }
345 else
346 {
347 if (res <= 0)
348 res = xget (0, "screenpixelsperinch").double_value ();
349
350 double f = 0.0;
351
352 if (from_units.compare ("points"))
353 f = res / 72.0;
354 else if (from_units.compare ("inches"))
355 f = res;
356 else if (from_units.compare ("centimeters"))
357 f = res / 2.54;
358
359 if (f > 0)
360 {
361 retval(0) = pos(0) * f + 1;
362 retval(1) = pos(1) * f + 1;
363 retval(2) = pos(2) * f;
364 retval(3) = pos(3) * f;
365 }
366 }
367
368 if (! to_units.compare ("pixels"))
369 {
370 if (to_units.compare ("normalized"))
371 {
372 retval(0) = (retval(0) - 1) / parent_dim(0);
373 retval(1) = (retval(1) - 1) / parent_dim(1);
374 retval(2) /= parent_dim(0);
375 retval(3) /= parent_dim(1);
376 }
377 else if (to_units.compare ("characters"))
378 {
379 if (res <= 0)
380 res = xget (0, "screenpixelsperinch").double_value ();
381
382 double f = 0.0;
383
384 f = 12.0 * res / 74.951;
385
386 if (f > 0)
387 {
388 retval(0) = 2 * retval(0) / f;
389 retval(1) = retval(1) / f;
390 retval(2) = 2 * retval(2) / f;
391 retval(3) = retval(3) / f;
392 }
393 }
394 else
395 {
396 if (res <= 0)
397 res = xget (0, "screenpixelsperinch").double_value ();
398
399 double f = 0.0;
400
401 if (to_units.compare ("points"))
402 f = res / 72.0;
403 else if (to_units.compare ("inches"))
404 f = res;
405 else if (to_units.compare ("centimeters"))
406 f = res / 2.54;
407
408 if (f > 0)
409 {
410 retval(0) = (retval(0) - 1) / f;
411 retval(1) = (retval(1) - 1) / f;
412 retval(2) /= f;
413 retval(3) /= f;
414 }
415 }
416 }
417
418 return retval;
419}
420
421static graphics_object
422xget_ancestor (const graphics_object& go_arg, const std::string& type)
423{
424 graphics_object go = go_arg;
425
426 do
427 {
428 if (go.valid_object ())
429 {
430 if (go.isa (type))
431 return go;
432 else
433 go = gh_manager::get_object (go.get_parent ());
434 }
435 else
436 return graphics_object ();
437 }
438 while (true);
439}
440
441static octave_value
442convert_cdata (const base_properties& props, const octave_value& cdata,
443 bool is_scaled, int cdim)
444{
445 dim_vector dv (cdata.dims ());
446
447 if (dv.length () == cdim && dv(cdim-1) == 3)
448 return cdata;
449
450 Matrix cmap (1, 3, 0.0);
451 Matrix clim (1, 2, 0.0);
452
453 graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
454 graphics_object fig = xget_ancestor (go, "figure");
455
456 if (fig.valid_object ())
457 {
458 Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
459
460 if (! error_state)
461 cmap = _cmap;
462 }
463
464 if (is_scaled)
465 {
466 graphics_object ax = xget_ancestor (go, "axes");
467
468 if (ax.valid_object ())
469 {
470 Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
471
472 if (! error_state)
473 clim = _clim;
474 }
475 }
476
477 dv.resize (cdim);
478 dv(cdim-1) = 3;
479
480 NDArray a (dv);
481
482 octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
483 octave_idx_type nc = cmap.rows ();
484
485 double *av = a.fortran_vec ();
486 const double *cmapv = cmap.data ();
487 const double *cv = 0;
488 const octave_uint8 *icv = 0;
489
490 if (cdata.is_integer_type ())
491 icv = cdata.uint8_array_value ().data ();
492 else
493 cv = cdata.array_value ().data ();
494
495 for (octave_idx_type i = 0; i < lda; i++)
496 {
497 double x = (cv ? cv[i] : double (icv[i]));
498
499 if (is_scaled)
500 x = xround ((nc - 1) * (x - clim(0)) / (clim(1) - clim(0)));
501 else
502 x = xround (x - 1);
503
504 if (xisnan (x))
505 {
506 av[i] = x;
507 av[i+lda] = x;
508 av[i+2*lda] = x;
509 }
510 else
511 {
512 if (x < 0)
513 x = 0;
514 else if (x >= nc)
515 x = (nc - 1);
516
517 octave_idx_type idx = static_cast<octave_idx_type> (x);
518
519 av[i] = cmapv[idx];
520 av[i+lda] = cmapv[idx+nc];
521 av[i+2*lda] = cmapv[idx+2*nc];
522 }
523 }
524
525 return octave_value (a);
526}
527
528template<class T>
529static void
530get_array_limits (const Array<T>& m, double& emin, double& emax,
531 double& eminp)
532{
533 const T *data = m.data ();
534 octave_idx_type n = m.numel ();
535
536 for (octave_idx_type i = 0; i < n; i++)
537 {
538 double e = double (data[i]);
539
540 if (! (xisinf (e) || xisnan (e)))
541 {
542 if (e < emin)
543 emin = e;
544
545 if (e > emax)
546 emax = e;
547
548 if (e > 0 && e < eminp)
549 eminp = e;
550 }
551 }
552}
553
554static bool
555lookup_object_name (const caseless_str& name, caseless_str& go_name,
556 caseless_str& rest)
557{
558 int len = name.length ();
559 int offset = 0;
560 bool result = false;
561
562 if (len >= 4)
563 {
564 caseless_str pfx = name.substr (0, 4);
565
566 if (pfx.compare ("axes") || pfx.compare ("line")
567 || pfx.compare ("text"))
568 offset = 4;
569 else if (len >= 5)
570 {
571 pfx = name.substr (0, 5);
572
573 if (pfx.compare ("image") || pfx.compare ("patch"))
574 offset = 5;
575 else if (len >= 6)
576 {
577 pfx = name.substr (0, 6);
578
579 if (pfx.compare ("figure"))
580 offset = 6;
581 else if (len >= 7)
582 {
583 pfx = name.substr (0, 7);
584
585 if (pfx.compare ("surface") || pfx.compare ("hggroup"))
586 offset = 7;
587 }
588 }
589 }
590
591 if (offset > 0)
592 {
593 go_name = pfx;
594 rest = name.substr (offset);
595 result = true;
596 }
597 }
598
599 return result;
600}
601
602static base_graphics_object*
603make_graphics_object_from_type (const caseless_str& type,
604 const graphics_handle& h = graphics_handle (),
605 const graphics_handle& p = graphics_handle ())
606{
607 base_graphics_object *go = 0;
608
609 if (type.compare ("figure"))
610 go = new figure (h, p);
611 else if (type.compare ("axes"))
612 go = new axes (h, p);
613 else if (type.compare ("line"))
614 go = new line (h, p);
615 else if (type.compare ("text"))
616 go = new text (h, p);
617 else if (type.compare ("image"))
618 go = new image (h, p);
619 else if (type.compare ("patch"))
620 go = new patch (h, p);
621 else if (type.compare ("surface"))
622 go = new surface (h, p);
623 else if (type.compare ("hggroup"))
624 go = new hggroup (h, p);
625
626 return go;
627}
628
629// ---------------------------------------------------------------------
630
631bool
632base_property::set (const octave_value& v, bool do_run )
633{
634 if (do_set (v))
635 {
636
637 // notify backend
638 if (id >= 0)
639 {
640 graphics_object go = gh_manager::get_object (parent);
641 if (go)
642 {
643 graphics_backend backend = go.get_backend();
644 if (backend)
645 backend.property_changed (go, id);
646 }
647 }
648
649 // run listeners
650 if (do_run && ! error_state)
651 run_listeners (POSTSET);
652
653 return true;
654 }
655
656 return false;
657}
658
659
660void
661base_property::run_listeners (listener_mode mode)
662{
663 const octave_value_list& l = listeners[mode];
664
665 for (int i = 0; i < l.length (); i++)
666 {
667 gh_manager::execute_callback (parent, l(i), octave_value ());
668
669 if (error_state)
670 break;
671 }
672}
673
674radio_values::radio_values (const std::string& opt_string)
675{
676 size_t beg = 0;
677 size_t len = opt_string.length ();
678 bool done = len == 0;
679
680 while (! done)
681 {
682 size_t end = opt_string.find ('|', beg);
683
684 if (end == std::string::npos)
685 {
686 end = len;
687 done = true;
688 }
689
690 std::string t = opt_string.substr (beg, end-beg);
691
692 // Might want more error checking here...
693 if (t[0] == '{')
694 {
695 t = t.substr (1, t.length () - 2);
696 default_val = t;
697 }
698 else if (beg == 0) // ensure default value
699 default_val = t;
700
701 possible_vals.insert (t);
702
703 beg = end + 1;
704 }
705}
706
707bool
708color_values::str2rgb (std::string str)
709{
710 double tmp_rgb[3] = {0, 0, 0};
711 bool retval = true;
712 unsigned int len = str.length();
713
714 std::transform (str.begin (), str.end (), str.begin (), tolower);
715
716 if (str.compare(0, len, "blue", 0, len) == 0)
717 tmp_rgb[2] = 1;
718 else if (str.compare(0, len, "black", 0, len) == 0
719 || str.compare(0, len, "k", 0, len) == 0)
720 tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
721 else if (str.compare(0, len, "red", 0, len) == 0)
722 tmp_rgb[0] = 1;
723 else if (str.compare(0, len, "green", 0, len) == 0)
724 tmp_rgb[1] = 1;
725 else if (str.compare(0, len, "yellow", 0, len) == 0)
726 tmp_rgb[0] = tmp_rgb[1] = 1;
727 else if (str.compare(0, len, "magenta", 0, len) == 0)
728 tmp_rgb[0] = tmp_rgb[2] = 1;
729 else if (str.compare(0, len, "cyan", 0, len) == 0)
730 tmp_rgb[1] = tmp_rgb[2] = 1;
731 else if (str.compare(0, len, "white", 0, len) == 0
732 || str.compare(0, len, "w", 0, len) == 0)
733 tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
734 else
735 retval = false;
736
737 if (retval)
738 {
739 for (int i = 0; i < 3; i++)
740 xrgb(i) = tmp_rgb[i];
741 }
742
743 return retval;
744}
745
746bool
747color_property::do_set (const octave_value& val)
748{
749 if (val.is_string ())
750 {
751 std::string s = val.string_value ();
752
753 if (! s.empty ())
754 {
755 if (radio_val.contains (s))
756 {
757 if (current_type != radio_t || current_val != s)
758 {
759 current_val = s;
760 current_type = radio_t;
761 return true;
762 }
763 }
764 else
765 {
766 color_values col (s);
767 if (! error_state)
768 {
769 if (current_type != color_t || col != color_val)
770 {
771 color_val = col;
772 current_type = color_t;
773 return true;
774 }
775 }
776 else
777 error ("invalid value for color property \"%s\" (value = %s)",
778 get_name ().c_str (), s.c_str ());
779 }
780 }
781 else
782 error ("invalid value for color property \"%s\"",
783 get_name ().c_str ());
784 }
785 else if (val.is_numeric_type ())
786 {
787 Matrix m = val.matrix_value ();
788
789 if (m.numel () == 3)
790 {
791 color_values col (m (0), m (1), m(2));
792 if (! error_state)
793 {
794 if (current_type != color_t || col != color_val)
795 {
796 color_val = col;
797 current_type = color_t;
798 return true;
799 }
800 }
801 }
802 else
803 error ("invalid value for color property \"%s\"",
804 get_name ().c_str ());
805 }
806 else
807 error ("invalid value for color property \"%s\"",
808 get_name ().c_str ());
809
810 return false;
811}
812
813bool
814double_radio_property::do_set (const octave_value& val)
815{
816 if (val.is_string ())
817 {
818 std::string s = val.string_value ();
819
820 if (! s.empty () && radio_val.contains (s))
821 {
822 if (current_type != radio_t || s != current_val)
823 {
824 current_val = s;
825 current_type = radio_t;
826 return true;
827 }
828 }
829 else
830 error ("invalid value for double_radio property \"%s\"",
831 get_name ().c_str ());
832 }
833 else if (val.is_scalar_type () && val.is_real_type ())
834 {
835 double new_dval = val.double_value ();
836
837 if (current_type != double_t || new_dval != dval)
838 {
839 dval = new_dval;
840 current_type = double_t;
841 return true;
842 }
843 }
844 else
845 error ("invalid value for double_radio property \"%s\"",
846 get_name ().c_str ());
847
848 return false;
849}
850
851bool
852array_property::validate (const octave_value& v)
853{
854 bool xok = false;
855
856 // FIXME -- should we always support []?
857 if (v.is_empty () && v.is_numeric_type ())
858 return true;
859
860 // check value type
861 if (type_constraints.size () > 0)
862 {
863 for (std::list<std::string>::const_iterator it = type_constraints.begin ();
864 ! xok && it != type_constraints.end (); ++it)
865 if ((*it) == v.class_name ())
866 xok = true;
867 }
868 else
869 xok = v.is_numeric_type ();
870
871 if (xok)
872 {
873 dim_vector vdims = v.dims ();
874 int vlen = vdims.length ();
875
876 xok = false;
877
878 // check value size
879 if (size_constraints.size () > 0)
880 for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
881 ! xok && it != size_constraints.end (); ++it)
882 {
883 dim_vector itdims = (*it);
884
885 if (itdims.length () == vlen)
886 {
887 xok = true;
888
889 for (int i = 0; xok && i < vlen; i++)
890 if (itdims(i) >= 0 && itdims(i) != vdims(i))
891 xok = false;
892 }
893 }
894 else
895 return true;
896 }
897
898 return xok;
899}
900
901bool
902array_property::is_equal (const octave_value& v) const
903{
904 if (data.type_name () == v.type_name ())
905 {
906 if (data.dims () == v.dims ())
907 {
908
909#define CHECK_ARRAY_EQUAL(T,F,A) \
910 { \
911 if (data.numel () == 1) \
912 return data.F ## scalar_value () == \
913 v.F ## scalar_value (); \
914 else \
915 { \
916 /* Keep copy of array_value to allow sparse/bool arrays */ \
917 /* that are converted, to not be deallocated early */ \
918 const A m1 = data.F ## array_value (); \
919 const T* d1 = m1.data (); \
920 const A m2 = v.F ## array_value (); \
921 const T* d2 = m2.data ();\
922 \
923 bool flag = true; \
924 \
925 for (int i = 0; flag && i < data.numel (); i++) \
926 if (d1[i] != d2[i]) \
927 flag = false; \
928 \
929 return flag; \
930 } \
931 }
932
933 if (data.is_double_type() || data.is_bool_type ())
934 CHECK_ARRAY_EQUAL (double, , NDArray)
935 else if (data.is_single_type ())
936 CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
937 else if (data.is_int8_type ())
938 CHECK_ARRAY_EQUAL (octave_int8, int8_, int8NDArray)
939 else if (data.is_int16_type ())
940 CHECK_ARRAY_EQUAL (octave_int16, int16_, int16NDArray)
941 else if (data.is_int32_type ())
942 CHECK_ARRAY_EQUAL (octave_int32, int32_, int32NDArray)
943 else if (data.is_int64_type ())
944 CHECK_ARRAY_EQUAL (octave_int64, int64_, int64NDArray)
945 else if (data.is_uint8_type ())
946 CHECK_ARRAY_EQUAL (octave_uint8, uint8_, uint8NDArray)
947 else if (data.is_uint16_type ())
948 CHECK_ARRAY_EQUAL (octave_uint16, uint16_, uint16NDArray)
949 else if (data.is_uint32_type ())
950 CHECK_ARRAY_EQUAL (octave_uint32, uint32_, uint32NDArray)
951 else if (data.is_uint64_type ())
952 CHECK_ARRAY_EQUAL (octave_uint64, uint64_, uint64NDArray)
953 }
954 }
955
956 return false;
957}
958
959void
960array_property::get_data_limits (void)
961{
962 xmin = xminp = octave_Inf;
963 xmax = -octave_Inf;
964
965 if (! data.is_empty ())
966 {
967 if (data.is_integer_type ())
968 {
969 if (data.is_int8_type ())
970 get_array_limits (data.int8_array_value (), xmin, xmax, xminp);
971 else if (data.is_uint8_type ())
972 get_array_limits (data.uint8_array_value (), xmin, xmax, xminp);
973 else if (data.is_int16_type ())
974 get_array_limits (data.int16_array_value (), xmin, xmax, xminp);
975 else if (data.is_uint16_type ())
976 get_array_limits (data.uint16_array_value (), xmin, xmax, xminp);
977 else if (data.is_int32_type ())
978 get_array_limits (data.int32_array_value (), xmin, xmax, xminp);
979 else if (data.is_uint32_type ())
980 get_array_limits (data.uint32_array_value (), xmin, xmax, xminp);
981 else if (data.is_int64_type ())
982 get_array_limits (data.int64_array_value (), xmin, xmax, xminp);
983 else if (data.is_uint64_type ())
984 get_array_limits (data.uint64_array_value (), xmin, xmax, xminp);
985 }
986 else
987 get_array_limits (data.array_value (), xmin, xmax, xminp);
988 }
989}
990
991bool
992handle_property::do_set (const octave_value& v)
993{
994 double dv = v.double_value ();
995
996 if (! error_state)
997 {
998 graphics_handle gh = gh_manager::lookup (dv);
999
1000 if (xisnan (gh.value ()) || gh.ok ())
1001 {
1002 if (current_val != gh)
1003 {
1004 current_val = gh;
1005 return true;
1006 }
1007 }
1008 else
1009 error ("set: invalid graphics handle (= %g) for property \"%s\"",
1010 dv, get_name ().c_str ());
1011 }
1012 else
1013 error ("set: invalid graphics handle for property \"%s\"",
1014 get_name ().c_str ());
1015
1016 return false;
1017}
1018
1019bool
1020callback_property::validate (const octave_value& v) const
1021{
1022 // case 1: function handle
1023 // case 2: cell array with first element being a function handle
1024 // case 3: string corresponding to known function name
1025 // case 4: evaluatable string
1026 // case 5: empty matrix
1027
1028 if (v.is_function_handle ())
1029 return true;
1030 else if (v.is_string ())
1031 // complete validation will be done at execution-time
1032 return true;
1033 else if (v.is_cell () && v.length () > 0
1034 && (v.rows() == 1 || v.columns () == 1)
1035 && v.cell_value ()(0).is_function_handle ())
1036 return true;
1037 else if (v.is_empty ())
1038 return true;
1039
1040 return false;
1041}
1042
1043void
1044callback_property::execute (const octave_value& data) const
1045{
1046 if (callback.is_defined () && ! callback.is_empty ())
1047 gh_manager::execute_callback (get_parent (), callback, data);
1048}
1049
1050// Used to cache dummy graphics objects from which dynamic
1051// properties can be cloned.
1052static std::map<caseless_str, graphics_object> dprop_obj_map;
1053
1054property
1055property::create (const std::string& name, const graphics_handle& h,
1056 const caseless_str& type, const octave_value_list& args)
1057{
1058 property retval;
1059
1060 if (type.compare ("string"))
1061 {
1062 std::string val = (args.length () > 0 ? args(0).string_value () : "");
1063
1064 if (! error_state)
1065 retval = property (new string_property (name, h, val));
1066 }
1067 else if (type.compare ("any"))
1068 {
1069 octave_value val =
1070 (args.length () > 0 ? args(0) : octave_value (Matrix ()));
1071
1072 retval = property (new any_property (name, h, val));
1073 }
1074 else if (type.compare ("radio"))
1075 {
1076 if (args.length () > 0)
1077 {
1078 std::string vals = args(0).string_value ();
1079
1080 if (! error_state)
1081 {
1082 retval = property (new radio_property (name, h, vals));
1083
1084 if (args.length () > 1)
1085 retval.set (args(1));
1086 }
1087 else
1088 error ("addproperty: invalid argument for radio property, expected a string value");
1089 }
1090 else
1091 error ("addproperty: missing possible values for radio property");
1092 }
1093 else if (type.compare ("double"))
1094 {
1095 double d = (args.length () > 0 ? args(0).double_value () : 0);
1096
1097 if (! error_state)
1098 retval = property (new double_property (name, h, d));
1099 }
1100 else if (type.compare ("handle"))
1101 {
1102 double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
1103
1104 if (! error_state)
1105 {
1106 graphics_handle gh (hh);
1107
1108 retval = property (new handle_property (name, h, gh));
1109 }
1110 }
1111 else if (type.compare ("boolean"))
1112 {
1113 retval = property (new bool_property (name, h, false));
1114
1115 if (args.length () > 0)
1116 retval.set (args(0));
1117 }
1118 else if (type.compare ("data"))
1119 {
1120 retval = property (new array_property (name, h, Matrix ()));
1121
1122 if (args.length () > 0)
1123 {
1124 retval.set (args(0));
1125
1126 // FIXME -- additional argument could define constraints,
1127 // but is this really useful?
1128 }
1129 }
1130 else if (type.compare ("color"))
1131 {
1132 color_values cv (0, 0, 0);
1133 radio_values rv;
1134
1135 if (args.length () > 1)
1136 rv = radio_values (args(1).string_value ());
1137
1138 if (! error_state)
1139 {
1140 retval = property (new color_property (name, h, cv, rv));
1141
1142 if (! error_state)
1143 {
1144 if (args.length () > 0 && ! args(0).is_empty ())
1145 retval.set (args(0));
1146 else
1147 retval.set (rv.default_value ());
1148 }
1149 }
1150 }
1151 else
1152 {
1153 caseless_str go_name, go_rest;
1154
1155 if (lookup_object_name (type, go_name, go_rest))
1156 {
1157 graphics_object go;
1158
1159 std::map<caseless_str, graphics_object>::const_iterator it =
1160 dprop_obj_map.find (go_name);
1161
1162 if (it == dprop_obj_map.end ())
1163 {
1164 base_graphics_object *bgo =
1165 make_graphics_object_from_type (go_name);
1166
1167 if (bgo)
1168 {
1169 go = graphics_object (bgo);
1170
1171 dprop_obj_map[go_name] = go;
1172 }
1173 }
1174 else
1175 go = it->second;
1176
1177 if (go.valid_object ())
1178 {
1179 property prop = go.get_properties ().get_property (go_rest);
1180
1181 if (! error_state)
1182 {
1183 retval = prop.clone ();
1184
1185 retval.set_parent (h);
1186 retval.set_name (name);
1187
1188 if (args.length () > 0)
1189 retval.set (args(0));
1190 }
1191 }
1192 else
1193 error ("addproperty: invalid object type (= %s)",
1194 go_name.c_str ());
1195 }
1196 else
1197 error ("addproperty: unsupported type for dynamic property (= %s)",
1198 type.c_str ());
1199 }
1200
1201 return retval;
1202}
1203
1204// ---------------------------------------------------------------------
1205
1206void
1207property_list::set (const caseless_str& name, const octave_value& val)
1208{
1209 size_t offset = 0;
1210
1211 size_t len = name.length ();
1212
1213 if (len > 4)
1214 {
1215 caseless_str pfx = name.substr (0, 4);
1216
1217 if (pfx.compare ("axes") || pfx.compare ("line")
1218 || pfx.compare ("text"))
1219 offset = 4;
1220 else if (len > 5)
1221 {
1222 pfx = name.substr (0, 5);
1223
1224 if (pfx.compare ("image") || pfx.compare ("patch"))
1225 offset = 5;
1226 else if (len > 6)
1227 {
1228 pfx = name.substr (0, 6);
1229
1230 if (pfx.compare ("figure"))
1231 offset = 6;
1232 else if (len > 7)
1233 {
1234 pfx = name.substr (0, 7);
1235
1236 if (pfx.compare ("surface") || pfx.compare ("hggroup"))
1237 offset = 7;
1238 }
1239 }
1240 }
1241
1242 if (offset > 0)
1243 {
1244 // FIXME -- should we validate property names and values here?
1245
1246 std::string pname = name.substr (offset);
1247
1248 std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1249 std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
1250
1251 bool has_property = false;
1252 if (pfx == "axes")
1253 has_property = axes::properties::has_core_property (pname);
1254 else if (pfx == "line")
1255 has_property = line::properties::has_core_property (pname);
1256 else if (pfx == "text")
1257 has_property = text::properties::has_core_property (pname);
1258 else if (pfx == "image")
1259 has_property = image::properties::has_core_property (pname);
1260 else if (pfx == "patch")
1261 has_property = patch::properties::has_core_property (pname);
1262 else if (pfx == "figure")
1263 has_property = figure::properties::has_core_property (pname);
1264 else if (pfx == "surface")
1265 has_property = surface::properties::has_core_property (pname);
1266 else if (pfx == "hggroup")
1267 has_property = hggroup::properties::has_core_property (pname);
1268
1269 if (has_property)
1270 {
1271 bool remove = false;
1272 if (val.is_string ())
1273 {
1274 caseless_str tval = val.string_value ();
1275
1276 remove = tval.compare ("remove");
1277 }
1278
1279 pval_map_type& pval_map = plist_map[pfx];
1280
1281 if (remove)
1282 {
1283 pval_map_iterator p = pval_map.find (pname);
1284
1285 if (p != pval_map.end ())
1286 pval_map.erase (p);
1287 }
1288 else
1289 pval_map[pname] = val;
1290 }
1291 else
1292 error ("invalid %s property `%s'", pfx.c_str (), pname.c_str ());
1293 }
1294 }
1295
1296 if (! error_state && offset == 0)
1297 error ("invalid default property specification");
1298}
1299
1300octave_value
1301property_list::lookup (const caseless_str& name) const
1302{
1303 octave_value retval;
1304
1305 size_t offset = 0;
1306
1307 size_t len = name.length ();
1308
1309 if (len > 4)
1310 {
1311 caseless_str pfx = name.substr (0, 4);
1312
1313 if (pfx.compare ("axes") || pfx.compare ("line")
1314 || pfx.compare ("text"))
1315 offset = 4;
1316 else if (len > 5)
1317 {
1318 pfx = name.substr (0, 5);
1319
1320 if (pfx.compare ("image") || pfx.compare ("patch"))
1321 offset = 5;
1322 else if (len > 6)
1323 {
1324 pfx = name.substr (0, 6);
1325
1326 if (pfx.compare ("figure"))
1327 offset = 6;
1328 else if (len > 7)
1329 {
1330 pfx = name.substr (0, 7);
1331
1332 if (pfx.compare ("surface") || pfx.compare ("hggroup"))
1333 offset = 7;
1334 }
1335 }
1336 }
1337
1338 if (offset > 0)
1339 {
1340 std::string pname = name.substr (offset);
1341
1342 std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1343 std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
1344
1345 plist_map_const_iterator p = find (pfx);
1346
1347 if (p != end ())
1348 {
1349 const pval_map_type& pval_map = p->second;
1350
1351 pval_map_const_iterator q = pval_map.find (pname);
1352
1353 if (q != pval_map.end ())
1354 retval = q->second;
1355 }
1356 }
1357 }
1358
1359 return retval;
1360}
1361
1362Octave_map
1363property_list::as_struct (const std::string& prefix_arg) const
1364{
1365 Octave_map m;
1366
1367 for (plist_map_const_iterator p = begin (); p != end (); p++)
1368 {
1369 std::string prefix = prefix_arg + p->first;
1370
1371 const pval_map_type pval_map = p->second;
1372
1373 for (pval_map_const_iterator q = pval_map.begin ();
1374 q != pval_map.end ();
1375 q++)
1376 m.assign (prefix + q->first, q->second);
1377 }
1378
1379 return m;
1380}
1381
1382graphics_handle::graphics_handle (const octave_value& a)
1383 : val (octave_NaN)
1384{
1385 if (a.is_empty ())
1386 /* do nothing */;
1387 else
1388 {
1389 double tval = a.double_value ();
1390
1391 if (! error_state)
1392 val = tval;
1393 else
1394 error ("invalid graphics handle");
1395 }
1396}
1397
1398// Set properties given as a cs-list of name, value pairs.
1399
1400void
1401graphics_object::set (const octave_value_list& args)
1402{
1403 int nargin = args.length ();
1404
1405 if (nargin == 0)
1406 rep->defaults ();
1407 else if (nargin % 2 == 0)
1408 {
1409 for (int i = 0; i < nargin; i += 2)
1410 {
1411 caseless_str name = args(i).string_value ();
1412
1413 if (! error_state)
1414 {
1415 octave_value val = args(i+1);
1416
1417 set_value_or_default (name, val);
1418
1419 if (error_state)
1420 break;
1421 }
1422 else
1423 error ("set: expecting argument %d to be a property name", i);
1424 }
1425 }
1426 else
1427 error ("set: invalid number of arguments");
1428}
1429
1430/*
1431%!# test set with name, value pairs
1432%!test
1433%! set(gcf, "visible", "off");
1434%! h = plot (1:10, 10:-1:1);
1435%! set (h, "linewidth", 10, "marker", "x");
1436%! assert (get (h, "linewidth"), 10);
1437%! assert (get (h, "marker"), "x");
1438*/
1439
1440// Set properties given in two cell arrays containing names and values.
1441void
1442graphics_object::set (const Array<std::string>& names,
1443 const Cell& values, octave_idx_type row)
1444{
1445 if (names.numel () != values.columns ())
1446 {
1447 error("set: number of names must match number of value columns (%d != %d)",
1448 names.numel (), values.columns ());
1449 }
1450
1451 octave_idx_type k = names.columns ();
1452
1453 for (octave_idx_type column = 0; column < k; column++)
1454 {
1455 caseless_str name = names(column);
1456 octave_value val = values(row, column);
1457
1458 set_value_or_default (name, val);
1459
1460 if (error_state)
1461 break;
1462 }
1463}
1464
1465/*
1466%!# test set with cell array arguments
1467%!test
1468%! set (gcf, "visible", "off");
1469%! h = plot (1:10, 10:-1:1);
1470%! set (h, {"linewidth", "marker"}, {10, "x"});
1471%! assert (get(h, "linewidth"), 10);
1472%! assert (get(h, "marker"), "x");
1473
1474%!# test set with multiple handles and cell array arguments
1475%!test
1476%! set (gcf, "visible", "off");
1477%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1478%! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
1479%! assert (get (h, "linewidth"), {10; 5});
1480%! assert (get (h, "marker"), {"x"; "o"});
1481%! set (h, {"linewidth", "marker"}, {10, "x"});
1482%! assert (get (h, "linewidth"), {10; 10});
1483%! assert (get (h, "marker"), {"x"; "x"});
1484
1485%!error <set: number of graphics handles must match number of value rows>
1486%! set (gcf, "visible", "off");
1487%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1488%! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
1489
1490%!error <set: number of names must match number of value columns>
1491%! set (gcf, "visible", "off");
1492%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1493%! set (h, {"linewidth"}, {10, "x"; 5, "o"});
1494*/
1495
1496// Set properties given in a struct array
1497void
1498graphics_object::set (const Octave_map& m)
1499{
1500 for (Octave_map::const_iterator p = m.begin ();
1501 p != m.end (); p++)
1502 {
1503 caseless_str name = m.key (p);
1504
1505 octave_value val = octave_value (m.contents (p).elem (m.numel () - 1));
1506
1507 set_value_or_default (name, val);
1508
1509 if (error_state)
1510 break;
1511 }
1512}
1513
1514/*
1515%!# test set with struct arguments
1516%!test
1517%! set (gcf, "visible", "off");
1518%! h = plot (1:10, 10:-1:1);
1519%! set (h, struct ("linewidth", 10, "marker", "x"));
1520%! assert (get (h, "linewidth"), 10);
1521%! assert (get (h, "marker"), "x");
1522%! h = plot (1:10, 10:-1:1, 1:10, 1:10);
1523%! set (h, struct ("linewidth", {5, 10}));
1524%! assert (get(h, "linewidth"), {10; 10});
1525*/
1526
1527// Set a property to a value or to its (factory) default value.
1528
1529void
1530graphics_object::set_value_or_default (const caseless_str& name,
1531 const octave_value& val)
1532{
1533 if (val.is_string ())
1534 {
1535 caseless_str tval = val.string_value ();
1536
1537 octave_value default_val;
1538
1539 if (tval.compare ("default"))
1540 {
1541 default_val = get_default (name);
1542
1543 if (error_state)
1544 return;
1545
1546 rep->set (name, default_val);
1547 }
1548 else if (tval.compare ("factory"))
1549 {
1550 default_val = get_factory_default (name);
1551
1552 if (error_state)
1553 return;
1554
1555 rep->set (name, default_val);
1556 }
1557 else
1558 rep->set (name, val);
1559 }
1560 else
1561 rep->set (name, val);
1562}
1563
1564/*
1565%!# test setting of default values
1566%!test
1567%! set (gcf, "visible", "off");
1568%! h = plot (1:10, 10:-1:1);
1569%! set (0, "defaultlinelinewidth", 20);
1570%! set (h, "linewidth", "default");
1571%! assert (get (h, "linewidth"), 20);
1572%! set (h, "linewidth", "factory");
1573%! assert (get (h, "linewidth"), 0.5);
1574*/
1575
1576static double
1577make_handle_fraction (void)
1578{
1579 static double maxrand = RAND_MAX + 2.0;
1580
1581 return (rand () + 1.0) / maxrand;
1582}
1583
1584graphics_handle
1585gh_manager::get_handle (const std::string& go_name)
1586{
1587 graphics_handle retval;
1588
1589 if (go_name == "figure")
1590 {
1591 // Figure handles are positive integers corresponding to the
1592 // figure number.
1593
1594 // We always want the lowest unused figure number.
1595
1596 retval = 1;
1597
1598 while (handle_map.find (retval) != handle_map.end ())
1599 retval++;
1600 }
1601 else
1602 {
1603 // Other graphics handles are negative integers plus some random
1604 // fractional part. To avoid running out of integers, we
1605 // recycle the integer part but tack on a new random part each
1606 // time.
1607
1608 free_list_iterator p = handle_free_list.begin ();
1609
1610 if (p != handle_free_list.end ())
1611 {
1612 retval = *p;
1613 handle_free_list.erase (p);
1614 }
1615 else
1616 {
1617 retval = graphics_handle (next_handle);
1618
1619 next_handle = ceil (next_handle) - 1.0 - make_handle_fraction ();
1620 }
1621 }
1622
1623 return retval;
1624}
1625
1626void
1627gh_manager::do_free (const graphics_handle& h)
1628{
1629 if (h.ok ())
1630 {
1631 if (h.value () != 0)
1632 {
1633 iterator p = handle_map.find (h);
1634
1635 if (p != handle_map.end ())
1636 {
1637 base_properties& bp = p->second.get_properties ();
1638
1639 bp.set_beingdeleted (true);
1640
1641 bp.delete_children ();
1642
1643 octave_value val = bp.get_deletefcn ();
1644
1645 bp.execute_deletefcn ();
1646
1647 // notify backend
1648 graphics_backend backend = p->second.get_backend ();
1649 if (backend)
1650 backend.object_destroyed (p->second);
1651
1652 // Note: this will be valid only for first explicitly
1653 // deleted object. All its children will then have an
1654 // unknown backend.
1655
1656 // Graphics handles for non-figure objects are negative
1657 // integers plus some random fractional part. To avoid
1658 // running out of integers, we recycle the integer part
1659 // but tack on a new random part each time.
1660
1661 handle_map.erase (p);
1662
1663 if (h.value () < 0)
1664 handle_free_list.insert (ceil (h.value ()) - make_handle_fraction ());
1665 }
1666 else
1667 error ("graphics_handle::free: invalid object %g", h.value ());
1668 }
1669 else
1670 error ("graphics_handle::free: can't delete root figure");
1671 }
1672}
1673
1674gh_manager *gh_manager::instance = 0;
1675
1676static void
1677xset (const graphics_handle& h, const caseless_str& name,
1678 const octave_value& val)
1679{
1680 graphics_object obj = gh_manager::get_object (h);
1681 obj.set (name, val);
1682}
1683
1684static void
1685xset (const graphics_handle& h, const octave_value_list& args)
1686{
1687 if (args.length () > 0)
1688 {
1689 graphics_object obj = gh_manager::get_object (h);
1690 obj.set (args);
1691 }
1692}
1693
1694
1695static octave_value
1696xget (const graphics_handle& h, const caseless_str& name)
1697{
1698 graphics_object obj = gh_manager::get_object (h);
1699 return obj.get (name);
1700}
1701
1702static graphics_handle
1703reparent (const octave_value& ov, const std::string& who,
1704 const std::string& property, const graphics_handle& new_parent,
1705 bool adopt = true)
1706{
1707 graphics_handle h = octave_NaN;
1708
1709 double val = ov.double_value ();
1710
1711 if (! error_state)
1712 {
1713 h = gh_manager::lookup (val);
1714
1715 if (h.ok ())
1716 {
1717 graphics_object obj = gh_manager::get_object (h);
1718
1719 graphics_handle parent_h = obj.get_parent ();
1720
1721 graphics_object parent_obj = gh_manager::get_object (parent_h);
1722
1723 parent_obj.remove_child (h);
1724
1725 if (adopt)
1726 obj.set ("parent", new_parent.value ());
1727 else
1728 obj.reparent (new_parent);
1729 }
1730 else
1731 error ("%s: invalid graphics handle (= %g) for %s",
1732 who.c_str (), val, property.c_str ());
1733 }
1734 else
1735 error ("%s: expecting %s to be a graphics handle",
1736 who.c_str (), property.c_str ());
1737
1738 return h;
1739}
1740
1741// This function is NOT equivalent to the scripting language function gcf.
1742graphics_handle
1743gcf (void)
1744{
1745 octave_value val = xget (0, "currentfigure");
1746
1747 return val.is_empty () ? octave_NaN : val.double_value ();
1748}
1749
1750// This function is NOT equivalent to the scripting language function gca.
1751graphics_handle
1752gca (void)
1753{
1754 octave_value val = xget (gcf (), "currentaxes");
1755
1756 return val.is_empty () ? octave_NaN : val.double_value ();
1757}
1758
1759static void
1760adopt (const graphics_handle& p, const graphics_handle& h)
1761{
1762 graphics_object parent_obj = gh_manager::get_object (p);
1763
1764 parent_obj.adopt (h);
1765}
1766
1767static bool
1768is_handle (const graphics_handle& h)
1769{
1770 return h.ok ();
1771}
1772
1773static bool
1774is_handle (double val)
1775{
1776 graphics_handle h = gh_manager::lookup (val);
1777
1778 return h.ok ();
1779}
1780
1781static octave_value
1782is_handle (const octave_value& val)
1783{
1784 octave_value retval = false;
1785
1786 if (val.is_real_scalar () && is_handle (val.double_value ()))
1787 retval = true;
1788 else if (val.is_real_matrix ())
1789 {
1790 if (val.is_string ())
1791 retval = boolNDArray (val.dims (), false);
1792 else
1793 {
1794 const NDArray handles = val.array_value ();
1795
1796 if (! error_state)
1797 {
1798 boolNDArray result (handles.dims ());
1799
1800 for (octave_idx_type i = 0; i < handles.numel (); i++)
1801 result.xelem (i) = is_handle (handles (i));
1802
1803 retval = result;
1804 }
1805 }
1806 }
1807
1808 return retval;
1809}
1810
1811static bool
1812is_figure (double val)
1813{
1814 graphics_object obj = gh_manager::get_object (val);
1815
1816 return obj && obj.isa ("figure");
1817}
1818
1819static void
1820xcreatefcn (const graphics_handle& h)
1821{
1822 graphics_object obj = gh_manager::get_object (h);
1823 obj.get_properties ().execute_createfcn ();
1824}
1825
1826// ---------------------------------------------------------------------
1827
1828void
1829base_graphics_backend::property_changed (const graphics_handle& h, int id)
1830{
1831 graphics_object go = gh_manager::get_object (h);
1832
1833 property_changed (go, id);
1834}
1835
1836void
1837base_graphics_backend::object_created (const graphics_handle& h)
1838{
1839 graphics_object go = gh_manager::get_object (h);
1840
1841 object_created (go);
1842}
1843
1844void
1845base_graphics_backend::object_destroyed (const graphics_handle& h)
1846{
1847 graphics_object go = gh_manager::get_object (h);
1848
1849 object_destroyed (go);
1850}
1851// ---------------------------------------------------------------------
1852
1853void
1854base_properties::set_from_list (base_graphics_object& obj,
1855 property_list& defaults)
1856{
1857 std::string go_name = graphics_object_name ();
1858
1859 property_list::plist_map_const_iterator p = defaults.find (go_name);
1860
1861 if (p != defaults.end ())
1862 {
1863 const property_list::pval_map_type pval_map = p->second;
1864
1865 for (property_list::pval_map_const_iterator q = pval_map.begin ();
1866 q != pval_map.end ();
1867 q++)
1868 {
1869 std::string pname = q->first;
1870
1871 obj.set (pname, q->second);
1872
1873 if (error_state)
1874 {
1875 error ("error setting default property %s", pname.c_str ());
1876 break;
1877 }
1878 }
1879 }
1880}
1881
1882octave_value
1883base_properties::get_dynamic (const caseless_str& name) const
1884{
1885 octave_value retval;
1886
1887 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1888
1889 if (it != all_props.end ())
1890 retval = it->second.get ();
1891 else
1892 error ("get: unknown property \"%s\"", name.c_str ());
1893
1894 return retval;
1895}
1896
1897octave_value
1898base_properties::get_dynamic (bool all) const
1899{
1900 Octave_map m;
1901
1902 for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.begin ();
1903 it != all_props.end (); ++it)
1904 if (all || ! it->second.is_hidden ())
1905 m.assign (it->second.get_name (), it->second.get ());
1906
1907 return m;
1908}
1909
1910std::set<std::string>
1911base_properties::dynamic_property_names (void) const
1912{
1913 return dynamic_properties;
1914}
1915
1916bool
1917base_properties::has_dynamic_property (const std::string& pname)
1918{
1919 const std::set<std::string>& dynprops = dynamic_property_names ();
1920
1921 return dynprops.find (pname) != dynprops.end ();
1922}
1923
1924void
1925base_properties::set_dynamic (const caseless_str& pname,
1926 const octave_value& val)
1927{
1928 std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
1929
1930 if (it != all_props.end ())
1931 it->second.set (val);
1932 else
1933 error ("set: unknown property \"%s\"", pname.c_str ());
1934
1935 if (! error_state)
1936 {
1937 dynamic_properties.insert (pname);
1938
1939 mark_modified ();
1940 }
1941}
1942
1943property
1944base_properties::get_property_dynamic (const caseless_str& name)
1945{
1946 std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
1947
1948 if (it == all_props.end ())
1949 {
1950 error ("get_property: unknown property \"%s\"", name.c_str ());
1951 return property ();
1952 }
1953 else
1954 return it->second;
1955}
1956
1957void
1958base_properties::remove_child (const graphics_handle& h)
1959{
1960 octave_idx_type k = -1;
1961 octave_idx_type n = children.numel ();
1962 for (octave_idx_type i = 0; i < n; i++)
1963 {
1964 if (h.value () == children(i))
1965 {
1966 k = i;
1967 break;
1968 }
1969 }
1970
1971 if (k >= 0)
1972 {
1973 Matrix new_kids (n-1, 1);
1974 octave_idx_type j = 0;
1975 for (octave_idx_type i = 0; i < n; i++)
1976 {
1977 if (i != k)
1978 new_kids(j++) = children(i);
1979 }
1980 children = new_kids;
1981 mark_modified ();
1982 }
1983}
1984
1985void
1986base_properties::set_parent (const octave_value& val)
1987{
1988 double tmp = val.double_value ();
1989
1990 graphics_handle new_parent = octave_NaN;
1991
1992 if (! error_state)
1993 {
1994 new_parent = gh_manager::lookup (tmp);
1995
1996 if (new_parent.ok ())
1997 {
1998 graphics_object parent_obj = gh_manager::get_object (get_parent ());
1999
2000 parent_obj.remove_child (__myhandle__);
2001
2002 parent = new_parent.as_octave_value ();
2003
2004 ::adopt (parent.handle_value (), __myhandle__);
2005 }
2006 else
2007 error ("set: invalid graphics handle (= %g) for parent", tmp);
2008 }
2009 else
2010 error ("set: expecting parent to be a graphics handle");
2011}
2012
2013void
2014base_properties::set_children (const octave_value& val)
2015{
2016 const Matrix new_kids = val.matrix_value ();
2017
2018 bool ok = true;
2019
2020 if (! error_state)
2021 {
2022 const Matrix visible_kids = get_children ();
2023
2024 if (visible_kids.numel () == new_kids.numel ())
2025 {
2026 Matrix t1 = visible_kids.sort ();
2027 Matrix t2 = new_kids.sort ();
2028
2029 if (t1 != t2)
2030 ok = false;
2031 }
2032 else
2033 ok = false;
2034
2035 if (! ok)
2036 error ("set: new children must be a permutation of existing children");
2037 }
2038 else
2039 {
2040 ok = false;
2041 error ("set: expecting children to be array of graphics handles");
2042 }
2043
2044 if (ok)
2045 children = new_kids.stack (get_hidden_children ());
2046}
2047
2048void
2049base_properties::mark_modified (void)
2050{
2051 __modified__ = "on";
2052 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2053 if (parent_obj)
2054 parent_obj.mark_modified ();
2055}
2056
2057void
2058base_properties::override_defaults (base_graphics_object& obj)
2059{
2060 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2061
2062 if (parent_obj)
2063 parent_obj.override_defaults (obj);
2064}
2065
2066void
2067base_properties::update_axis_limits (const std::string& axis_type) const
2068{
2069 graphics_object obj = gh_manager::get_object (__myhandle__);
2070
2071 if (obj)
2072 obj.update_axis_limits (axis_type);
2073}
2074
2075void
2076base_properties::delete_children (void)
2077{
2078 octave_idx_type n = children.numel ();
2079
2080 // A callback function might have already deleted the child,
2081 // so check before deleting
2082 for (octave_idx_type i = 0; i < n; i++)
2083 {
2084 graphics_object go = gh_manager::get_object (children(i));
2085
2086 if (go.valid_object ())
2087 gh_manager::free (children(i));
2088 }
2089}
2090
2091graphics_backend
2092base_properties::get_backend (void) const
2093{
2094 graphics_object go = gh_manager::get_object (get_parent ());
2095
2096 if (go)
2097 return go.get_backend ();
2098 else
2099 return graphics_backend ();
2100}
2101
2102void
2103base_properties::update_boundingbox (void)
2104{
2105 Matrix kids = get_children ();
2106
2107 for (int i = 0; i < kids.numel (); i++)
2108 {
2109 graphics_object go = gh_manager::get_object (kids(i));
2110
2111 if (go.valid_object ())
2112 go.get_properties ().update_boundingbox ();
2113 }
2114}
2115
2116void
2117base_properties::add_listener (const caseless_str& nm, const octave_value& v,
2118 listener_mode mode)
2119{
2120 property p = get_property (nm);
2121
2122 if (! error_state && p.ok ())
2123 p.add_listener (v, mode);
2124}
2125
2126void
2127base_properties::delete_listener (const caseless_str& nm,
2128 const octave_value& v, listener_mode mode)
2129{
2130 property p = get_property (nm);
2131
2132 if (! error_state && p.ok ())
2133 p.delete_listener (v, mode);
2134}
2135
2136// ---------------------------------------------------------------------
2137
2138class gnuplot_backend : public base_graphics_backend
2139{
2140public:
2141 gnuplot_backend (void)
2142 : base_graphics_backend ("gnuplot") { }
2143
2144 ~gnuplot_backend (void) { }
2145
2146 bool is_valid (void) const { return true; }
2147
2148 void object_destroyed (const graphics_object& go)
2149 {
2150 if (go.isa ("figure"))
2151 {
2152 const figure::properties& props =
2153 dynamic_cast<const figure::properties&> (go.get_properties ());
2154
2155 send_quit (props.get___plot_stream__ ());
2156 }
2157 }
2158
2159 void property_changed (const graphics_object& go, int id)
2160 {
2161 if (go.isa ("figure"))
2162 {
2163 graphics_object obj (go);
2164
2165 figure::properties& props =
2166 dynamic_cast<figure::properties&> (obj.get_properties ());
2167
2168 switch (id)
2169 {
2170 case base_properties::VISIBLE:
2171 if (! props.is_visible ())
2172 {
2173 send_quit (props.get___plot_stream__ ());
2174 props.set___plot_stream__ (Matrix ());
2175 props.set___enhanced__ (false);
2176 }
2177 break;
2178 }
2179 }
2180 }
2181
2182 void redraw_figure (const graphics_object& go) const
2183 {
2184 octave_value_list args;
2185 args(0) = go.get_handle ().as_octave_value ();
2186 feval ("gnuplot_drawnow", args);
2187 }
2188
2189 void print_figure (const graphics_object& go, const std::string& term,
2190 const std::string& file, bool mono,
2191 const std::string& debug_file) const
2192 {
2193 octave_value_list args;
2194 if (! debug_file.empty ())
2195 args(4) = debug_file;
2196 args(3) = mono;
2197 args(2) = file;
2198 args(1) = term;
2199 args(0) = go.get_handle ().as_octave_value ();
2200 feval ("gnuplot_drawnow", args);
2201 }
2202
2203 Matrix get_canvas_size (const graphics_handle&) const
2204 {
2205 Matrix sz (1, 2, 0.0);
2206 return sz;
2207 }
2208
2209 double get_screen_resolution (void) const
2210 { return 72.0; }
2211
2212 Matrix get_screen_size (void) const
2213 { return Matrix (1, 2, 0.0); }
2214
2215private:
2216 void send_quit (const octave_value& pstream) const
2217 {
2218 if (! pstream.is_empty ())
2219 {
2220 octave_value_list args;
2221 Matrix fids = pstream.matrix_value ();
2222
2223 if (! error_state)
2224 {
2225 args(1) = "\nquit;\n";
2226 args(0) = fids(0);
2227 feval ("fputs", args);
2228
2229 args.resize (1);
2230 feval ("fflush", args);
2231 feval ("pclose", args);
2232
2233 if (fids.numel () > 1)
2234 {
2235 args(0) = fids(1);
2236 feval ("pclose", args);
2237
2238 if (fids.numel () > 2)
2239 {
2240 args(0) = fids(2);
2241 feval ("waitpid", args);
2242 }
2243 }
2244 }
2245 }
2246 }
2247};
2248
2249graphics_backend
2250graphics_backend::default_backend (void)
2251{
2252 if (available_backends.size () == 0)
2253 register_backend (new gnuplot_backend ());
2254
2255 return available_backends["gnuplot"];
2256}
2257
2258std::map<std::string, graphics_backend> graphics_backend::available_backends;
2259
2260// ---------------------------------------------------------------------
2261
2262void
2263base_graphics_object::update_axis_limits (const std::string& axis_type)
2264{
2265 if (valid_object ())
2266 {
2267 graphics_object parent_obj = gh_manager::get_object (get_parent ());
2268
2269 if (parent_obj)
2270 parent_obj.update_axis_limits (axis_type);
2271 }
2272 else
2273 error ("base_graphics_object::update_axis_limits: invalid graphics object");
2274}
2275
2276void
2277base_graphics_object::remove_all_listeners (void)
2278{
2279 Octave_map m = get (true).map_value ();
2280
2281 for (Octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2282 {
2283 // FIXME -- there has to be a better way. I think we want to
2284 // ask whether it is OK to delete the listener for the given
2285 // property. How can we know in advance that it will be OK?
2286
2287 unwind_protect frame;
2288
2289 frame.protect_var (discard_error_messages);
2290 frame.protect_var (error_state);
2291
2292 discard_error_messages = true;
2293
2294 property p = get_properties ().get_property (pa->first);
2295
2296 if (! error_state && p.ok ())
2297 p.delete_listener ();
2298 }
2299}
2300
2301// ---------------------------------------------------------------------
2302
2303#include "graphics-props.cc"
2304
2305// ---------------------------------------------------------------------
2306
2307void
2308root_figure::properties::set_currentfigure (const octave_value& v)
2309{
2310 graphics_handle val (v);
2311
2312 if (error_state)
2313 return;
2314
2315 if (xisnan (val.value ()) || is_handle (val))
2316 {
2317 currentfigure = val;
2318
2319 gh_manager::push_figure (val);
2320 }
2321 else
2322 gripe_set_invalid ("currentfigure");
2323}
2324
2325void
2326root_figure::properties::set_callbackobject (const octave_value& v)
2327{
2328 graphics_handle val (v);
2329
2330 if (error_state)
2331 return;
2332
2333 if (xisnan (val.value ()))
2334 {
2335 if (! cbo_stack.empty ())
2336 {
2337 val = cbo_stack.front ();
2338
2339 cbo_stack.pop_front ();
2340 }
2341
2342 callbackobject = val;
2343 }
2344 else if (is_handle (val))
2345 {
2346 if (get_callbackobject ().ok ())
2347 cbo_stack.push_front (get_callbackobject ());
2348
2349 callbackobject = val;
2350 }
2351 else
2352 gripe_set_invalid ("callbackobject");
2353}
2354
2355void
2356root_figure::properties::update_units (void)
2357{
2358 caseless_str xunits = get_units ();
2359
2360 Matrix ss = default_screensize ();
2361
2362 double dpi = get_screenpixelsperinch ();
2363
2364 if (xunits.compare ("inches"))
2365 {
2366 ss(0) = 0;
2367 ss(1) = 0;
2368 ss(2) /= dpi;
2369 ss(3) /= dpi;
2370 }
2371 else if (xunits.compare ("centimeters"))
2372 {
2373 ss(0) = 0;
2374 ss(1) = 0;
2375 ss(2) *= 2.54 / dpi;
2376 ss(3) *= 2.54 / dpi;
2377 }
2378 else if (xunits.compare ("normalized"))
2379 {
2380 ss = Matrix (1, 4, 1.0);
2381 }
2382 else if (xunits.compare ("points"))
2383 {
2384 ss(0) = 0;
2385 ss(1) = 0;
2386 ss(2) *= 72 / dpi;
2387 ss(3) *= 72 / dpi;
2388 }
2389
2390 set_screensize (ss);
2391}
2392
2393void
2394root_figure::properties::remove_child (const graphics_handle& gh)
2395{
2396 gh_manager::pop_figure (gh);
2397
2398 graphics_handle cf = gh_manager::current_figure ();
2399
2400 xset (0, "currentfigure", cf.value ());
2401
2402 base_properties::remove_child (gh);
2403}
2404
2405property_list
2406root_figure::factory_properties = root_figure::init_factory_properties ();
2407
2408// ---------------------------------------------------------------------
2409
2410void
2411figure::properties::set_currentaxes (const octave_value& v)
2412{
2413 graphics_handle val (v);
2414
2415 if (error_state)
2416 return;
2417
2418 if (xisnan (val.value ()) || is_handle (val))
2419 currentaxes = val;
2420 else
2421 gripe_set_invalid ("currentaxes");
2422}
2423
2424void
2425figure::properties::remove_child (const graphics_handle& gh)
2426{
2427 base_properties::remove_child (gh);
2428
2429 if (gh == currentaxes.handle_value ())
2430 {
2431 graphics_handle new_currentaxes;
2432
2433 for (octave_idx_type i = 0; i < children.numel (); i++)
2434 {
2435 graphics_handle kid = children(i);
2436
2437 graphics_object go = gh_manager::get_object (kid);
2438
2439 if (go.isa ("axes"))
2440 {
2441 new_currentaxes = kid;
2442 break;
2443 }
2444 }
2445
2446 currentaxes = new_currentaxes;
2447 }
2448}
2449
2450void
2451figure::properties::set_visible (const octave_value& val)
2452{
2453 std::string s = val.string_value ();
2454
2455 if (! error_state)
2456 {
2457 if (s == "on")
2458 xset (0, "currentfigure", __myhandle__.value ());
2459
2460 visible = val;
2461 }
2462}
2463
2464Matrix
2465figure::properties::get_boundingbox (bool) const
2466{
2467 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2468 Matrix pos;
2469
2470 pos = convert_position (get_position ().matrix_value (), get_units (),
2471 "pixels", screen_size);
2472
2473 pos(0)--;
2474 pos(1)--;
2475 pos(1) = screen_size(1) - pos(1) - pos(3);
2476
2477 return pos;
2478}
2479
2480void
2481figure::properties::set_boundingbox (const Matrix& bb)
2482{
2483 Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
2484 Matrix pos = bb;
2485
2486 pos(1) = screen_size(1) - pos(1) - pos(3);
2487 pos(1)++;
2488 pos(0)++;
2489 pos = convert_position (pos, "pixels", get_units (), screen_size);
2490
2491 set_position (pos);
2492}
2493
2494void
2495figure::properties::set_position (const octave_value& v)
2496{
2497 if (! error_state)
2498 {
2499 Matrix old_bb, new_bb;
2500
2501 old_bb = get_boundingbox ();
2502 position = v;
2503 new_bb = get_boundingbox ();
2504
2505 if (old_bb != new_bb)
2506 {
2507 if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
2508 {
2509 execute_resizefcn ();
2510 update_boundingbox ();
2511 }
2512 }
2513
2514 mark_modified ();
2515 }
2516}
2517
2518std::string
2519figure::properties::get_title (void) const
2520{
2521 if (is_numbertitle ())
2522 {
2523 std::ostringstream os;
2524 std::string nm = get_name ();
2525
2526 os << "Figure " << __myhandle__.value ();
2527 if (! nm.empty ())
2528 os << ": " << get_name ();
2529
2530 return os.str ();
2531 }
2532 else
2533 return get_name ();
2534}
2535
2536octave_value
2537figure::get_default (const caseless_str& name) const
2538{
2539 octave_value retval = default_properties.lookup (name);
2540
2541 if (retval.is_undefined ())
2542 {
2543 graphics_handle parent = get_parent ();
2544 graphics_object parent_obj = gh_manager::get_object (parent);
2545
2546 retval = parent_obj.get_default (name);
2547 }
2548
2549 return retval;
2550}
2551
2552// ---------------------------------------------------------------------
2553
2554void
2555axes::properties::init (void)
2556{
2557 position.add_constraint (dim_vector (1, 4));
2558 position.add_constraint (dim_vector (0, 0));
2559 outerposition.add_constraint (dim_vector (1, 4));
2560 colororder.add_constraint (dim_vector (-1, 3));
2561 dataaspectratio.add_constraint (dim_vector (1, 3));
2562 plotboxaspectratio.add_constraint (dim_vector (1, 3));
2563 xlim.add_constraint (2);
2564 ylim.add_constraint (2);
2565 zlim.add_constraint (2);
2566 clim.add_constraint (2);
2567 alim.add_constraint (2);
2568 xtick.add_constraint (dim_vector (1, -1));
2569 ytick.add_constraint (dim_vector (1, -1));
2570 ztick.add_constraint (dim_vector (1, -1));
2571 Matrix vw (1, 2, 0);
2572 vw(1) = 90;
2573 view = vw;
2574 view.add_constraint (dim_vector (1, 2));
2575 cameraposition.add_constraint (dim_vector (1, 3));
2576 Matrix upv (1, 3, 0.0);
2577 upv(2) = 1.0;
2578 cameraupvector = upv;
2579 cameraupvector.add_constraint (dim_vector (1, 3));
2580 currentpoint.add_constraint (dim_vector (2, 3));
2581 ticklength.add_constraint (dim_vector (1, 2));
2582 tightinset.add_constraint (dim_vector (1, 4));
2583
2584 x_zlim.resize (1, 2);
2585
2586 sx = "linear";
2587 sy = "linear";
2588 sz = "linear";
2589
2590 calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
2591 calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
2592 calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
2593
2594 xset (xlabel.handle_value (), "handlevisibility", "off");
2595 xset (ylabel.handle_value (), "handlevisibility", "off");
2596 xset (zlabel.handle_value (), "handlevisibility", "off");
2597 xset (title.handle_value (), "handlevisibility", "off");
2598
2599 xset (xlabel.handle_value (), "horizontalalignment", "center");
2600 xset (ylabel.handle_value (), "horizontalalignment", "center");
2601 xset (zlabel.handle_value (), "horizontalalignment", "right");
2602 xset (title.handle_value (), "horizontalalignment", "center");
2603
2604 xset (xlabel.handle_value (), "verticalalignment", "cap");
2605 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2606 xset (title.handle_value (), "verticalalignment", "bottom");
2607
2608 xset (ylabel.handle_value (), "rotation", 90.0);
2609 xset (zlabel.handle_value (), "visible", "off");
2610
2611 xset (xlabel.handle_value (), "clipping", "off");
2612 xset (ylabel.handle_value (), "clipping", "off");
2613 xset (zlabel.handle_value (), "clipping", "off");
2614 xset (title.handle_value (), "clipping", "off");
2615
2616 adopt (xlabel.handle_value ());
2617 adopt (ylabel.handle_value ());
2618 adopt (zlabel.handle_value ());
2619 adopt (title.handle_value ());
2620}
2621
2622void
2623axes::properties::sync_positions (void)
2624{
2625#if 0
2626 // FIXME -- this should take font metrics into consideration,
2627 // and also the fact that the colorbox leaves the outerposition
2628 // alone but alters the position. For now just don't adjust the
2629 // positions relative to each other.
2630
2631 if (activepositionproperty.is ("outerposition"))
2632 {
2633 Matrix outpos = outerposition.get ().matrix_value ();
2634 Matrix defpos = default_axes_position ();
2635 Matrix pos(outpos);
2636 pos(0) = outpos(0) + defpos(0) * outpos(2);
2637 pos(1) = outpos(1) + defpos(1) * outpos(3);
2638 pos(2) = outpos(2) * defpos(2);
2639 pos(3) = outpos(3) * defpos(3);
2640 position = pos;
2641 }
2642 else
2643 {
2644 Matrix pos = position.get ().matrix_value ();
2645 pos(0) -= pos(2)*0.05;
2646 pos(1) -= pos(3)*0.05;
2647 pos(2) *= 1.1;
2648 pos(3) *= 1.1;
2649 outerposition = pos;
2650 }
2651#endif
2652
2653 update_transform ();
2654}
2655
2656void
2657axes::properties::set_text_child (handle_property& hp,
2658 const std::string& who,
2659 const octave_value& v)
2660{
2661 graphics_handle val;
2662
2663 if (v.is_string ())
2664 {
2665 val = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2666
2667 xset (val, "string", v);
2668 }
2669 else
2670 {
2671 graphics_object go = gh_manager::get_object (gh_manager::lookup (v));
2672
2673 if (go.isa ("text"))
2674 val = ::reparent (v, "set", who, __myhandle__, false);
2675 else
2676 {
2677 std::string cname = v.class_name ();
2678
2679 error ("set: expecting text graphics object or character string for %s property, found %s",
2680 who.c_str (), cname.c_str ());
2681 }
2682 }
2683
2684 if (! error_state)
2685 {
2686 xset (val, "handlevisibility", "off");
2687
2688 gh_manager::free (hp.handle_value ());
2689
2690 base_properties::remove_child (hp.handle_value ());
2691
2692 hp = val;
2693
2694 adopt (hp.handle_value ());
2695 }
2696}
2697
2698void
2699axes::properties::set_xlabel (const octave_value& v)
2700{
2701 set_text_child (xlabel, "xlabel", v);
2702}
2703
2704void
2705axes::properties::set_ylabel (const octave_value& v)
2706{
2707 set_text_child (ylabel, "ylabel", v);
2708}
2709
2710void
2711axes::properties::set_zlabel (const octave_value& v)
2712{
2713 set_text_child (zlabel, "zlabel", v);
2714}
2715
2716void
2717axes::properties::set_title (const octave_value& v)
2718{
2719 set_text_child (title, "title", v);
2720}
2721
2722void
2723axes::properties::set_defaults (base_graphics_object& obj,
2724 const std::string& mode)
2725{
2726 box = "on";
2727 key = "off";
2728 keybox = "off";
2729 keyreverse = "off";
2730 keypos = 1.0;
2731 colororder = default_colororder ();
2732 dataaspectratio = Matrix (1, 3, 1.0);
2733 dataaspectratiomode = "auto";
2734 layer = "bottom";
2735
2736 Matrix tlim (1, 2, 0.0);
2737 tlim(1) = 1;
2738 xlim = tlim;
2739 ylim = tlim;
2740 zlim = tlim;
2741
2742 Matrix cl (1, 2, 0);
2743 cl(1) = 1;
2744 clim = cl;
2745
2746 xlimmode = "auto";
2747 ylimmode = "auto";
2748 zlimmode = "auto";
2749 climmode = "auto";
2750
2751 xgrid = "off";
2752 ygrid = "off";
2753 zgrid = "off";
2754 xminorgrid = "off";
2755 yminorgrid = "off";
2756 zminorgrid = "off";
2757 xtick = Matrix ();
2758 ytick = Matrix ();
2759 ztick = Matrix ();
2760 xtickmode = "auto";
2761 ytickmode = "auto";
2762 ztickmode = "auto";
2763 xticklabel = "";
2764 yticklabel = "";
2765 zticklabel = "";
2766 xticklabelmode = "auto";
2767 yticklabelmode = "auto";
2768 zticklabelmode = "auto";
2769 color = color_values (1, 1, 1);
2770 xcolor = color_values ("black");
2771 ycolor = color_values ("black");
2772 zcolor = color_values ("black");
2773 xscale = "linear";
2774 yscale = "linear";
2775 zscale = "linear";
2776 xdir = "normal";
2777 ydir = "normal";
2778 zdir = "normal";
2779 yaxislocation = "left";
2780 xaxislocation = "bottom";
2781
2782 // Note: camera properties will be set through update_transform
2783 camerapositionmode = "auto";
2784 cameratargetmode = "auto";
2785 cameraupvectormode = "auto";
2786 cameraviewanglemode = "auto";
2787 plotboxaspectratio = Matrix (1, 3, 1.0);
2788 drawmode = "normal";
2789 gridlinestyle = ":";
2790 linestyleorder = "-";
2791 linewidth = 0.5;
2792 minorgridlinestyle = ":";
2793 // Note: plotboxaspectratio will be set through update_aspectratiors
2794 plotboxaspectratiomode = "auto";
2795 projection = "orthographic";
2796 tickdir = "in";
2797 tickdirmode = "auto";
2798 ticklength = default_axes_ticklength ();
2799 tightinset = Matrix (1, 4, 0.0);
2800
2801 sx = "linear";
2802 sy = "linear";
2803 sz = "linear";
2804
2805 Matrix tview (1, 2, 0.0);
2806 tview(1) = 90;
2807 view = tview;
2808
2809 visible = "on";
2810 nextplot = "replace";
2811
2812 if (mode != "replace")
2813 {
2814 fontangle = "normal";
2815 fontname = OCTAVE_DEFAULT_FONTNAME;
2816 fontsize = 12;
2817 fontunits = "points";
2818 fontweight = "normal";
2819
2820 Matrix touterposition (1, 4, 0.0);
2821 touterposition(2) = 1;
2822 touterposition(3) = 1;
2823 outerposition = touterposition;
2824
2825 position = default_axes_position ();
2826
2827 activepositionproperty = "outerposition";
2828 }
2829
2830 delete_children ();
2831
2832 children = Matrix ();
2833
2834 xlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2835 ylabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2836 zlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2837 title = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2838
2839 xset (xlabel.handle_value (), "handlevisibility", "off");
2840 xset (ylabel.handle_value (), "handlevisibility", "off");
2841 xset (zlabel.handle_value (), "handlevisibility", "off");
2842 xset (title.handle_value (), "handlevisibility", "off");
2843
2844 xset (xlabel.handle_value (), "horizontalalignment", "center");
2845 xset (ylabel.handle_value (), "horizontalalignment", "center");
2846 xset (zlabel.handle_value (), "horizontalalignment", "right");
2847 xset (title.handle_value (), "horizontalalignment", "center");
2848
2849 xset (xlabel.handle_value (), "verticalalignment", "cap");
2850 xset (ylabel.handle_value (), "verticalalignment", "bottom");
2851 xset (title.handle_value (), "verticalalignment", "bottom");
2852
2853 xset (ylabel.handle_value (), "rotation", 90.0);
2854 xset (zlabel.handle_value (), "visible", "off");
2855
2856 xset (xlabel.handle_value (), "clipping", "off");
2857 xset (ylabel.handle_value (), "clipping", "off");
2858 xset (zlabel.handle_value (), "clipping", "off");
2859 xset (title.handle_value (), "clipping", "off");
2860
2861 adopt (xlabel.handle_value ());
2862 adopt (ylabel.handle_value ());
2863 adopt (zlabel.handle_value ());
2864 adopt (title.handle_value ());
2865
2866 update_transform ();
2867
2868 override_defaults (obj);
2869}
2870
2871void
2872axes::properties::delete_text_child (handle_property& hp)
2873{
2874 graphics_handle h = hp.handle_value ();
2875
2876 if (h.ok ())
2877 {
2878 graphics_object go = gh_manager::get_object (h);
2879
2880 if (go.valid_object ())
2881 gh_manager::free (h);
2882
2883 base_properties::remove_child (h);
2884 }
2885
2886 // FIXME -- is it necessary to check whether the axes object is
2887 // being deleted now? I think this function is only called when an
2888 // individual child object is delete and not when the parent axes
2889 // object is deleted.
2890
2891 if (! is_beingdeleted ())
2892 {
2893 hp = gh_manager::make_graphics_handle ("text", __myhandle__, false);
2894
2895 xset (hp.handle_value (), "handlevisibility", "off");
2896
2897 adopt (hp.handle_value ());
2898 }
2899}
2900
2901void
2902axes::properties::remove_child (const graphics_handle& h)
2903{
2904 if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
2905 delete_text_child (xlabel);
2906 else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
2907 delete_text_child (ylabel);
2908 else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
2909 delete_text_child (zlabel);
2910 else if (title.handle_value ().ok () && h == title.handle_value ())
2911 delete_text_child (title);
2912 else
2913 base_properties::remove_child (h);
2914}
2915
2916Matrix
2917base_properties::get_children_internal (bool return_hidden) const
2918{
2919 Matrix retval = children;
2920
2921 graphics_object go = gh_manager::get_object (0);
2922
2923 root_figure::properties& props =
2924 dynamic_cast<root_figure::properties&> (go.get_properties ());
2925
2926 if (! props.is_showhiddenhandles ())
2927 {
2928 octave_idx_type k = 0;
2929
2930 for (octave_idx_type i = 0; i < children.numel (); i++)
2931 {
2932 graphics_handle kid = children (i);
2933
2934 if (gh_manager::is_handle_visible (kid))
2935 {
2936 if (! return_hidden)
2937 retval(k++) = children(i);
2938 }
2939 else
2940 {
2941 if (return_hidden)
2942 retval(k++) = children(i);
2943 }
2944 }
2945
2946 retval.resize (k, 1);
2947 }
2948
2949 return retval;
2950}
2951
2952Matrix
2953base_properties::get_children (void) const
2954{
2955 return get_children_internal (false);
2956}
2957
2958Matrix
2959base_properties::get_hidden_children (void) const
2960{
2961 return get_children_internal (true);
2962}
2963
2964inline Matrix
2965xform_matrix (void)
2966{
2967 Matrix m (4, 4, 0.0);
2968 for (int i = 0; i < 4; i++)
2969 m(i,i) = 1;
2970 return m;
2971}
2972
2973inline ColumnVector
2974xform_vector (void)
2975{
2976 ColumnVector v (4, 0.0);
2977 v(3) = 1;
2978 return v;
2979}
2980
2981inline ColumnVector
2982xform_vector (double x, double y, double z)
2983{
2984 ColumnVector v (4, 1.0);
2985 v(0) = x; v(1) = y; v(2) = z;
2986 return v;
2987}
2988
2989inline ColumnVector
2990transform (const Matrix& m, double x, double y, double z)
2991{
2992 return (m * xform_vector (x, y, z));
2993}
2994
2995inline Matrix
2996xform_scale (double x, double y, double z)
2997{
2998 Matrix m (4, 4, 0.0);
2999 m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
3000 return m;
3001}
3002
3003inline Matrix
3004xform_translate (double x, double y, double z)
3005{
3006 Matrix m = xform_matrix ();
3007 m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
3008 return m;
3009}
3010
3011inline void
3012scale (Matrix& m, double x, double y, double z)
3013{
3014 m = m * xform_scale (x, y, z);
3015}
3016
3017inline void
3018translate (Matrix& m, double x, double y, double z)
3019{
3020 m = m * xform_translate (x, y, z);
3021}
3022
3023inline void
3024xform (ColumnVector& v, const Matrix& m)
3025{
3026 v = m*v;
3027}
3028
3029inline void
3030scale (ColumnVector& v, double x, double y, double z)
3031{
3032 v(0) *= x;
3033 v(1) *= y;
3034 v(2) *= z;
3035}
3036
3037inline void
3038translate (ColumnVector& v, double x, double y, double z)
3039{
3040 v(0) += x;
3041 v(1) += y;
3042 v(2) += z;
3043}
3044
3045inline void
3046normalize (ColumnVector& v)
3047{
3048 double fact = 1.0/sqrt(v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
3049 scale (v, fact, fact, fact);
3050}
3051
3052inline double
3053dot (const ColumnVector& v1, const ColumnVector& v2)
3054{
3055 return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
3056}
3057
3058inline double
3059norm (const ColumnVector& v)
3060{
3061 return sqrt (dot (v, v));
3062}
3063
3064inline ColumnVector
3065cross (const ColumnVector& v1, const ColumnVector& v2)
3066{
3067 ColumnVector r = xform_vector ();
3068 r(0) = v1(1)*v2(2)-v1(2)*v2(1);
3069 r(1) = v1(2)*v2(0)-v1(0)*v2(2);
3070 r(2) = v1(0)*v2(1)-v1(1)*v2(0);
3071 return r;
3072}
3073
3074inline Matrix
3075unit_cube (void)
3076{
3077 static double data[32] = {
3078 0,0,0,1,
3079 1,0,0,1,
3080 0,1,0,1,
3081 0,0,1,1,
3082 1,1,0,1,
3083 1,0,1,1,
3084 0,1,1,1,
3085 1,1,1,1};
3086 Matrix m (4, 8);
3087 memcpy (m.fortran_vec (), data, sizeof(double)*32);
3088 return m;
3089}
3090
3091inline ColumnVector
3092cam2xform (const Array<double>& m)
3093{
3094 ColumnVector retval (4, 1.0);
3095 memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof(double)*3);
3096 return retval;
3097}
3098
3099inline RowVector
3100xform2cam (const ColumnVector& v)
3101{
3102 return v.extract_n (0, 3).transpose ();
3103}
3104
3105void
3106axes::properties::update_camera (void)
3107{
3108 double xd = (xdir_is ("normal") ? 1 : -1);
3109 double yd = (ydir_is ("normal") ? 1 : -1);
3110 double zd = (zdir_is ("normal") ? 1 : -1);
3111
3112 Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
3113 Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
3114 Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
3115
3116 double xo = xlimits(xd > 0 ? 0 : 1);
3117 double yo = ylimits(yd > 0 ? 0 : 1);
3118 double zo = zlimits(zd > 0 ? 0 : 1);
3119
3120 Matrix pb = get_plotboxaspectratio ().matrix_value ();
3121
3122 bool autocam = (camerapositionmode_is ("auto")
3123 && cameratargetmode_is ("auto")
3124 && cameraupvectormode_is ("auto")
3125 && cameraviewanglemode_is ("auto"));
3126 bool dowarp = (autocam && dataaspectratiomode_is("auto")
3127 && plotboxaspectratiomode_is ("auto"));
3128
3129 ColumnVector c_eye (xform_vector ());
3130 ColumnVector c_center (xform_vector ());
3131 ColumnVector c_upv (xform_vector ());
3132
3133 if (cameratargetmode_is ("auto"))
3134 {
3135 c_center(0) = (xlimits(0)+xlimits(1))/2;
3136 c_center(1) = (ylimits(0)+ylimits(1))/2;
3137 c_center(2) = (zlimits(0)+zlimits(1))/2;
3138
3139 cameratarget = xform2cam (c_center);
3140 }
3141 else
3142 c_center = cam2xform (get_cameratarget ().matrix_value ());
3143
3144 if (camerapositionmode_is ("auto"))
3145 {
3146 Matrix tview = get_view ().matrix_value ();
3147 double az = tview(0), el = tview(1);
3148 double d = 5*sqrt(pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
3149
3150 if (el == 90 || el == -90)
3151 c_eye(2) = d*signum(el);
3152 else
3153 {
3154 az *= M_PI/180.0;
3155 el *= M_PI/180.0;
3156 c_eye(0) = d*cos(el)*sin(az);
3157 c_eye(1) = -d*cos(el)*cos(az);
3158 c_eye(2) = d*sin(el);
3159 }
3160 c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
3161 c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
3162 c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
3163
3164 cameraposition = xform2cam (c_eye);
3165 }
3166 else
3167 c_eye = cam2xform (get_cameraposition ().matrix_value ());
3168
3169 if (cameraupvectormode_is ("auto"))
3170 {
3171 Matrix tview = get_view ().matrix_value ();
3172 double az = tview(0), el = tview(1);
3173
3174 if (el == 90 || el == -90)
3175 {
3176 c_upv(0) = -sin(az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
3177 c_upv(1) = cos(az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
3178 }
3179 else
3180 c_upv(2) = 1;
3181
3182 cameraupvector = xform2cam (c_upv);
3183 }
3184 else
3185 c_upv = cam2xform (get_cameraupvector ().matrix_value ());
3186
3187 Matrix x_view = xform_matrix ();
3188 Matrix x_projection = xform_matrix ();
3189 Matrix x_viewport = xform_matrix ();
3190 Matrix x_normrender = xform_matrix ();
3191 Matrix x_pre = xform_matrix ();
3192
3193 x_render = xform_matrix ();
3194 x_render_inv = xform_matrix ();
3195
3196 scale (x_pre, pb(0), pb(1), pb(2));
3197 translate (x_pre, -0.5, -0.5, -0.5);
3198 scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3199 zd/(zlimits(1)-zlimits(0)));
3200 translate (x_pre, -xo, -yo, -zo);
3201
3202 xform (c_eye, x_pre);
3203 xform (c_center, x_pre);
3204 scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
3205 pb(2)/(zlimits(1)-zlimits(0)));
3206 translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
3207
3208 ColumnVector F (c_center), f (F), UP (c_upv);
3209 normalize (f);
3210 normalize (UP);
3211
3212 if (std::abs (dot (f, UP)) > 1e-15)
3213 {
3214 double fa = 1/sqrt(1-f(2)*f(2));
3215 scale (UP, fa, fa, fa);
3216 }
3217
3218 ColumnVector s = cross (f, UP);
3219 ColumnVector u = cross (s, f);
3220
3221 scale (x_view, 1, 1, -1);
3222 Matrix l = xform_matrix ();
3223 l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
3224 l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
3225 l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
3226 x_view = x_view * l;
3227 translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
3228 scale (x_view, pb(0), pb(1), pb(2));
3229 translate (x_view, -0.5, -0.5, -0.5);
3230
3231 Matrix x_cube = x_view * unit_cube ();
3232 ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
3233 double xM = cmax(0)-cmin(0);
3234 double yM = cmax(1)-cmin(1);
3235
3236 Matrix bb = get_boundingbox (true);
3237
3238 double v_angle;
3239
3240 if (cameraviewanglemode_is ("auto"))
3241 {
3242 double af;
3243
3244 // FIXME -- was this really needed? When compared to Matlab, it
3245 // does not seem to be required. Need investigation with concrete
3246 // backend to see results visually.
3247 if (false && dowarp)
3248 af = 1.0 / (xM > yM ? xM : yM);
3249 else
3250 {
3251 if ((bb(2)/bb(3)) > (xM/yM))
3252 af = 1.0 / yM;
3253 else
3254 af = 1.0 / xM;
3255 }
3256 v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
3257
3258 cameraviewangle = v_angle;
3259 }
3260 else
3261 v_angle = get_cameraviewangle ();
3262
3263 double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
3264 scale (x_projection, pf, pf, 1);
3265
3266 if (dowarp)
3267 {
3268 xM *= pf;
3269 yM *= pf;
3270 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
3271 scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
3272 }
3273 else
3274 {
3275 double pix = 1;
3276 if (autocam)
3277 {
3278 if ((bb(2)/bb(3)) > (xM/yM))
3279 pix = bb(3);
3280 else
3281 pix = bb(2);
3282 }
3283 else
3284 pix = (bb(2) < bb(3) ? bb(2) : bb(3));
3285 translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
3286 scale (x_viewport, pix, -pix, 1);
3287 }
3288
3289 x_normrender = x_viewport * x_projection * x_view;
3290
3291 x_cube = x_normrender * unit_cube ();
3292 cmin = x_cube.row_min ();
3293 cmax = x_cube.row_max ();
3294 x_zlim.resize (1, 2);
3295 x_zlim(0) = cmin(2);
3296 x_zlim(1) = cmax(2);
3297
3298 x_render = x_normrender;
3299 scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3300 zd/(zlimits(1)-zlimits(0)));
3301 translate (x_render, -xo, -yo, -zo);
3302
3303 x_viewtransform = x_view;
3304 x_projectiontransform = x_projection;
3305 x_viewporttransform = x_viewport;
3306 x_normrendertransform = x_normrender;
3307 x_rendertransform = x_render;
3308
3309 x_render_inv = x_render.inverse ();
3310
3311 // Note: these matrices are a slight modified version of the regular
3312 // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
3313 // => x_gl_mat2)
3314 x_gl_mat1 = x_view;
3315 scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
3316 zd/(zlimits(1)-zlimits(0)));
3317 translate (x_gl_mat1, -xo, -yo, -zo);
3318 x_gl_mat2 = x_viewport * x_projection;
3319}
3320
3321void
3322axes::properties::update_aspectratios (void)
3323{
3324 Matrix xlimits = get_xlim ().matrix_value ();
3325 Matrix ylimits = get_ylim ().matrix_value ();
3326 Matrix zlimits = get_zlim ().matrix_value ();
3327
3328 double dx = (xlimits(1)-xlimits(0));
3329 double dy = (ylimits(1)-ylimits(0));
3330 double dz = (zlimits(1)-zlimits(0));
3331
3332 if (dataaspectratiomode_is ("auto"))
3333 {
3334 double dmin = xmin (xmin (dx, dy), dz);
3335 Matrix da (1, 3, 0.0);
3336
3337 da(0) = dx/dmin;
3338 da(1) = dy/dmin;
3339 da(2) = dz/dmin;
3340
3341 dataaspectratio = da;
3342 }
3343
3344 if (plotboxaspectratiomode_is ("auto"))
3345 {
3346 if (dataaspectratiomode_is ("auto"))
3347 plotboxaspectratio = Matrix (1, 3, 1.0);
3348 else
3349 {
3350 Matrix da = get_dataaspectratio ().matrix_value ();
3351 Matrix pba (1, 3, 0.0);
3352
3353 pba(0) = dx/da(0);
3354 pba(1) = dy/da(1);
3355 pba(2) = dz/da(2);
3356 }
3357 }
3358
3359 // FIXME -- if plotboxaspectratiomode is "manual", limits
3360 // and/or dataaspectratio might be adapted.
3361}
3362
3363// The INTERNAL flag defines whether position or outerposition is used.
3364
3365Matrix
3366axes::properties::get_boundingbox (bool internal) const
3367{
3368 graphics_object obj = gh_manager::get_object (get_parent ());
3369 Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
3370 Matrix pos = (internal ?
3371 get_position ().matrix_value ()
3372 : get_outerposition ().matrix_value ());
3373
3374
3375 pos = convert_position (pos, get_units (), "pixels",
3376 parent_bb.extract_n (0, 2, 1, 2));
3377 pos(0)--;
3378 pos(1)--;
3379 pos(1) = parent_bb(3) - pos(1) - pos(3);
3380
3381 return pos;
3382}
3383
3384ColumnVector
3385graphics_xform::xform_vector (double x, double y, double z)
3386{
3387 return ::xform_vector (x, y, z);
3388}
3389
3390Matrix
3391graphics_xform::xform_eye (void)
3392{
3393 return ::xform_matrix ();
3394}
3395
3396ColumnVector
3397graphics_xform::transform (double x, double y, double z,
3398 bool use_scale) const
3399{
3400 if (use_scale)
3401 {
3402 x = sx.scale (x);
3403 y = sy.scale (y);
3404 z = sz.scale (z);
3405 }
3406
3407 return ::transform (xform, x, y, z);
3408}
3409
3410ColumnVector
3411graphics_xform::untransform (double x, double y, double z,
3412 bool use_scale) const
3413{
3414 ColumnVector v = ::transform (xform_inv, x, y, z);
3415
3416 if (use_scale)
3417 {
3418 v(0) = sx.unscale (v(0));
3419 v(1) = sy.unscale (v(1));
3420 v(2) = sz.unscale (v(2));
3421 }
3422
3423 return v;
3424}
3425
3426octave_value
3427axes::get_default (const caseless_str& name) const
3428{
3429 octave_value retval = default_properties.lookup (name);
3430
3431 if (retval.is_undefined ())
3432 {
3433 graphics_handle parent = get_parent ();
3434 graphics_object parent_obj = gh_manager::get_object (parent);
3435
3436 retval = parent_obj.get_default (name);
3437 }
3438
3439 return retval;
3440}
3441
3442// FIXME -- remove.
3443// FIXME -- maybe this should go into array_property class?
3444/*
3445static void
3446check_limit_vals (double& min_val, double& max_val, double& min_pos,
3447 const array_property& data)
3448{
3449 double val = data.min_val ();
3450 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3451 min_val = val;
3452 val = data.max_val ();
3453 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3454 max_val = val;
3455 val = data.min_pos ();
3456 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3457 min_pos = val;
3458}
3459*/
3460
3461static void
3462check_limit_vals (double& min_val, double& max_val, double& min_pos,
3463 const octave_value& data)
3464{
3465 if (data.is_matrix_type ())
3466 {
3467 Matrix m = data.matrix_value ();
3468
3469 if (! error_state && m.numel () == 3)
3470 {
3471 double val;
3472
3473 val = m(0);
3474 if (! (xisinf (val) || xisnan (val)) && val < min_val)
3475 min_val = val;
3476
3477 val = m(1);
3478 if (! (xisinf (val) || xisnan (val)) && val > max_val)
3479 max_val = val;
3480
3481 val = m(2);
3482 if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
3483 min_pos = val;
3484 }
3485 }
3486}
3487
3488// magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
3489// integral.
3490
3491static void
3492magform (double x, double& a, int& b)
3493{
3494 if (x == 0)
3495 {
3496 a = 0;
3497 b = 0;
3498 }
3499 else
3500 {
3501 double l = std::log10 (std::abs (x));
3502 double r = std::fmod (l, 1.);
3503 a = std::pow (10.0, r);
3504 b = static_cast<int> (l-r);
3505 if (a < 1)
3506 {
3507 a *= 10;
3508 b -= 1;
3509 }
3510
3511 if (x < 0)
3512 a = -a;
3513 }
3514}
3515
3516// A translation from Tom Holoryd's python code at
3517// http://kurage.nimh.nih.gov/tomh/tics.py
3518// FIXME -- add log ticks
3519
3520double
3